245 maintenance window (#535)
* Maintenance Window: Introduced dynamic update to polling time. (#245) Resolves #245 Added new configuration parameters and controller handling that can be used to enable reduction in polling time near to start of a maintenance window. - The device may only get to know of start of maintenance window when the device polls the server. If a window is available, server responds with maintenanceWindow as 'available' and the update as 'forced'. Hence, as the time for start of a maintenance window approaches, the device should poll the server more often in order to decrease the possibility of missing the maintenance window start time due to non-alignment of polling with the start time, or due to packet losses and other network issues. - Additional configuration parameter is introduced that will be used to specify the minimum number of times the controller should poll between current time and the start of maintenance window. Using this parameter, an "inverse exponential back-off" scheme for reducing the polling time is implemented. - Implemented inverse exponential back-off for polling. For example, considering default polling-time = 00:05:00, min-polling-time = 00:00:30, maintenance-window-poll-count = 3, we expect that controller should poll the server 3 times between current time and start of a maintenance window. Thus in following scenario controller will get to know of start of maintenance 3 secs after scheduled start. Whereas without this scheme, delay may be as much as 4 minutes. time until start: 00:21:00; polling time: 00:05:00 time until start: 00:16:00; polling time: 00:05:00 time until start: 00:11:00; polling time: 00:03:40 time until start: 00:07:20; polling time: 00:02:27 time until start: 00:04:53; polling time: 00:01:38 time until start: 00:02:10; polling time: 00:00:43 time until start: 00:01:27; polling time: 00:00:30 time until start: 00:00:57; polling time: 00:00:30 time until start: 00:00:27; polling time: 00:00:30 time until start: -00:00:03; polling time: 00:05:00 - Once a maintenance window starts, the polling time is reverted to default. Polling time is not changed near the end of a maintenance window. 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> * Maintenance Window: Updated the DMF API. Added maintenance window handling for DMF API. Currently no timer or scheduler is used to send updates to devices when a maintenance window becomes available. The device can simulate a poll behaviour by sending a message on UPDATE_ACTION_STATUS topic. Depending on whether there is a maintenance window available or not, server should send DOWNLOAD_AND_INSTALL or DOWNLOAD_AND_SKIP. - Additional action status (DOWNLOADED) and event topic (DOWNLOAD_AND_SKIP) - Server sends DOWNLOAD_AND_SKIP or DOWNLOAD_AND_INSTALL based on maintenance window using ControllerManagement 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> * Maintenance Window: Added tests for DDI and Management APIs. Added test cases for DDI and Management API to test for maintenance window related logic. - Tests for Management API for DistributionSet assignment with and without valid maintenance schedule specified. - Tests for lapsed maintenance schedule. - Additional test helper methods. 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> * Maintenance Window: Added new fields, enums, APIs and helper classes. Extended the data model and Action API to support maintenance schedule. Also provided helper classes to parse cron expression and calculate maintenance window based on the schedule. - DOWNLOADED status added to Action interface for controller to record action status. - Extra fields and APIs are added to Action to set schedule, duration and time zone of maintenance window. - Maintenance schedule can be defined using: a cron expression (e.g. "0 30 10-13 ? * WED,FRI" for 30 minutes past the hour, between 10:00 AM and 01:59 PM, only on Wednesday and Friday), a duration (e.g. "00:30:00" for 30 minutes) and the time zone (e.g. "+02:00" for CET summer time or "+00:00" for UTC) relative to which maintenance window start time is calculated. - Extra APIs added to check if a maintenance schedule is defined or lapsed and if a maintenance window is available based on the schedule. - Added database fields to store the schedule, duration and timezone of maintenance window. - Added helper classes to parse and calculate the maintenance window schedule. - Added exception handling classes to handle invalid maintenance window schedule. 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> * Maintenance Window: Extended JSON model for REST APIs. Extended the JSON model to support defining and communicating the maintenance window information using the REST APIs. - Additional JSON objects are defined to extend request and response bodies. - When requesting a distribution set assignment to a target, the maintenance schedule information can be specified. Example below specifies a maintenance schedule at 11:00 PM, only on Saturday, only in 2017, for 30 minutes. Maintenance window start time is then calculated relative to the time zone specified, in this case CET summer time. $ curl 'http://127.0.0.1:8080/rest/v1/targets/2/assignedDS' -i -X POST -H 'Content-Type: application/json;charset=UTF-8' -d '{ "forcetime" : "0", "id" : "1", "type" : "forced", "maintenanceWindow": { "schedule": "0 0 23 ? * SAT 2017", "duration": "00:30:00", "timezone": "+02:00" } }' - When device requests the action information it gets to know if there is a 'maintenance schedule' for this action or not based on presence of maintenanceWindow field in the response. The value of maintenanceWindow is either 'available' (i.e. the maintenance window is now available as per defined schedule and the update can progress) or 'unavailable' (implying that maintenance window is not available now and update should not be attempted). If there is no 'maintenance schedule' defined, the field is null or not present. $ curl 'http://127.0.0.1:8080/default/controller/v1/2/deploymentBase/1?c=411629670' -i -H 'Accept: application/hal+json' HTTP/1.1 200 OK Content-Type: application/hal+json;charset=UTF-8 Content-Length: 1665 { "id": "1", "deployment": { "download": "forced", "update": "forced", "chunks": [ { "part": "os", "version": "1", "name": "1", "artifacts" : [ { "filename" : "binary.tgz", "hashes" : { "sha1" : "8b71973e835cd0718827238b1cf89f0079e44dae", "md5" : "a90218782025c48e09ba0436cf30d029" }, "size" : 5, "_links" : { "download" : { "href" : "http://127.0.0.1:8080/api/v1/default/download/controller/2/softwaremodules/8/filename/binary.tgz" }, "md5sum" : { "href" : "http://127.0.0.1:8080/api/v1/default/download/controller/2/softwaremodules/8/filename/binary.tgz.MD5SUM" }, "download-http" : { "href" : "http://127.0.0.1:8080/api/v1/default/download/controller/2/softwaremodules/8/filename/binary.tgz" }, "md5sum-http" : { "href" : "http://127.0.0.1:8080/api/v1/default/download/controller/2/softwaremodules/8/filename/binary.tgz.MD5SUM" } } } ] } ], "maintenanceWindow": "available" } } 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> * Maintenance Window: Added additional UI elements. Added additional UI elements to specify maintenance window schedule in hawkBit UI while assigning distribution set(s) to target(s). - Added UI control to hawkBit UI when distribution set assignment is confirmed. - Also added required theme updates and resource strings. 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> * Maintenance Window: Modified DDI and Mgmt APIs for maintenance window. Modified controller management and deployment management to handle maintenance window schedule in Management API and DDI API. - Modified REST endpoints to accept and respond with maintenance window information. $ curl 'http://127.0.0.1:8080/rest/v1/targets/2/assignedDS' -i -X POST -H 'Content-Type: application/json;charset=UTF-8' -d '{ "forcetime" : "0", "id" : "1", "type" : "forced", "maintenanceWindow": { "schedule": "0 0 23 ? * SAT 2017", "duration": "00:30:00", "timezone": "+02:00" } }' - Check for availability of a maintenance window based on the schedule. - Device gets go-ahead to install by change in ETAG and 'update' status to 'forced' when it polls during an available maintenance window. $ curl 'http://127.0.0.1:8080/default/controller/v1/2/deploymentBase/1?c=411659461' -i -H 'Accept: application/hal+json' HTTP/1.1 200 OK Content-Type: application/hal+json;charset=UTF-8 Content-Length: 1665 { "id": "1", "deployment": { "download": "forced", "update": "forced", "chunks": [ { "part": "os", "version": "1", "name": "1", "artifacts" : [ { "filename" : "binary.tgz", "hashes" : { "sha1" : "8b71973e835cd0718827238b1cf89f0079e44dae", "md5" : "a90218782025c48e09ba0436cf30d029" }, "size" : 5, "_links" : { "download" : { "href" : "http://127.0.0.1:8080/api/v1/default/download/controller/2/softwaremodules/8/filename/binary.tgz" }, "md5sum" : { "href" : "http://127.0.0.1:8080/api/v1/default/download/controller/2/softwaremodules/8/filename/binary.tgz.MD5SUM" }, "download-http" : { "href" : "http://127.0.0.1:8080/api/v1/default/download/controller/2/softwaremodules/8/filename/binary.tgz" }, "md5sum-http" : { "href" : "http://127.0.0.1:8080/api/v1/default/download/controller/2/softwaremodules/8/filename/binary.tgz.MD5SUM" } } } ] } ], "maintenanceWindow": "available" } } - In case the current available maintenance window lapses, the device is again sent 'update' status as 'skip'. The device may choose then to abort the update and restart during next available window. $ curl 'http://127.0.0.1:8080/default/controller/v1/2/deploymentBase/1?c=411659455' -i -H 'Accept: application/hal+json' HTTP/1.1 200 OK Content-Type: application/hal+json;charset=UTF-8 Content-Length: 1665 { "id": "1", "deployment": { "download": "forced", "update": "skip", "chunks": [ { "part": "os", "version": "1", "name": "1", "artifacts" : [ { "filename" : "binary.tgz", "hashes" : { "sha1" : "8b71973e835cd0718827238b1cf89f0079e44dae", "md5" : "a90218782025c48e09ba0436cf30d029" }, "size" : 5, "_links" : { "download" : { "href" : "http://127.0.0.1:8080/api/v1/default/download/controller/2/softwaremodules/8/filename/binary.tgz" }, "md5sum" : { "href" : "http://127.0.0.1:8080/api/v1/default/download/controller/2/softwaremodules/8/filename/binary.tgz.MD5SUM" }, "download-http" : { "href" : "http://127.0.0.1:8080/api/v1/default/download/controller/2/softwaremodules/8/filename/binary.tgz" }, "md5sum-http" : { "href" : "http://127.0.0.1:8080/api/v1/default/download/controller/2/softwaremodules/8/filename/binary.tgz.MD5SUM" } } } ] } ], "maintenanceWindow": "unavailable" } } - In case the entire maintenance schedule lapses, before device is able to send the completed status, the action is canceled. $ curl 'http://127.0.0.1:8080/default/controller/v1/2' -i -H 'Accept: application/hal+json' HTTP/1.1 200 OK Content-Type: application/hal+json;charset=UTF-8 Content-Length: 340 { "config": { "polling": { "sleep": "00:05:00" } }, "_links": { "cancelAction": { "href": "http://127.0.0.1:8080/default/controller/v1/2/cancelAction/1" }, "configData": { "href": "http://127.0.0.1:8080/default/controller/v1/2/configData" } } } 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:
@@ -21,6 +21,7 @@ import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
|
||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||
@@ -211,11 +212,51 @@ public interface ControllerManagement {
|
||||
Optional<Action> getActionForDownloadByTargetAndSoftwareModule(@NotEmpty String controllerId, long moduleId);
|
||||
|
||||
/**
|
||||
* Returns configured polling interval at which the controller polls hawkBit
|
||||
* server.
|
||||
*
|
||||
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
String getPollingTime();
|
||||
|
||||
/**
|
||||
* Returns the configured minimum polling interval.
|
||||
*
|
||||
* @return current {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
String getMinPollingTime();
|
||||
|
||||
/**
|
||||
* Returns the count to be used for reducing polling interval while calling
|
||||
* {@link ControllerManagement#getPollingTimeForAction()}.
|
||||
*
|
||||
* @return configured value of
|
||||
* {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
public int getMaintenanceWindowPollCount();
|
||||
|
||||
/**
|
||||
* Returns polling time based on the maintenance window for an action.
|
||||
* Server will reduce the polling interval as the start time for maintenance
|
||||
* window approaches, so that at least these many attempts are made between
|
||||
* current polling until start of maintenance window. Poll time keeps
|
||||
* reducing with MinPollingTime as lower limit
|
||||
* {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}. After the start
|
||||
* of maintenance window, it resets to default
|
||||
* {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
|
||||
*
|
||||
* @param action
|
||||
* id the {@link Action} for which polling time is calculated
|
||||
* based on it having maintenance window or not
|
||||
*
|
||||
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
String getPollingTimeForAction(final Action action);
|
||||
|
||||
/**
|
||||
* Checks if a given target has currently or has even been assigned to the
|
||||
* given artifact through the action history list. This can e.g. indicate if
|
||||
@@ -349,4 +390,23 @@ public interface ControllerManagement {
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
List<String> getActionHistoryMessages(long actionId, int messageCount);
|
||||
|
||||
/**
|
||||
* Cancels given {@link Action} for this {@link Target}. However, it might
|
||||
* be possible that the controller will continue to work on the cancelation.
|
||||
* The controller needs to acknowledge or reject the cancelation using
|
||||
* {@link DdiRootController#postCancelActionFeedback}.
|
||||
*
|
||||
* @param actionId
|
||||
* to be canceled
|
||||
*
|
||||
* @return canceled {@link Action}
|
||||
*
|
||||
* @throws CancelActionNotAllowedException
|
||||
* in case the given action is not active or is already canceled
|
||||
* @throws EntityNotFoundException
|
||||
* if action with given actionId does not exist.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
Action cancelAction(long actionId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* Copyright (c) Siemens AG, 2018
|
||||
*
|
||||
* 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.repository;
|
||||
|
||||
import static com.cronutils.model.CronType.QUARTZ;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Optional;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
|
||||
|
||||
import com.cronutils.model.Cron;
|
||||
import com.cronutils.model.definition.CronDefinition;
|
||||
import com.cronutils.model.definition.CronDefinitionBuilder;
|
||||
import com.cronutils.model.time.ExecutionTime;
|
||||
import com.cronutils.parser.CronParser;
|
||||
|
||||
/**
|
||||
* Helper class to check validity of maintenance schedule definition and manage
|
||||
* scheduling of maintenance window using a cron expression based scheduler. It
|
||||
* also provides a helper method for conversion of duration specified in
|
||||
* HH:mm:ss format to ISO format.
|
||||
*/
|
||||
public class MaintenanceScheduleHelper {
|
||||
|
||||
ExecutionTime scheduleExecutor = null;
|
||||
Duration duration = null;
|
||||
TimeZone timeZone = null;
|
||||
|
||||
/**
|
||||
* Constructor that accepts a cron expression, duration and time zone and
|
||||
* instantiates the cron parser and scheduler executor.
|
||||
*
|
||||
* @param cronSchedule
|
||||
* is the cron expression to be used for scheduling the
|
||||
* maintenance window. Expression has 6 mandatory fields and 1
|
||||
* last optional field: "second minute hour dayofmonth month
|
||||
* weekday year"
|
||||
* @param duration
|
||||
* in HH:mm:ss format specifying the duration of a maintenance
|
||||
* window, for example 00:30:00 for 30 minutes
|
||||
* @param timezone
|
||||
* is the time zone specified as +/-hh:mm offset from UTC. For
|
||||
* example +02:00 for CET summer time and +00:00 for UTC. The
|
||||
* start time of a maintenance window calculated based on the
|
||||
* cron expression is relative to this time zone.
|
||||
*/
|
||||
public MaintenanceScheduleHelper(String cronSchedule, String duration, String timeZone) {
|
||||
this.timeZone = TimeZone.getTimeZone(ZoneOffset.of(timeZone));
|
||||
this.duration = Duration.parse(convertToISODuration(duration));
|
||||
|
||||
CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(QUARTZ);
|
||||
CronParser parser = new CronParser(cronDefinition);
|
||||
Cron quartzCron = parser.parse(cronSchedule);
|
||||
this.scheduleExecutor = ExecutionTime.forCron(quartzCron);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method calculates the next available maintenance window within the
|
||||
* schedule but after a given time.
|
||||
*
|
||||
* @param after
|
||||
* is the {@link ZonedDateTime} after which the window is
|
||||
* required
|
||||
*
|
||||
* @return {@link Optional<ZonedDateTime>} of the next available window. In
|
||||
* case there is none, returns empty value.
|
||||
*/
|
||||
public Optional<ZonedDateTime> nextExecution(ZonedDateTime after) {
|
||||
try {
|
||||
ZonedDateTime next = this.scheduleExecutor.nextExecution(after);
|
||||
return Optional.of(next);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method checks if there are any more valid maintenance windows after a
|
||||
* given time.
|
||||
*
|
||||
* @param after
|
||||
* is the {@link ZonedDateTime} after which the windows are
|
||||
* checked
|
||||
*
|
||||
* @return true if there is at least one valid schedule remaining, else
|
||||
* false.
|
||||
*/
|
||||
public boolean hasValidScheduleAfter(ZonedDateTime after) {
|
||||
return nextExecution(after).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the maintenance schedule definition is valid in terms of
|
||||
* validity of cron expression, duration and availability of at least one
|
||||
* valid maintenance window. Further a maintenance schedule is valid if
|
||||
* either all the parameters: schedule, duration and time zone are valid or
|
||||
* are null.
|
||||
*
|
||||
* @param cronSchedule
|
||||
* is a cron expression with 6 mandatory fields and 1 last
|
||||
* optional field: "second minute hour dayofmonth month weekday
|
||||
* year"
|
||||
* @param duration
|
||||
* in HH:mm:ss format specifying the duration of a maintenance
|
||||
* window, for example 00:30:00 for 30 minutes
|
||||
* @param timezone
|
||||
* is the time zone specified as +/-hh:mm offset from UTC. For
|
||||
* example +02:00 for CET summer time and +00:00 for UTC. The
|
||||
* start time of a maintenance window calculated based on the
|
||||
* cron expression is relative to this time zone
|
||||
*
|
||||
* @return true if the schedule is valid, else throw an exception
|
||||
*
|
||||
* @throws InvalidMaintenanceScheduleException
|
||||
* if the defined schedule fails the validity criteria.
|
||||
*/
|
||||
public static boolean validateMaintenanceSchedule(String cronSchedule, String duration, String timezone) {
|
||||
// check if schedule, duration and timezone are all not null.
|
||||
if (cronSchedule != null && duration != null && timezone != null) {
|
||||
// check if schedule, duration and timezone are all not empty.
|
||||
if (!(cronSchedule.isEmpty() || duration.isEmpty() || timezone.isEmpty())) {
|
||||
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(timezone));
|
||||
MaintenanceScheduleHelper scheduleHelper = new MaintenanceScheduleHelper(cronSchedule, duration,
|
||||
timezone);
|
||||
// check if there is a window currently active or available in
|
||||
// future.
|
||||
if (!scheduleHelper.hasValidScheduleAfter(now.minus(Duration.parse(convertToISODuration(duration))))) {
|
||||
throw new InvalidMaintenanceScheduleException(
|
||||
"No valid maintenance window available after current time");
|
||||
}
|
||||
} else {
|
||||
throw new InvalidMaintenanceScheduleException("Either of schedule, duration or timezone empty.");
|
||||
}
|
||||
} else if (!(cronSchedule == null && duration == null && timezone == null)) {
|
||||
throw new InvalidMaintenanceScheduleException(
|
||||
"All of schedule, duration and timezone should either be null or non empty.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the time interval or duration specified in "HH:mm:ss" format to
|
||||
* ISO format.
|
||||
*
|
||||
* @param timeInterval
|
||||
* in "HH:mm:ss" string format. This format is popularly used but
|
||||
* can be confused with time of the day, hence conversion to ISO
|
||||
* specified format for time duration is required
|
||||
*
|
||||
* @return the time interval or duration in ISO format
|
||||
*
|
||||
* @throws DateTimeParseException
|
||||
* if the text cannot be converted to ISO format.
|
||||
*/
|
||||
public static String convertToISODuration(String timeInterval) {
|
||||
return Duration.between(LocalTime.MIN, LocalTime.parse(timeInterval)).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) Siemens AG, 2018
|
||||
*
|
||||
* 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.repository.exception;
|
||||
|
||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
|
||||
/**
|
||||
* This exception is thrown if trying to set a maintenance schedule that is
|
||||
* invalid. A maintenance schedule is considered to be valid only if schedule,
|
||||
* duration and timezone are all null, or are all valid; in which case there
|
||||
* should be at least one valid window after the current time.
|
||||
*/
|
||||
public class InvalidMaintenanceScheduleException extends AbstractServerRtException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructor for {@link InvalidMaintenanceScheduleException}.
|
||||
*
|
||||
* @param message
|
||||
* the message for this exception.
|
||||
*/
|
||||
public InvalidMaintenanceScheduleException(final String message) {
|
||||
super(message, SpServerError.SP_MAINTENANCE_SCHEDULE_INVALID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for {@link InvalidMaintenanceScheduleException}.
|
||||
*
|
||||
* @param message
|
||||
* the message for this exception
|
||||
* @param cause
|
||||
* the cause for this exception.
|
||||
*/
|
||||
public InvalidMaintenanceScheduleException(final String message, final Throwable cause) {
|
||||
super(message, SpServerError.SP_MAINTENANCE_SCHEDULE_INVALID, cause);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,21 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
public interface Action extends TenantAwareBaseEntity {
|
||||
|
||||
/**
|
||||
* Maximum length of controllerId.
|
||||
*/
|
||||
int MAINTENANCE_SCHEDULE_CRON_LENGTH = 128;
|
||||
|
||||
/**
|
||||
* Maximum length of controllerId.
|
||||
*/
|
||||
int MAINTENANCE_WINDOW_DURATION_LENGTH = 16;
|
||||
|
||||
/**
|
||||
* Maximum length of controllerId.
|
||||
*/
|
||||
int MAINTENANCE_WINDOW_TIMEZONE_LENGTH = 8;
|
||||
|
||||
/**
|
||||
* @return the distributionSet
|
||||
*/
|
||||
@@ -168,7 +183,13 @@ public interface Action extends TenantAwareBaseEntity {
|
||||
/**
|
||||
* Cancellation has been rejected by the controller.
|
||||
*/
|
||||
CANCEL_REJECTED;
|
||||
CANCEL_REJECTED,
|
||||
|
||||
/**
|
||||
* Action has been downloaded by the target and waiting for update to
|
||||
* start.
|
||||
*/
|
||||
DOWNLOADED;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,4 +214,36 @@ public interface Action extends TenantAwareBaseEntity {
|
||||
*/
|
||||
TIMEFORCED;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method checks whether the action has a maintenance schedule defined
|
||||
* for it. A maintenance schedule defines a set of maintenance windows
|
||||
* during which actual update can be performed. A valid schedule defines at
|
||||
* least one maintenance window.
|
||||
*
|
||||
* @return true if action has a maintenance schedule, else false.
|
||||
*/
|
||||
boolean hasMaintenanceSchedule();
|
||||
|
||||
/**
|
||||
* The method checks whether the maintenance schedule has already lapsed for
|
||||
* the action, i.e. there are no more windows available for maintenance.
|
||||
* Controller manager uses the method to check if the maintenance schedule
|
||||
* has lapsed, and automatically cancels the action if it is lapsed.
|
||||
*
|
||||
* @return true if maintenance schedule has lapsed, else false.
|
||||
*/
|
||||
boolean isMaintenanceScheduleLapsed();
|
||||
|
||||
/**
|
||||
* The method checks whether a maintenance window is available for the
|
||||
* action to proceed. If it is available, a 'true' value is returned. The
|
||||
* maintenance window is considered available: 1) If there is no maintenance
|
||||
* schedule at all, in which case device can start update any time after
|
||||
* download is finished; or 2) the current time is within a scheduled
|
||||
* maintenance window start and end time.
|
||||
*
|
||||
* @return true if maintenance window is available, else false.
|
||||
*/
|
||||
boolean isMaintenanceWindowAvailable();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.model;
|
||||
|
||||
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
|
||||
/**
|
||||
@@ -21,6 +23,9 @@ public class TargetWithActionType {
|
||||
private final String controllerId;
|
||||
private final ActionType actionType;
|
||||
private final long forceTime;
|
||||
private String maintenanceSchedule = null;
|
||||
private String maintenanceWindowDuration = null;
|
||||
private String maintenanceWindowTimeZone = null;
|
||||
|
||||
public TargetWithActionType(final String controllerId) {
|
||||
this.controllerId = controllerId;
|
||||
@@ -34,6 +39,46 @@ public class TargetWithActionType {
|
||||
this.forceTime = forceTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that also accepts maintenance schedule parameters and checks
|
||||
* for validity of the specified maintenance schedule.
|
||||
*
|
||||
* @param controllerId
|
||||
* for which the action is created.
|
||||
* @param actionType
|
||||
* specified for the action.
|
||||
* @param maintenanceSchedule
|
||||
* is the cron expression to be used for scheduling maintenance
|
||||
* windows. Expression has 6 mandatory fields and 1 last optional
|
||||
* field: "second minute hour dayofmonth month weekday year"
|
||||
* @param maintenanceWindowDuration
|
||||
* in HH:mm:ss format specifying the duration of a maintenance
|
||||
* window, for example 00:30:00 for 30 minutes
|
||||
* @param maintenanceWindowTimeZone
|
||||
* is the time zone specified as +/-hh:mm offset from UTC, for
|
||||
* example +02:00 for CET summer time and +00:00 for UTC. The
|
||||
* start time of a maintenance window calculated based on the
|
||||
* cron expression is relative to this time zone.
|
||||
*
|
||||
* @throws InvalidMaintenanceScheduleException
|
||||
* if the parameters do not define a valid maintenance schedule.
|
||||
*/
|
||||
public TargetWithActionType(final String controllerId, final ActionType actionType, final long forceTime,
|
||||
String maintenanceSchedule, String maintenanceWindowDuration, String maintenanceWindowTimeZone) {
|
||||
this.controllerId = controllerId;
|
||||
this.actionType = actionType;
|
||||
this.forceTime = forceTime;
|
||||
|
||||
if (MaintenanceScheduleHelper.validateMaintenanceSchedule(maintenanceSchedule, maintenanceWindowDuration,
|
||||
maintenanceWindowTimeZone)) {
|
||||
this.maintenanceSchedule = maintenanceSchedule;
|
||||
this.maintenanceWindowDuration = maintenanceWindowDuration;
|
||||
this.maintenanceWindowTimeZone = maintenanceWindowTimeZone;
|
||||
} else {
|
||||
throw new InvalidMaintenanceScheduleException("Invalid maintenance window definition");
|
||||
}
|
||||
}
|
||||
|
||||
public ActionType getActionType() {
|
||||
if (actionType != null) {
|
||||
return actionType;
|
||||
@@ -52,4 +97,31 @@ public class TargetWithActionType {
|
||||
public String getControllerId() {
|
||||
return controllerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maintenance schedule for the {@link Action}.
|
||||
*
|
||||
* @return cron expression as {@link String}.
|
||||
*/
|
||||
public String getMaintenanceSchedule() {
|
||||
return this.maintenanceSchedule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the duration of maintenance window for the {@link Action}.
|
||||
*
|
||||
* @return duration in HH:mm:ss format as {@link String}.
|
||||
*/
|
||||
public String getMaintenanceWindowDuration() {
|
||||
return maintenanceWindowDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timezone of maintenance window for the {@link Action}.
|
||||
*
|
||||
* @return the timezone offset from UTC in +/-hh:mm as {@link String}.
|
||||
*/
|
||||
public String getMaintenanceWindowTimeZone() {
|
||||
return maintenanceWindowTimeZone;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,18 @@ public class TenantConfigurationProperties {
|
||||
*/
|
||||
public static final String POLLING_TIME_INTERVAL = "pollingTime";
|
||||
|
||||
/**
|
||||
* See system default in
|
||||
* {@link ControllerPollProperties#getMinPollingTime()}.
|
||||
*/
|
||||
public static final String MIN_POLLING_TIME_INTERVAL = "minPollingTime";
|
||||
|
||||
/**
|
||||
* See system default in
|
||||
* {@link ControllerPollProperties#getMaintenanceWindowPollCount()}.
|
||||
*/
|
||||
public static final String MAINTENANCE_WINDOW_POLL_COUNT = "maintenanceWindowPollCount";
|
||||
|
||||
/**
|
||||
* See system default in
|
||||
* {@link ControllerPollProperties#getPollingOverdueTime()}.
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) Siemens AG, 2018
|
||||
*
|
||||
* 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.tenancy.configuration.validator;
|
||||
|
||||
/**
|
||||
* Specific tenant configuration validator, which validates that the given value
|
||||
* is an Integer.
|
||||
*/
|
||||
public class TenantConfigurationIntegerValidator implements TenantConfigurationValidator {
|
||||
|
||||
@Override
|
||||
public Class<?> validateToClass() {
|
||||
return Integer.class;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user