Título alternativo: How to get CPU usage per core with python
Teste de indexação do google ;)

Mais um post para a série “Parseando o /proc” [1]. Dessa vez sem motivo explícito, apenas diversão!

Ambientes de testes:

  • Ubunto c/ 1 núcleo e Python 2.6
  • Ubunto c/ 2 núcleos e Python 2.6
  • CentOS c/ 4 núcleos e Python 2.4.
  • Comparei os resultados através do htop e obtive uma proximidade alta nos valores, as diferenças se dão provavelmente pelo momento e duração da medição!

    Quem puder testar em ambientes com mais núcleos ou uso intensivo da CPU e comparar com as ferramentas do sistema por favor me de um feedback

    E quem rir da qualidade do meu inglês nos comentários eu vou banir o ip! :)

    ?View Code PYTHON
    #!/usr/bin/env python
    import time
    import os
     
    # [ reference ]
    # Detect number of cpus
    # http://www.boduch.ca/2009/06/python-cpus.html
    # Get cpu usage (But this example is not working 100%)
    # http://ubuntuforums.org/showpost.php?p=853257&postcount=4
     
    INTERVAL = 2
     
    class CPUUsage:
        def __init__(self):
            self.cores = self.detectCPUs()
     
        def createCoreArray(self):
            cores = []
            for i in range(self.cores):
                cores.append("")
            return cores
     
        def detectCPUs(self):
            if hasattr(os, "sysconf"):
                if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
                    ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
                    if isinstance(ncpus, int) and ncpus > 0:
                        return ncpus
            return 1 # Default
     
        def getTimeList(self):
            core = self.createCoreArray()
     
            statFile = file("/proc/stat", "r")
            if not self.cores == 1:
                statFile.readline()
     
            # this FOR will split lines from proc/stat for every active core
            for cid in range(self.cores):
                # when have just one core, split a different range
                if self.cores == 1:
                    core[cid] = statFile.readline().split(" ")[2:7] # 2:6
                else:
                    core[cid] = statFile.readline().split(" ")[1:6] # 1:5
            statFile.close()
     
            # convert all itens in splited list from STR to INT for every core
            for i in range(len(core[0])):
                for cid in range(self.cores):
                    core[cid][i] = int(core[cid][i])
     
            # return the core list now with INT values inside their cpu values
            # like this: ([core,1,values],[core,2,values],[core,3,values])
            return core
     
        def deltaTime(self,interval):
            coreT1=self.createCoreArray()
            coreT2=self.createCoreArray()
     
            # get core TIME 1 state per core
            coreT1 = self.getTimeList()
            # wait X seconds to get new state core
            time.sleep(interval)
            # get core TIME 2 state per core
            coreT2 = self.getTimeList()
     
            #Get the diference of coreT2.item[i] and coreT1.item[i] for every core
            for i in range(len(coreT1[0])):
                for cid in range(self.cores):
                    coreT2[cid][i] -= coreT1[cid][i]
            return coreT2
     
        def Usage(self):
            core = self.createCoreArray()
            cpuUsage = []
     
            core = self.deltaTime(INTERVAL)
     
            for cid in range(self.cores):
                # sum of user,system and nice usage
                tmp_use = core[cid][0] + core[cid][1]+core[cid][2]
                # 100.00 * usage / sum of usage + idle + waiting
                usage = "%.2f" % ((100.00 * tmp_use) / sum(core[cid]))
                cpuUsage.append(usage)
            return cpuUsage
     
    if __name__ == "__main__":
        # Show usage per core
        #print CPUUsage().Usage()
     
        x = 0
        for item in CPUUsage().Usage():
            x = x + 1
            print "CPU[%s]: %s" % (str(x),str(item))

    Na tarde de sábado eu precisava pegar as portas abertas no servidor, da forma mais Real Time possível.  Primeira idéia? Naturalmente, foi parsear o resultado do netstat e ‘grepar’ o LISTEN ou OUÇA, mas depois imaginei que beber diretamente da fonte pudesse fazer o negócio andar mais rápido. Pesquisa daqui, pesquisa dali, aparentemente o netstat pega suas informações dos arquivos em /proc/net.

    Após umas horinhas consegui o protótipo inicial do código que preciso. Mas durante a caminhada perdi o foco e acabei brincando um pouco mais com o /proc/net/tcp, no fim tinha um netstat simplificado feito em Python.

    Então se alguém algum dia precisar recuperar informações de conexões ao estilo netstat aí está o código:

    ?View Code PYTHON
    #!/usr/bin/python
     
    # Criado por Ricardo Pascal, qualquer dúvida ou
    # contribuição mande para  'pascal.linux' no gmail!
    # Pode usar a vontade só lembre-se de citar a autoria ;)
    # Agradecimento ao pessoal do #python-br na freenode.
    # A galera lá sempre ajuda.
     
    PROC_TCP = "/proc/net/tcp"
    STATE = {
    	    '01':'ESTABLISHED',
    	    '02':'SYN_SENT',
    	    '03':'SYN_RECV',
    	    '04':'FIN_WAIT1',
    	    '05':'FIN_WAIT2',
    	    '06':'TIME_WAIT',
    	    '07':'CLOSE',
    	    '08':'CLOSE_WAIT',
    	    '09':'LAST_ACK',
    	    '0A':'LISTEN',
    	    '0B':'CLOSING'
    	}
     
    tcp_FH = open(PROC_TCP,'r')
    content = tcp_FH.readlines()
    content.pop(0) # remove header
    tcp_FH.close()
     
    def hex2dec(s):
        return int(s,16)
     
    def ip(s):
        return str(hex2dec(s[6:8])) +"."+ str(hex2dec(s[4:6])) +"."+ \
    	    str(hex2dec(s[2:4])) +"."+ str(hex2dec(s[0:2]))
     
    def remove_empty(list):
        return [x for x in list if x !='']
     
    def get_list(c):
        result = []
        result.append(['ID','IP','PORT','rIP','rPORT','STATS'])
        for lines in content:
    	line_array = remove_empty(lines.split(' '))
     
    	line = [line_array[0], line_array[1].split(':'), \
    		line_array[2].split(":"),line_array[3]]
     
    	line = [line[0], ip(line[1][0]), hex2dec(line[1][1]), \
    		ip(line[2][0]), hex2dec(line[2][1]), STATE[(line[3])]]
     
    	result.append(line)
        return result
     
    if __name__ == '__main__':
        for lines in get_list(content):
    	print lines

    PS. Quando eu tiver tempo eu refaço a cama de gato que ta ali na função get_list() e ip().

    Imagino que no final vou acabar usando o Standalone WSGI Server (engine do CherryPy) com a solução apresentada no Static. Mas até lá continuo fazendo meus testes com o CherryPy mesmo.

    Alguma vez enquanto você escrevia um código já aconteceu de “acidentalmente” soltar um rm * no lugar errado? Pois é, comigo já. Aconteceu ALGUMAS vezes, teve até um dia que isso aconteceu DUAS vezes, e lá vai o tanso aqui reescrever todo o código.

    Alguns podem dizer que a melhor maneira de evitar isso é usar um repositório versionado. Mas e se você quer apenas escrever um script? Não precisa nem ser “bonito”, basta funcionar? Nesses casos o uso de um repositório parece que só te atrasa.

    Pensando nisso (e usando a filosofia do não precisa nem ser bonito, basta funcionar), criei esse fim de semana o backup_this. Basicamente é uma ferramenta que comprime a pasta corrente em um arquivo .tar.gz e envia ela para o gmail.

    Funciona assim:

    Se você quiser testar pode fazer o download aqui, depois é só mover para /usr/bin/ e colocar a permissão de execução.

    Eu estou gostando, acho que até que vou fazer umas melhorias. Incluir envio por ssh, exclusão de tipos de arquivos, passagem de alvos como parâmetros e etecétera.

    Eu não gosto muito de trabalhar com servidores de e-mail, na sua maioria são caóticos em se tratando do gerenciamento. Tudo que você quer fazer depende de muitas modificações nos arquivos de configuração, adição de patches ou softwares extras. Particularmente me desagrada muito a maneira como se linka os mail servers a um banco de dados para gerenciar usuários e domínios. Mas aparentemente não sou o único a pensar assim.

    Duncan McGreggor levou isso ao próximo nível, o cara codificou o próprio mail server. Infelizmente ele parou com a “loucura” mas felizmente ele disponibilizou o código no launchpad.

    Estive pensando em dar uma olhada, e quem sabe, trabalhar em cima do código, ou talvez eu deva fazer o meu próprio? Com certeza traria um bocado de conhecimento e muitas horas de diversão! :D

    E você? Já teve vontade de reescrever um software por não gostar dos já disponíveis na comunidade?

    Um outro dia me deu vontade de trabalhar com o random do Python e para fazer algo útil resolvi criar um código que gerasse passwords aleatórios e de tamanho fixo.

    Acontece que naquele momento eu não dispunha de internet ou bons materiais sobre Python(exceto o help) para suportar o processo de criação do código, de tal modo que aproveitei a situação para medir a quantas andava o meu conhecimento sobre Python.

    Então, usando apenas o conhecimento disponível na minha cabeça eu comecei a escrever o código e após alguns minutos de tentativa e erros cheguei ao seguinte resultado:

    ?View Code PYTHON
    #!/usr/bin/python
    import random
     
    def rand_op():
        return random.randint(0,1)
     
    def rand_number():
        return random.randint(0,9)
     
    def rand_letter():
        alpha = [ 'a','b','c','d','e','f','g','h','i','j','k','l','n','o','p','q','r','s','t','u','v','x','z','w','y' ]
        return random.choice(alpha)
     
    def generate (size):
        final = ''
        for x in range(size):
    	y = rand_op()
    	#print y
    	if y == 0:
    	    value = rand_number()
    	if y == 1:
    	    value = rand_letter()
    	    op = random.randint(0,1)
    	    if op == 0:
    		pass
    	    if op == 1:
    		value = value.upper()
    	final = final + str(value)
        print final
     
    for k in range(20):
        generate(10)
    Bem feio né? Pois eis que hoje de tarde me deparei com esse código perdido em alguma pasta dentro do meu $home e decidi recriar o código, agora consultando a internet e outros materiais relativos a Python que tenho disponível em casa, o resultado que se seguiu foi:
    ?View Code PYTHON
    #!/usr/bin/python
    import string
    import random
     
    def genPass(lenght):
        AlphaNumeric = string.ascii_letters + string.digits
        PassWord = ''.join([random.choice(AlphaNumeric) for _ in range(lenght)])
        return PassWord
     
    for _ in range(10):
        print genPass(12)
    Bem mais agradável não? Custou aproximadamente o mesmo tempo para ser desenvolvido e ainda me agregou 2 novos conhecimentos:

    É eu sei… aprender é divertido!

    Recentemente procurando material sobre escalabilidade/alta disponibilidade, esbarrei em uma matéria do highscalability.com sobre o mmo EVE Online. O texto é muito bom e recomendo a leitura, mas vou replicar aqui alguns detalhes que me chamaram a atenção.
    • são 300.000 usuários cadastrados
    • suporta até 40.000 usuários concorrentes(conectados ao mesmo tempo)
    • Stackless Python é usado tanto no servidor quanto no cliente.
    • o banco de dados usado é o SQL Server da microsoft
    • o cluster de banco de dados roda sobre SSD

    Ainda segundo o texto, os discos SSD ajudam MUITO no desempenho do sistema. Assim que eles ficarem mais acessíveis eu compro um para testes.

    Se você já quis um arquivo de configuração para fazer um programa/script ter uma cara mais profissional, eis aqui uma solução simples em python. Read the rest of this entry »