def uninstall(ctx, ignore_failure=False, **kwargs): """Default uninstall workflow""" lifecycle.uninstall_node_instances( graph=ctx.graph_mode(), node_instances=set(ctx.node_instances), ignore_failure=ignore_failure)
def uninstall(ctx, ignore_failure=False, **kwargs): """Default uninstall workflow""" lifecycle.uninstall_node_instances( graph=ctx.graph_mode(), node_instances=set(ctx.node_instances), ignore_failure=ignore_failure)
def _uninstall_instances(ctx, graph, removed, related, ignore_failure, node_sequence): # cleanup tasks for task in graph.tasks_iter(): graph.remove_task(task) if removed: if node_sequence: subgraph_func = lifecycle.uninstall_node_instance_subgraph _process_node_instances(ctx=ctx, graph=graph, node_instances=removed, ignore_failure=ignore_failure, node_instance_subgraph_func=subgraph_func, node_sequence=node_sequence[::-1]) else: lifecycle.uninstall_node_instances(graph=graph, node_instances=removed, related_nodes=related, ignore_failure=ignore_failure) # clean up properties instance_ids = [ node_instance._node_instance.id for node_instance in removed ] _cleanup_instances(ctx, instance_ids)
def _uninstall_instances(ctx, graph, instance_ids, related_ids, ignore_failure): # cleanup tasks for task in graph.tasks_iter(): graph.remove_task(task) # hacks for remove removed = [] related = [] for node in ctx.nodes: for instance in node.instances: if instance.id in instance_ids: removed.append(instance) if instance.id in related_ids: related.append(instance) if removed: lifecycle.uninstall_node_instances( graph=graph, node_instances=removed, related_nodes=related, ignore_failure=ignore_failure) # clean up properties _cleanup_instances(ctx, instance_ids)
def rollback(ctx, type_names, node_ids, node_instance_ids, full_rollback=False, **kwargs): """Rollback workflow. Rollback workflow will look at each node state, decide if the node state is unresolved, and for those that are, execute the corresponding node operation that will get us back to a resolved node state, and then execute the unfinished workflow. Unresolved states are: creating, configuring, starting. Nodes that are in `creating` and `configuring` states will rollback to `uninitialized` state. Nodes that are in `starting` state will rollback to `configured` state. :param ctx : Cloudify context :param type_names: A list of type names. The operation will be executed only on node instances which are of these types or of types which (recursively) derive from them. An empty list means no filtering will take place and all type names are valid. :param node_ids: A list of node ids. The operation will be executed only on node instances which are instances of these nodes. An empty list means no filtering will take place and all nodes are valid. :param node_instance_ids: A list of node instance ids. The operation will be executed only on the node instances specified. An empty list means no filtering will take place and all node instances are valid. :param full_rollback Whether to perform uninstall after rollback to resolved state. """ # Find all node instances in unresolved state unresolved_node_instances = _find_all_unresolved_node_instances( ctx, node_ids, node_instance_ids, type_names) ctx.logger.debug("unresolved node instances: %s", [instance.id for instance in unresolved_node_instances]) intact_nodes = set(ctx.node_instances) - set(unresolved_node_instances) ctx.logger.debug("intact node instances: %s", [instance.id for instance in intact_nodes]) lifecycle.rollback_node_instances( graph=ctx.graph_mode(), node_instances=set(unresolved_node_instances), related_nodes=intact_nodes) ctx.refresh_node_instances() if full_rollback: ctx.logger.debug("Start uninstall after rollback.") lifecycle.uninstall_node_instances(graph=ctx.graph_mode(), node_instances=set( ctx.node_instances), ignore_failure=False, name_prefix='uninstall-a')
def generic_scale(_ctx, delta, modification, graph): try: _ctx.logger.info('Deployment modification started. ' '[modification_id={0}]'.format(modification.id)) if delta > 0: added_and_related = set(modification.added.node_instances) added = set(i for i in added_and_related if i.modification == 'added') related = added_and_related - added try: lifecycle.install_node_instances(graph=graph, node_instances=added, related_nodes=related) except: _ctx.logger.error('Scale out failed, scaling back in.') for task in graph.tasks_iter(): graph.remove_task(task) lifecycle.uninstall_node_instances(graph=graph, node_instances=added, related_nodes=related) raise else: removed_and_related = set(modification.removed.node_instances) removed = set(i for i in removed_and_related if i.modification == 'removed') related = removed_and_related - removed lifecycle.uninstall_node_instances(graph=graph, node_instances=removed, related_nodes=related) except: _ctx.logger.warn('Rolling back deployment modification. ' '[modification_id={0}]'.format(modification.id)) try: modification.rollback() except: _ctx.logger.warn('Deployment modification rollback failed. The ' 'deployment model is most likely in some ' 'corrupted state.' '[modification_id={0}]'.format(modification.id)) raise raise else: try: modification.finish() except: _ctx.logger.warn('Deployment modification finish failed. The ' 'deployment model is most likely in some ' 'corrupted state.' '[modification_id={0}]'.format(modification.id)) raise
def _uninstall_nodes(): if skip_uninstall: return lifecycle.execute_unlink_relationships( graph=graph, node_instances=set( instances_by_change['reduced_and_target_instances'][1]), modified_relationship_ids=modified_entity_ids['relationship']) lifecycle.uninstall_node_instances( graph=graph, node_instances=to_uninstall, ignore_failure=ignore_failure, related_nodes=set( instances_by_change['remove_target_instance_ids'][1]))
def _uninstall_instances(ctx, graph, removed, related, ignore_failure): # cleanup tasks for task in graph.tasks_iter(): graph.remove_task(task) if removed: lifecycle.uninstall_node_instances( graph=graph, node_instances=removed, related_nodes=related, ignore_failure=ignore_failure) # clean up properties instance_ids = [node_instance._node_instance.id for node_instance in removed] _cleanup_instances(ctx, instance_ids)
def _abort_started_deployment_modifications(ctx, ignore_failure): """Aborts any started deployment modifications running in this context. :param ctx: cloudify context :param ignore_failure: ignore operations failures in uninstall workflow """ started_modifications = ctx.deployment.list_started_modifications() graph = ctx.graph_mode() for modification in started_modifications: ctx.logger.info( 'Rolling back deployment modification. ' '[modification_id=%s]', modification.id) added_and_related = set(modification.added.node_instances) added = set(i for i in added_and_related if i.modification == 'added') related = added_and_related - added if added: lifecycle.uninstall_node_instances( graph=graph, node_instances=added, ignore_failure=ignore_failure, related_nodes=related, ) modification.rollback()
def _run_scale_settings(ctx, scale_settings, scalable_entity_properties, scale_transaction_field=None, scale_transaction_value=None, ignore_failure=False, instances_remove_ids=None): modification = ctx.deployment.start_modification(scale_settings) graph = ctx.graph_mode() try: ctx.logger.info('Deployment modification started. ' '[modification_id={0}]'.format(modification.id)) if len(set(modification.added.node_instances)): ctx.logger.info('Added: {}'.format(repr([ node_instance._node_instance.id for node_instance in modification.added.node_instances if node_instance.modification == 'added' ]))) added_and_related = set(modification.added.node_instances) added = set(i for i in added_and_related if i.modification == 'added') related = added_and_related - added try: for node_instance in added: properties_updates = scalable_entity_properties.get( node_instance._node_instance.node_id, {}) # save properties updates properties = {} if properties_updates: # pop one dict for runtime properties properties.update(properties_updates.pop()) # save transaction list if scale_transaction_field: # save original set of instances in scale up. if scale_transaction_value: properties.update({ scale_transaction_field: scale_transaction_value }) else: properties.update({ scale_transaction_field: modification.id }) # check properties to update if properties: ctx.logger.debug( "{}: Updating {} runtime properties by {}".format( node_instance._node_instance.node_id, node_instance._node_instance.id, repr(properties))) _update_runtime_properties( ctx, node_instance._node_instance.id, properties) lifecycle.install_node_instances( graph=graph, node_instances=added, related_nodes=related) except Exception as ex: ctx.logger.error('Scale out failed, scaling back in. {}' .format(repr(ex))) _uninstall_instances( ctx, graph, [ node_instance._node_instance.id for node_instance in added ], [ node_instance._node_instance.id for node_instance in related ], ignore_failure) raise ex if len(set(modification.removed.node_instances)): ctx.logger.info('Removed: {}'.format(repr([ node_instance._node_instance.id for node_instance in modification.removed.node_instances if node_instance.modification == 'removed' ]))) removed_and_related = set(modification.removed.node_instances) removed = set(i for i in removed_and_related if i.modification == 'removed') ctx.logger.info('Proposed: {}' .format(repr(instances_remove_ids))) if instances_remove_ids: for instance in removed: if instance._node_instance.id not in instances_remove_ids: raise Exception( "Instance {} not in proposed list {}.".format( repr(instance._node_instance.id), repr(instances_remove_ids) ) ) related = removed_and_related - removed lifecycle.uninstall_node_instances( graph=graph, node_instances=removed, ignore_failure=ignore_failure, related_nodes=related) except Exception as ex: ctx.logger.warn('Rolling back deployment modification. ' '[modification_id={0}]: {1}' .format(modification.id, repr(ex))) modification.rollback() raise ex else: modification.finish()
def scale_entity(ctx, scalable_entity_name, delta, scale_compute, ignore_failure=False, include_instances=None, exclude_instances=None, rollback_if_failed=True, **kwargs): """Scales in/out the subgraph of node_or_group_name. If a node name is passed, and `scale_compute` is set to false, the subgraph will consist of all the nodes that are contained in the node and the node itself. If a node name is passed, and `scale_compute` is set to true, the subgraph will consist of all nodes that are contained in the compute node that contains the node and the compute node itself. If a group name or a node that is not contained in a compute node, is passed, this property is ignored. `delta` is used to specify the scale factor. For `delta > 0`: If current number of instances is `N`, scale out to `N + delta`. For `delta < 0`: If current number of instances is `N`, scale in to `N - |delta|`. :param ctx: cloudify context :param scalable_entity_name: the node or group name to scale :param delta: scale in/out factor :param scale_compute: should scale apply on compute node containing the specified node :param ignore_failure: ignore operations failures in uninstall workflow :param include_instances: Instances to include when scaling down :param exclude_instances: Instances to exclude when scaling down :param rollback_if_failed: when False, no rollback will be triggered. """ include_instances = include_instances or [] exclude_instances = exclude_instances or [] if isinstance(include_instances, basestring): include_instances = [include_instances] if isinstance(exclude_instances, basestring): exclude_instances = [exclude_instances] include_instances = [str(inst) for inst in include_instances] exclude_instances = [str(inst) for inst in exclude_instances] if isinstance(delta, basestring): try: delta = int(delta) except ValueError: raise ValueError( 'The delta parameter must be a number. Got: {0}'.format(delta)) if delta == 0: ctx.logger.info('delta parameter is 0, so no scaling will take place.') return if delta > 0 and (include_instances or exclude_instances): raise ValueError( 'Instances cannot be included or excluded when scaling up.') scaling_group = ctx.deployment.scaling_groups.get(scalable_entity_name) if scaling_group: groups_members = get_groups_with_members(ctx) # Available instances for checking inclusions/exclusions needs to # include all groups and their members available_instances = set(chain.from_iterable( groups_members.values())).union(groups_members) validate_inclusions_and_exclusions( include_instances, exclude_instances, available_instances=available_instances, delta=delta, scale_compute=scale_compute, groups_members=groups_members, ) curr_num_instances = scaling_group['properties']['current_instances'] planned_num_instances = curr_num_instances + delta scale_id = scalable_entity_name else: node = ctx.get_node(scalable_entity_name) if not node: raise ValueError("No scalable entity named {0} was found".format( scalable_entity_name)) validate_inclusions_and_exclusions( include_instances, exclude_instances, available_instances=[instance.id for instance in node.instances], delta=delta, scale_compute=scale_compute, ) host_node = node.host_node scaled_node = host_node if (scale_compute and host_node) else node curr_num_instances = scaled_node.number_of_instances planned_num_instances = curr_num_instances + delta scale_id = scaled_node.id if planned_num_instances < 0: raise ValueError('Provided delta: {0} is illegal. current number of ' 'instances of entity {1} is {2}'.format( delta, scalable_entity_name, curr_num_instances)) modification = ctx.deployment.start_modification({ scale_id: { 'instances': planned_num_instances, 'removed_ids_exclude_hint': exclude_instances, 'removed_ids_include_hint': include_instances, # While these parameters are now exposed, this comment is being # kept as it provides useful insight into the hints # These following parameters are not exposed at the moment, # but should be used to control which node instances get scaled in # (when scaling in). # They are mentioned here, because currently, the modification API # is not very documented. # Special care should be taken because if `scale_compute == True` # (which is the default), then these ids should be the compute node # instance ids which are not necessarily instances of the node # specified by `scalable_entity_name`. # Node instances denoted by these instance ids should be *kept* if # possible. # 'removed_ids_exclude_hint': [], # Node instances denoted by these instance ids should be *removed* # if possible. # 'removed_ids_include_hint': [] } }) # refresh node instances so that workflow_ctx.get_node_instance() can # return the new node instance that were just created ctx.refresh_node_instances() graph = ctx.graph_mode() try: ctx.logger.info('Deployment modification started. ' '[modification_id={0}]'.format(modification.id)) if delta > 0: added_and_related = set(modification.added.node_instances) added = set(i for i in added_and_related if i.modification == 'added') related = added_and_related - added try: lifecycle.install_node_instances(graph=graph, node_instances=added, related_nodes=related) except Exception: if not rollback_if_failed: ctx.logger.error('Scale out failed.') raise ctx.logger.error('Scale out failed, scaling back in.') for task in graph.tasks_iter(): graph.remove_task(task) lifecycle.uninstall_node_instances( graph=graph, node_instances=added, ignore_failure=ignore_failure, related_nodes=related) raise else: removed_and_related = set(modification.removed.node_instances) removed = set(i for i in removed_and_related if i.modification == 'removed') related = removed_and_related - removed lifecycle.uninstall_node_instances(graph=graph, node_instances=removed, ignore_failure=ignore_failure, related_nodes=related) except Exception: if not rollback_if_failed: raise ctx.logger.warn('Rolling back deployment modification. ' '[modification_id={0}]'.format(modification.id)) try: modification.rollback() except Exception: ctx.logger.warn('Deployment modification rollback failed. The ' 'deployment model is most likely in some corrupted' ' state.' '[modification_id={0}]'.format(modification.id)) raise raise else: try: modification.finish() except Exception: ctx.logger.warn('Deployment modification finish failed. The ' 'deployment model is most likely in some corrupted' ' state.' '[modification_id={0}]'.format(modification.id)) raise
def scale(ctx, node_id, delta, scale_compute, **kwargs): """Scales in/out the subgraph of node_id. If `scale_compute` is set to false, the subgraph will consist of all the nodes that are contained in `node_id` and `node_id` itself. If `scale_compute` is set to true, the subgraph will consist of all nodes that are contained in the compute node that contains `node_id` and the compute node itself. If `node_id` is not contained in a compute node and is not a compute node, this property is ignored. `delta` is used to specify the scale factor. For `delta > 0`: If current number of instances is `N`, scale out to `N + delta`. For `delta < 0`: If current number of instances is `N`, scale in to `N - |delta|`. :param ctx: cloudify context :param node_id: the node_id to scale :param delta: scale in/out factor :param scale_compute: should scale apply on compute node containing 'node_id' """ graph = ctx.graph_mode() node = ctx.get_node(node_id) if not node: raise ValueError("Node {0} doesn't exist".format(node_id)) if delta == 0: ctx.logger.info('delta parameter is 0, so no scaling will take place.') return host_node = node.host_node scaled_node = host_node if (scale_compute and host_node) else node curr_num_instances = scaled_node.number_of_instances planned_num_instances = curr_num_instances + delta if planned_num_instances < 0: raise ValueError('Provided delta: {0} is illegal. current number of' 'instances of node {1} is {2}' .format(delta, node_id, curr_num_instances)) modification = ctx.deployment.start_modification({ scaled_node.id: { 'instances': planned_num_instances # These following parameters are not exposed at the moment, # but should be used to control which node instances get scaled in # (when scaling in). # They are mentioned here, because currently, the modification API # is not very documented. # Special care should be taken because if `scale_compute == True` # (which is the default), then these ids should be the compute node # instance ids which are not necessarily instances of the node # specified by `node_id`. # Node instances denoted by these instance ids should be *kept* if # possible. # 'removed_ids_exclude_hint': [], # Node instances denoted by these instance ids should be *removed* # if possible. # 'removed_ids_include_hint': [] } }) try: ctx.logger.info('Deployment modification started. ' '[modification_id={0}]'.format(modification.id)) if delta > 0: added_and_related = set(modification.added.node_instances) added = set(i for i in added_and_related if i.modification == 'added') related = added_and_related - added try: lifecycle.install_node_instances( graph=graph, node_instances=added, intact_nodes=related) except: ctx.logger.error('Scale out failed, scaling back in.') for task in graph.tasks_iter(): graph.remove_task(task) lifecycle.uninstall_node_instances( graph=graph, node_instances=added, intact_nodes=related) raise else: removed_and_related = set(modification.removed.node_instances) removed = set(i for i in removed_and_related if i.modification == 'removed') related = removed_and_related - removed lifecycle.uninstall_node_instances( graph=graph, node_instances=removed, intact_nodes=related) except: ctx.logger.warn('Rolling back deployment modification. ' '[modification_id={0}]'.format(modification.id)) try: modification.rollback() except: ctx.logger.warn('Deployment modification rollback failed. The ' 'deployment model is most likely in some corrupted' ' state.' '[modification_id={0}]'.format(modification.id)) raise raise else: try: modification.finish() except: ctx.logger.warn('Deployment modification finish failed. The ' 'deployment model is most likely in some corrupted' ' state.' '[modification_id={0}]'.format(modification.id)) raise
def uninstall(ctx, **kwargs): """Default uninstall workflow""" lifecycle.uninstall_node_instances( graph=ctx.graph_mode(), node_instances=set(ctx.node_instances))
def scale(ctx, node_id, delta, scale_compute, **kwargs): """Scales in/out the subgraph of node_id. If `scale_compute` is set to false, the subgraph will consist of all the nodes that are contained in `node_id` and `node_id` itself. If `scale_compute` is set to true, the subgraph will consist of all nodes that are contained in the compute node that contains `node_id` and the compute node itself. If `node_id` is not contained in a compute node and is not a compute node, this property is ignored. `delta` is used to specify the scale factor. For `delta > 0`: If current number of instances is `N`, scale out to `N + delta`. For `delta < 0`: If current number of instances is `N`, scale in to `N - |delta|`. :param ctx: cloudify context :param node_id: the node_id to scale :param delta: scale in/out factor :param scale_compute: should scale apply on compute node containing 'node_id' """ graph = ctx.graph_mode() node = ctx.get_node(node_id) if not node: raise ValueError("Node {0} doesn't exist".format(node_id)) if delta == 0: ctx.logger.info('delta parameter is 0, so no scaling will take place.') return host_node = node.host_node scaled_node = host_node if (scale_compute and host_node) else node curr_num_instances = scaled_node.number_of_instances planned_num_instances = curr_num_instances + delta if planned_num_instances < 0: raise ValueError('Provided delta: {0} is illegal. current number of' 'instances of node {1} is {2}'.format( delta, node_id, curr_num_instances)) modification = ctx.deployment.start_modification({ scaled_node.id: { 'instances': planned_num_instances # These following parameters are not exposed at the moment, # but should be used to control which node instances get scaled in # (when scaling in). # They are mentioned here, because currently, the modification API # is not very documented. # Special care should be taken because if `scale_compute == True` # (which is the default), then these ids should be the compute node # instance ids which are not necessarily instances of the node # specified by `node_id`. # Node instances denoted by these instance ids should be *kept* if # possible. # 'removed_ids_exclude_hint': [], # Node instances denoted by these instance ids should be *removed* # if possible. # 'removed_ids_include_hint': [] } }) try: ctx.logger.info('Deployment modification started. ' '[modification_id={0}]'.format(modification.id)) if delta > 0: added_and_related = set(modification.added.node_instances) added = set(i for i in added_and_related if i.modification == 'added') related = added_and_related - added try: lifecycle.install_node_instances(graph=graph, node_instances=added, intact_nodes=related) except: ctx.logger.error('Scale out failed, scaling back in.') for task in graph.tasks_iter(): graph.remove_task(task) lifecycle.uninstall_node_instances(graph=graph, node_instances=added, intact_nodes=related) raise else: removed_and_related = set(modification.removed.node_instances) removed = set(i for i in removed_and_related if i.modification == 'removed') related = removed_and_related - removed lifecycle.uninstall_node_instances(graph=graph, node_instances=removed, intact_nodes=related) except: ctx.logger.warn('Rolling back deployment modification. ' '[modification_id={0}]'.format(modification.id)) try: modification.rollback() except: ctx.logger.warn('Deployment modification rollback failed. The ' 'deployment model is most likely in some corrupted' ' state.' '[modification_id={0}]'.format(modification.id)) raise raise else: try: modification.finish() except: ctx.logger.warn('Deployment modification finish failed. The ' 'deployment model is most likely in some corrupted' ' state.' '[modification_id={0}]'.format(modification.id)) raise
def uninstall(ctx, **kwargs): """Default uninstall workflow""" lifecycle.uninstall_node_instances(graph=ctx.graph_mode(), node_instances=set(ctx.node_instances))
def scale_entity(ctx, scalable_entity_name, delta, scale_compute, ignore_failure=False, **kwargs): """Scales in/out the subgraph of node_or_group_name. If a node name is passed, and `scale_compute` is set to false, the subgraph will consist of all the nodes that are contained in the node and the node itself. If a node name is passed, and `scale_compute` is set to true, the subgraph will consist of all nodes that are contained in the compute node that contains the node and the compute node itself. If a group name or a node that is not contained in a compute node, is passed, this property is ignored. `delta` is used to specify the scale factor. For `delta > 0`: If current number of instances is `N`, scale out to `N + delta`. For `delta < 0`: If current number of instances is `N`, scale in to `N - |delta|`. :param ctx: cloudify context :param scalable_entity_name: the node or group name to scale :param delta: scale in/out factor :param scale_compute: should scale apply on compute node containing the specified node :param ignore_failure: ignore operations failures in uninstall workflow """ if isinstance(delta, basestring): try: delta = int(delta) except ValueError: raise ValueError( 'The delta parameter must be a number. Got: {0}'.format(delta)) if delta == 0: ctx.logger.info('delta parameter is 0, so no scaling will take place.') return scaling_group = ctx.deployment.scaling_groups.get(scalable_entity_name) if scaling_group: curr_num_instances = scaling_group['properties']['current_instances'] planned_num_instances = curr_num_instances + delta scale_id = scalable_entity_name else: node = ctx.get_node(scalable_entity_name) if not node: raise ValueError("No scalable entity named {0} was found".format( scalable_entity_name)) host_node = node.host_node scaled_node = host_node if (scale_compute and host_node) else node curr_num_instances = scaled_node.number_of_instances planned_num_instances = curr_num_instances + delta scale_id = scaled_node.id if planned_num_instances < 0: raise ValueError('Provided delta: {0} is illegal. current number of ' 'instances of entity {1} is {2}'.format( delta, scalable_entity_name, curr_num_instances)) modification = ctx.deployment.start_modification({ scale_id: { 'instances': planned_num_instances # These following parameters are not exposed at the moment, # but should be used to control which node instances get scaled in # (when scaling in). # They are mentioned here, because currently, the modification API # is not very documented. # Special care should be taken because if `scale_compute == True` # (which is the default), then these ids should be the compute node # instance ids which are not necessarily instances of the node # specified by `scalable_entity_name`. # Node instances denoted by these instance ids should be *kept* if # possible. # 'removed_ids_exclude_hint': [], # Node instances denoted by these instance ids should be *removed* # if possible. # 'removed_ids_include_hint': [] } }) graph = ctx.graph_mode() try: ctx.logger.info('Deployment modification started. ' '[modification_id={0}]'.format(modification.id)) if delta > 0: added_and_related = set(modification.added.node_instances) added = set(i for i in added_and_related if i.modification == 'added') related = added_and_related - added try: lifecycle.install_node_instances(graph=graph, node_instances=added, related_nodes=related) except Exception: ctx.logger.error('Scale out failed, scaling back in.') for task in graph.tasks_iter(): graph.remove_task(task) lifecycle.uninstall_node_instances( graph=graph, node_instances=added, ignore_failure=ignore_failure, related_nodes=related) raise else: removed_and_related = set(modification.removed.node_instances) removed = set(i for i in removed_and_related if i.modification == 'removed') related = removed_and_related - removed lifecycle.uninstall_node_instances(graph=graph, node_instances=removed, ignore_failure=ignore_failure, related_nodes=related) except Exception: ctx.logger.warn('Rolling back deployment modification. ' '[modification_id={0}]'.format(modification.id)) try: modification.rollback() except Exception: ctx.logger.warn('Deployment modification rollback failed. The ' 'deployment model is most likely in some corrupted' ' state.' '[modification_id={0}]'.format(modification.id)) raise raise else: try: modification.finish() except Exception: ctx.logger.warn('Deployment modification finish failed. The ' 'deployment model is most likely in some corrupted' ' state.' '[modification_id={0}]'.format(modification.id)) raise
def update(ctx, update_id, added_instance_ids, added_target_instances_ids, removed_instance_ids, remove_target_instance_ids, modified_entity_ids, extended_instance_ids, extend_target_instance_ids, reduced_instance_ids, reduce_target_instance_ids, skip_install, skip_uninstall, ignore_failure=False): instances_by_change = { 'added_instances': (added_instance_ids, []), 'added_target_instances_ids': (added_target_instances_ids, []), 'removed_instances': (removed_instance_ids, []), 'remove_target_instance_ids': (remove_target_instance_ids, []), 'extended_and_target_instances': (extended_instance_ids + extend_target_instance_ids, []), 'reduced_and_target_instances': (reduced_instance_ids + reduce_target_instance_ids, []), } for instance in ctx.node_instances: instance_holders = \ [instance_holder for _, (changed_ids, instance_holder) in instances_by_change.iteritems() if instance.id in changed_ids] for instance_holder in instance_holders: instance_holder.append(instance) if not skip_install: graph = ctx.graph_mode() # Adding nodes or node instances should be based on modified instances lifecycle.install_node_instances( graph=graph, node_instances=set(instances_by_change['added_instances'][1]), related_nodes=set(instances_by_change['added_target_instances_ids'] [1])) # This one as well. lifecycle.execute_establish_relationships( graph=ctx.graph_mode(), node_instances=set( instances_by_change['extended_and_target_instances'][1]), modified_relationship_ids=modified_entity_ids['relationship'] ) if not skip_uninstall: graph = ctx.graph_mode() lifecycle.execute_unlink_relationships( graph=graph, node_instances=set( instances_by_change['reduced_and_target_instances'][1]), modified_relationship_ids=modified_entity_ids['relationship'] ) lifecycle.uninstall_node_instances( graph=graph, node_instances=set(instances_by_change['removed_instances'][1]), ignore_failure=ignore_failure, related_nodes=set( instances_by_change['remove_target_instance_ids'][1]) ) # Finalize the commit (i.e. remove relationships or nodes) client = get_rest_client() client.deployment_updates.finalize_commit(update_id)
def rollback(ctx, type_names, node_ids, node_instance_ids, full_rollback, **kwargs): """Rollback workflow. Rollback workflow will look at each node state, decide if the node state is unresolved, and for those that are, execute the corresponding node operation that will get us back to a resolved node state, and then execute the unfinished workflow. :param ctx : Cloudify context :param type_names: A list of type names. The operation will be executed only on node instances which are of these types or of types which (recursively) derive from them. An empty list means no filtering will take place and all type names are valid. :param node_ids: A list of node ids. The operation will be executed only on node instances which are instances of these nodes. An empty list means no filtering will take place and all nodes are valid. :param node_instance_ids: A list of node instance ids. The operation will be executed only on the node instances specified. An empty list means no filtering will take place and all node instances are valid. :param full_rollback Whether to rollback to resolved state or full uninstall. """ # Find all node instances in unresolved state unresolved_node_instances = _find_all_unresolved_node_instances( ctx, node_ids, node_instance_ids, type_names) for instance in unresolved_node_instances: ctx.logger.debug("unresolved node instance:{}".format(instance.id)) intact_nodes = set(ctx.node_instances) - set(unresolved_node_instances) for instance in intact_nodes: ctx.logger.debug("intact node instance:{}".format(instance.id)) if full_rollback: # The first uninstall is because a bug in the uninstall workflow: # if node in `configured` state the uninstall try's to run stop so # until then we need rollback always to `deleted`. ctx.logger.debug("Start full rollback") uninstall_node_instances(ctx.graph_mode(), node_instances=set(unresolved_node_instances), related_nodes=intact_nodes, ignore_failure=True, name_prefix='uninstall-a' ) # For older than 5.1 Cloudify versions. ctx.refresh_node_instances() uninstall_node_instances( graph=ctx.graph_mode(), node_instances=ctx.node_instances, ignore_failure=False, name_prefix='uninstall-b') # Build rollback graph and execute else: utilitieslifecycle.rollback_node_instances( graph=ctx.graph_mode(), node_instances=set(unresolved_node_instances), related_nodes=intact_nodes )
def scale_entity(ctx, scalable_entity_name, delta, scale_compute, ignore_failure=False, **kwargs): """Scales in/out the subgraph of node_or_group_name. If a node name is passed, and `scale_compute` is set to false, the subgraph will consist of all the nodes that are contained in the node and the node itself. If a node name is passed, and `scale_compute` is set to true, the subgraph will consist of all nodes that are contained in the compute node that contains the node and the compute node itself. If a group name or a node that is not contained in a compute node, is passed, this property is ignored. `delta` is used to specify the scale factor. For `delta > 0`: If current number of instances is `N`, scale out to `N + delta`. For `delta < 0`: If current number of instances is `N`, scale in to `N - |delta|`. :param ctx: cloudify context :param scalable_entity_name: the node or group name to scale :param delta: scale in/out factor :param scale_compute: should scale apply on compute node containing the specified node :param ignore_failure: ignore operations failures in uninstall workflow """ if isinstance(delta, basestring): try: delta = int(delta) except ValueError: raise ValueError('The delta parameter must be a number. Got: {0}' .format(delta)) if delta == 0: ctx.logger.info('delta parameter is 0, so no scaling will take place.') return scaling_group = ctx.deployment.scaling_groups.get(scalable_entity_name) if scaling_group: curr_num_instances = scaling_group['properties']['current_instances'] planned_num_instances = curr_num_instances + delta scale_id = scalable_entity_name else: node = ctx.get_node(scalable_entity_name) if not node: raise ValueError("No scalable entity named {0} was found".format( scalable_entity_name)) host_node = node.host_node scaled_node = host_node if (scale_compute and host_node) else node curr_num_instances = scaled_node.number_of_instances planned_num_instances = curr_num_instances + delta scale_id = scaled_node.id if planned_num_instances < 0: raise ValueError('Provided delta: {0} is illegal. current number of ' 'instances of entity {1} is {2}' .format(delta, scalable_entity_name, curr_num_instances)) modification = ctx.deployment.start_modification({ scale_id: { 'instances': planned_num_instances # These following parameters are not exposed at the moment, # but should be used to control which node instances get scaled in # (when scaling in). # They are mentioned here, because currently, the modification API # is not very documented. # Special care should be taken because if `scale_compute == True` # (which is the default), then these ids should be the compute node # instance ids which are not necessarily instances of the node # specified by `scalable_entity_name`. # Node instances denoted by these instance ids should be *kept* if # possible. # 'removed_ids_exclude_hint': [], # Node instances denoted by these instance ids should be *removed* # if possible. # 'removed_ids_include_hint': [] } }) graph = ctx.graph_mode() try: ctx.logger.info('Deployment modification started. ' '[modification_id={0}]'.format(modification.id)) if delta > 0: added_and_related = set(modification.added.node_instances) added = set(i for i in added_and_related if i.modification == 'added') related = added_and_related - added try: lifecycle.install_node_instances( graph=graph, node_instances=added, related_nodes=related) except Exception: ctx.logger.error('Scale out failed, scaling back in.') for task in graph.tasks_iter(): graph.remove_task(task) lifecycle.uninstall_node_instances( graph=graph, node_instances=added, ignore_failure=ignore_failure, related_nodes=related) raise else: removed_and_related = set(modification.removed.node_instances) removed = set(i for i in removed_and_related if i.modification == 'removed') related = removed_and_related - removed lifecycle.uninstall_node_instances( graph=graph, node_instances=removed, ignore_failure=ignore_failure, related_nodes=related) except Exception: ctx.logger.warn('Rolling back deployment modification. ' '[modification_id={0}]'.format(modification.id)) try: modification.rollback() except Exception: ctx.logger.warn('Deployment modification rollback failed. The ' 'deployment model is most likely in some corrupted' ' state.' '[modification_id={0}]'.format(modification.id)) raise raise else: try: modification.finish() except Exception: ctx.logger.warn('Deployment modification finish failed. The ' 'deployment model is most likely in some corrupted' ' state.' '[modification_id={0}]'.format(modification.id)) raise
def update(ctx, update_id, added_instance_ids, added_target_instances_ids, removed_instance_ids, remove_target_instance_ids, modified_entity_ids, extended_instance_ids, extend_target_instance_ids, reduced_instance_ids, reduce_target_instance_ids, skip_install, skip_uninstall, ignore_failure=False): instances_by_change = { 'added_instances': (added_instance_ids, []), 'added_target_instances_ids': (added_target_instances_ids, []), 'removed_instances': (removed_instance_ids, []), 'remove_target_instance_ids': (remove_target_instance_ids, []), 'extended_and_target_instances': (extended_instance_ids + extend_target_instance_ids, []), 'reduced_and_target_instances': (reduced_instance_ids + reduce_target_instance_ids, []), } for instance in ctx.node_instances: instance_holders = \ [instance_holder for _, (changed_ids, instance_holder) in instances_by_change.iteritems() if instance.id in changed_ids] for instance_holder in instance_holders: instance_holder.append(instance) if not skip_install: graph = ctx.graph_mode() # Adding nodes or node instances should be based on modified instances lifecycle.install_node_instances( graph=graph, node_instances=set(instances_by_change['added_instances'][1]), related_nodes=set(instances_by_change['added_target_instances_ids'] [1])) # This one as well. lifecycle.execute_establish_relationships( graph=ctx.graph_mode(), node_instances=set( instances_by_change['extended_and_target_instances'][1]), modified_relationship_ids=modified_entity_ids['relationship'] ) if not skip_uninstall: graph = ctx.graph_mode() lifecycle.execute_unlink_relationships( graph=graph, node_instances=set( instances_by_change['reduced_and_target_instances'][1]), modified_relationship_ids=modified_entity_ids['relationship'] ) lifecycle.uninstall_node_instances( graph=graph, node_instances=set(instances_by_change['removed_instances'][1]), ignore_failure=ignore_failure, related_nodes=set( instances_by_change['remove_target_instance_ids'][1]) ) # Finalize the commit (i.e. remove relationships or nodes) client = get_rest_client() client.deployment_updates.finalize_commit(update_id)