DDI API: Extend API response to report previous update execution status. (#506)

* DDI API: Limit number of feedback messages for an action status.

Even though the number of ActionStatus entries are limited to 1000 per
action using QuotaManagement, there is no limit on the number of
messages that can be sent as part of a single ActionStatus. This allows
a controller to potentially send large number of messages for a single
action.

Limiting the number of allowed messages to 50 using the
javax.validation.constraints.Size within DdiStatus.

Signed-off-by: Christian Storm <christian.storm@siemens.com>
Signed-off-by: Himanshu Kumar Singh <himanshu.singh@siemens.com>
Signed-off-by: Raju HS <raju.hs@siemens.com>

* DDI API: Extend API response to retrieve controller feedback. (#381)

Resolves #381

The mechanism is useful for example, when the client software running on
the device loses this information prior to reporting a final execution
status such as 'closed' to hawkBit. This may happen, e.g., due to a
power cycle or simply a crash. Upon the client software restarting, it
installs the same payload again as advertised by hawkBit (as the device
has not sent a final update execution status). Instead, if the last
feedback sent to hawkBit would be reported back to the device, the
client may resume installation.

Feedback messages sent as part of POST
    /{tenant}/controller/v1/{targetid}/deploymentBase/{actionId}/feedback,
are sent back to controller as part of response to GET
    /{tenant}/controller/v1/{targetid}/deploymentBase/{actionId}.

Following example illustrates the API changes:

1. After retrieving the action from server, controller starts download
and sends a feedback.
curl
'http://127.0.0.1:8080/default/controller/v1/1/deploymentBase/1/feedback'
-i -X POST -H 'Content-Type: application/json;charset=UTF-8' -H 'Accept:
application/hal+json' -d '{
  "id" : "1",
  "time" : "20170406T121500",
  "status" : {
    "result" : {
      "progress" : {
        "of" : 1,
        "cnt" : 0
      },
      "finished" : "none"
    },
    "execution" : "proceeding",
    "details" : [ "proceeding with download" ]
  }
}'

2. Once artifact download has finished, controller sends another
feedback.
curl
'http://127.0.0.1:8080/default/controller/v1/1/deploymentBase/1/feedback'
-i -X POST -H 'Content-Type: application/json;charset=UTF-8' -H 'Accept:
application/hal+json' -d '{
{
  "id" : "1",
  "time" : "20170406T123000",
  "status" : {
    "result" : {
      "progress" : {
        "of" : 1,
        "cnt" : 0
      },
      "finished" : "none"
    },
    "execution" : "proceeding",
    "details" : [ "downloaded artifacts for update" ]
  }
}'

3. If there is a power outage now, the controller can retrieve the
messages posted earlier from the action history when it restarts again.
curl
'http://127.0.0.1:8080/default/controller/v1/1/deploymentBase/1?c=411599879&actionHistory=-1'
-i -H 'Accept: application/hal+json'

Response will be like below
{
  "id": "1",
  "deployment": {
    "download": "forced",
    "update": "forced",
    "chunks": [
      {
      "part": "os",
      "version": "1",
      "name": "1",
      "artifacts": [....],
      }
    ],
  },
  "actionHistory": {
    "status": "RETRIEVED",
    "messages": [
      "downloaded artifacts for update",
      "proceeding with download"
    ]
  }
}

4. Based on the feedback messages, controller may be able to skip the
download and resume with installation and send additional feedback.
curl
'http://127.0.0.1:8080/default/controller/v1/1/deploymentBase/1/feedback'
-i -X POST -H 'Content-Type: application/json;charset=UTF-8' -H 'Accept:
application/hal+json' -d '{
  "id" : "1",
  "time" : "20170406T121314",
  "status" : {
    "result" : {
      "progress" : {
        "of" : 1,
        "cnt" : 0
      },
      "finished" : "none"
    },
    "execution" : "resumed",
    "details" : [ "resuming installation based on previous feedback,
download skipped" ]
  }
}'

Note: The maximum number of messages to be retrieved from the database
by a controller is limited to 100. The actionHistory parameter's value
has the following meaning:

Input Value         | Output
-----------------------------
  <0                | Retrieve maximum allowed number of messages from
                    | action history.
  unspecified or =0 | Do not retrieve any message (default).
  >0                | Retrieve specified number of messages, limited by
                    | maximum allowed number.

Signed-off-by: Christian Storm <christian.storm@siemens.com>
Signed-off-by: Himanshu Kumar Singh <himanshu.singh@siemens.com>
Signed-off-by: Raju HS <raju.hs@siemens.com>
This commit is contained in:
stormc
2017-06-06 08:26:01 +02:00
committed by Kai Zimmermann
parent 672d4270d7
commit b69bedd8f9
14 changed files with 369 additions and 8 deletions

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) Siemens AG, 2017
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import java.util.List;
import org.eclipse.hawkbit.ddi.rest.api.DdiRootControllerRestApi;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
/**
* Provide action history information to the controller as part of response to
* {@link DdiRootControllerRestApi#getControllerBasedeploymentAction}: 1.
* Current action status at the server; 2. List of messages from action history
* that were sent to server earlier by the controller using
* {@link DdiActionFeedback}.
*/
@JsonPropertyOrder({ "status", "messages" })
public class DdiActionHistory {
@JsonProperty("status")
private final String actionStatus;
@JsonProperty("messages")
private final List<String> messages;
/**
* Parameterized constructor for creating {@link DdiActionHistory}.
*
* @param actionStatus
* is the current action status at the server
* @param messages
* is a list of messages retrieved from action history.
*/
@JsonCreator
public DdiActionHistory(@JsonProperty("status") final String actionStatus,
@JsonProperty("messages") List<String> messages) {
this.actionStatus = actionStatus;
this.messages = messages;
}
@Override
public String toString() {
return "Action history [" + "status=" + actionStatus + ", messages={" + messages.toString() + "}]";
}
}

View File

@@ -13,42 +13,70 @@ import javax.validation.constraints.NotNull;
import org.springframework.hateoas.ResourceSupport;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
/**
* Update action resource.
*/
@JsonPropertyOrder({ "id", "deployment", "actionHistory" })
public class DdiDeploymentBase extends ResourceSupport {
@JsonProperty("id")
@NotNull
private final String deplyomentId;
@JsonProperty("deployment")
@NotNull
private final DdiDeployment deployment;
/**
* Action history containing current action status and a list of feedback
* messages received earlier from the controller.
*/
@JsonProperty("actionHistory")
@JsonInclude(JsonInclude.Include.NON_NULL)
private final DdiActionHistory actionHistory;
/**
* Constructor.
*
* @param id
* of the update action
* @param deployment
* details.
* details
* @param actionHistory
* containing current action status and a list of feedback
* messages received earlier from the controller.
*/
@JsonCreator
public DdiDeploymentBase(@JsonProperty("id") final String id,
@JsonProperty("deplyomentId") final DdiDeployment deployment) {
@JsonProperty("deplyomentId") final DdiDeployment deployment,
@JsonProperty("actionHistory") final DdiActionHistory actionHistory) {
this.deplyomentId = id;
this.deployment = deployment;
this.actionHistory = actionHistory;
}
public DdiDeployment getDeployment() {
return deployment;
}
/**
* Returns the action history containing current action status and a list of
* feedback messages received earlier from the controller.
*
* @return {@link DdiActionHistory}
*/
public DdiActionHistory getActionHistory() {
return actionHistory;
}
@Override
public String toString() {
return "DeploymentBase [id=" + deplyomentId + ", deployment=" + deployment + "]";
return "DeploymentBase [id=" + deplyomentId + ", deployment=" + deployment + " actionHistory="
+ actionHistory.toString() + "]";
}
}

View File

@@ -13,6 +13,7 @@ import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -31,6 +32,7 @@ public class DdiStatus {
@Valid
private final DdiResult result;
@Size(min = 0, max = 50)
private final List<String> details;
/**

View File

@@ -43,6 +43,13 @@ public final class DdiRestConstants {
*/
public static final String CONFIG_DATA_ACTION = "configData";
/**
* Default value specifying that no action history to be sent as part of
* response to deploymentBase
* {@link DdiRootControllerRestApi#getControllerBasedeploymentAction}.
*/
public static final String NO_ACTION_HISTORY = "0";
private DdiRestConstants() {
// constant class, private constructor.
}

View File

@@ -136,6 +136,17 @@ public interface DdiRootControllerRestApi {
* an hashcode of the resource which indicates if the action has
* been changed, e.g. from 'soft' to 'force' and the eTag needs
* to be re-generated
* @param actionHistoryMessageCount
* specifies the number of messages to be returned from action
* history. Regardless of the passed value, in order to restrict
* resource utilization by controllers, maximum number of
* messages that are retrieved from database is limited by
* {@link RepositoryConstants#MAX_ACTION_HISTORY_MSG_COUNT}.
* actionHistoryMessageCount < 0, retrieves the maximum allowed
* number of action status messages from history;
* actionHistoryMessageCount = 0, does not retrieve any message;
* and actionHistoryMessageCount > 0, retrieves the specified
* number of messages, limited by maximum allowed number.
* @param request
* the HTTP request injected by spring
* @return the response
@@ -146,7 +157,8 @@ public interface DdiRootControllerRestApi {
ResponseEntity<DdiDeploymentBase> getControllerBasedeploymentAction(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") @NotEmpty final String controllerId,
@PathVariable("actionId") @NotEmpty final Long actionId,
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource);
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource,
@RequestParam(value = "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount);
/**
* This is the feedback channel for the {@link DdiDeploymentBase} action.