GenieACS: Virtual Params

VirtualParameters.CodigoConexao no device GenieACS

No GenieACS, a árvore do device mostra o que o CPE reportou: serial, OUI, modelo, tags. Só que no dia a dia o analista não fica pensando em serial: pensa em código de conexão, contrato, aquele identificador que o atendimento já usa.

Virtual Parameter é um parâmetro que não existe no CPE. O ACS calcula (ou monta) o valor em JavaScript e mostra na UI como se fosse nativo, sob VirtualParameters.*.

Neste post a gente cria três exemplos que eu validei no lab (GenieACS 1.2.16 + CPE virtual):

  • OUI: espelha DeviceID.OUI (primeira prática, simples)
  • CodigoConexao: monta CURSO-{serial} (padrão “código do cliente” na tela)
  • WAN: IPv4 de internet padronizado (TR-098 .1/.2/.3 e TR-181), sem if por vendor

Pré-requisitos:
Instalação GenieACS: Bare Metal x Docker: ACS no ar (UI :3000, CWMP :7547, NBI :7557). No caminho Docker do curso, o CPE virtual (:8080) já vem no compose.

Variáveis
preencha uma vez · Copiar já sai pronto · clique no token para voltar

IP público: curl ifconfig.me

Por que isso facilita o dia a dia

Sem virtual param, o analista abre o device, copia o serial e vai no ERP/billing achar o cliente. Com CodigoConexao na árvore, o valor útil já está no GenieACS: filtro, olho na lista, atendimento.

O mesmo vale para IP da WAN. Huawei manda PPPoE em …WANPPPConnection.1; ZTE às vezes sobe internet em .2 e deixa .1 para outro serviço; já o CPE em TR-181 trabalha com Device.IP.Interface.* (não com InternetGatewayDevice). Se cada script ou tela fica caçando path por modelo, alguém sempre quebra. Com VirtualParameters.WAN, todo mundo lê um campo.

O OUI sozinho parece “bobinho” (o campo já existe em DeviceID). Serve para aprender o mecanismo sem lógica: se o espelho funciona, o caminho até o device está certo. Depois você troca o script por algo do seu provedor.

Virtual Param ≠ Provision

  • Provision: age no CPE / no device (declare, set, tag). No lab: coloca a tag provisionado.
  • Virtual Parameter: calcula no ACS. O CPE nunca “tem” esse parâmetro; a UI é que mostra.

Cuidado clássico: criar o VP em Admin e achar que acabou. A documentação do GenieACS é clara: o valor precisa ser buscado (manual ou via preset/provision). Sem o declare("VirtualParameters.…"), a definição existe e o device fica sem o campo.

O que vamos montar

  1. Três virtual parameters via NBI (JavaScript puro no body do curl)
  2. Uma provision que pede o fetch dos VPs (e marca a tag)
  3. Preset no CPE virtual (ProductClass = VirtualRouterLAB)
  4. Inform / Connection Request → valor na árvore do device

Se preferir um atalho depois de entender o fluxo, o pacote do lab tem ./scripts/seed-curso.sh: é exatamente esses recursos. Aqui a gente faz na mão.

1) Conferir o ACS e o CPE

UI:

http://SEU_IP:3000

Lab Docker do curso: login admin / admin.

Painel do roteador virtual:

http://SEU_IP:8080

NBI responde (sem auth no lab padrão):

curl -s http://SEU_IP:7557/devices/ | head

Se a NBI estiver com Basic Auth (bare metal com nginx, ou lab com NBI_AUTH_ENABLED=true), acrescente -u usuario:senha nos curl abaixo.

No painel do CPE (:8080), aba TR-069: ACS URL http://SEU_IP:7547/ → salvar e conectar. O device precisa aparecer na UI antes (ou na sequência) do Connection Request.

2) Criar o Virtual Parameter OUI

Body do PUT é JavaScript, não JSON. O script lê um parâmetro do device e devolve o valor tipado:

curl -X PUT "http://SEU_IP:7557/virtual_parameters/OUI" --data-binary @- <<'EOF'
const oui = declare("DeviceID.OUI", {value: 1}).value[0];
return {writable: false, value: [oui, "xsd:string"]};
EOF

O que importa nessa primeira peça:

  • declare("DeviceID.OUI", {value: 1}): pede o OUI com frescor (TTL 1 segundo no cache do GenieACS)
  • return { writable: false, value: [valor, "xsd:string"] }: formato que a UI espera

Confira se gravou:

