Full Changelog

Release: v6.5.2

Sprint: v6.5.2

Breaking Changes

Jira References

  • DEV-1609 - USP > Cpeadmin > update a cpe > ping not working

  • DEV-1745 - OneIoT ACS > USP > Monitoring > Bulk data > MQTT > Activate > Irrelevant object path in SPV task sent

  • DEV-1795 - OneIoT ACS > Group Update > Add USP compatibility for GU Diagnostics as Historical data

  • DEV-1828 - CPE Admin > USP > Group Update > Restore > Incorrect URL is sent to device

  • DEV-1857 - OneIoT ACS > USP > Monitoring > New / Edit > Save and activate > Subscriptions are duplicated

  • DEV-2061 - OneIoT ACS > Docs Portal > Installation & Deployment > "5.2. Docker Compose File" > Expand "compose.yml" example > Absent "TZ" environment variable

  • DEV-2117 - QoE GUI > Group update with Diagnostics > Diagnostic data present in QoE DB > Single device > No data available in table

  • DEV-2123 - Angular Project > Several devices > Management portal > Group update with Download / Upload diagnostics > "Add results to monitoring data" = "1" > Not all results are stored into CH DB

  • DEV-2131 - Angular Project > Several devices > Management portal > Group update with Download / Upload diagnostics > "Add results to monitoring data" = "1" > Data is not stored in the CH DB

  • DEV-2196 - OneIoT ACS > TR-069 > Any event from device > Prometheus metrics endpoint > Event distribution counter > No counter value

  • DEV-2209 - Angular Project > Support Portal > TR-098 > "Ports" cube > Edit > Add any port > "Request device to connect" = true > Device activity > Parameter name is empty for "Add object" task

  • DEV-2212 - Angular Project > TR-069 > Initiate Connection Request > Connection Request is not performed

  • DEV-2222 - OneIoT ACS > Multi-domain / Multi-ISP > Specific profile in super domain > Default profile in specific domain > Device related to specific domain > Specific profile related to super domain applied

  • DEV-2249 - OneIoT ACS > Big amount of devices > Update group > More than one diagnostic > "8 DIAGNOSTICS COMPLETE" event > Next diagnostic task stucked

  • DEV-2269 - CPE Admin > Device Update > Trace a Device > Start > Open device > Trace not active

  • DEV-2270 - OneIoT ACS > Oracle > Monitoring > "QoE: set periodic inform interval" task is not sent to the device

  • DEV-2286 - OneIoT ACS > Update group > More than one diagnostic > Add results to monitoring data="1" > "8 DIAGNOSTICS COMPLETE" event > Event absent from DB table

  • DEV-2307 - OneIoT ACS > Run > Error in server log file is present

  • DEV-2349 - OneIoT ACS > Monitoring > Device brings monitored value > Events from device stored in DB

  • DEV-539 - OneIoT ACS > USP > Group Update > Upload > Vendor Configuration File > Incorrect file format

  • DEV-688 - OneIoT ACS > MySQL > Management Portal > Monitoring > Active monitor > Add parameter > Save > Stop and start monitoring-related tasks are generated for all parameters

  • DEV-775 - OneIoT ACS > TR-069 / USP > Device Update > File Download > 7 TRANSFER COMPLETE > List of tasks disappears

  • DEV-893 - sensitive_params.xml placement

Features

  • Add dataStoreExclusionModels ACS setting to exclude old devices deletion with selected product classes and/or OUIs.

  • Added doNotDeletePreprovisionedDevices ACS setting to skip preprovisioned devices during old data deletion.

Bug Fixes

  • correct undefined variable in basic auth password check

Reference storedPassword instead of the nonexistent stored variable
that broke compilation of tr069-http.
  • use storedPassword in BASIC auth comparison

_authenticateBasic referenced an undefined variable `stored` instead of
the in-scope local `storedPassword`, failing compilation of tr069-http.
Compare the decrypted stored password to the provided plaintext one.
  • set misfire instructions on UpdateGroup start triggers

In a clustered Quartz scheduler the UpdateGroup start triggers were
created without an explicit misfire instruction, unlike the activate
trigger which already sets MISFIRE_INSTRUCTION_DO_NOTHING. When a start
trigger missed its scheduled fire-time (aggravated by a 60s
batchTriggerAcquisitionFireAheadTimeWindow), the cluster MisfireHandler
recovered it with smart policy (FIRE_ONCE_NOW) on another node, starting
the whole UpdateGroup a second time within seconds of the on-time fire.
Every CPE was re-activated, so a chained multi-diagnostic update group
(IPPing -> Download -> Upload, "Add results to monitoring data") ran the
full diagnostic chain twice and its tasks were duplicated. The
"already running" guard does not help because the first activation
finishes before the misfire re-fire.

