This commit is contained in:
Kevin L'Hereec committed 2026-06-01 16:58:52 +02:00
1 parent ea73e91aa4
commit a5b3135396
4 files changed
+154 -1

No files matched your search

+137
View File
@@ -0,0 +1,137 @@
# Debug: Reclassification Properties → Parameters
## Contexte du problème
Les paramètres `Device.ManagementServer.PeriodicInformInterval` et `Device.DeviceInfo.ProvisioningCode` apparaissent comme **properties** (read-only) dans l'interface alors qu'ils devraient être des **parameters** (writable).
Le catalogue `config/tr181-catalog.json` les marque bien comme `writable: true`.
La fonction `reclassifyDeviceFromCatalog()` dans `app/services/parameter_writability.ts` est censée les déplacer de properties vers parameters quand `gpnStatus === 'unsupported'`.
Le dernier log de debug montre : `[Reclassify] properties=9, reclassified=0, catalogSize=2953` — la fonction est appelée mais ne reclassifie rien malgré les paths présents dans le catalogue.
## Fichiers impliqués
- `app/services/parameter_writability.ts` — contient `reclassifyDeviceFromCatalog()` et `normalizePath()`
- `app/services/cwmp/device_action_executor.ts` — appelle la reclassification quand GPN échoue
- `app/services/cwmp/main_state_machine.ts` — `CompletedState` appelle aussi la reclassification
- `app/controllers/cwmp_controller.ts` — `handleInform()` appelle la reclassification si `gpnStatus === 'unsupported'`
- `app/domain/device/device.ts` — `updateFromValues()` stocke les nouveaux params comme properties par défaut
- `config/tr181-catalog.json` — référentiel de writabilité TR-181
## Procédure de test complète
### 1. Faire la modification de code
Modifier le fichier concerné, puis vérifier la compilation :
```bash
npx tsc --noEmit
```
### 2. Build et relance Docker
```bash
docker compose build app
docker compose up -d app
```
Attendre que l'app soit healthy :
```bash
docker compose logs app --tail 5
```
### 3. Supprimer le device Sagemcom de l'ACS
```bash
curl -s -X DELETE http://localhost:8080/api/v1/devices/2e933565b8fc09f34f42531e
```
### 4. Supprimer le device du simulateur (home-device-simulator)
```bash
curl -s -X DELETE "http://localhost:9898/api/v1/device-simulator/delete-devices" \
-H 'Content-Type: application/json' \
-d '["F8:B7:FF:00:11:22"]'
```
### 5. Recréer le device dans le simulateur
```bash
curl -s -X PUT "http://localhost:9898/api/v1/device-simulator/create-devices?noOfDevices=1&autoGenerate=false&ipAddressType=ipv4" \
-H 'Content-Type: application/json' \
-d '{
"manufacturer": "Sagemcom",
"oui": "F8B7FF",
"productClass": "Livebox 4",
"serialNumber": "LB4-TEST-001",
"macaddress": "F8:B7:FF:00:11:22",
"parameters": [
{"type": "Manufacturer", "model": "Device.DeviceInfo.Manufacturer", "value": "Sagemcom"},
{"type": "ManufacturerOUI", "model": "Device.DeviceInfo.ManufacturerOUI", "value": "F8B7FF"},
{"type": "ModelName", "model": "Device.DeviceInfo.ModelName", "value": "Livebox 4"},
{"type": "ProductClass", "model": "Device.DeviceInfo.ProductClass", "value": "Livebox 4"},
{"type": "SerialNumber", "model": "Device.DeviceInfo.SerialNumber", "value": "LB4-TEST-001"},
{"type": "HardwareVersion", "model": "Device.DeviceInfo.HardwareVersion", "value": "SR40_sip-fr-4.62.0"},
{"type": "SoftwareVersion", "model": "Device.DeviceInfo.SoftwareVersion", "value": "SR40_sip-fr-4.62.0"}
]
}'
```
### 6. Attendre l'Inform (30s max) puis vérifier
```bash
sleep 30
```
Vérifier la classification :
```bash
curl -s "http://localhost:8080/api/v1/devices/2e933565b8fc09f34f42531e" | \
python3 -c "
import sys,json
d=json.load(sys.stdin)
print('gpnStatus:', d.get('gpnStatus'))
props = [p['name'] for p in d.get('properties',[]) if 'PeriodicInform' in p['name'] or 'ProvisioningCode' in p['name']]
params = [p['name'] for p in d.get('parameters',[]) if 'PeriodicInform' in p['name'] or 'ProvisioningCode' in p['name']]
print('Dans properties (MAUVAIS):', props)
print('Dans parameters (BON):', params)
"
```
### 7. Regarder les logs de reclassification
```bash
docker compose logs app --tail 50 2>&1 | grep -i "reclassify\|Reclassify"
```
## Résultat attendu
```
gpnStatus: unsupported
Dans properties (MAUVAIS): []
Dans parameters (BON): ['Device.ManagementServer.PeriodicInformInterval', 'Device.DeviceInfo.ProvisioningCode']
```
## Identifiants du device de test
| Champ | Valeur |
|-------|--------|
| ACS identifier | `2e933565b8fc09f34f42531e` |
| Manufacturer | Sagemcom |
| OUI | F8B7FF |
| Product Class | Livebox 4 |
| Serial Number | LB4-TEST-001 |
| MAC (simulateur) | F8:B7:FF:00:11:22 |
| Data Model | tr-181 |
## API du simulateur
- Base URL : `http://localhost:9898/api/v1/device-simulator`
- Swagger : `http://localhost:9898/swagger/`
- Suppression par MAC : `DELETE /delete-devices` body `["MAC"]`
- Création : `PUT /create-devices?noOfDevices=1&autoGenerate=false&ipAddressType=ipv4`
## Trace TR-069
Le device est déjà dans la config de trace (`data/tr069-trace.json` dans le volume Docker `app_tr069_data`). Les logs SOAP sont visibles sur `http://localhost:8080/tr069-trace`.
@@ -108,7 +108,8 @@ export class ConfigurationEvaluationState {
)
return { response }
} else if (this.inventoryQueue.length > 0) {
// GPN unsupported — clear inventory queue and skip to next phase
// GPN unsupported — skip inventory (GPV on object paths also fails on most simulators)
// Parameters will be discovered via Inform values and SPV responses
logger.debug({ identifier: context.device.identifier, skipped: this.inventoryQueue.length }, '[ConfigEval] Skipping inventory (GPN unsupported)')
this.inventoryQueue = []
}
@@ -98,6 +98,9 @@ export class DeviceActionExecutor {
const list = resp?.ParameterList?.ParameterValueStruct
if (Array.isArray(list) && list.length > 0) {
device.updateFromValues(list)
if (device.gpnStatus === 'unsupported') {
reclassifyDeviceFromCatalog(device)
}
await this.persistDevice(device)
}
} catch (e) {
@@ -235,6 +238,9 @@ export class DeviceActionExecutor {
async (result: TaskResult) => {
if (result.status === ResultStatus.Success) {
device.updateFromValues(pvStructs as any)
if (device.gpnStatus === 'unsupported') {
reclassifyDeviceFromCatalog(device)
}
await this.persistDevice(device)
return await callback(true)
}
@@ -396,6 +402,9 @@ export class DeviceActionExecutor {
async (result: TaskResult) => {
if (result.status === ResultStatus.Success) {
device.updateFromValues(pvStructs as any)
if (device.gpnStatus === 'unsupported') {
reclassifyDeviceFromCatalog(device)
}
await this.persistDevice(device)
device.tmpPC = ''
return await callback(true)
+6
View File
@@ -8,6 +8,7 @@ import CommandModel from '../../models/command.js'
import { ModeType } from '../../domain/shared/mode.js'
import logger from '@adonisjs/core/services/logger'
import { ConfigurationEvaluationState } from './configuration_evaluation_state.js'
import { reclassifyDeviceFromCatalog } from '../parameter_writability.js'
// ─── Interfaces ────────────────────────────────────────────────────────────────
@@ -277,6 +278,11 @@ class CompletedState extends DeviceState {
name = 'completed'
async handle(context: DeviceContext): Promise<DeviceStateResult> {
// Reclassify properties → parameters if GPN is unsupported
if (context.device.gpnStatus === 'unsupported') {
reclassifyDeviceFromCatalog(context.device)
}
// Persist device on completion
try {
const snapshot = context.device.toSnapshot()