curl -s http://SEU_IP:7557/virtual_parameters/OUI

Na UI: Admin → Virtual parameters: deve listar OUI.

3) Criar o Virtual Parameter CodigoConexao

curl -X PUT "http://SEU_IP:7557/virtual_parameters/CodigoConexao" --data-binary @- <<'EOF'
const serial = declare("DeviceID.SerialNumber", {value: 1}).value[0];
return {writable: false, value: ["CURSO-" + serial, "xsd:string"]};
EOF

No lab, com serial LAB000001, o valor esperado depois da sessão é:

CURSO-LAB000001

No seu provedor você troca o prefixo CURSO- pelo padrão interno (código do contrato, ID do CRM, etc.). A lógica é a mesma: pegar um campo confiável do Inform e montar o identificador que o time já usa no WhatsApp/ERP.

4) Criar o Virtual Parameter WAN (padronizar IP de internet)

Nome do VP não pode ter ponto: por isso é WAN, e na árvore aparece VirtualParameters.WAN (não wan.1).

A ideia: varrer com wildcard as árvores comuns, preferir PPPoE com ConnectionStatus = Connected (no lab o internet está no .2; o .1 fica Disconnected), depois cair em WANIPConnection e, por último, TR-181.

curl -X PUT "http://SEU_IP:7557/virtual_parameters/WAN" --data-binary @- <<'EOF'
// Padroniza o IPv4 da WAN de internet.
// Vendor A: WANPPPConnection.1 | Vendor B: .2/.3 | TR-181: Device.IP.Interface.*
// Você consulta só VirtualParameters.WAN, sem if por árvore.
let ip = "";
const fresh = {value: Date.now()};

const pppIp = declare(
  "InternetGatewayDevice.WANDevice.*.WANConnectionDevice.*.WANPPPConnection.*.ExternalIPAddress",
  fresh
);
const pppSt = declare(
  "InternetGatewayDevice.WANDevice.*.WANConnectionDevice.*.WANPPPConnection.*.ConnectionStatus",
  fresh
);
const ipconn = declare(
  "InternetGatewayDevice.WANDevice.*.WANConnectionDevice.*.WANIPConnection.*.ExternalIPAddress",
  fresh
);
const d181 = declare("Device.IP.Interface.*.IPv4Address.*.IPAddress", fresh);

function statusMap(iter) {
  const m = {};
  if (!iter.size) return m;
  for (let p of iter) m[p.path] = p.value[0];
  return m;
}

function firstConnectedOrAny(ips, statuses) {
  if (!ips.size) return "";
  let fallback = "";
  for (let p of ips) {
    const v = p.value[0];
    if (!v) continue;
    const stPath = p.path.replace(/\.ExternalIPAddress$/, ".ConnectionStatus");
    const st = statuses[stPath] || "";
    if (st === "Connected") return v;
    if (!fallback) fallback = v;
  }
  return fallback;
}

function firstNonEmpty(iter) {
  if (!iter.size) return "";
  for (let p of iter) {
    if (p.value[0]) return p.value[0];
  }
  return "";
}

ip = firstConnectedOrAny(pppIp, statusMap(pppSt));
if (!ip) ip = firstNonEmpty(ipconn);
if (!ip) ip = firstNonEmpty(d181);

return {writable: false, value: [ip, "xsd:string"]};
EOF

No lab (CPE virtual), o data model tem de propósito:

  • …WANPPPConnection.1Disconnected, sem IP
  • …WANPPPConnection.2Connected, IP 203.0.113.50
  • Device.IP.Interface.1.IPv4Address.1.IPAddress198.51.100.10 (TR-181 “isca”, não deve ganhar se o PPPoE Connected existir)

Depois da sessão, o valor validado foi:

VirtualParameters.WAN = 203.0.113.50

Ou seja: pegou o .2 Connected e não o IP da árvore TR-181. Na prática, filtro e automação passam a olhar VirtualParameters.WAN: sem montar query por path de ZTE vs Huawei vs TR-181.

Obs.: em CPE real o wildcard * também cobre .3, .4… Ajuste a ordem (PPP → IPConnection → Device.IP) se o seu parque for majoritariamente TR-181 puro.

5) Provision que “liga” os VPs ao device

Só criar em Admin não coloca o valor no device. A provision pede o fetch:

