Skip to content

Commit 16039cc

Browse files
Gupta, SuryaGupta, Surya
authored andcommitted
[CSTACKEX-204] ASUP Changes without restart
1 parent 91f1146 commit 16039cc

2 files changed

Lines changed: 65 additions & 10 deletions

File tree

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/asup/OntapAsupManager.java

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949

5050
import javax.inject.Inject;
5151
import javax.naming.ConfigurationException;
52+
import java.time.Duration;
53+
import java.time.Instant;
5254
import java.util.EnumSet;
5355
import java.util.HashSet;
5456
import java.util.LinkedHashMap;
@@ -84,11 +86,19 @@ public class OntapAsupManager extends ManagerBase implements Configurable {
8486
OntapStorageConstants.ASUP_INTERVAL_CONFIG_KEY,
8587
String.valueOf(OntapStorageConstants.ASUP_DEFAULT_INTERVAL_SECONDS),
8688
OntapStorageConstants.ASUP_INTERVAL_DESCRIPTION,
87-
false, ConfigKey.Scope.Global);
89+
true, ConfigKey.Scope.Global);
8890

8991
/** Time (in seconds) to wait while acquiring the single-emitter global lock. */
9092
private static final int ASUP_LOCK_TIMEOUT_SECONDS = 5;
9193

94+
/**
95+
* Fixed wakeup interval (ms) for {@link OntapAsupPollTask}. The task wakes on this
96+
* cadence and checks whether the configured push interval ({@link #AsupIntervalSeconds})
97+
* has elapsed. This decouples the scheduler's fixed delay from the live config value,
98+
* so changes made in the CloudStack UI take effect without a management-server restart.
99+
*/
100+
static final long ASUP_POLL_CHECK_INTERVAL_MS = 60_000L;
101+
92102
/**
93103
* Volume states that guarantee a physical object exists on the ONTAP FlexVolume.
94104
* States like {@link Volume.State#Allocated} have a CloudStack DB row pointing to this
@@ -109,6 +119,13 @@ public class OntapAsupManager extends ManagerBase implements Configurable {
109119
/** Serializes the structured event-description payloads to JSON. */
110120
private final ObjectMapper objectMapper = new ObjectMapper();
111121

