示例#1
0
def rack_get(request):
    """
    List all racks within specified range.
    """
    range_serializer = RackRangeSerializer(data=request.data)

    if not range_serializer.is_valid():
        return JsonResponse(
            {
                "failure_message":
                Status.INVALID_INPUT.value +
                parse_serializer_errors(range_serializer.errors),
                "errors":
                str(range_serializer.errors),
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    (change_plan, failure_response) = get_change_plan(
        request.query_params.get("change_plan"))
    if failure_response:
        return failure_response
    racks = Rack.objects.filter(
        datacenter=range_serializer.get_datacenter(),
        rack_num__range=range_serializer.get_number_range(),  # inclusive range
        row_letter__range=range_serializer.get_row_range(),
    )

    return get_rack_detailed_response(racks, change_plan)
def change_plan_detail(request, id):
    """
    Retrieve a single change plan.
    """
    (change_plan, failure_response) = get_change_plan(id)
    if failure_response:
        return failure_response
    if request.user != change_plan.owner:
        return JsonResponse(
            {
                "failure_message": Status.ERROR.value
                + "You do not have access to this change plan.",
                "errors": "User "
                + request.user.username
                + " does not own change plan with id="
                + str(id),
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    change_plan_serializer = GetChangePlanSerializer(change_plan)
    modifications = get_modifications_in_cp(change_plan)
    return JsonResponse(
        {"change_plan": change_plan_serializer.data, "modifications": modifications,},
        status=HTTPStatus.OK,
    )
def decommission_asset(request):
    """
    Decommission a live asset
    """
    data = JSONParser().parse(request)
    (change_plan, failure_response) = get_change_plan(
        request.query_params.get("change_plan"))
    if failure_response:
        return failure_response
    if "id" not in data:
        return JsonResponse(
            {
                "failure_message":
                Status.DECOMMISSION_ERROR.value +
                GenericFailure.INTERNAL.value,
                "errors":
                "Must include 'id' when decommissioning an asset",
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    asset_id = data["id"]
    success_response, failure_response = decommission_asset_parameterized(
        asset_id, request.query_params, request.user, change_plan)
    if failure_response:
        return failure_response
    if not change_plan:
        blades = Asset.objects.filter(chassis=asset_id)
        for blade in blades:
            blade.delete()
        Asset.objects.get(id=asset_id).delete()
    return success_response
def change_plan_resolve_conflict(request, id):
    """
    Resolve a merge conflict
    """
    (change_plan, failure_response) = get_change_plan(id)
    if failure_response:
        return failure_response
    failure_response = get_cp_already_executed_response(change_plan)
    if failure_response:
        return failure_response
    data = JSONParser().parse(request)
    if "asset_cp" not in data or "override_live" not in data:
        return JsonResponse(
            {
                "failure_message": Status.ERROR.value + GenericFailure.INTERNAL.value,
                "errors": "Must include both 'asset_cp' and `override_live` when resolving a merge conflict",
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    asset_cp = data["asset_cp"]
    override_live = data["override_live"]
    try:
        asset_cp = AssetCP.objects.get(id=asset_cp)
    except ObjectDoesNotExist:
        return JsonResponse(
            {
                "failure_message": Status.ERROR.value
                + "AssetCP"
                + GenericFailure.DOES_NOT_EXIST.value,
                "errors": "No existing Asset CP with id=" + str(asset_cp),
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    try:
        if override_live:
            asset_cp.is_conflict = False
            asset_cp.save()
        else:
            asset_cp.delete()
    except Exception as error:
        return JsonResponse(
            {
                "failure_message": Status.DELETE_ERROR.value
                + "Change Plan"
                + GenericFailure.ON_DELETE.value,
                "errors": str(error),
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    return JsonResponse(
        {"success_message": Status.SUCCESS.value + "Sucessfully resolved conflict "},
        status=HTTPStatus.OK,
    )
def change_plan_modify(request):
    """
    Modify single existing change plan
    """
    data = JSONParser().parse(request)
    if "id" not in data:
        return JsonResponse(
            {
                "failure_message": Status.MODIFY_ERROR.value
                + GenericFailure.INTERNAL.value,
                "errors": "Must include 'id' when modifying a change plan",
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    id = data["id"]
    (existing_change_plan, failure_response) = get_change_plan(id)
    if failure_response:
        return failure_response
    failure_response = get_cp_already_executed_response(existing_change_plan)
    if failure_response:
        return failure_response
    for field in data.keys():
        value = data[field]
        setattr(existing_change_plan, field, value)
    try:
        existing_change_plan.save()
        return JsonResponse(
            {
                "success_message": Status.SUCCESS.value
                + "Change Plan "
                + str(existing_change_plan.name)
                + " modified",
                "related_id": str(existing_change_plan.id),
            },
            status=HTTPStatus.OK,
        )
    except Exception as error:
        return JsonResponse(
            {
                "failure_message": Status.MODIFY_ERROR.value
                + parse_save_validation_error(error, "Asset"),
                "errors": str(error),
            },
            status=HTTPStatus.BAD_REQUEST,
        )
def asset_detail(request, id):
    """
    Retrieve a single asset.
    """
    (change_plan, failure_response) = get_change_plan(
        request.query_params.get("change_plan")
    )
    if failure_response:
        return failure_response
    try:
        if change_plan:
            assets, assets_cp = get_assets_for_cp(
                change_plan.id, show_decommissioned=True
            )
            # if id of live asset is given on a change plan, just return the corresponding related assetCP
            if assets_cp.filter(related_asset=id).exists():
                asset = assets_cp.get(related_asset=id)
                serializer = RecursiveAssetCPSerializer(asset)
            elif assets_cp.filter(id=id).exists():
                asset = assets_cp.get(id=id)
                serializer = RecursiveAssetCPSerializer(asset)
            else:
                asset = assets.get(id=id)
                serializer = RecursiveAssetSerializer(asset)
        else:
            asset = Asset.objects.get(id=id)
            serializer = RecursiveAssetSerializer(asset)
    except Asset.DoesNotExist:
        try:
            decommissioned_asset = DecommissionedAsset.objects.get(live_id=id)
        except DecommissionedAsset.DoesNotExist:
            return JsonResponse(
                {
                    "failure_message": Status.ERROR.value
                    + "Asset"
                    + GenericFailure.DOES_NOT_EXIST.value,
                    "errors": "No existing asset with id=" + str(id),
                },
                status=HTTPStatus.BAD_REQUEST,
            )
        else:
            serializer = GetDecommissionedAssetSerializer(decommissioned_asset)

    return JsonResponse(serializer.data, status=HTTPStatus.OK)
def decommissioned_asset_page_count(request):
    """
    Return total number of pages according to page size, which must be
    specified as query parameter.
    """
    (change_plan, failure_response) = get_change_plan(
        request.query_params.get("change_plan"))
    if failure_response:
        return failure_response
    if change_plan:
        return get_page_count_response_for_cp(request,
                                              change_plan,
                                              decommissioned=True)
    else:
        return get_page_count_response(
            DecommissionedAsset,
            request.query_params,
            data_for_filters=request.data,
        )
def offline_storage_asset_page_count(request):
    """
    Return total number of pages according to page size, which must be
    specified as query parameter.
    """
    (change_plan, failure_response) = get_change_plan(
        request.query_params.get("change_plan")
    )
    if failure_response:
        return failure_response
    if change_plan:
        return get_page_count_response_for_cp(request, change_plan, stored=True)
    else:
        stored_assets = assets_offline_queryset()
        return get_page_count_response(
            Asset,
            request.query_params,
            data_for_filters=request.data,
            premade_object_query=stored_assets,
        )
def change_plan_delete(request):
    """
    Delete a single existing change plan
    """
    data = JSONParser().parse(request)
    if "id" not in data:
        return JsonResponse(
            {
                "failure_message": Status.DELETE_ERROR.value
                + GenericFailure.INTERNAL.value,
                "errors": "Must include 'id' when deleting a Change Plan",
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    (existing_change_plan, failure_response) = get_change_plan(data["id"])
    if failure_response:
        return failure_response
    failure_response = get_cp_already_executed_response(existing_change_plan)
    if failure_response:
        return failure_response
    try:
        existing_change_plan.delete()
    except Exception as error:
        return JsonResponse(
            {
                "failure_message": Status.DELETE_ERROR.value
                + "Change Plan"
                + GenericFailure.ON_DELETE.value,
                "errors": str(error),
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    return JsonResponse(
        {
            "success_message": Status.SUCCESS.value
            + "Change Plan "
            + str(existing_change_plan.name)
            + " deleted"
        },
        status=HTTPStatus.OK,
    )
示例#10
0
def rack_get_all(request):
    """
    List all racks
    """

    datacenter_id = request.query_params.get("datacenter")
    if not datacenter_id:
        return JsonResponse(
            {
                "failure_message":
                Status.ERROR.value + "Must specifiy datacenter",
                "errors": "Query parameter 'datacenter' is required",
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    (change_plan, failure_response) = get_change_plan(
        request.query_params.get("change_plan"))
    if failure_response:
        return failure_response
    racks = Rack.objects.filter(datacenter=datacenter_id)
    return get_rack_detailed_response(racks, change_plan)
示例#11
0
def offline_storage_asset_many(request):
    """
    List many assets in offline storage. If page is not specified as a query parameter, all
    assets are returned. If page is specified as a query parameter, page
    size must also be specified, and a page of assets will be returned.
    """
    (change_plan, failure_response) = get_change_plan(
        request.query_params.get("change_plan")
    )
    if failure_response:
        return failure_response
    if change_plan:
        return get_many_assets_response_for_cp(request, change_plan, stored=True)
    else:
        stored_assets = assets_offline_queryset()
        return get_many_response(
            Asset,
            RecursiveAssetSerializer,
            "assets",
            request,
            premade_object_query=stored_assets,
        )
def decommissioned_asset_many(request):
    """
    List many decommissioned assets. If page is not specified as a query parameter, all
    assets are returned. If page is specified as a query parameter, page
    size must also be specified, and a page of assets will be returned.
    """
    (change_plan, failure_response) = get_change_plan(
        request.query_params.get("change_plan"))
    if failure_response:
        return failure_response
    if change_plan:
        return get_many_assets_response_for_cp(
            request,
            change_plan,
            decommissioned=True,
        )
    else:
        return get_many_response(
            DecommissionedAsset,
            GetDecommissionedAssetSerializer,
            "assets",
            request,
        )
def change_plan_remove_asset(request, id):
    """
    Remove a single assetCP from a change plan
    """
    (change_plan, failure_response) = get_change_plan(id)
    if failure_response:
        return failure_response
    failure_response = get_cp_already_executed_response(change_plan)
    if failure_response:
        return failure_response
    data = JSONParser().parse(request)
    if "asset_cp" not in data:
        return JsonResponse(
            {
                "failure_message": Status.ERROR.value + GenericFailure.INTERNAL.value,
                "errors": "Must include 'asset_cp' when removing an asset from change plan",
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    asset_cp = data["asset_cp"]

    try:
        asset_cp_object = AssetCP.objects.get(id=asset_cp)
    except ObjectDoesNotExist:
        return JsonResponse(
            {
                "failure_message": Status.ERROR.value
                + "AssetCP"
                + GenericFailure.DOES_NOT_EXIST.value,
                "errors": "No existing Asset CP with id=" + str(asset_cp),
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    try:
        if asset_cp_object.model.is_blade_chassis():
            blades = AssetCP.objects.filter(chassis=asset_cp_object, change_plan=change_plan)
            remove_chassis = True
            for blade in blades:
                if len(get_changes_on_asset(blade.related_asset, blade)) == 0:
                    remove_chassis = False
            if remove_chassis:
                asset_cp_object.delete()
            else:
                for field in asset_cp.related_asset._meta.fields:
                    if (
                            field.name != "id"
                            and field.name != "assetid_ptr"
                            and field.name != "chassis"
                    ):
                        setattr(asset_cp, field.name, getattr(asset_cp.related_asset, field.name))

        else:
            asset_cp_object.delete()
    except Exception as error:
        return JsonResponse(
            {
                "failure_message": Status.DELETE_ERROR.value
                + "Change Plan"
                + GenericFailure.ON_DELETE.value,
                "errors": str(error),
            },
            status=HTTPStatus.BAD_REQUEST,
        )

    return JsonResponse(
        {
            "success_message": Status.SUCCESS.value
            + "Asset successfully removed from change plan"
        },
        status=HTTPStatus.OK,
    )
示例#14
0
def pdu_port_availability(request):
    rack_id = request.query_params.get("id")
    if not rack_id:
        return JsonResponse(
            {
                "failure_message":
                Status.ERROR.value + GenericFailure.INTERNAL.value,
                "errors": "Query parameter 'id' is required",
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    try:
        rack = Rack.objects.get(id=rack_id)
    except Rack.DoesNotExist:
        return JsonResponse(
            {
                "failure_message":
                Status.ERROR.value + "Rack" +
                GenericFailure.DOES_NOT_EXIST.value,
                "errors":
                "No existing rack with id=" + str(rack_id),
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    (change_plan, failure_response) = get_change_plan(
        request.query_params.get("change_plan"))
    if failure_response:
        return failure_response
    if change_plan:
        assets, assets_cp = get_assets_for_cp(change_plan.id)
        assets_cp = assets_cp.filter(rack=rack.id)
        assets = assets.filter(rack=rack.id)
    else:
        assets_cp = None
        assets = Asset.objects.filter(rack=rack.id)
    available_left = list(range(1, 25))
    available_right = list(range(1, 25))
    try:
        remove_unavailable_pdu_ports(available_left, available_right, assets,
                                     PowerPort)
    except Exception as error:
        return JsonResponse(
            {"failure_message": Status.ERROR.value + str(error)},
            status=HTTPStatus.BAD_REQUEST,
        )
    if change_plan:
        try:
            remove_unavailable_pdu_ports(available_left, available_right,
                                         assets_cp, PowerPortCP)
        except Exception as error:
            return JsonResponse(
                {"failure_message": Status.ERROR.value + str(error)},
                status=HTTPStatus.BAD_REQUEST,
            )
    suggest = None
    for index in available_left:
        if index in available_right:
            suggest = index
            break
    return JsonResponse(
        {
            "left_available": available_left,
            "right_available": available_right,
            "left_suggest": suggest,
            "right_suggest": suggest,
        },
        status=HTTPStatus.OK,
    )
示例#15
0
def asset_add(request):
    """
    Add a new asset.
    """
    data = JSONParser().parse(request)
    if "id" in data:
        return JsonResponse(
            {
                "failure_message": Status.CREATE_ERROR.value
                + GenericFailure.INTERNAL.value,
                "errors": "Don't include 'id' when creating an asset",
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    (change_plan, failure_response) = get_change_plan(
        request.query_params.get("change_plan")
    )
    if failure_response:
        return failure_response
    chassis_id_live = None
    if change_plan:
        failure_response = get_cp_already_executed_response(change_plan)
        if failure_response:
            return failure_response
        data["change_plan"] = change_plan.id

        if data["chassis"] and not AssetCP.objects.filter(id=data["chassis"]).exists():
            # ignore the chassis because we will replace it with a new chassis on AssetCP later
            chassis_id_live = data["chassis"]
            del data["chassis"]

        serializer = AssetCPSerializer(data=data)
    else:
        serializer = AssetSerializer(data=data)
    if not serializer.is_valid(raise_exception=False):
        return JsonResponse(
            {
                "failure_message": Status.INVALID_INPUT.value
                + parse_serializer_errors(serializer.errors),
                "errors": str(serializer.errors),
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    try:
        validate_user_permission_on_new_asset_data(
            request.user,
            serializer.validated_data,
            data_is_validated=True,
            change_plan=change_plan,
            chassis_id_live=chassis_id_live,
        )
    except UserAssetPermissionException as auth_error:
        return JsonResponse(
            {"failure_message": Status.AUTH_ERROR.value + str(auth_error)},
            status=HTTPStatus.UNAUTHORIZED,
        )
    except Exception as error:
        return JsonResponse(
            {"failure_message": Status.CREATE_ERROR.value + str(error)},
            status=HTTPStatus.BAD_REQUEST,
        )
    if not (
        "offline_storage_site" in serializer.validated_data
        and serializer.validated_data["offline_storage_site"]
    ):
        if serializer.validated_data["model"].is_rackmount():
            if (
                "rack" not in serializer.validated_data
                or not serializer.validated_data["rack"]
                or "rack_position" not in serializer.validated_data
                or not serializer.validated_data["rack_position"]
            ):
                return JsonResponse(
                    {
                        "failure_message": Status.INVALID_INPUT.value
                        + "Must include rack and rack position to add a rackmount asset. "
                    },
                    status=HTTPStatus.BAD_REQUEST,
                )
            rack_id = serializer.validated_data["rack"].id
            rack_position = serializer.validated_data["rack_position"]
            height = serializer.validated_data["model"].height
            try:
                validate_asset_location_in_rack(
                    rack_id, rack_position, height, change_plan=change_plan
                )
            except LocationException as error:
                return JsonResponse(
                    {"failure_message": Status.CREATE_ERROR.value + str(error)},
                    status=HTTPStatus.BAD_REQUEST,
                )
        else:
            if (
                (
                    (
                        "chassis" not in serializer.validated_data
                        or not serializer.validated_data["chassis"]
                    )
                    and not chassis_id_live
                )
                or "chassis_slot" not in serializer.validated_data
                or not serializer.validated_data["chassis_slot"]
            ):
                return JsonResponse(
                    {
                        "failure_message": Status.INVALID_INPUT.value
                        + "Must include chassis and chassis slot to add a blade asset. "
                    },
                    status=HTTPStatus.BAD_REQUEST,
                )

            if chassis_id_live:
                try:
                    chassis_live = Asset.objects.get(id=chassis_id_live)
                except ObjectDoesNotExist:
                    return JsonResponse(
                        {
                            "failure_message": Status.MODIFY_ERROR.value
                            + "Chassis"
                            + GenericFailure.DOES_NOT_EXIST.value,
                            "errors": "No existing chassis with id="
                            + str(chassis_id_live),
                        },
                        status=HTTPStatus.BAD_REQUEST,
                    )
                chassis_cp = add_chassis_to_cp(chassis_live, change_plan)
                chassis_id = chassis_cp.id
                serializer.validated_data["chassis"] = chassis_cp

            else:
                chassis_id = serializer.validated_data["chassis"].id
            chassis_slot = serializer.validated_data["chassis_slot"]
            try:
                validate_asset_location_in_chassis(
                    chassis_id, chassis_slot, change_plan=change_plan
                )
            except LocationException as error:
                return JsonResponse(
                    {"failure_message": Status.CREATE_ERROR.value + str(error)},
                    status=HTTPStatus.BAD_REQUEST,
                )
    try:
        asset = serializer.save()
    except Exception as error:
        return JsonResponse(
            {
                "failure_message": Status.CREATE_ERROR.value
                + parse_save_validation_error(error, "Asset"),
                "errors": str(error),
            },
            status=HTTPStatus.BAD_REQUEST,
        )

    warning_message = save_all_connection_data(
        data, asset, request.user, change_plan=change_plan
    )
    if warning_message:
        return JsonResponse({"warning_message": warning_message}, status=HTTPStatus.OK)
    if change_plan:
        return JsonResponse(
            {
                "success_message": Status.SUCCESS.value
                + "Asset created on change plan "
                + change_plan.name,
                "related_id": change_plan.id,
            },
            status=HTTPStatus.OK,
        )
    else:
        log_action(request.user, asset, Action.CREATE)
        return JsonResponse(
            {
                "success_message": Status.SUCCESS.value
                + "Asset "
                + str(asset.asset_number)
                + " created"
            },
            status=HTTPStatus.OK,
        )
示例#16
0
def asset_modify(request):
    """
    Modify a single existing asset
    """
    data = JSONParser().parse(request)
    if "id" not in data:
        return JsonResponse(
            {
                "failure_message": Status.MODIFY_ERROR.value
                + GenericFailure.INTERNAL.value,
                "errors": "Must include 'id' when modifying an asset",
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    asset_id = data["id"]

    (change_plan, failure_response) = get_change_plan(
        request.query_params.get("change_plan")
    )
    if failure_response:
        return failure_response
    create_new_asset_cp = False
    existing_asset = None
    if change_plan:
        failure_response = get_cp_already_executed_response(change_plan)
        if failure_response:
            return failure_response
        if not does_asset_exist(asset_id, change_plan):
            return JsonResponse(
                {
                    "failure_message": Status.MODIFY_ERROR.value
                    + "Asset"
                    + GenericFailure.DOES_NOT_EXIST.value,
                    "errors": "No existing asset with id=" + str(asset_id),
                },
                status=HTTPStatus.BAD_REQUEST,
            )
        try:
            existing_asset = AssetCP.objects.get(
                id=asset_id, change_plan=change_plan.id
            )
        except ObjectDoesNotExist:
            create_new_asset_cp = True

    if create_new_asset_cp or not change_plan:
        try:
            existing_asset = Asset.objects.get(id=asset_id)
        except ObjectDoesNotExist:
            return JsonResponse(
                {
                    "failure_message": Status.MODIFY_ERROR.value
                    + "Model"
                    + GenericFailure.DOES_NOT_EXIST.value,
                    "errors": "No existing asset with id=" + str(asset_id),
                },
                status=HTTPStatus.BAD_REQUEST,
            )
    try:
        validate_user_permission_on_existing_asset(request.user, existing_asset)
    except UserAssetPermissionException as auth_error:
        return JsonResponse(
            {"failure_message": Status.AUTH_ERROR.value + str(auth_error)},
            status=HTTPStatus.UNAUTHORIZED,
        )

    try:
        validate_user_permission_on_new_asset_data(
            request.user, data, data_is_validated=False, change_plan=change_plan
        )
    except UserAssetPermissionException as auth_error:
        return JsonResponse(
            {"failure_message": Status.AUTH_ERROR.value + str(auth_error)},
            status=HTTPStatus.UNAUTHORIZED,
        )
    try:
        validate_hostname_deletion(data, existing_asset)
    except AssetModificationException as modifcation_exception:
        return JsonResponse(
            {
                "failure_message": Status.MODIFY_ERROR.value
                + "Invalid hostname deletion. "
                + str(modifcation_exception)
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    try:
        validate_location_modification(data, existing_asset, change_plan=change_plan)
    except Exception as error:
        return JsonResponse(
            {
                "failure_message": Status.MODIFY_ERROR.value
                + "Invalid location change. "
                + str(error)
            },
            status=HTTPStatus.BAD_REQUEST,
        )
    asset_cp = None
    if change_plan:
        (asset_cp, failure_message) = save_all_field_data_cp(
            data, existing_asset, change_plan, create_new_asset_cp
        )
    else:
        failure_message = save_all_field_data_live(data, existing_asset)
    if failure_message:
        return JsonResponse(
            {"failure_message": Status.MODIFY_ERROR.value + failure_message},
            status=HTTPStatus.BAD_REQUEST,
        )

    if asset_cp:
        existing_asset = asset_cp

    warning_message = save_all_connection_data(
        data, existing_asset, request.user, change_plan=change_plan
    )
    if warning_message:
        return JsonResponse({"warning_message": warning_message}, status=HTTPStatus.OK)

    if change_plan:
        return JsonResponse(
            {
                "success_message": Status.SUCCESS.value
                + "Asset modified on change plan "
                + change_plan.name,
                "related_id": change_plan.id,
            },
            status=HTTPStatus.OK,
        )
    else:
        log_action(request.user, existing_asset, Action.MODIFY)
        return JsonResponse(
            {
                "success_message": Status.SUCCESS.value
                + "Asset "
                + str(existing_asset.asset_number)
                + " modified"
            },
            status=HTTPStatus.OK,
        )
def change_plan_execute(request, id):
    """
    Execute all changes associated with a change plan.
    """
    (change_plan, failure_response) = get_change_plan(id)
    if failure_response:
        return failure_response
    failure_response = get_cp_already_executed_response(change_plan)
    if failure_response:
        return failure_response
    if request.user != change_plan.owner:
        return JsonResponse(
            {
                "failure_message": Status.ERROR.value
                + "You do not have access to execute this change plan.",
                "errors": "User "
                + request.user.username
                + " does not own change plan with id="
                + str(id),
            },
            status=HTTPStatus.BAD_REQUEST,
        )

    assets_cp = AssetCP.objects.filter(change_plan=change_plan)
    for asset_cp in assets_cp:
        if get_cp_modification_conflicts(asset_cp):
            return JsonResponse(
                {
                    "failure_message": Status.ERROR.value
                    + "All conflicts must be resolved before a change "
                    + "plan can be executed.",
                    "errors": "Conflict found on AssetCP with id=" + str(asset_cp.id),
                },
                status=HTTPStatus.BAD_REQUEST,
            )

    change_plan.execution_time = datetime.now()
    change_plan.save()

    num_created = 0
    num_modified = 0
    num_decommissioned = 0
    updated_asset_mappings = {}

    # rackmount assets first
    rackmount_assets_cp = assets_cp.filter(
        ~Q(model__model_type=ModelType.BLADE_ASSET.value)
    )
    for asset_cp in rackmount_assets_cp:
        changes = []
        if asset_cp.related_asset:
            changes = get_changes_on_asset(asset_cp.related_asset, asset_cp)
        updated_asset, created = get_updated_asset(asset_cp)
        updated_asset_mappings[asset_cp] = updated_asset
        differs_from_live = True

        if created and not asset_cp.is_decommissioned:
            num_created += 1
            log_action(
                request.user, updated_asset, Action.CREATE, change_plan=change_plan,
            )
        elif not asset_cp.is_decommissioned:
            if len(changes) > 0:
                num_modified += 1
                log_action(
                    request.user, updated_asset, Action.MODIFY, change_plan=change_plan,
                )
            else:
                differs_from_live = False
        asset_cp.differs_from_live = differs_from_live
        asset_cp.save()

        update_network_ports(updated_asset, asset_cp, change_plan)
        update_power_ports(updated_asset, asset_cp, change_plan)
    ##blade assets
    blade_assets_cp = assets_cp.filter(model__model_type=ModelType.BLADE_ASSET.value)
    for asset_cp in blade_assets_cp:
        changes = []
        if asset_cp.related_asset:
            changes = get_changes_on_asset(asset_cp.related_asset, asset_cp)
        chassis_live = updated_asset_mappings[asset_cp.chassis]
        updated_asset, created = get_updated_asset(asset_cp, chassis_live)
        updated_asset_mappings[asset_cp] = updated_asset
        differs_from_live = True

        if created:
            num_created += 1
            log_action(
                request.user, updated_asset, Action.CREATE, change_plan=change_plan,
            )
        elif not asset_cp.is_decommissioned:
            if len(changes) > 0:
                num_modified += 1
                log_action(
                    request.user, updated_asset, Action.MODIFY, change_plan=change_plan,
                )
            else:
                differs_from_live = False
        asset_cp.differs_from_live = differs_from_live
        asset_cp.save()
        update_network_ports(updated_asset, asset_cp, change_plan)
        update_power_ports(updated_asset, asset_cp, change_plan)
        updated_asset.save()
    ## decomission blades first
    blades_cp = assets_cp.filter(model__model_type=ModelType.BLADE_ASSET.value)
    rackmount_cp = assets_cp.filter(~Q(model__model_type=ModelType.BLADE_ASSET.value))
    for asset_cp_query in [blades_cp, rackmount_cp]:
        for asset_cp in asset_cp_query:
            # Decommission only after all changes have been made to all CP assets
            updated_asset = updated_asset_mappings[asset_cp]
            if asset_cp.is_decommissioned:
                failure_response = decommission_asset_cp(
                    updated_asset, asset_cp, change_plan,
                )
                if failure_response:
                    return failure_response
                num_decommissioned += 1
                log_action(
                    request.user, None, Action.DECOMMISSION, change_plan=change_plan,
                )

    log_execute_change_plan(
        request.user, change_plan.name, num_created, num_modified, num_decommissioned,
    )

    return JsonResponse(
        {
            "success_message": "Change Plan '"
            + change_plan.name
            + "' executed: "
            + str(num_created)
            + " assets created, "
            + str(num_modified)
            + " assets modified, "
            + str(num_decommissioned)
            + " assets decommissioned."
        },
        status=HTTPStatus.OK,
    )