curl -X PUT "http://SEU_IP:7557/provisions/lab-curso-bootstrap" --data-binary @- <<'EOF'
const serial = declare("DeviceID.SerialNumber", {value: 1}).value[0];
log("Provision curso, serial " + serial);
declare("Tags.provisionado", null, {value: true});
declare("VirtualParameters.CodigoConexao", {value: 1});
declare("VirtualParameters.OUI", {value: 1});
declare("VirtualParameters.WAN", {value: 1});
EOF

Quatro coisas nessa provision:

  1. log(...): aparece nos logs do GenieACS (útil pra debug)
  2. Tags.provisionado: marca visual na UI
  3. declare("VirtualParameters.…", {value: 1}): dispara o cálculo de cada VP na sessão
  4. Incluir o WAN na mesma provision evita um preset só para isso

6) Preset: quando a provision roda

O preset não calcula o VP. Ele só diz: “nesse device, rode essa provision”.

Para o CPE virtual do lab (ProductClass = VirtualRouterLAB):

curl -X PUT "http://SEU_IP:7557/presets/lab-virtual-router" \
  -H "Content-Type: application/json" \
  --data '{"weight": 1, "channel": "default", "precondition": "{\"DeviceID.ProductClass\":\"VirtualRouterLAB\"}", "events": {}, "configurations": [{"type": "provision", "name": "lab-curso-bootstrap"}]}'

E o canal bootstrap (primeiro Inform com evento 0 BOOTSTRAP):

curl -X PUT "http://SEU_IP:7557/presets/lab-virtual-router-bootstrap" \
  -H "Content-Type: application/json" \
  --data '{"weight": 5, "channel": "bootstrap", "precondition": "{\"DeviceID.ProductClass\":\"VirtualRouterLAB\"}", "events": {"0 BOOTSTRAP": true}, "configurations": [{"type": "provision", "name": "lab-curso-bootstrap"}]}'

Precondition em JSON dentro de string é chato, mas é o formato da NBI. Errou aspas, o preset não casa e a provision não roda (sintoma: device sobe sem tag e sem VirtualParameters).

Atalho equivalente (mesmo conteúdo):

cd /opt/tr069-lab   # ou a pasta do lab
NBI_HOST=http://SEU_IP:7557 ./scripts/seed-curso.sh

7) Disparar a sessão e ver o valor

Se o CPE já está conectado, acorde com Connection Request (credencial padrão do lab: acs / acs):

curl -u acs:acs http://SEU_IP:7548/tr069/connection-request

Espere uns segundos e consulte pela NBI:

curl -s "http://SEU_IP:7557/devices/?projection=DeviceID.SerialNumber,DeviceID.OUI,VirtualParameters,Tags" | python3 -m json.tool

No lab validado, o device ACAD01-VirtualRouterLAB-LAB000001 veio com:

VirtualParameters.CodigoConexao = CURSO-LAB000001
VirtualParameters.OUI           = ACAD01
VirtualParameters.WAN           = 203.0.113.50

Na UI: abra o device → busque CodigoConexao, WAN ou navegue em VirtualParameters.

Apagou o device no ACS? A definição do VP em Admin permanece. O valor no device só volta depois de nova sessão CWMP com a provision ativa, reconecte no :8080 se precisar e dispare o CR de novo.

O que costuma falhar

  • Criou o VP e não colocou declare("VirtualParameters.X", {value: 1}) na provision → Admin mostra o script, device não.
  • Preset com precondition errada (ProductClass diferente do CPE) → provision nunca roda.
  • Body do curl em JSON por engano → NBI aceita de forma estranha ou o script não executa. Use --data-binary com o JS puro.
  • LAB_PUBLIC_IP errado no Docker → painel do CPE mostra URL inútil; Inform não sobe. Veja o post de instalação.
  • VP WAN veio vazio → o CPE ainda não tem ExternalIP na árvore (refresh/GetParameterValues) ou o status não é Connected e não há fallback. Olhe …WANPPPConnection.*.ExternalIPAddress no device antes de culpar o script.
  • NBI 401 → faltou -u (auth ligada).

Fechando

Virtual param é ferramenta de operação: código de conexão na tela, IP de WAN estável, sem o CPE “saber” de billing e sem cada um decorar path de fabricante. Comece pelo espelho (OUI), feche o ciclo com provision + preset, depois padronize o que o time mais pergunta (CodigoConexao, WAN, sinal, PPPoE…).

Stack e portas: Instalação GenieACS: Bare Metal x Docker. Se algo não bater no seu lab, deixa um comentário no post.

Gostou do conteúdo? Veja os cursos em aberto ou entre na lista de interesse. Os treinamentos são ao vivo e 100% práticos.

Você pode gostar...

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

×