Ejemplo n.º 1
0
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)
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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')
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
        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]))
Ejemplo n.º 8
0
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)
Ejemplo n.º 9
0
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()
Ejemplo n.º 10
0
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()
Ejemplo n.º 11
0
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))
Ejemplo n.º 16
0
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)
Ejemplo n.º 18
0
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
Ejemplo n.º 20
0
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)