122+
/**
123+
* Timestamp of the last successful ASUP push. Starts at {@link Instant#EPOCH} so the
124+
* very first wakeup always fires immediately. {@code volatile} ensures the poll-task
125+
* thread's write is visible without synchronization overhead.
126+
*/
127+
volatile Instant lastPushTime = Instant.EPOCH;
128+
112129
@Inject
113130
private PrimaryDataStoreDao storagePoolDao;
114131
@Inject
@@ -510,13 +527,26 @@ public ConfigKey<?>[] getConfigKeys() {
510527
/**
511528
* Background poll task that runs the ASUP push within a managed CloudStack context.
512529
*
513-
* <p>Submitted once to the shared {@link BackgroundPollManager} during the configure-phase;
514-
* the poll manager owns the thread and invokes this task every {@link #getDelay()} ms.</p>
530+
* <p>Wakes every {@link #ASUP_POLL_CHECK_INTERVAL_MS} ms. On each wakeup it reads
531+
* the live value of {@link #AsupIntervalSeconds} and only pushes if enough time has
532+
* elapsed since {@link #lastPushTimeMs}. This means changing the interval in the
533+
* CloudStack UI takes effect within one check cycle — no restart required.</p>
515534
*/
516535
protected class OntapAsupPollTask extends ManagedContextRunnable implements BackgroundPollTask {
517536
@Override
518537
protected void runInContext() {
519538
try {
539+
int intervalSeconds = AsupIntervalSeconds.value() != null
540+
? AsupIntervalSeconds.value() : OntapStorageConstants.ASUP_DEFAULT_INTERVAL_SECONDS;
541+
if (intervalSeconds <= 0) {
542+
intervalSeconds = OntapStorageConstants.ASUP_DEFAULT_INTERVAL_SECONDS;
543+
}
544+
Duration configuredInterval = Duration.ofSeconds(intervalSeconds);
545+
Instant now = Instant.now();
546+
if (Duration.between(lastPushTime, now).compareTo(configuredInterval) < 0) {
547+
return; // configured interval has not elapsed yet
548+
}
549+
lastPushTime = now;
520550
pushAsupForAllStoragePools();
521551
} catch (Exception e) {
522552
logger.warn("ONTAP ASUP: unexpected error during periodic push: {}", e.getMessage());
@@ -525,12 +555,7 @@ protected void runInContext() {
525555

526556
@Override
527557
public Long getDelay() {
528-
int intervalSeconds = AsupIntervalSeconds.value() != null
529-
? AsupIntervalSeconds.value() : OntapStorageConstants.ASUP_DEFAULT_INTERVAL_SECONDS;
530-
if (intervalSeconds <= 0) {
531-
intervalSeconds = OntapStorageConstants.ASUP_DEFAULT_INTERVAL_SECONDS;
532-
}
533-
return intervalSeconds * 1000L;
558+
return ASUP_POLL_CHECK_INTERVAL_MS;
534559
}
535560
}
536561
}

plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/asup/OntapAsupManagerTest.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.mockito.MockedStatic;
4545
import org.mockito.junit.jupiter.MockitoExtension;
4646

47+
import java.time.Instant;
4748
import java.util.Arrays;
4849
import java.util.Collections;
4950
import java.util.HashMap;
@@ -420,14 +421,43 @@ void allMessages_haveCorrectEnvelopeFields() {
420421

421422
@Test
422423
void asupIntervalSeconds_defaultIsProductionValue() {
423-
assertEquals("120", OntapAsupManager.AsupIntervalSeconds.defaultValue());
424+
assertEquals(String.valueOf(OntapStorageConstants.ASUP_DEFAULT_INTERVAL_SECONDS),
425+
OntapAsupManager.AsupIntervalSeconds.defaultValue());
424426
}
425427

426428
@Test
427429
void asupEnabled_defaultIsTrue() {
428430
assertEquals("true", OntapAsupManager.AsupEnabled.defaultValue());
429431
}
430432

433+
// ──────────────────────────────────────────────────────────────────────────
434+
// OntapAsupPollTask – self-throttle (interval change takes effect without restart)
435+
// ──────────────────────────────────────────────────────────────────────────
436+
437+
@Test
438+
void pollTask_getDelay_returnsFixedCheckInterval() {
439+
OntapAsupManager.OntapAsupPollTask task = asupManager.new OntapAsupPollTask();
440+
assertEquals(OntapAsupManager.ASUP_POLL_CHECK_INTERVAL_MS, task.getDelay());
441+
}
442+
443+
@Test
444+
void pollTask_skipsWhenIntervalNotElapsed() {
445+
asupManager.lastPushTime = Instant.now(); // just pushed
446+
OntapAsupManager.OntapAsupPollTask task = asupManager.new OntapAsupPollTask();
447+
task.run();
448+
verify(storagePoolDao, never()).findPoolsByProvider(any());
449+
}
450+
451+
@Test
452+
void pollTask_pushesWhenIntervalElapsed() {
453+
asupManager.lastPushTime = Instant.EPOCH; // never pushed
454+
when(storagePoolDao.findPoolsByProvider(OntapStorageConstants.ONTAP_PLUGIN_NAME))
455+
.thenReturn(Collections.emptyList());
456+
OntapAsupManager.OntapAsupPollTask task = asupManager.new OntapAsupPollTask();
457+
task.run();
458+
verify(storagePoolDao).findPoolsByProvider(OntapStorageConstants.ONTAP_PLUGIN_NAME);
459+
}
460+
431461
// ──────────────────────────────────────────────────────────────────────────
432462
// Utility helpers
433463
// ──────────────────────────────────────────────────────────────────────────

0 commit comments

Comments
 (0)