Mirror createActivateTrigger: the cron reactivation start trigger now
uses MISFIRE_INSTRUCTION_DO_NOTHING (skip the missed occurrence) and the
one-shot scheduled start trigger uses MISFIRE_INSTRUCTION_FIRE_NOW (fire
exactly once, never duplicate, never drop the only activation).
  • move UG completion gate to authoritative cpe_pending_task DB query

In a chained multi-diagnostic update group (IPPing -> Download -> Upload,
"Add results to monitoring data"), part of the CPEs stayed Pending in
ug_cpe_completed even though cpe_pending_task had no rows left for them.

onTaskResult decided "this CPE finished the UG transaction" via
CpeTaskProcessor.isExistsSameTransactionTasksForCpe, which prefers a
node-local in-memory cache kept consistent only for tasks completed on the
same node. The diagnostic chain hops between ACS nodes across QoE sessions,
so the node finishing the last diagnostic held a stale cache listing sibling
tasks completed elsewhere -> false-positive "tasks still exist",
addCpeToQueue skipped, CPE never moved to Completed.

Gate the decision on pendingTaskDao.isExistsSameTransactionTasksForCpe
giving the only correct cross-node view. The in-memory cache stays as an
optimization for next-task selection but is no longer trusted for the
cross-node completion gate.
  • log Inform events to cpe_log for QoE sessions

cpe_log rows for Inform events are written by FTACSInformAop.onInit, a @Before
advice on synchronizeInformWithDB. A QoE session never reaches that method:
TR069ManagementProcessor early-returns on qoeSession=true before
tr069ResponseHandler.handle(Inform) runs, so the "8 DIAGNOSTICS COMPLETE" event
(and any other event a QoE-session Inform carries) was never stored in cpe_log -
visible e.g. for an "Add results to monitoring data" group update.

Add CpeLogEventWriter and call it from the QoE-session branch before the
early-return, so the Inform's events are logged exactly when the regular path
(and its AOP advice) is skipped. Non-QoE Informs keep logging through the advice,
so there is no double write. The VALUE CHANGE -> changed-parameter linkage is
intentionally omitted: it belongs to the synchronizeInformWithDB flow, which a
QoE session does not run.
  • send next chained diagnostic when 8 DC finalizes on GPV pass

A QoE-monitored CPE that reports "8 DIAGNOSTICS COMPLETE" runs a QoE
session, so TR069ManagementProcessor skips the regular ACS task chain and
the next chained DiagnosticUpdateGroup task is not sent until a Connection
Request or the next ACS-session window (up to 24h) - the chain appeared
stuck.

For diagnostics whose values are not inline in the 8 DC Inform (IPPing,
Upload report only DiagnosticsState; the values arrive via a follow-up GPV)
the in-flight task finalizes only on the GPV-response pass, after
handleInform already latched qoeSession=true. Clear qoeSession on that pass
once the diagnostic is finalized so the next chained diagnostic is sent in
the same session.

That clearing check (and the inline path at handleInform) read
isAcsSessionTime() right after nextSessionOnAcs() writes it, so switch that
write from IMap.setAsync to a synchronous IMap.set: setAsync gave no
happens-before guarantee and the immediate read-after-write saw the stale
future timestamp under cluster/load.
  • send next chained diagnostic in the 8 DC session (DEV-2249)

After v3 fixed completion of the in-flight DiagnosticUpdateGroup task, the
next chained diagnostic (IPPing -> Download -> Upload) still was not sent
after "8 DIAGNOSTICS COMPLETE": for a QoE-monitored CPE that Inform is a QoE
session (qoeSession=true) and TR069ManagementProcessor skips the regular ACS
task chain, so the next type-66 task stays in cpe_pending_task until a manual
Connection Request or the next ACS-session window (qoeMonitorCpeSyncDuration,
default 1440 min = 24h). Confirmed on a single-device trace + cluster ACS

checkDiagnosticComplete runs BEFORE setQoeSession on the Inform pass, and the
8 DC result is inline, so forcing the ACS session right after completing the
diagnostic makes isAcsSessionTime() true -> qoeSession=false -> the next
chained diagnostic is sent in the same session, with no manual CR and no
24h wait. Mirrors the existing WiFi-rescan path (nextSessionOnAcs).
  • resolve diagnostic task ids via ActionSetParamValueDao (DEV-2249)

