# Primera implementación mesh upgrade
Primera implementación.
Nodo main: desde donde se lanza la operacion
Nodos bot: ejecutan y esperan
Userdevice: dispositivo donde corre lime-app
Las llamadas syncronas se realizaran des de la limeapp y **No** des del nodo maestro. Esto se debe a varias cosas:
- Se puede utilizar el sistema de autentificacion ya implementado en la limeapp para protejer los endpoints de ejecutar upgrade y confirmar. Usando la password de administración de red, la limeapp deberia autontificarse como administradora en cada uno de los nodos y seguidamente mandar la orden a los nodos para q actualizen.
- Otro motivo es q existe una dificultad añadida para la comunicación y autentificación de nodo a nodo: no existe actualmente ninguna implementación (a parte de shared state) para poder hacerlo de una forma sencilla.
## Workflows
### secuence diagram
[diagrama](https://www.plantuml.com/plantuml/uml/hLN1Jjj04BtdAqPSSAJ6Y41xYA0bRGBYG8BISEXfjV5Ex2thNMjtJ8Ig_dkzBiQmDb9GUm7RE-_Dp7lFpeKZRgdCrU3EeHNu81E4qMUuuUZ8cWaeawPImnU0mTEpLyV7aAFBM5cabWkCKoJsyW6hwAbqqR22VpLqxB7-_-HTiF4JRBmtRD0xx1YmWReAkZFsNaWR7HnHNXoz93hephitCXXuVznR3fp4WSYrPQoiRwfTpqQmNiwMiEMAk0OoWZjG76oH12eqVEE03wcwPbjfynsto2mMnaaozZ4Qli5myff1wgsnEHDcfvNXWYcJS996UoVw2Kx1PToY67dBS98PSnyC5NhhUqJDZU34Fy1gFftzO9VB5RkxlLfDP_EsNMkSx_MAcQnCKxJCPMXHrFrPKMwKT5dKnnW4uJJUE_CTvFX_RFrzFLsjhnTNFVmdVK7HHk2pDL5frR232rwCfXyl9Alg-p6dBKOwfd777j0wQJJZ3rmglb4O9Jp9K32ZrMENu6DBq4ptdC58SK97zSRFo3O_6L41AH9KTSuNnZ-TqOqWPwDHM_hK0hoGuAjWAnM-JTcEq4jNxGbDHI-Jt-pJFmGk-0uqSdLyRLrAJAd9AnYHp7rYMEw6PwE_tmHhSl1j0_KTts8lMpUrgY8jIIh7HIvrx97CuiOOYdANdl-g_iGyyMyxd9_3OSrs-3kqUxA_9D-MYylhrSrqVRrSiDlvObPdFP2bVxZ66POOxQ-ZJdRUFyTy5ZPloTxhoc3mujTXCm5Je0RA43Q8jZwgqrQV9HcYTOCBrABCrHy0)

### descripción
0. Tipo de dato del shared state q guarda la información. Esta información podria tener cosas como la versión de libre router o en q estado del upgrade está.
Si entras en la limeapp des de cualquier nodo bot tienes q tener esa informacion de q eso esta ocurriendo "el nodo esta siendo controlado remotamente, no apagar".
2. Existe una upgrade disp y se muestra al user en el nodo maestro, ya esta
3. Actualizar uno y boton actualizar mesh wide. En un futuro se va a añadir "hay nodos no disponibles"
4. [USER_INTERACTION] El nodo maestro se descarga el firmware de la url definida en eupgrade
6. Se lo guarda en tmp o le dice al usuario q hay error en el nodo. Es una operación sincrona en el nodo. Re aprovechar lo q ya hay implementado.
7. Despues de la descarga hay un segundo chequeo para verificar la imagen en el master.
- Cuando se ha descargado eupgrade se verifica y todo el royo y en la interface se muestra nu boton de "iniciar mesh upgrade". Este boton publicarà en en shared state la informacion para la mesh upgrade
9. [USER_INTERACTION] Se utiliza shared state comun para avisar de q inicia una transacción global de tipo upgrade y ya se puede descargar el firmware de una url interna la red.
10. Esperamos q el estado de cada nodo bot va a pasando x descargando, fallo, o listo para actualizacion en shared state comun.
11. En todos los nodos: En el caso de q la red sea mixta se podria hacer un mesh upgrade aunq haya habido errores. Pero con un cartel de aviso d q haya errores. Ordenar los nodos con los q tiene problemas al principio. Los nodos q estan Ok ponerlos en un desplegable o algo asi
12. [USER_INTERACTION] **Syncrono** Desde el nodo maestro Enviamos la orden de Safe upgrade en un tiempo "prudencial". Se va a hacer una http request casi vacia en paralelo. Si te falló, mala suerte, puede haver un mensaje de error de q X nodo no se pudo upgradear, te avisa pero sigue. Como feature para despues podria ser la de abortar el proceso.
La llamada q dice q empieza el countdown tiene q ser sincrona y se necesita q todos los nodos estan escuchando. Para la siguiente iteración se deberia encontrar un sistema para avisar q un nodo se ha reiniciado durante el proceso. Probablemente la manera simplemente va a ser ver la informacion de node info (uptime ? ?)
12. Se muestra el countdown del firmware upgrade q es el countdown del nodo, en cualquier nodo.
https://github.com/spiccinini/lime-packages/blob/9a9b8c656012f4a60f7ba3cccdb1112b90fe0e56/packages/safe-upgrade/Readme.md#usage
14. Se reinician los nodos y se van actualizando el tipo de dato de shared state (node info y upgrade) informando q se tiene q confirmar. Cuanto tiempo tenemos para confirmar? `/etc/safe_upgrade_auto_reboot_confirm_timeout_s`.
16. **Syncrono** El estado es confirmable. Comparando el estado de referencia de justo antes del estado con el estado actual en todos los nodos. O hacer una prueba sincrona, una ping probe, a todos los nodos. Quizas se podria guardar la lista de todos los nodos a los q se les ha enviado el orden de empezar el upgrade, pues ir comprobando esos nodos. Se deberia poder ver el timer de confirmar/revertir.
a. Confirmar aunq haya problemas
b. confirmar cuando todos los nodos esten up.
18. [USER_INTERACTION] **Syncrono** La confirmación deberia ser sincrona. Des del nodo base enviamos otra orden de confirmación. poner un boton rojo con tapita.
19. Red upgradeada
Plus: si hay una transaccion empezada, hay q redirigir al nodo main en la ui si se accede des de otro nodo
## Abort
Always check:
- Solo un main node distinto de aborted else abort (done)
- Si existe un main node y yo ya empece mi txid tiene que ser el mismo que el otro
- antes de become main node verificar que no existe otro main node
## Data Types
#### meshwide_upgrade (shared state comun)
##### Shared state mesh upgrade
Ejemplo del body q va ha devolver:
El primero es el master node y el otro es el bot. Simplemente hay q encontrar el master node en el shared state. De este modo se reduce la cantidad de información q viaja por el shared state
```json
{
"limeXXX" : {
"data": {
"candidate_fw": "xxxx",
"repo_url": "http://10.13.0.1/lros/api/v1/",
"upgrade_state": "upgrade_states_list",
"error": "CODE",
"master_node": true,
"board_name": "qemu-standard-pc-i440fx-piix-1996",
"current_fw": "LibreRouterOs 1.5 r0+11434-e93615c947"
},
"author": "limeXXX",
"bleachTTL": 14
},
"limeYYY" : {
"data": {
"upgrade_state": "upgrade_states_list",
"error": "CODE",
"board_name": "qemu-standard-pc-i440fx-piix-1996",
"current_fw": "LibreRouterOs 1.5 r0+11434-e93615c947"
},
"author": "limeXXX",
"bleachTTL": 14
}
}
```
Las mesh wide transactions quedan para despues, pero parecen necesarias para bloquear otras acciones meshwide y evitar que sucedan de manera concurrente.
Queda pendiente acabar de definir el sistema con el que vamos a gestionar esto. https://github.com/libremesh/lime-packages/issues/1066
##### Upgrade states enumeration
```lua
upgrade_states = {
DEFAULT = "not upgrading", -- When no transaction is startd
STARTING = "starting", -- ?? Is this when we are defining de master node? En ese caso, mejor no llamarlo STARTING. Tmb quizas molaria tener asociados estados al master node? Es decir, el estado de la descarga del master node
DOWNLOADING = "downloading", -- Downloading the software
READY_FOR_UPGRADE = "ready_for_upgrade", -- Fiwmare downloaded and checked,
UPGRADE_SCHEDULED = "upgrade_scheluded", -- Upgrade scheduled
CONFIRMATION_PENDING = "confirmation_pending", -- Awaiting confirmation if safe upgrade is enabled and firmware upgrade is performed
CONFIRMED = "confirmed", -- Synonim of updated and all is ok
ERROR = "error"
}
```

https://www.plantuml.com/plantuml/umla/RLDTRzim37pNh-3pWFw1F0mrQgSBaDc5cs0OXY6W9RenfeY1FXBqtqzMl4oj_QIGTxexKl6ZysXzE6dnwyjlU7Zu2cMrBlRRdPZEr7lT5UrkKp_VcolmHm95FGRj8MfuoZ0s-XqieJnYfmcmzsHZtJ5x8VvftSoQ7_MsASfOtu6Rjc55O4aE5r8mc9xj2Vt01Z8UcWWvm8w31y-GPe4hsp-3UHCY4n_DsgeeVnxMJNlOlpotHLdbX35CrRPDcpUdIQ8tTEnJf8_m2Pqdcu0R5OtXO2IvM4fAWnnP4pZouJncjOJgNOXPbj5_-dbuVVfMbVjjLSvfA-W7JGwEU27eY0n8jfQaZzwAhqOpAbAmT74mUTn91oq1Nd3GwQ4MSi7_nOXPlX5yQkhrflrUx3PDVNYfwd4nS-OA7FO4uVncOy0OttrExyX7U7upyr-ooY9KvI9ZmTe5AOdIeXOrCyzqVFm6xdRBtTlVzvgU_MnPadEVJH_9g73IVm40
##### list of possible errors
```lua
errors = {
DOWNLOAD_FAILED = "download failed",
NO_LATEST_AVAILABLE = "no latest data available",
CONFIRMATION_TIME_OUT = "confirmation timeout",
ABORTED = "aborted"
}
```
## Endpoints
Endpoint sincronos:
- Iniciar proceso de mesh wide upgrade
- Iniciar la descarga del firmware. Utilizar la libreria eupgrade y descar de el repositorio en el nodo maestro. Esto al finalizar escribe en shared state q el firmware esta alli.
- Cambiar el shared state a modo firmware upgrade
- Iniciar el upgrade: cuando el usuario requiera se iniciar el upgrade y e hace un loop des de la lime app autentificandose y llamando a un endpoint especifico de cada uno de los nodos q estan ready for upgrade en el shared state
- Ping probe: des de limeapp o des de nodo master??
- Confirmation: loop rpcd a los nodos q estan esperando confirmación.
Endpoint asincronos
- Estado actual de firmware upgrade
- Dar info de en q parte del proceso esta cada nodo, errores, hostnames, ips....
- Informacion de donde descargar el firmware (quien es el nodo master)
- Countdowns varios
Procesos accionados por el shared state
- Iniciar la descarga del nodo maestro cuando esta ready for download, y hacer las comprobaciones necesarias conforme el FW se ha descargado correctamente y es compatible (package eupgrade).
- Actualizar el estado del propio nodo
## Error cases
- No hay internet para descargar
- El nodo master se reinicia
- Q ocurre si inicias un firmware upgrade en un nodo, pero no lo llegas as setear como master y otro nodo se setea como master antes?
-
--------
# Old, notas antiguas
Notas actualizacion de firmware
- Funcionalidad para actualizar firmware de multiples nodos en el caso de q la red esté en estado de la red es sincronizado
- Se puede desarrollar paralelamente al estado de referencia. Implementar primero el update de configuracion y de firmware.
- Es decir, implementar el update de firmware remoto sin verificar nada de momento.
- Para implementar (seria parecido con el aplicar el limecommunity):
1. Cargar el firmware a todos,
2. Checkeo del firmware
3. Lanzar el upgrade en todos los nodos (hay q comprobar el camino para empezar por los nodos mas lejanos? Sino poner un timeout)
Los nodos se dan como un handshake al user q esta usando la limeapp, con la info d OK k todo esta bien. Al presionar el boton de upgrade la red, le da un delay de un minuto o asi.
## Implementación en el backend
- Kizas sysupgrade tiene alguna opcion para saber si el firmware es correcto
```bash
-T | --test Verify image and config .tar.gz but do not actually flash.
```
- Cosas rotas
- Le he podido subir un `.sh` cualquiera y luego responde q "The upgrade should be done".
- Implementar si checksum del archivo es valido
### Notas para implementación
Aquí notas de implementación de la implementación escogida en [Estrategias de implementación](#Estrategias-de-implementación).
Backend endpoints:
- `upgrade_firmware_info`: Aquí se podrian exponer de forma sincrona la infó compartida en e shared state:
- ~~Versión actual del firmware~~ Actualmente la version de firmware esta rota, lo q significa q tampoco se puede consguir esa info. X otro lado, no creo q sea realmente necesario saber eso, simplemente cargas el firmware si quieres independientemente de la version de firmware q tenga, no?
- Version descargada del firmware (la q se va a actualizar)
- ~~SUM del FW~~ no es necesario, con el verify ya es suficiente para saber si la imagen esta bien o no
- Momento de la descarga de la imagen?? para saber si es updated?
- El FW esta soportado?
- Tiene safe upgrade?
- `set_remote_firmware_urls`:
Se podria subir el archivo x la via habitual y luego crear un metodo q sea `set_remote_firmware_url` donde se definen los firmwares donde pueden ir a buscar todos. Entonces, al subir una url local, comprueba q el archivo exista en algun path de `/www` y si no está hace un `ln -s` allí, o por el estilo.
Si no ha podido xk el firmware no existe devuelve error...? El frontend puede subir mas de uno con el actual sistema??
Gestión de errores:
- `get_remote_firmware`: de una lista de firmwares remotos es la encargada de escoger uno y decargarlo.
Usa `wget` para hacerlo
#### Integración con el package eupgrade
El package eupgrade implementa la descarga de un paquete remoto de la red de librerouterOs.
> El package eupgrade ya implementa la descarga de firmwares oficiales de librerouter. Seguimos teniendo interés en desarrollar las features q hablamos?
> - Poder subir una lista de urls
> - Poder subir un firmware
>
> Xk quizá cambia un poco el paradigma: alomejor lo único q se tiene q hacer es q el shared state comparta información de si una update esta disponible, y cuantos nodos hay ready for update, y con un botón ejecutar la descarga usando eupgrade y cuando todos lo tengan descargado, del mismo modo como hablamos, ejecutar el upgrade en los nodos que han podido descargarse el archivo
> Tambien se podria hacer q se pudiera configurar la url donde sacar las updates en una red.
### Estrategias de implementación
#### Pasos
1. Recopilar informacion: a traves de shared state. Ver [Información relevante a recopilar](#Información-relevante-a-recopilar)
Para este paso habria q implementar una función q recopile toda la información necesaria que subir al shared state:
3. Descargar/mandar firmware: lo ideal seria descentralizado para no saturar un solo nodo
- ~~Bittorrent?~~ descartado x necesitar instalar transmission
- ~~Base64 en el shared state?~~ descartado x ser un blog muy grande q puede ser buggie
- pull o push?
- ~~Copiar de los nodos q esten cerca?~~ A traves del shared state podria saber si un nodo esta listo y tiene el archivo e intentar de recibir el archivo. Hay como nua cosa para saber los nodos vecinos a traves de los timouts de ping o algo asi. Descartado, en un futuro puede ser q tus nodos vecinos sean librerouter2 y tu seas 1 y no tengan tu firmware
- Con uhttp servir el archivo a un nodo
- En internet servir el archivo y traerlo de internet. Los nodos q tengan el archivo puedne añadir su url al shared state
**ESCOGIDO**: Poder suber una url o una lista de url con diferentes versiones para diferentes hardwares, en internet o en red local. Alternativamente, se podria subir uno o multiples files (si caben) en un nodo, el nodo lo publica en la red usando uhttpd o lo q sea. Estas url, locales, en internet, o en red local, se publican en el shared state. Si el nodo X encuentra un firmware q sea adapto a su arquitectura, lo descarga.
3. Iniciar el upgrade:
- Autentificacion? con el hash del archivo mas el hash de la shared password
- Si no estan todos listos q no se pueda hacer el mesh wide upgrade, o k te avise no tokes este boton si no sabes lo q estas haciendo
Hemos barajado diferentes opciones:
- **ESCOGIDO**: Via RPC (sincrono). Con un boton Verificar viabilidad del upgrade con algo tipo ping con cada nodo para saber la conexion entre los nodos x si hay un gran delay pues notificarlo antes de hacer el upgrade. Se decide de probar implementarlo en la limeapp x velocidad de implementacion y xk no tenemos cliente http en lua
- Nodo coordinador.
- En el nodo central se genera un archivo q sea la cosa q dice q se hace el upgrade a tal hora. Es importante saber q tengan la hora sincronizada en el shared state.
- todos los nodos la hora
- Todos los nodos el firmware
- Todos los nodos reciben el aviso de la hora en la q va a suceder.
4. Diagnosis y status de la red after upgrade.
- TODO
##### En resumen..
En el lime packages vamos a tener:
- Especificar una lista de urls de donde descargar firmwares al shared state
- Exponer un o mas firmwares files a la red.
- Descargar un archivo de una url y comprobar q es un firmware valido
- Recopilar toda la información necesaria del estado de la actualización y subirla a la red
En la limeapp.
- Visualizacion del shared state
- Upload de archivo/s o especificacion de file/s del firmware.
- Hacer una test performativo de la lejania de cada nodo
- Poder "aplicar" la actualización
#### Información relevante a recopilar
Propuesta de lista de información relevante para recopilar q puede ayudar a saber el estado del upgrade.
A añadir en un endpoint en concreto, o para ampliar el de `get_upgrade_info`, o dejar q se encargue el shared state de esto?
```json
{
"node_name": "ql_flor",
"firmware_version": "librearouterOs_1.3 r0+11434-e93615c947",
"target_firmware": "nombre del archivo cargado del firmware | null (en el caso de q no haya ningun archivo compatible)",
"target_supported": true, // Vendria a ser el resultado de sysupgrade -T, si es muy lento de hacer se tendria q hacer en otro momento
"safe_upgrade": true ,
"time": "", // No estoy seguro pero si es necesario, serviria para saber si los nodos estan a la misma hora para hacer una schedule upgrade
"upgrade_status" : { // Same as on get_upgrade_info
"safe_upgrade_confirm_remaining_s": -1,
"status": "ok",
"is_upgrade_confirm_supported": false
}
}
```
## Notas desarrollo
#### Endpoints ubus
Existen dos scripts en bin q se pueden llamar para jugar: `eupgrade-check` y `eupgrade-download`.
Actualmente el frontend llama a los siguientes endpoints.
##### Para upgrade manual
- `"lime-utils", "get_upgrade_info"`: varia info. Ver "command results" abajo. En realidad llama a `packages/ubus-lime-utils/files/usr/lib/lua/lime/upgrade.lua` la funcion `pkg.get_upgrade_info()`. Mira el `UPGRADE_INFO_CACHE_FILE` y devuelve la info si la tiene. El valor `status` solo puede tener los valores:
```
pkg.UPGRADE_STATUS_DEFAULT = 'NOT_STARTED'
pkg.UPGRADE_STATUS_UPGRADING = 'UPGRADING'
pkg.UPGRADE_STATUS_FAILED = 'FAILED'
```
En el caso de q sea en un archivo. Tambien puede tener el valor `ok` si no es un archivo. En `limeutils.get_upgrade_info()` le machacan siempre el valor con `ok`, asi q puede ser q aunq haya un archvo si pides la info te lo machaquen.
- `"lime-utils-admin", "firmware_confirm"`: usa `safe-upgrade confirm > /dev/null 2>&1`, ~~quizá es mejor usar `sysupgrade -T`?~~ Ver mas abajo explicación del comando. El comando `confirm` aparentemente lo q hace es "confirmar la current partition".
- `"lime-utils-admin", "firmware_upgrade"`: hay q pasarle parametros. NO COMPRUEVA MD5, y deberia hacerlo si se ha pasado en la metadata, por ejemplo, o alog asi
```js
{
fw_path: filepath,
metadata: { upgrade_timestamp: (Date.now() / 1000).toFixed(1) }, // in seconds;
}
```
##### Para eupgrade (download firmware)
Actualmente no va, ver lo escrito en `is_new_version_available`:
- `"eupgrade", "is_new_version_available"`: check if new version is available on internet en `uci:get('eupgrade', 'main', 'api_url')`. En la linea 20 de `libremesh/lime-packages/packages/eupgrade/files/usr/libexec/rpcd/eupgrade` aparece k `cached_only` es true. Lo q significa q nunca mira a internet, solo mira en local. Como nunca se ha mirado antes solo devuelve ok y ninguna otra info.
- `"eupgrade", "start_download"`: si hay una nueva version available cacheada, executa por debajo `bin/eupgrade-download` de forma daemonized. LLama a una funcion de lua de `eupgrade.download_firmware` . Este va guardando el estatus usando `utils.write_obj_store_var` . Al final de la descaga comprueba el sha256
- `"eupgrade", "download_status"`: usa `read_obj_store` para ver el stado de la descarga
##### Otros endpoints interesantes
- `"system", "reboot"`:
- `/cgi-bin/cgi-upload` para uploads de archivos. Se encuentra en la función `uploadFile` de `firmwareApi.js`.
- `safe-upgrade`: lua script k se usa para realizar el upgrade. Utiliza `mtd` para realizar el upgrade. Significa que una vez booteado el firmware nuevo, lo queres confirmar para que siempre que se reinicie siga booteando ese firmware. Si no lo confirmas a un tiempo de timeout se reinicia y vuelve al firmware anterior
- Para la session de admin:
```bash
curl -X POST 10.219.123.10/ubus/ -d '{"id":1,"jsonrpc":"2.0","method":"call","params":["00000000000000000000000000000000","session","login",{"username":"root","password":"1234567890p","timeout":5000}]}' | jq '.result[1].ubus_rpc_session'
```
#### Commands results
```bash
root@lapastoramesh-c:~# ubus call lime-utils get_upgrade_info '{}'
{
"safe_upgrade_confirm_remaining_s": -1,
"status": "ok",
"is_upgrade_confirm_supported": false
}
```
SAN:
El remaining_s es cuantos segundos te quedan para confirmar (si no confirmas se reinicia). Si no se confirmó "se pierde" (queda en la partición que no queda activada)
no se si queda claro: hay dos particiones. Una es la que tiene el bit de booteo activo. Cuando se ejecuta el safe-upgrade upgrade eso instala en la partición no actual (o sea, no se pisa a si misma) y pone un bit de booteo por unica vez a la particion nueva. Luego de eso se reinicia y bootea la nueva pero solo por unica vez. Entones el confirm lo que hace, desde la nueva, es pasar el bit de booteo a la nueva para que siempre siga booteando. Si no se hace esto se reinicia y queda la anterior
la pantalla de la lime-app que hace todo el upgrade de firmware deberia mostrarte cuanto tiempo tenes para darle al boton de confirmar
### OLD Estrategias de implementacion
#### En la limeapp o en el nodo?
- ~~Se podria implementar todo des de la limeapp. Es decir, automatizar el proceso para q sea la limeapp la q va enviando el archivo y ejecutando nodo por nodo el proceso de upgrade del firmware. ~~
- O por contraposición, exponer cada uno de los endpoints necesarios para el upgrade pero q acepten una lista de Ip's o macs, entonces ejecutar en loop el comando q sea. Por ejemplo upgrade_info, y devolver la info al user. Habria q implementar la autentificacion RPCD y gestion de errores.
Tiene mas sentido hacerlo en el nodo ya q asi se puede ejecutar como si fuera un script. O des de curl o como sea, es agnostico al frontend.
Al final vamos a tener un nodo q va a actuar entre la lime app y la network, q va a hacer todas las requests necesarias para mostrar la información de red, o ejecutar los uploads del firmware o ejecutar el endpoint bajo aotentificacion q hace el safe upgrade. Basicamente lo q hace la limeapp pero para un numero N de nodos, incluido el mismo
#### Estrategias de upgrade
Estrategias para pasar del estado: "preparando el upgrade" a "realiza el upgrade"
- De lejos a cerca
- Usando un timeout (perform del upgrade en 120 segundos)
- Scheduling upgrade. Do the upgrade at same time, ex: 5:00 a.m
- Algún tipo de flag en el shared state, ejemplo, network esta en mode upgrade, y todos los nodos estan ready. En ese momento.. pum