Migrazione dei box dalla 3.2.x alla nuova versione¶
Analisi del 29/10/2026 sul codice di mdm_installation: origin/main (3.2.9, commit 531e6552) e origin/stage (3.9.0, commit d02dbaee). Nessuna prova ancora fatta su un box 3.2.x (ticket MDM-081).
Ricontrollo del 30/10/2026 su origin/stage al commit ebde226c: i 7 commit successivi (correzioni portate da main, anti-manomissione, audio) non toccano migrazioni, updater, update.yml, compose né build tag. A quel commit l'import e il ponte non erano iniziati. Aggiornamento del 02/11/2026: l'import è ora committato in db444987; il ponte resta da realizzare. infra/environments/prod/.env.build_tag indica ancora 3.2.8/3.3.0. Le correzioni di main sono riportate (dettaglio).
Verifica del 02/11/2026¶
Riferimenti remoti controllati con git ls-remote origin refs/heads/main refs/heads/stage: main = 531e6552, stage = db444987. Nessuna modifica ai branch remoti, ai segreti AWS o ai servizi dei box durante queste verifiche.
| Controllo | Esito osservato |
|---|---|
Frigate .168 |
internal_server:3.2.9, wrapper 3.4.0, Frigate 0.17.1 healthy; checkout 531e6552, sola modifica locale al .env del box; lettore execpipe.sh presente |
| Stato Frigate | /status e /api/v1/detection/status HTTP 200; box, detector e wrapper online, una telecamera, inserito (disarm.status = false, wrapper status = true) |
| Isolamento Frigate | HTTP/MQTT verso il dev, LOGSERVERBASEURL disabilitato. Il webhook alarm.php non punta a localhost né a un'interfaccia assegnata al box: Lorenzo conferma il 02/11 che è un ricevitore di laboratorio isolato; nessun allarme sintetico inviato in questa verifica |
GANZ .165 |
internal_server:3.9.0-stage, checkout host ancora d02dbaee; API HTTP 200, una telecamera, detector online e disinserito. Il checkout host non prova quale codice sia stato copiato nel container |
| Isolamento GANZ | HTTP/MQTT verso il dev; invio dei log verso BSQ non disabilitato. Da verificare/isolare prima di prove che producono eventi |
| Test locali dell'import | uv run pytest tests/test_app/test_core/test_db/test_legacy_import.py tests/test_app/test_core/test_db/test_migration_manager.py -q: 17 passed. ruff check e ruff format --check sui due moduli di import e sul relativo test: superati |
| Import su backup reale GANZ | Copia privata e temporanea del backup stage-cutover-20261028T151536Z/installation-original, sul solo box di laboratorio; sorgente aperta in sola lettura e database copiato con SQLite backup API. Migrazioni candidate di db444987 montate nell'immagine stage già presente, senza avviare il servizio |
| Stato nel backup GANZ | La tabella arm_state esiste ma è vuota. Senza stato acquisito e con rete disabilitata l'import si ferma con LegacyImportError. La presenza della tabella non basta per scegliere il percorso della migrazione |
| Import GANZ con stato acquisito | Letto lo stato dal detector configurato mediante GET, poi fornito quel booleano al lettore del processo di verifica. Migrazione effettuata su copia in container --network none: schema 7, configurazione, impostazioni, I/O e stato disinserito conservati; JSON originali e copie uguali, SQLite integro prima/dopo, seconda esecuzione senza cambiamenti di configurazione o stato. Nel backup ci sono 0 log: il conteggio resta 0, non è una prova su log esistenti. Un campo segnalato come non importato, da verificare prima di dichiarare la migrazione completa |
| Pannello → midserver → box | Prova manuale di Lorenzo del 02/11 su Frigate: versione 3.2.9, inizialmente inserito, telecamera presente; disinserimento e reinserimento riusciti, nessun nuovo errore relativo ai due comandi. La persistenza dopo refresh non è riportata separatamente. Le prove di aggiornamento A1/A2 sotto restano evidenze del 30/10 |
| Live Frigate dal pannello | Non riuscito: il pannello segnala che il nome della telecamera manca nella configurazione degli stream MDM. Controllo SSH in sola lettura: webrtc_streams contiene 0 elementi, CAM_1 assente, coerente con la configurazione iniziale del laboratorio. La presenza della telecamera nel detector non crea lo stream MDM. Il codice origin/main:app/services/mqtt/handlers/web_rtc_handler.py cerca il nome in webrtc_streams prima di avviare FFmpeg: questa prova non verifica RTSP, codec o OME. Prima di aggiungere lo stream e riprovare, risolvere/verificare l'uscita SRT della 3.2.9, ancora scritta nel codice verso OME di produzione (185.31.66.185:9999, M15). Nessuno stream o servizio modificato |
| Ponte, cambio Compose, ripristino automatico | Non implementati né provati da questa verifica; MDM-081 resta aperto |
Preparazione del live Frigate dev (02/11/2026)¶
Dopo l'esito manuale dei comandi, Lorenzo autorizza la preparazione del live sul solo box .168:
- Salvati JSON originale, copia coerente SQLite, sorgente originale dell'handler e variante dev in
/home/bsqsecurity/backup/live-dev-20261102T103058Z/sul box, directory privata. Nessuna credenziale copiata nella documentazione. - Aggiunto
webrtc_streamscon nomeCAM_1e lo stesso ingresso RTSP già usato dalla telecamera Frigate. Frigate non ha un restreamCAM_1configurato in go2rtc: il live apre quindi una connessione aggiuntiva alla sorgente NVR, senza cambiare il detector o i suoi parametri. - Nella sola copia dell'handler dentro il container
internal_serversostituita l'uscita SRT fissa di produzione consrt://170.10.0.107:9999?mode=caller&streamid=live/app/<token>. Nessuna modifica amain, alle immagini ECR, al Compose o al server di produzione. È una modifica locale di laboratorio: la versione dichiarata resta 3.2.9, ma l'handler runtime differisce dall'immagine; un restart la conserva, una ricreazione del container la perde. La configurazione JSON è invece persistente sul volume host. Non considerare questo container una baseline 3.2.9 intatta per la prova del ponte. - Riavviato il solo
internal_server. Controlli successivi: API HTTP 200, box/detector/wrapper online, una telecamera, box e wrapper inseriti; Frigate healthy e data di avvio invariata (2026-10-30T16:27:34Z). Compilazione AST dell'handler riuscita; esecuzione isolata del solo costruttore del comando FFmpeg con URL/token fittizi conferma l'uscita dev e l'assenza del vecchio URL SRT di produzione. - Il signaling configurato nel pannello è
wss://mdm.dev.ome.bsqdev.uk; il dominio risolve a185.31.66.187. Dal box,https://mdm.dev.ome.bsqdev.ukrisponde HTTP 404 alla radice, così come il listener diretto170.10.0.107:3333: prova di raggiungibilità HTTP, non prova di handshake WebRTC. Il Compose dev espone SRT su 9999/UDP; il precedente tentativo TCP su 9999 non ne misura la disponibilità. - Riproduzione video confermata da Lorenzo nel pannello dev il 02/11: «il video compare». Il percorso sorgente NVR → box → SRT OME dev → player del pannello è quindi riuscito in questa prova. Non misurati tempo di avvio, durata, latenza o audio; nessuna conclusione generale sulla compatibilità H.265 di altri browser. Controllo SSH successivo: API HTTP 200, box e detector online, box e wrapper ancora inseriti, Frigate healthy con avvio invariato (
2026-10-30T16:27:34Z); 1 connessione live ancora attiva al controllo. La chiusura e il rilascio della connessione restano da verificare dopo che Lorenzo chiude il player.
Ripristino della sola preparazione live: conservare gli eventuali cambiamenti intervenuti nel frattempo; ripristinare config.json dal backup e il sorgente originale nell'handler del container, quindi riavviare solo internal_server e verificare stato inserito e detector. Non ripristinare l'intero database per annullare queste due modifiche: la copia SQLite serve come baseline, non è stata modificata per configurare il live.
Uscita dal player, prova successiva del 02/11: Lorenzo cambia pagina e segnala che nel player non c'è un pulsante di chiusura. Due letture successive del box mostrano ancora 1 stream attivo; la seconda conferma anche 1 processo FFmpeg con uscita SRT. Il cambio pagina non ha quindi arrestato la pubblicazione in questa prova. Box/detector online, impianto inserito e Frigate healthy senza riavvio. Nel sorgente locale del pannello, live_stream_player.page.dart non invia lo stop quando si esce; l'azione esplicita Stop stream (icona quadrata) è nella lista dei live attivi, web_rtc_streams.dart, e chiama LiveStream.stop. Da provare manualmente quell'azione per completare la verifica dell'arresto; non è stato terminato alcun processo via SSH.
Stop esplicito, prova successiva del 02/11: Lorenzo preme Stop stream e conferma l'assenza di errori nel pannello. Il box riporta 0 live attivi, ma due controlli successivi rilevano ancora 1 processo FFmpeg SRT, non zombie (sleeping), con destinazione OME dev. Quindi la risposta di successo e il contatore a zero non provano la terminazione del processo. Il codice 3.2.9 remove_connection invia process.terminate() e rimuove subito la voce dal registro, senza wait() né fallback kill(); anche app/services/livestream/live_stream_service.py della candidata usa terminate() senza attesa: da correggere e provare prima di chiudere l'accettazione video della nuova versione. Non attribuita una causa specifica alla mancata uscita di FFmpeg.
Pulizia della sola prova: riconfermati eseguibile e destinazione del PID residuo prima di terminarlo con SIGKILL. Controllo finale: 0 processi FFmpeg SRT, 0 live attivi, API HTTP 200, detector online, impianto ancora inserito, Frigate healthy con avvio invariato. Nessun riavvio di servizio o modifica del detector per questa pulizia. Lo stop dal pannello resta parzialmente riuscito, richiedendo intervento sul processo residuo.
Lo script verifica_import_legacy.py verifica una copia sacrificabile montata in /verification/config, con DATABASE_URL=sqlite:////verification/config/installation.db, nell'immagine dell'installazione e con le migrazioni candidate montate in /app/app/core/db/migrations in sola lettura. Avviare solo lo script con /app/.venv/bin/python, senza run.py, Docker socket o rete; PYTHONPATH=/app. Se manca uno stato salvato, CAPTURED_DETECTOR_DISARMED=true|false deve contenere il risultato di una lettura reale precedente, mai un valore scelto per far passare la prova. Il verificatore sostituisce solo il lettore del detector nel proprio processo isolato. Non è un comando di migrazione operativa. Output limitato a booleani e conteggi; i file di prova sono stati rimossi al termine e non trasferiti nel repository.
Sequenza per completare la migrazione in laboratorio¶
- Confermare l'isolamento di ogni uscita del GANZ, compresi log BSQ e notifiche; salvare baseline e backup coerente, stato reale del detector e digest delle immagini.
- Ripetere dal pannello dev un comando su ciascun box, verificandone l'effetto reale e la successiva sincronizzazione; su Frigate provare disinserimento e reinserimento, lasciandolo infine nello stato iniziale. Provare snapshot, live ed evento solo dopo aver verificato le rispettive destinazioni.
- Preparare il ponte di MDM-081 e una candidata isolata dal vero
main. Acquisire lo stato del detector prima del fermo anche se la tabellaarm_stateesiste ma non contiene righe. Conservare snapshot coerente di SQLite, JSON, configurazioni detector/wrapper, progetto Compose e digest precedenti prima della prima scrittura della nuova versione. - Provare import su copia anche con log esistenti e verificare singolarmente i campi non importati; poi provare il passaggio reale con gli indirizzi dev, senza modificare il segreto AWS di produzione.
- Verificare versione/digest, identità, configurazioni, stato reale del detector, telecamere e comando di ritorno dal pannello. Ripetere con un errore intenzionale della candidata e dimostrare il ritorno automatico a codice, database e container precedenti. Misurare separatamente download e fermo.
- Ripetere per i formati di partenza previsti in MDM-081 e verificare un secondo aggiornamento con il nuovo updater. Solo dopo questi esiti valutare la release di produzione: nessun merge nel vero
maindurante le prove.
Regola (D22)¶
- La linea
stageè sviluppo: gira solo sui box di prova installati come Stage (per esempioMDMTESTGANZ1, collegato al dev). - Il codice di
stagearriva ai clienti solo dopo essere stato unito inmaine rilasciato con le immagini di produzione. - I box dei clienti restano sempre sul branch
main, nell'ambienteprod, e si aggiornano con l'update normale dal pannello. Nessun box dei clienti va portato sustage, nemmeno temporaneamente.
Il codice della 3.9 segue già questa logica: l'ambiente prod corrisponde al branch main e al file infra/environments/prod/.env.build_tag, l'ambiente stage al branch stage (dev_scripts/commands/compose_config.py).
Risposta breve¶
Dopo l'unione in main, l'update di un box 3.2.x da solo non basta. Il download del codice funziona, ma l'avvio no. In più la nuova versione non legge la configurazione della 3.2.x. Servono, dentro la release unita in main (senza modificare la 3.2.x):
- l'import delle configurazioni della 3.2.x al primo avvio;
- un ponte che permetta all'update della 3.2.x di avviare la nuova versione, che poi completa da sola il passaggio alla nuova struttura.
Come funziona l'update oggi¶
- Il pulsante non aggiorna di uno step e non sceglie una versione. Il pannello invia solo la lista dei box a
POST /installation/update(mdm_web_panel/lib/features/installation/data_source/installation.dts.dart); il midserver inoltraupdate_system(mdm_mid_server/app/features/installation/data/repo_impl/installation.py). Il box sceglie il branch dal proprio ambiente, scarica la testa del branch e usa i tag indicati nel file di build. Esempio: oggi un boxproduction3.1.9 può arrivare direttamente alla 3.2.9 dimain, come nella prova A1; non passa prima per 3.2.1 e 3.2.2. Le migrazioni applicative pendenti sono un meccanismo separato e vengono eseguite all'avvio. - "Ultima" significa tag configurati in quel branch, non il tag numericamente maggiore di ECR e non la linea
stageper un boxproduction/prod. Se il branch punta a immagini vecchie, il pulsante usa quelle. La notifica di successo del pannello non prova che i container siano stati aggiornati: vedere il problema della pipe sotto. - Il pannello manda il comando di update. Il box (
host_manager.py) leggeupdate.ymldalla versione che sta girando e scrive il comando sulla named pipe verso l'host. - 3.2.x: con
envirment = productionusa il branchmaine il file.env.build_tag. Eseguedev_scripts/updater.py:git pull origin main, login ECR, poidocker compose --env-file .env.build_tag -f docker-compose.yml --profile <detector> --profile production pulleup -d --remove-orphans. - 3.9:
uv run mdm update <detector> <ambiente>:git pulldel branch dell'ambiente, sottomoduli (external/infra-lib), login ECR, poiinfra-cli. Questo legge il segreto AWSmdm/installation/<ambiente>e avviainfra/compose/docker-compose.ymlcon il progetto Composeinstallation.
Entrambi gli updater contengono una chiave AWS scritta nel codice (rischio già noto, T6.5/T10.6).
Cosa cambia tra le due versioni¶
| 3.2.x | nuova versione (oggi stage) |
|
|---|---|---|
| Configurazione | file JSON in app/config/configuration_files/: config.json, setting.json, pin.json, .env (UUID durante la registrazione), .applied_migrations.json |
database SQLite installation.db nella stessa cartella: installation_config, feature_settings, io_settings, alarm_schedule, notification_config, health_config, cluster_settings, schema_versions |
| Stato inserito/disinserito | tabella arm_state (disarmed) |
tabella system_state (JSON system_state) |
| Log | tabella logs |
logs con la colonna is_synced in più (migrazione 002) |
| Compose | docker-compose.yml nella radice, progetto con il nome della cartella |
infra/compose/docker-compose.yml, progetto installation, avviato da infra-cli |
| Variabili d'ambiente | .env.build_tag nella radice |
infra/environments/<ambiente>/.env.build_tag più .infra.generated.env dal segreto AWS |
| Nome dell'ambiente | production (anche come profilo Compose) |
prod |
| Strumenti sull'host | python3, aws, docker |
in più uv e il sottomodulo external/infra-lib (infra-lib, scaricato da CodeCommit; ogni avvio con infra-cli legge il segreto AWS) |
| Container | internal_server, frigate, frigate_wrapper, mosquitto |
stessi nomi, tranne installation-mosquitto |
Restano dove sono: configurazione e registrazioni di Frigate (frigate_wrapper/frigate/config e storage), configurazione del wrapper (frigate_wrapper/config/configuration_files), immagini e video in app/logs/logs_assets. Il compose nuovo monta gli stessi percorsi.
Corrispondenza dei campi¶
| 3.2.x | Nuova versione | Nota |
|---|---|---|
config.json → mac |
installation_config.mac |
è il codice dell'impianto: va conservato |
config.json → envirment (production/stage) |
installation_config.environment (prod/stage) |
cambiano il nome e il valore |
config.json → detector, webrtc_streams, privacy_mode |
stessi nomi in installation_config |
stessa struttura |
config.json → configured (scritto alla registrazione; manca nel file di esempio) |
installation_config.configured |
deve risultare true per un box già in servizio |
config.json → gpio_usb |
io_settings.gpio_usb |
spostato |
config.json → sound_url |
io_settings.horns (struttura nuova) |
da mappare o riconfigurare |
setting.json → enable_webRtc, enable_screen_shot |
feature_settings.enable_webrtc, enable_screenshot |
nomi cambiati |
setting.json → check_camera_damage, fire_detection, monitoring_delay, monitoring_ganz_alarm_in, clean_disk |
stessi nomi in feature_settings |
|
arm_state.disarmed |
system_state (ARMED/DISARMED) |
critico |
pin.json, gpio_usb |
nessuno: la scheda GPIO su USB ("PIN") è stata tolta dalla linea stage |
non usato da nessuno dei 29 box del censimento; l'import lo segnala se gpio_usb è valorizzato (dettaglio) |
Versioni più vecchie e altri casi del parco¶
Dal censimento del 28/10/2026: 28 box rispondono (3.2.9 → 15, 3.2.2 → 2, 3.2.1 → 2, 3.1.9 → 10, di cui 4 GANZ). Confronto fatto sui commit in cui .env.build_tag passa a ciascuna versione (3.1.9: 31764c60, 3.2.1: 4eaf7092, 3.2.2: 3fe09496, 3.2.9: cdb7acd1).
Uguale dalla 3.1.9 alla 3.2.9: update.yml, dev_scripts/updater.py, host_manager.py, app_config.py, app_setting.py e le 15 migrazioni JSON della configurazione. Il compose differisce solo per le variabili MQTT del wrapper. Quindi l'update si comporta allo stesso modo su tutte queste versioni e il formato di config.json/setting.json è lo stesso.
Cosa cambia:
| Versione | Stato inserito/disinserito | Wrapper Frigate |
|---|---|---|
| 3.1.9 | nel detector: wrapper Frigate o GANZ (get_disarm_status), più una copia in memoria (DISARMSTATUS) |
2.0.1, Flask |
| 3.2.1, 3.2.2 | come la 3.1.9 | 2.1.3, Flask |
| 3.2.9 | tabella arm_state in installation.db (dal 03/08/2026) |
3.3.0 nel commit della 3.2.9, 3.4.0 dal commit successivo 91591ec9; FastAPI |
Versioni del wrapper lette da .env.build_tag nei commit di ogni versione (correzione del 30/10: prima qui c'era 3.0.6 per la 3.2.1/3.2.2, che in ECR esiste solo come 3.0.6-stage). Quale wrapper giri davvero su ogni box si sa solo con docker ps (MDM-012).
Il file di configurazione del wrapper (config/configuration_files/config.json, lista web_hooks) ha la stessa struttura di base nelle due generazioni; i campi interni vanno confrontati in MDM-080. Lo stato di rilevamento di Frigate è nella configurazione di Frigate, che resta al suo posto.
Altri casi del censimento:
| Caso | Impianti | Cosa fare |
|---|---|---|
| Box 3.1.9–3.2.9 che rispondono | 28 | update dal pannello con la nuova release (soluzione sotto), dopo aver verificato con SSH che la versione in esecuzione sia quella dichiarata (MDM-012) |
| Linux con SSH ma API 5005 non raggiungibile | 5 | prima capire versione reale e stato con SSH (MDM-012, MDM-013); se sono sotto la 3.1.9 o modificati a mano, reinstallazione guidata con import della configurazione |
| Windows (probabile GANZ del sistema vecchio) | 8 | non sono box MDM: nessun update possibile, serve una nuova installazione (T10.3) |
| Solo altre porte o nessuna risposta | 26 | riconciliare con l'inventario (MDM-011): dismessi → pulizia (T9.3); attivi ma irraggiungibili → intervento sul posto |
| Impianti con più box | — | ogni box (master e slave <MAC>-01) si aggiorna da solo; lo slave si raggiunge dal proxy del master |
Versioni sotto la 3.1.9 non risultano tra i box che rispondono. Se ne emergono dal controllo SSH, si valutano caso per caso: se hanno lo stesso update.yml, valgono le regole della 3.1.9.
Percorso dentro la linea main (3.1.9 → 3.2.2 → 3.2.9)¶
Analisi del 30/10/2026 sul codice di mdm_installation (origin/main, 531e6552) e mdm_frigate_wrapper (2.0.1 = 847a0f8, 3.4.0 = 87c4f4e), più i tag in ECR (solo lettura). Nessuna prova ancora fatta su un box: le prove A1–A3 di macchine-di-test.md servono a confermarla.
Risposta breve: l'update esiste ed è lo stesso su tutte queste versioni. Una migrazione dedicata non serve, perché configurazione e stato restano dove sono. Va però provato, e ci sono tre punti da verificare.
Non ci sono passaggi intermedi. L'update fa sempre git pull origin main, quindi un box 3.1.9 va direttamente alla testa di main: .env.build_tag con INTERNAL_BUILD_TAG=3.2.9 e WRAPPER_BUILD_TAG=3.4.0. Non si passa dalla 3.2.2, e la 3.2.2 non si può scegliere come destinazione.
Cosa resta compatibile, verificato nel codice:
| Parte | Perché funziona |
|---|---|
| Updater | update.yml, dev_scripts/updater.py e host_manager.py uguali dalla 3.1.9 alla testa di main |
| Compose | stessi servizi e percorsi; il wrapper riceve in più le variabili MQTT_*, con valori predefiniti uguali al broker locale; Frigate 0.17.1 in tutte le versioni |
| Configurazione del box | config.json/setting.json con lo stesso formato e le stesse 15 migrazioni JSON |
.env del box (indirizzi del midserver) |
file uguale dalla 3.1.9 alla testa: il git pull non lo tocca |
| Database | la 3.2.9 apre lo stesso installation.db; logs ha le stesse colonne e arm_state viene creata vuota |
| Stato inserito/disinserito, GANZ | resta nel GANZ; la 3.2.9 lo salva in arm_state solo al primo inserimento o disinserimento successivo |
| Stato inserito/disinserito, Frigate | il wrapper 2.0.1 lo scrive in config.yaml di Frigate (detect.enabled), che resta al suo posto. Il 3.4.0 non trova ancora uno stato salvato (detection.armed nella sua configurazione) e quindi non riapplica niente; Frigate riparte da config.yaml, cioè nello stato di prima |
| Stato all'avvio della 3.2.9 | nella testa di main (app/services/object_detector/arm_state.py) arm_state è solo una copia dell'ultimo stato letto dal detector, scritta a ogni cambio: nessun ripristino all'avvio (la funzione restore_disarm_state del commit cdb7acd1 è stata tolta dopo). Lo stato lo tiene il wrapper 3.4.0 |
| Configurazione del wrapper | file di esempio identico tra 2.0.1 e 3.4.0; stesso percorso montato |
| MQTT del wrapper | il 2.0.1 legge già gli eventi di Frigate dal broker locale (172.17.0.1:1883, utente test); il 3.4.0 usa lo stesso broker (localhost:1883 in rete host) e lo stesso utente |
Da verificare:
- Il tag
3.2.9è stato sovrascritto. In ECR l'immagineinstallation:3.2.9è stata pubblicata il 19/10/2026 alle 10:02, tre minuti dopo il commit531e6552("stream audio") e oltre due mesi dopo la 3.2.9 del 03/08. I box aggiornati prima del 19/10 hanno quindi un'altra immagine con lo stesso tag, e un box aggiornato oggi riceve anche la modifica audio del live (-map 0:a?, AAC), che è in[Unreleased]nel changelog. L'immagine originale del 03/08 è ancora in ECR, senza tag:sha256:bd38a99e…. È quella del backup diMDMTESTGANZ1(mdm-local-rollback:3.2.9-20261028t151536z, verificato il 30/10). Quella del 19/10 èsha256:4661ae33…. I tag non sono immutabili e la versione dichiarata non distingue le due immagini: va confrontato il digest sui box (MDM-012). Per le prove in laboratorio: un update dal pannello scarica oggi l'immagine del 19/10; per riprodurre un box aggiornato prima del 19/10 si avvia la 3.2.9 per digest. - Inserimento e disinserimento dopo il passaggio al wrapper 3.x. Dalla 3.2.0 il wrapper non riscrive più
config.yamle non riavvia Frigate: mandadetect/setvia MQTT e aspetta la conferma (DETECTION_CONFIRM_TIMEOUT_SECONDS, 2 s). Serve che Frigate accetti i comandi dal broker locale. Sui 15 box 3.2.9 succede già; sui 3.1.9–3.2.2 non è mai stato usato. La sezionemqttdiconfig.yamlè scritta a mano o dal pannello sul box, non da un repository. -
Wrapper sui box 3.2.9. Dipende da quando sono stati aggiornati: 3.3.0 se prima di
91591ec9, 3.4.0 dopo. Con il 3.3.0 lo stato di Frigate non sopravvive a un riavvio di Frigate. Da leggere condocker ps(MDM-012). -
L'update può dichiarare successo senza aggiornare niente (osservato il 30/10/2026 sulla prova A1). Il box riceve
update_system, scrive il comando sulla named pipe e, se sull'host il lettoreexecpipe.shnon è in esecuzione, dopo il timeout scrive[Error] Timeout: Host scanner is not listening on the pipe.(host_commander.py) ma risponde comunqueupdated successfully: il pannello mostra Installation update triggered successfully e il box resta sulla versione di prima. Il lettore è avviato da cron (@reboot): se manca la riga in crontab, o il processo è morto, gli update dal pannello non hanno effetto. Dopo ogni update va controllata la versione reale (docker ps, MDM-012); in campo conviene verificare prima cheexecpipe.shsia in esecuzione.
Cosa provare (prove A1–A3): partire da un box configurato e inserito, poi update dal pannello. Dopo l'update controllare:
- versione e tag delle immagini con docker ps;
- stesso mac e stessa configurazione;
- stesso stato in /status;
- un disinserimento e un reinserimento dal pannello, con Frigate che conferma;
- un allarme di prova che arriva al dev;
- un riavvio di Frigate, dopo il quale lo stato deve restare quello impostato (wrapper 3.4.0).
Misurare anche il tempo di fermo.
Esito della prova A1 (30/10/2026)¶
Box MDMTESTFRIGATE1 sulla .168 (macchine-di-test.md): 3.1.9, wrapper 2.0.1, Frigate 0.17.1 con Coral USB, una telecamera, inserito, collegato al midserver dev (v3). Update lanciato dal pannello dev.
| Controllo | Esito |
|---|---|
| Comando dal pannello | arrivato (q_security/installation_topic, update_system). Al primo tentativo il lettore della pipe non girava: nessun effetto ma risposta di successo (punto 4 sopra). Dopo un riavvio, con il lettore avviato da cron, l'update è partito |
| Codice | git pull origin main da 31764c60 a 531e6552 senza conflitti; il .env modificato in locale è rimasto com'era |
| Immagini | installation:3.2.9 = sha256:4661ae33… (quella del 19/10), frigate_wrapper:3.4.0 = sha256:66f4ae00…; in più parte installation-dsniff-1, perché l'updater usa anche il profilo production, che l'installer non usa |
| Configurazione | config.json, setting.json e il codice impianto invariati |
| Stato inserito | invariato: config.yaml con detect.enabled: true, wrapper 3.4.0 {"status": true}, /status della 3.2.9 con disarm.status: false; arm_state creata e scritta alla prima lettura (disarmed = 0); nel wrapper nessuno stato salvato, quindi il reconciler non interviene |
| Frigate | non riavviato: il rilevamento non si è mai fermato |
| Fermo | API del box non raggiungibile per circa 8 s (ultimo 200 alle 14:01:26, di nuovo 200 alle 14:01:34, campionamento ogni 2 s); stato del wrapper non leggibile per un campione. Download delle immagini (circa 300 MB per la 3.2.9) fatto prima, senza fermo |
| Disinserimento dal pannello dopo l'update | riuscito: wrapper 3.4.0 via MQTT, detection.armed: false salvato, arm_state aggiornata |
Esito della prova A2 (30/10/2026)¶
Stesso box, ripristinato dall'istantanea della 3.1.9 inserita e portato alla 3.2.2 (3fe09496, wrapper 2.1.3 Flask) con un fast-forward di main, poi riavviato perché cron rilanciasse il lettore della pipe. Update lanciato dal pannello dev, che mostrava la 3.2.2.
| Controllo | Esito |
|---|---|
| Comando e pipe | eseguito al primo tentativo |
| Codice e immagini | 3fe09496 → 531e6552; installation:3.2.9 sha256:4661ae33…, frigate_wrapper:3.4.0 sha256:66f4ae00…, più dsniff |
| Configurazione | invariata |
| Stato inserito | invariato (wrapper 3.4.0 {"status": true}, /status disarm.status: false, config.yaml true, arm_state disarmed = 0, nessuno stato salvato nel wrapper) |
| Frigate | non riavviato |
| Fermo | API del box circa 8 s (ultimo 200 alle 14:31:32, di nuovo 200 alle 14:31:42, campionamento ogni 2 s); stato del wrapper non leggibile per due campioni. Le immagini erano già sul box dalla prova A1 |
Conclusione A1 e A2: l'update dentro la linea main porta un box Frigate 3.1.9 o 3.2.2 alla 3.2.9 con wrapper 3.4.0 senza perdere configurazione né stato inserito, con circa 8 s di fermo delle API e senza fermare il rilevamento. Restano da provare: un allarme vero dopo l'update, il percorso GANZ (A3).
Trovato: finestra di rilevamento attivo su un impianto disinserito dopo un riavvio di Frigate. Il wrapper 3.4.0 disinserisce solo a runtime (detect/set) e lascia config.yaml con detect.enabled: true. Al riavvio Frigate riparte dal file, quindi inserito, finché il reconciler non riapplica lo stato salvato. Misurato con Frigate riavviato a mano: inserito da +5 s a +13 s dal riavvio (circa 8 s); il controllo gira ogni DETECTION_RECONCILE_INTERVAL_SECONDS (30 s), quindi il caso peggiore è circa 30 s. In quella finestra un impianto disinserito rileva e può generare allarmi. Il caso opposto (inserito a runtime, config.yaml con false) lascia l'impianto non protetto per lo stesso tempo. Possibili correzioni nel wrapper: riapplicare lo stato salvato appena Frigate risponde, prima del primo ciclo; oppure scrivere anche config.yaml. Non verificato se in quella finestra gli eventi arrivano davvero al box e alla centrale.
Cosa succederebbe con l'update dopo l'unione in main¶
Si suppone che stage sia unito in main con un merge, così che il vecchio main resti un antenato del nuovo.
git pull origin main: funziona. Il box è su un commit del vecchiomaine il nuovomainlo contiene, quindi il pull avanza senza conflitti (salvo file modificati a mano sul box).- L'avvio fallisce. L'updater della 3.2.x è già in memoria e continua con i passi vecchi:
- cerca
docker-compose.ymle.env.build_tagnella radice, che nella nuova versione non ci sono; - usa il profilo
production, mentre la nuova versione usaprod; - non installa
uv, non scarica il sottomodulo e non legge il segreto AWS.
Il box resterebbe con il codice nuovo sul disco e i container vecchi in esecuzione: funziona ancora, ma a un riavvio o a un update successivo il risultato non è prevedibile.
3. Anche avviando a mano la nuova versione:
- il progetto Compose si chiama installation e i nomi dei container sono gli stessi: se lo stack vecchio non viene spento prima, l'avvio fallisce per nomi già in uso;
- la nuova versione non trova schema_versions e la migrazione iniziale scrive i valori di default (InitialMigration_001): mac vuoto, configured falso, detector di esempio, nessuno stream e stato disinserito. Il box si registrerebbe di nuovo sul midserver come un box nuovo. I file JSON restano sul disco ma non vengono letti.
4. Immagini di produzione vecchie: in stage il file infra/environments/prod/.env.build_tag indica ancora INTERNAL_BUILD_TAG=3.2.8 e WRAPPER_BUILD_TAG=3.3.0. Al rilascio va aggiornato con i tag della nuova release, altrimenti il compose nuovo avvierebbe le immagini vecchie.
La prima versione della migrazione iniziale (febbraio 2026) leggeva config.json e setting.json. La lettura è stata tolta il 27/02/2026 (commit 902c533f); la funzione _load_json_config è rimasta ma non è usata.
Cosa si conserva comunque: la 3.2.9 ha già installation.db nello stesso percorso (app/config/configuration_files/installation.db). La nuova versione lo apre, crea le tabelle che mancano e con la migrazione 002 aggiunge is_synced alla tabella logs: i log della 3.2.9 restano. La tabella arm_state resta nel file ma nessuno la legge più. La migrazione 003 (production → prod) agisce solo su una configurazione già presente nel database, quindi su un box 3.2.x non fa nulla.
Limiti dell'updater nuovo (mdm update)¶
L'updater della nuova versione (dev_scripts/commands/updater.py, chiamato da update.yml di stage) va bene da una nuova versione alla successiva, perché la configurazione è già nel database e le migrazioni partono all'avvio. Non basta per il passaggio dalla 3.2.x e oggi:
- fa
git pull, sottomoduli, login ECR einfra-cli run --operation <detector>-up, senza spegnere un progetto Compose diverso dainstallation; - non importa configurazioni e non controlla il risultato (versione,
mac, stato inserito/disinserito, midserver); - non torna indietro se l'avvio fallisce: il box resta sul commit nuovo;
- richiede
uvin~/.local/bin, che i box 3.2.x non hanno; - contiene la chiave AWS scritta nel codice.
Il commit b9dd8510 della stage ("fix: missing rollback") riguarda il rollback delle transazioni SQLite, non quello dell'update.
Soluzione ideale¶
Un box 3.2.x si aggiorna dal pannello come sempre e si ritrova sulla nuova versione di produzione, branch main e ambiente prod, con lo stesso codice impianto, la stessa configurazione, lo stesso stato inserito/disinserito e gli stessi log. Se qualcosa va storto torna da solo alla versione precedente. La 3.2.x non si modifica: tutto il necessario sta nella release unita in main.
- Import delle configurazioni nella nuova versione, valido per tutte le versioni dalla 3.1.9 in su: al primo avvio, se il database non ha configurazioni e nella cartella ci sono
config.json/setting.json, la nuova versione li importa con la corrispondenza dei campi sopra. Impostaenvironment = prodeconfigured = truee conserva una copia dei file originali. Lo stesso import è disponibile come comando a secco (mdm db import-legacy). - Stato inserito/disinserito: da
arm_statese c'è (3.2.9). Altrimenti (3.1.9–3.2.2) lo chiede al detector, cioè al wrapper Frigate o al GANZ, prima di fermare lo stack vecchio. Se lo stato non si può leggere, il passaggio si ferma e torna alla versione precedente: meglio nessun update che un impianto disinserito senza saperlo. - Wrapper: la nuova release porta il wrapper FastAPI; la sua configurazione (
web_hooks) viene verificata e, se serve, convertita.
L'import supporta tutte le versioni dalla 3.1.9 come sicurezza. Se viene confermata la proposta P4 (portare prima tutti i box alla 3.2.9, T9.1), la migrazione parte sempre dalla 3.2.9; la decisione è aperta (A25).
2. Ponte per l'update della 3.2.x, cioè file nella radice del nuovo main usati una volta sola:
- docker-compose.yml e .env.build_tag compatibili con l'updater vecchio, con il profilo production. Avviano la nuova immagine internal_server di produzione e gli stessi servizi di prima con gli stessi nomi, così --remove-orphans non spegne il rilevamento;
- all'avvio la nuova versione riconosce di essere partita dal ponte, importa la configurazione (punto 1) e, attraverso la named pipe, esegue il passaggio alla struttura nuova:
- installa uv se manca e scarica i sottomoduli;
- spegne il progetto Compose vecchio;
- avvia lo stack con infra-cli, ambiente prod;
- controlli finali: stesso mac, stesso stato inserito/disinserito, telecamere, collegamento al midserver. Se uno fallisce, ripristina il commit e lo stack precedenti e lo segnala al midserver.
3. Release corretta: infra/environments/prod/.env.build_tag con i tag della nuova release; segreto AWS mdm/installation/prod corretto (M27).
4. Dopo il passaggio gli aggiornamenti usano l'update della nuova versione (mdm update, ambiente prod, branch main) e le migrazioni del database. I file del ponte si potranno togliere quando nessun box sarà più sulla 3.2.x.
Cosa dobbiamo fare¶
| # | Passo | Perché | Ticket |
|---|---|---|---|
| 1 | Riportare in stage le 42 correzioni di main (✅ i quattro comportamenti, committati entro il 30/10; restano /heartbeat e i test in laboratorio) |
non perdere correzioni già in produzione | MDM-030 |
| 2 | Import delle configurazioni 3.2.x nella nuova versione (automatico e a secco) — 🔄 committato in db444987; il 02/11 superati 17 test di import/gestore migrazioni e prova su copia del backup reale GANZ con stato acquisito dal detector (dettaglio sopra). Restano verifica dei campi non importati, compatibilità wrapper e log esistenti; PIN non più supportato |
senza, il box riparte non configurato e disinserito | MDM-080 |
| 3 | Chiarire pin.json e sound_url → horns |
due dati senza corrispondenza diretta | MDM-080 |
| 4 | Ponte per l'update della 3.2.x, con passaggio automatico alla struttura nuova e ripristino; controlli finali e ripristino anche in mdm update |
l'updater della 3.2.x non sa avviare la nuova versione, e mdm update non controlla né torna indietro |
MDM-081 |
| 5 | Correggere il segreto AWS di produzione (M27) | la nuova versione prende da lì gli indirizzi di MQTT, midserver e log | MDM-031 |
| 6 | Release candidata: unione di stage in main con un merge, tag di produzione in infra/environments/prod/.env.build_tag, matrice di compatibilità con il midserver |
i box ricevono solo versioni di produzione | MDM-032 |
| 7 | Prova in laboratorio con la release candidata, update dal pannello e ripristino, su: 3.1.9 GANZ, 3.1.9 o 3.2.2 Frigate (wrapper Flask), 3.2.9 Frigate; ognuno configurato e inserito | coprire i tre formati di partenza presenti nel parco | MDM-081 |
| 8 | Censimento con SSH: versione realmente in esecuzione, modifiche a mano, stato dei 5 box senza API 5005 | l'update parte dalla versione reale, non da quella dichiarata | MDM-012, MDM-013 |
| 9 | Riconciliare con l'inventario i 26 impianti che non rispondono e gli 8 Windows | sapere quali aggiornare, quali reinstallare, quali dismettere | MDM-011, T10.3, T9.3 |
| 10 | Aggiornamento a ondate (T9.2) dopo il cambio di midserver; se P4 è confermata (A25) i box partono tutti dalla 3.2.9 (T9.1) | con P4 una sola versione di partenza; senza, l'import copre già dalla 3.1.9 | backlog |
I passi 1–9 servono prima di toccare qualsiasi box dei clienti. Il passo 8 segue le regole di aggiornamento-box.md.
Nota per la prova del passo 7: il box di laboratorio deve essere in ambiente prod sul branch della release candidata, ma collegato al midserver dev. Come farlo senza usare il segreto di produzione va deciso nel ticket.