The identity match that completes the in-flight DiagnosticUpdateGroup task (so
the next chained diagnostic can be sent) resolved the task-name ids via
cpeProvisionDao.getByIds (cpe_provision) using CPEProvision.getCPEParameterNameId.
Wrong table: a DiagnosticUpdateGroup (type 66) task name carries
ug_set_param_value ids, which UpdateGroupSetValueMessageCreator resolves via
ActionSetParamValueDao.getByIds -> ActionSetParamValue.getNameId. The
cpe_provision lookup matched nothing, so no task was completed, the in-flight
diagnostic stayed in cpe_pending_task, and CpeTaskProcessor.resolveRejected
skipped every subsequent diagnostic ("existing not finished diagnostic").
Deterministic, reproduced single-device with "Add to monitoring data=1"
(monitoring=0 is unaffected: it never routes completion through this path).

Switch resolveSetParamValueNameIds to the already-injected actionSetParamValueDao
-> ActionSetParamValue.getId()/getNameId(), mirroring the message creator. Update
the unit test to mock ActionSetParamValueDao (the previous mock hid the bug).
Identity-match algorithm, finalization gate and IPLayerCapacity exclusion
unchanged.
  • match in-flight diagnostic task by parameter identity (DEV-2249)

The "8 DIAGNOSTICS COMPLETE" cleanup identified the in-flight
DiagnosticUpdateGroup task by confirmed=1, falling back to the oldest
non-completed type-66 task. Under load / cross-node the confirmed and repeats
flags are committed in a separate transaction on the SPV-response node, so the
confirmed lookup lost the race (task stuck) while the oldest fallback completed
the next, not-yet-sent diagnostic (silent drop, e.g. Download in
IPPing -> Download -> Upload).

Match the task to the diagnostic that actually finished, by that diagnostic's
own <root>DiagnosticsState parameter name id (fixed at task creation, so it is
immune to the async flags and visible cross-node). Gate completion on
finalization (process*Diag queued no result GPV-fetch this pass) so the
Inform + GPV-response double pass cannot complete a later round's task. Resolve
all candidate tasks' provision name ids in a single batched getByIds.
  • DEV-2249

Harden the chained-UG diagnostic-complete cleanup so an unguarded
IPLayerCapacity re-report can no longer complete a not-yet-sent
DiagnosticUpdateGroup task (silent diagnostic skip / data loss).

checkDiagnosticComplete now separates containsGuardedDiag (the five
pending-cache-backed process*Diag: Download/Upload/UdpEcho/Trace/Ping,
each of which proves an in-flight type-66 task exists) from the
unguarded processIPLayerCapacityDiag, and passes it to
resolveInFlightDiagnosticTasksToComplete(cpeId, allowOldestFallback).
The confirmed=1 branch runs unconditionally (unchanged); the oldest-id
fallback runs only for a guarded completion. IPLayerCapacity-only
events keep the pre-DEV-2249 confirmed-only behaviour, so there is no
regression and no stranding (IPLayerCapacity re-reports on every
periodic Inform, so a genuine in-flight task still completes via the
confirmed=1 branch). The repeats>0 filter was rejected (repeats is also
written async, ~2s, which would reopen the race).

Adds 2 boundary unit tests: oldest=not-yet-sent must not fall back, and
a confirmed task is still completed on an unguarded trigger.
  • persist ACS parameters via AcsConfigurationCache

removeFromExclusionModels called the removed file-based method
DMConfigurationInitializer.setConfigurationParameterValue, which broke the
ftdm-core compilation after the migration to ft-configs-service.

The ACS parameter write is moved into a shared AcsConfigurationCache — the
single write point for the acs-configuration Hazelcast snapshot. The snapshot
is updated via set(0, ...), mirroring IListCacheWriter.replaceSnapshot in
ft-configs-service: an atomic in-place replace, so the list is never empty.
The same event is consumed by FTIOTConfigurationSyncUtil (runtime parameters
on each node) and by AcsConfigurationCacheListener in ft-configs-service (DB
write-back). Reused in WSSystemService and FTCpeProductService.

Snapshot writes are logged (key, mode, entry count); sensitive values
(password/secret/token/credential keys) are masked.
  • DEV-2212

  • Fix an issue that caused the default autoDeleteCron value to be used instead of the configured one.

  • Do not ignore password’s case in Basic authentication.

  • Fix flow for device in the blocked list (blockedModelList ACS setting).

  • DEV-1828 OneIoT ACS > USP > Group Update > Restore > discover VendorConfigFile instance via USP GET

