O Raspberry Pi
Há algum tempo, comprei uma placa Raspberry Pi com o objetivo de conhecer o hardware e medir o desempenho do Linux nela.
Enquanto a plaquinha não chegava, as ideias iam surgindo. No início, pensei em um servidor de disco para armazenar meus arquivos pessoais. Em seguida, surgiu a ideia de usar a plaquinha como um servidor multimídia, usando o XBMC. Logo em seguida, pensei em instalar o Mame para diversão.
Atualização em 2014-12-28 - Leia no fim da página…
E foram surgindo várias e várias ideias de uso para o Raspberry.
Pois bem, a placa chegou! Instalei o Debian no SD card e testei… Que maravilha !!!! Tudo funcionou conforme o script e com um desempenho satisfatório para o hardware.
Após o teste desliguei tudo, guardei a placa na minha gaveta e lá ela ficou, dormindo por quase 6 meses.
Até que um dia, surgiu mais uma ideia de uso, mas desta vez com algo que estava me dando um certo trabalho. Como eu havia assinado um novo plano de banda larga no provedor (GVT), comecei a monitorar se estava recebendo a velocidade contratada. Para tal, usava o medidor Speedtest.net. Além disso, de tempos em tempos eu usava a interface web do meu modem para checar alguns parâmetros da conexão ADSL (atenuação da linha, nível de sinal-ruído, etc.)
Daí resolvi monitorar a “qualidade” da minha conexão usando o Raspberry Pi. Como se trata de uma placa que roda Linux é possível utilizar as ferramentas disponíveis deste ambiente e escrever pouco código para atingir o objetivo. Além disso, a placa tem uma característica importante para um sistema de monitoramento 24/7 doméstico: seu consumo é baixo (por volta de 2 watts).
As ferramentas
Antes de sair escrevendo código, resolvi buscar por alguma solução pronta para o teste de velocidade do link. Encontrei um código python aqui. (tespeed)
O tespeed faz um teste de velocidade utilizando as instâncias de servidores do Speedtest.net. Ou seja, faz o que eu estava fazendo “na mão”, quando acessava o site.
Para executar o teste de velocidade, em seu teste padrão (sem parâmetros adicionais), o testpeed seleciona um servidor dentre os servidores cadastrados em uma lista, e troca dados com este servidor com o objetivo de medir a velocidade do link. Na seleção do servidor, o tespeed utiliza aquele de menor latência e localizado próximo de sua conexão com a internet (a localização é estabelecida utilizando o endereço IP fornecido pelo provedor que você assina e a latência é medida pelo tempo tempo de ping)
O único problema neste processo de seleção é que o servidor escolhido poderá ser um servidor localizado dentro da rede de seu provedor. Isto ocorre porque alguns os provedores de internet instalam instâncias do server do speedtest.net em seus próprios servidores e os adicionam na lista de servidores disponíveis para teste do speedtest.net.
Com isso, quando um servidor de seu próprio provedor é selecionado, o seu teste poderá não estar testando a sua velocidade de conexão com a Internet, de fato. Isto porque o teste de velocidade será feito através de uma conexão entre o seu computador doméstico e o servidor instalado em seu provedor. Nenhum tráfego será gerado “para a Internet”. Se a “saída” de seu provedor estiver congestionada, o teste não captará este congestionamento, uma vez que apenas a infraestrutura do próprio provedor estará sendo testada (a conexão entre sua casa e o servidor instalado no provedor).
Apesar desta característica do teste padrão executado pelo tespeed, quando planejei o teste de minha conexão com a Internet, imaginei um teste dos parâmetros da minha conexão até o outro lado da linha telefônica, por entender que este seria o ponto mais frágil de minha conexão. (o caminho da tomada telefônica, em minha residência, até o DSLAM do provedor, pelo menos)
Mesmo assim, para quem estiver pensando em executar o teste de velocidade com algum servidor específico (dentre os disponíveis no speedtest.net), é possível fixar o servidor no tespeed, chamando o programa com um parâmetro adicional. (verifique o manual ou o help do programa)
Além do código do tespeed, neste projeto foram utilizadas outras ferramentas:
- bash - dispensa comentários. Um manual pode ser encontrado aqui.
- RRDtool - armazenamento dos valores medidos e geração dos gráficos
- Python - usei as funções de parser de arquivos HTML
- lighttpd - servidor html
- wget - utilizado para ler a página de estatísticas da conexão ADSL, gerada pelo modem
- cron
Script de definição de parâmetros (defs.sh)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
|
Neste script são definidos todos os parâmetros que serão compartilhados pelos módulos do projeto.
As variáveis MODEM_ADDR, MODEM_DSL_PAGE, MODEM_USER e MODEM_PASS definem os parâmetros necessários para que um script (que será apresentado mais adiante) obtenha as informações do modem. _(é necessário editar o arquivo defs.sh e atribuir os valores das variáveis MODEM_ADDR, MODEM_USER e MODEM_PASS)
Aqui cabe um comentário: geralmente, o protocolo SNMP é utilizado para obter informações de um dispositivo de rede (switchs, roteadores, modems, etc.). Acontece, porém, que o provedor que assino (GVT) não disponibiliza este protocolo para acesso às informações no modem (PowerBox). Caso disponibilizasse, seria possível utilizar o aplicativo snmpget para obter as informações do dispositivo e alimentar o banco de dados do RRDTools.
Para contornar o problema, a solução que implementei pega as informações diretamente de uma página web gerada pelo modem (as tradicionais páginas de configuração do dispoitivo, acessadas pelo browser). Para realizar esta tarefa, usei o aplicativo wget, que lê a página gerada pelo modem utilizando como parâmetro as variáveis definidas neste script (endereço do modem, username, password e local da página html).
Os demais parâmetros definidos nas linhas 36 em diante são ordenados na forma de um array, onde casa posição deste array armazena os dados referentes a alguma informação medida (ex: velocidadei de Download), ou obtida do modem (ex: Downstream Attenuation).
Os seguintes elementos fazem parte deste array:
- ARR_DATABASE_DS_NAME[] - nome que será utilizado no RRDTool (DS - database source)
- ARR_GRAPH_TITLE[] - nome que será exibido como título do gráfico gerado pelo rrdgraph (RRDTool)
- ARR_GRAPH_VERTICAL_LABEL[] - título do eixo vertical do gráfico gerado pelo rrdgraph
- ARR_GRAPH_FILENAME[] - nome do arquivo de saída gerado pelo rrdgraph (o rrdgraph gerará um arquivo no formato PNG)
- ARR_MODEM_STRING[] - string utilizada pelo script para localizar a posição da informação que será obtida do arquivo html que contém as estatísticas do modem
- VALUE_FROM_HTML[] - valor capturado do arquivo html gerado pelo modem (ou valores medidos pelo script de teste de velocidade)
Script de criação o banco de dados do RRDTool (create_database.sh)
Este script cria o banco de dados que será utilizado pelo RRDTool para armazenar os valores medidos e capturados do modem.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
|
A função deste script é montar a string que define os data-source do banco de dados e executar o aplicativo rrdtool para criar o banco de dados.
A string é dinamicamente montada, usando os valores definidos no script defs.sh.
(Deixei, como referência, o código comentado da string que será montada para a criação do banco de dados.)
Script de atualização do banco de dados (update_database.sh)
Este script, um dos mais importantes do projeto, é responsável pela atualização do banco de dados.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
|
A linha de código que executa o teste da velocidade é a seguinte:
1
|
|
Esta linha chama o interpretador python, que executa o tespeed.py, pega os valores de sua saída e armazena nas variáveis Download e Upload.
Para a leitura dos parâmetros do modem, há a necessidade de acessar a página html gerada por este dispositivo. Esta tarefa é realizada pela seguinte linha:
1
|
|
Esta linha executa o wget e armazena o resultado de sua saída na variável modem_out, utilizando o recurso command substitution do bash. Veja um trecho deste código html armazenado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
As seguintes linhas de código fazem o trabalho de extrair os valores que interessam (as estatísticas do modem):
1 2 3 4 |
|
Este loop pega, em cada iteração, uma string do array ARR_MODEM_STRING definido em defs.sh e a utiliza para filtrar a saída que foi armazenada na variável modem_out.
Exemplificando: no caso em que ARR_MODEM_STRING[2]=”Downstream Current Rate”, no arquivo defs.sh, o grep extrairá a seguinte linha:
1
|
|
Esta saída será repassada para o programa python extract_html_data.py, cujo código é reproduzido a seguir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
Este programa faz um parse no código html que é lido da entrada padrão (pipe da saída do grep) e extrai o valor de interesse (no código html exibido anteriormente, o valor que será extraído é o 17761). Em seguida, o programa escreve este valor na saída padrão, utilizando o parâmetro que foi utilizado na chamada (“VALUE_FROM_HTML[$idx]”).
Como resultado, no caso da iteração em que a variável idx tem o valor “2”, a saída será:
VALUE_FROM_HTML[2]=17761
Este a saída será avaliada pelo “eval”, que armazenará o valor extraído do código html, na variável VALUE_FROM_HTML[].
É importante ressaltar que nos arrays definidos no arquivo defs.sh, a primeira e a segunda posição (índices 0 e 1) são utizadas para armazenar os valores obtidos pela execução do testpeed, e não valores lidos do modem. E é justamente o que faz as seguintes linhas de código:
# Stores the values measured by tespeed in the VALUE_FROM_HTML array (TODO: change this variable name)
VALUE_FROM_HTML[0]=$Download
VALUE_FROM_HTML[1]=$Upload
Por fim, o trecho final do script update_database.sh executa o comando rrdtool update para armazenar no banco de dados do RRDTool os valores obtidos. A string que executa este comando é montada dinamicamente, da mesma maneira que foi montada a string do comando de criação do banco de dados (script create_database.sh)
Visualização dos dados coletados (gráficos)
Para visualização dos dados coletados, criei uma página web. Desta forma, todos os gráficos gerados pelo RRDTool são facilmente visualizados utilizando um browser. Além disso, a utilização de um servidor web no Raspberry permite acessar a página remotamente, bastando para isso utilizar um serviço de DNS dinâmico (o software do modem GVT implementa a facilidade de DNS dinâmico).
Usei como template um exemplo de código HTML5 que encontrei neste endereço.
Código HTML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
O código CSS não será apresentado aqui, mas pode ser baixado neste link
O resultado deste código (html e CSS) é exibido seguir:
Ao clicar em um dos “botões” na página, a função create_graphs será chamada, que por sua vez, executará o cgi create_graphs.cgi, exibido a seguir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Este cgi é um script bash que gera dinamicamente o código html que será exibido dentro do iframe definido na página html (speedtest.html).
O script chama o rrdtool para gerar o gráfico com o parâmetro “-z” (lazy), de forma que os gráficos só são gerados quando houver alteração no gráfico gerado anteriormente, ou no caso de nenhum gráfico ter sido gerado ainda. Esta opção evita a geração desnecessária de gráficos, se o usuário ficar navegando na pagina html. (importante pois estamos usando um SD card para armazenamento dos dados!)
Instalando a “tralha” toda
- Instalação do lighttpd:
1
|
|
Duas pequenas alterações são necessárias para que o lighttpd permita a execução de cgi bash. Para isto, edite o arquivo lighttpd.conf localizado no diretório /etc e inclua a seguinte linha nas definição do array server.modules:
1
|
|
O resultado final será (trecho do arquivo):
1 2 3 4 5 6 7 8 |
|
Adicione também, logo abaixo a definição da variável server.breakagelog, a seguinte linha:
1
|
|
O resultado final será (trecho do arquivo):
1 2 3 4 5 6 |
|
- Editar o arquivo /etc/groups e adicionar o usuário corrente do raspberry no grupo www-data (substituir o NOME_DO_USUARIO, na linha abaixo, pelo usuário que rodará o cron no seu raspberry):
1
|
|
- Instalação do wget:
1
|
|
- Instalação do RRDTool:
1
|
|
- Copiar todos os arquivos para o diretório /var/www/speedtest:
1 2 3 4 |
|
- Executar o comando para criar o banco de dados
1 2 |
|
- Editar o crontab e adicionar as linhas que atualizam o banco de dados
1
|
|
Adicionar as seguintes linhas:
1
|
|
Com esta configuração do cron, o script update_database.sh será executado de hora em hora, coletando os dados e atualizando o banco de dados do RRDTool.
Resultado final
Para abrir a página onde os gráficos gerados serão exibidos, digite o seguinte endereço em seu browser: http://endereco_ip_do_raspberry/speedtest/speedtest.html.
O resultado final, quando a página abrigada no servidor www for exibida pelo browser, para os gráficos do dia:
Para os gráficos da semana:
E aqui, o Raspberry, sem case, sem local definido, repousando temporariamente sobre o meu modem…
Os códigos estão disponíveis no repositório Github
2014-12-28 - Update: Devido a algumas alterações no site speedtest.net, o tespeed foi modificado para contemplá-las. Use o código mais recente do tespeed, encontrado em tespeed