linux

  • O Debian Jessie, última versão estável do sistema operacional universal Debian, foi lançado a quase 1 ano.  Somente agora criei coragem de fazer o upgrade.  Estava rodando Wheezy, a versão anterior que entrou em LTS (Long Term Support ou suporte estendido), que atendia bem o site mas, sempre existe um mas, descobri alguns problemas de limitação do kernel com o greyd.

    Alguns sites não estavam sendo marcados corretamente com ipset.  Como o greyd foi criado pra versões mais recentes de kernel (Wheezy rodava com um linux-3.2), a opção era desativar o greyd ou atualizar o sistema.  Então vamos atualizar!

    Sem ajuda dos trus La_Sombra e Rootsh nada disso seria possível.  Foi um trabalho de garimpo no passado pra achar como conectar na VM e quem ainda tinha a chave de conexão.  Culpa disso pela estabilidade do sistema.  Obrigado Debian!

    Eu já tinha feito o upgrade do Ubuntu LTS, de 14.04 pro 16.04, e esperava alguns problemas que seriam corrigidos com reboot e uns "apt-get -f install", então o acesso ao console era essencial.  Parte dessa espectativa era também o motivo pra ter postergado esse upgrade.  systemd é algo que ainda me dói no alma.

    Pra fazer o upgrade?  Do bom e velho jeito do Debian: primeiramente deixando atualizado na versão corrente, Wheezy

    # cd /etc/apt
    # apt-get update && apt-get upgrade
    

    Em seguida mover a versão pra Jessie e continuar com upgrade.

    # cp sources.list sources.list.wheezy
    # cat sources.list.wheezy | sed "s/wheezy/jessie/" > sources.list
    # apt-get update
    # apt-get dist-upgrade
    

    Pronto.  É isso.  Fácil assim.  

    Foram uns 500 MB de download, uma vez que é um servidor e roda só o necessário, um reboot e... saiu funcionando de primeira!  Eu achando que o systemd ia encrencar com alguma coisa e... nada!  Claro que nem tudo foi tão bem assim.  Precisei arrumar algumas configurações do servidor web, assim como do mail, mas muita coisa saiu funcionando sem mexer.  Ainda estou com problemas com o dovecot, mas nada que force uma volta ao Wheezy pra corrigir.

    Parabéns à equipe do Debian por um sistema tão afinado e redondo assim.  Continua sendo minha distro preferida.

  • Por uma coincidência incrível, daquelas que ocorrem a cada alinhamento de planetas ou algo do gênero, assisti ontem a palestra do John Maddog Hall na campus party 7 cujo nome é esse do texto: fique rico com software livre.  É o tipo de assunto que gosto de accompanhar, mas eu tinha baixado a palestra pra assistir e tinha esquecido completamente.  Eis que o wifi estava ruim ontem, Netflix não funcionando, Internet intermitente e... sim, resolvi vasculhar o HD pra ver se tinha algo que eu pudesse ler ou assistir.   Incrível o poder de abandonar coisas interessantes conforme a capacidade do HD aumenta...

     Em várias listas e grupos que participo existe uma noção errada, e até um pouco ingênua, de que software livre é o que basta pro negócio dar certo.  Que com software livre já existe uma vantagem competitiva.   Nessa apresentação do Maddog, que está em inglês, ele toca nesse ponto.  Eu peguei algumas partes pra comentar.

    Esse primeiro slide é bem interessante.  Ele comenta sobre as liberdades que definem um software como livre.  E coloca bem claro na parte de baixo: ninguém disse que não devia fazer dinheiro escrevendo software livre.  Sim, software livre pode ser vendido.  Pode-se ganhar dinheiro com ele.  Já comentei algumas vezes em sobre a 5a liberdade que criaram - mais acirramente no Brasil - onde transformar software livre em dinheiro virou algum tipo de pecado.  Não é.

    Business as usual.  Software livre significou uma quebra de paradigma na forma de fazer software, mas não na de fazer negócios.  Para ganhar dinheiro com software livre é preciso conhecer seu mercado, seus concorrentes, seu produto, seus consumidores, enfim tratar como outro negócio qualquer.  Não existe mágica com software livre.  Software é software e negócios são negócios.

    O por qual motivo as pessoas escrevem software livre?  São inumeradas as razões, que diferem em muito do software proprietário, onde somente um modelo de negócios existe.  Mas não acredite que software livre é feito somente por hobbistas.  Grandes empresas já fazem ou contribuem com a maioria dos projetos de software livre.  Em termos de negócios, abrir seu software como livre pode não trazer um benefício direto.  Pode ajudar na correção de bugs e adição de melhorias, mas não necessariamente no negócio em si (software livre não é vantagem competitiva, lembra?).

    E alguém pagaria pra ter um software livre?  Sim!  Maddog inumera alguma das razões.  A maior dela é usar o software livre pra atender mais rapidamente alguma necessidade da empresa.  Então essa empresa com certeza pagaria o desenvolvedor, ou que se quiser manter o software, pra adicionar uma melhoria.  Ou mesmo criar um fork.

    São consideradas outras facetas do software livre como negócio, mas não vou comentar uma por uma pois o melhor é assistir a palestra por inteiro.  Apenas lembre-se que é possível ganhar dinheiro com software livre, mas isso não significa que software livre seja uma vantagem competitiva pro negócio.

     

  • Esses dias estava analisando um código que era parecido com shell script.  Sua função era monitorar as interfaces de rede para, no caso de alteração (nova interface surgindo ou desaparecendo), registrar a mudança de objetos no sistema de dados do opensaf (objetos no IMM pra quem conhece).

    Mas o código fazia algo parecido com um shell que a todo healthcheck do AMF (framework do opensaf que é muito semelhante ao systemd e roda determinado programa ou script de tempos em tempos) fazia uma busca com o comando "ip link addr list" e comparava com o que estava  armazenado no IMM.  Algo como:

    def healthcheckCallback(self, invocation, compName, healthcheckKey):
    		        saAmfResponse(self.check_macs,
    				      invocation, eSaAisErrorT.SA_AIS_OK)

    def check_macs(self):
    macs = []
    for line in self.get_link_list().split("\n"):
    if not re.search("link/ether", line): continue
    # [ 'link/ether', '52:54:00:c3:5d:88', 'brd', 'ff:ff:ff:ff:ff:ff']
    mac.append(line.split()[1])
    imm_obj = self.get_from_imm()
    if imm_obj != macs:
    self.update_imm(mac)

    def get_link_list(self):
    linux_command = "ip link list"
    return self.run_shell(linux_command)

    Essa é uma forma bastante simplificada pra tentar visualizar como tudo funciona.  Eu propositalmente tirei comentários extras e deixei mais limpo apenas para poder comentar aqui. 

    • Agora explicando o que cada método faz ali:
    • healthcheckCallback(): esse é  o método que eu registrei junto ao AMF pra que seja chamado de tempos e tempos e rode a função check_macs().  Não vou entrar em detalhes dos outros parâmetros pois são inerentes em como o AMF funciona junto ao OpenSAF.  Deixei o link pra o exemplo de uma implementação completa ao final do artigo.
    • check_macs(): é uma função que pega uma listagem em formato array do comando "ip link list" e armazena os MAC, endereço de camada 2 da placa de rede.
    • get_link_list(): apenas pra deixar mais legível a parte que busca e monta o array de informações de rede de um sistema Linux.  O método run_shell() é apenas um subprocess.check_output() de forma mais legível (e por isso omiti essa parte do código).

    Como a chamada pra buscar os dados junto ao IMM no OpenSAF tem muitas linhas, eu só deixei um get_from_imm() que retornará um array de mac registrados anteriormente.  Se esse valor for diferente do coletado, então é chamado o método update_imm() com os macs que devem estar lá.

    Funciona?  Sim, funciona.  Mas... se não houve nenhuma mudança nas interfaces de rede (como a subida de uma interface de VIP ou mesmo um container em docker), por quê eu preciso rodar o get_link_list()? 

    Entendeu qual foi meu ponto?

    O código em si consiste em rodar o monitoramente separado numa thread.  Toda vez que o código detecta uma mudança (na verdade o kernel sinaliza isso), ele altera uma variável que o programa lê durante o healthcheck.  Algo como:

    def check_macs(self):
    if self.network_changed is False: return

    Assim bem simples.  Teve mudança? network_changed vira um True.

    Linux tem mecanismos pra detectar uma mudanças na interfaces de rede.  Por quê não usar?  E foi o que fiz.

    Criei um método chamado monitor_link() que é iniciado junto com programa no método initialize(), que é parte de como o AMF faz as chamadas de callback:

    self.thread = threading.Thread(target=self.monitor_link, args=())
    self.thread.start()

    E como funciona o monitor_link()?  Aqui tenho de pedir desculpas antecipadamente que enquanto o código utiliza menos CPU e memória que chamar um shell script, o tamanho e complexidade é bastante grande.  No fim troquei 2 linhas de código por umas 35 linhas.  Na verdade eu praticamente escrevi o código por trás do "ip link".  Mas o resultado ficou independente desse comando e mesmo de utilizar um shell externo pra buscar o resultado.

    A primeira coisa é criar um socket do tipo AF_NETLINK.  Em seguida fazer um bind() num ID aleatório e monitorar com RTMGRP_LINK.

    def monitor_link(self):
        # Create the netlink socket and bind to RTMGRP_LINK,
        s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE)
        s.bind((os.getpid(), RTMGRP_LINK))
    

    pra gerar o código aleatório que é um inteiro, usei os.getpid() pra usar o PID do próprio programa.

    Em seguida é iniciado um loop com select() em cima do descritor do socket pra leitura.  Quando aparecer algum dado, daí sim a informação é lida.

    rlist, wlist, xlist = select.select([s.fileno()], [], [], 1)

    O que vem a seguir são quebras da sequência de bits até chegar no ponto é possível ver o tipo de mensagem que chegou do select().  Se o tipo de mensagem for NOOP de algo nulo, apenas continue monitorando no select().  Se vier algum ERROR, pare o programa.  Se vier mensagem e não for do tipo NEWLINK pra um link novo ou mudança de MAC, também continue aguardando no select().

    if msg_type == NLMSG_NOOP: continue
    elif msg_type == NLMSG_ERROR: break
    elif msg_type != RTM_NEWLINK: continue

    Por fim uma iteração nos dados pra buscar o tipo.  Se o dado for do tipo IFLA_IFNAME, que é uma nova interface ou mudança de nome, ou IFLA_ADDRESS, que é MAC e endereço IP, muda a flag de network_changed pra True. 

    rta_type == IFLA_IFNAME or rta_type == IFLA_ADDRESS:

    E é isso.  O código completo segue abaixo.

    def monitor_link(self):
        # Create the netlink socket and bind to RTMGRP_LINK,
        s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE)
        s.bind((os.getpid(), RTMGRP_LINK))
    
        while self.terminating is False:
            rlist, wlist, xlist = select.select([s.fileno()], [], [], 1)
    if self.network_changed is True: continue if self.terminating is True: return
    try: data = os.read(rlist[0], 65535) except: continue msg_len, msg_type, flags, seq, pid = struct.unpack("=LHHLL", data[:16]) if msg_type == NLMSG_NOOP: continue elif msg_type == NLMSG_ERROR: break elif msg_type != RTM_NEWLINK: continue data = data[16:] family, _, if_type, index, flags, change = struct.unpack("=BBHiII", data[:16]) remaining = msg_len - 32 data = data[16:] while remaining: rta_len, rta_type = struct.unpack("=HH", data[:4]) if rta_len < 4: break rta_data = data[4:rta_len] increment = (rta_len + 4 - 1) & ~(4 - 1) data = data[increment:] remaining -= increment if rta_type == IFLA_IFNAME or rta_type == IFLA_ADDRESS: self.network_changed = True

    Encontrei essa implementação no Stack Overflow buscando informação do código em C.  Foi uma grande ajuda e deixou meu programa muito mais coerente com o que eu realmente queria.

    Ficou muito maior?  Ficou.  Mas também ficou muito mais 1337 :)

    Mais:

    [1] Exemplo de uso de python com AMF no OpenSAF: https://sourceforge.net/p/opensaf/staging/ci/default/tree/python/samples/amf_demo

    [2] Projeto OpenSAF: https://sourceforge.net/projects/opensaf/

    [3] Implementação original desse código: https://stackoverflow.com/questions/44299342/netlink-interface-listener-in-python