Replace the cache-only instance lookup with an async utility
discoverObjectInstance(clientId, cpeId, prefix, onResult, onError) in
AbstractUspTaskProcessor: tries the local parameter cache first; on a
miss, issues a USP GET against the object prefix, back-fills the cache
from the response, and extracts the lowest instance. Falls back to 0
(DEFAULT_INSTANCE_FALLBACK) only when both cache and GET yield nothing,
matching the existing backupConfiguration/uploadLogFile defaults.

Restore (UspRestoreCpeConfigurationTaskProcessor,
AbstractUspDownloadTaskProcessor) and Backup/UploadLog
(AbstractUploadTaskProcessor) all now route through the utility, so a
freshly-registered device with no cached Device.DeviceInfo.VendorConfigFile.*
parameters no longer falls through to a hard-coded instance 1 and triggers
"Command not defined" (err 7026).

Also lowers the legacy fallback in UspCommsService.getFileObjectInstanceNumber
from 1 to 0 so any caller that bypasses the utility stays consistent with
the rest of the file-transfer surface.

Documentation

  • update antora version to 6.5.2

  • document readonly registry credentials for docker login

Replace the <your-username>/<your-password> placeholders in the Docker
registry authentication section with the actual read-only pull account
(username: readonly), so users can authenticate against
hub.friendly-tech.com and pull the published images. The account is
pull-only and cannot push.
  • add TZ to mysql service in compose.yml example

The mysql service was the only one in the stack without timezone
propagation (no env_file, no explicit TZ), leaving the container in UTC
while the rest of the stack runs in Europe/Kiev. The mismatch causes
drift between DB and application timestamps.
  • update antora version to 6.5.2 (dev)

Chores

Refactoring

  • use shared ProductClassGroupCacheDto for qoeProductClassGroupCache

Replace the internal ProductClassGroupExtended shadow class with the
shared-dto ProductClassGroupCacheDto across all usages so the Hazelcast
qoeProductClassGroupCache stores the common DTO and ft-configs-service can
read it without a shadow class. Construction now goes through the DTO
builder since it has no public no-arg or two-arg constructor.

Add the shared-dto dependency (and its resolving repositories) to
ftapp-core and qoe-streaming-core, which now reference the DTO directly.
Drop the obsolete AcsProductModelsPublisher that duplicated the catalog
into a separate IList; the IMap is read directly instead.

Resolve model-level dataStoreExclusionModels tokens (OUI|model|protocol,
protocol optional) to product_class.id(s) via the DAOs in
FTCpeDeleteTaskJob, so exclusions work before any device has connected.

This changes the IMap value type, so existing cache data is incompatible
and the deployment must start with a fresh Hazelcast.
  • address DEV-1828 review feedback

HIGH-1 AbstractUspDownloadTaskProcessor.runRestore: stop mutating the
shared TransferFile returned by findFile() inside the async lambda and
build a fresh instance via the builder, mirroring what
UspRestoreCpeConfigurationTaskProcessor already does. Eliminates a
latent data race when concurrent restores share a cached DAO object.

HIGH-3 AbstractUspDownloadTaskProcessor.runRestore: bring the
VENDOR_CONFIG_FILE_3 path to parity with
UspRestoreCpeConfigurationTaskProcessor by filling targetFileName from
FileBackupProcessor.getCpeFileName when the caller did not supply one,
and by appending the same filename to the URL when the caller's URL
lacks a file extension. Mirrors the same logic in
AbstractUploadTaskProcessor, ensuring symmetric handling between the
group-update Download (file type 3) and the dedicated Restore task
types.

MEDIUM-1 UspCommsService.getFileObjectInstanceNumber: drop the legacy
NumberUtils.isParsable(targetFileName) branch. With every caller now
routed through AbstractUspTaskProcessor.discoverObjectInstance, the
target filename is always a real .cfg name and never a numeric instance
id, so the parsing branch was dead. Method is reduced to the early
return + a single warning + a default of 0, consistent with
backupConfiguration / uploadLogFile. Removes the now-unused
NumberUtils import.

MEDIUM-6 AbstractUspTaskProcessor.extractLowestInstance: pre-compile the
two known instance-extraction patterns into a static Map keyed by
prefix; only fall back to on-the-fly compile for unknown prefixes.

Performance

Tests

Build

CI