<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: MagnoRSantos</title>
    <description>The latest articles on DEV Community by MagnoRSantos (@magnorsantos).</description>
    <link>https://dev.to/magnorsantos</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F949512%2F2ded5805-387b-4339-8f5e-8a96fd22c55f.png</url>
      <title>DEV Community: MagnoRSantos</title>
      <link>https://dev.to/magnorsantos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/magnorsantos"/>
    <language>en</language>
    <item>
      <title>Evitando conversão implícita no Sql Server com Python usando o módulo pyodbc</title>
      <dc:creator>MagnoRSantos</dc:creator>
      <pubDate>Mon, 19 Aug 2024 04:00:32 +0000</pubDate>
      <link>https://dev.to/magnorsantos/evitando-conversao-implicita-no-sql-server-com-pyodbc-c23</link>
      <guid>https://dev.to/magnorsantos/evitando-conversao-implicita-no-sql-server-com-pyodbc-c23</guid>
      <description>&lt;p&gt;O python facilita muito trabalhar com processos de ingestão de dados em tabelas no Sql Server usando o módulo pyodbc, conforme abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# -*- coding: utf-8 -*-

import os
import dotenv
import pyodbc as po
from datetime import datetime


## Carrega os valores do .env que contem os dados sensíveis de conexao
dotenv.load_dotenv()


## funcao de formacao da connString Sql
def strConnectionSql():

    #variaveis de conexao SQL
    server   = os.getenv("SERVER_TARGET_SQL")
    port     = os.getenv("PORT_TARGET_SQL")
    database = os.getenv("DATABASE_TARGET_SQL")
    username = os.getenv("USERNAME_TARGET_SQL")
    password = os.getenv("PASSWORD_TARGET_SQL")

    strConnection = 'DRIVER={{ODBC Driver 17 for SQL Server}};\
        SERVER={v_server};\
        PORT={v_port};\
        DATABASE={v_database};\
        UID={v_username};\
        PWD={v_password}'.format(v_server = server, v_port = port, v_database = database, v_username = username, v_password = password)

    return strConnection


def gravaDadosSqlTable():

    try:
        ## Connection string
        connString = str(strConnectionSql())
        cnxn = po.connect(connString)
        cnxn.autocommit = False
        cursor = cnxn.cursor()

        RowCount = 0

        ## sql insert 
        sqlcmd = """  
        INSERT INTO [dbo].[Cliente] 
            (
                [Id], [Codigo], [Cidade], [Estado]
            ) VALUES (?, ?, ?, ?);  
        """

        listSqlSource = [  (1, 'A12345', 'São Paulo', 'SP'), 
                    (2, 'A12346', 'São Paulo', 'SP'),
                    (3, 'A12347', 'Campinas', 'SP'),
                    (4, 'B12348', 'Curitiba', 'PR'),
                    (5, 'C12349', 'Rio de Janeiro', 'RJ')

        ]


        for params in listSqlSource:
            cursor.execute(sqlcmd, params)
            RowCount = RowCount + cursor.rowcount


    except Exception as e:
        msgException = "Error: {0}".format(e)
        msgLog = 'Fim inserção de dados no destino [Erro]: {0}'.format(msgException)
        print(msgLog)
        cnxn.rollback()

    else:
        cnxn.commit()

    finally:
        ## Close the database connection
        cursor.close()
        del cursor
        cnxn.close()
        msgLog = 'Quantidade de Registros Inseridos no destino: {0}'.format(RowCount)
        print(msgLog)


## funcao inicial criada para iniciar as chamadas das demais funcoes
def main():
    ## log do início da aplicacao
    datahora = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    msgLog = '\n***** Início da aplicação: {0}'.format(datahora)
    print(msgLog)

    gravaDadosSqlTable()

    ## log do final da aplicacao
    datahora = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    msgLog = '***** Final da aplicação: {0}'.format(datahora)
    print(msgLog)

#### inicio da aplicacao ####
if __name__ == "__main__":
    ## chamada da função inicial
    main()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Porém analisando questões de performance no processo de insert acima é identificado que ocorrem conversões implícitas no Sql Server, pois por padrão o pyodbc envia os dados no formato nvarchar, e caso suas tabelas foram criadas com tipos de dados int, char, varchar entre outros essa conversão afeta a performance no Sql Server.&lt;/p&gt;

&lt;p&gt;Abaixo como é apresentado a instrução de insert no Sql Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?query --
(@P1 nvarchar(1),@P2 nvarchar(6),@P3 nvarchar(9),@P4 nvarchar(2))  
        INSERT INTO [dbo].[Teste] 
            (
                [Id], [Codigo], 
                [Cidade], [Estado]
            ) VALUES (@P1, @P2, @P3, @P4);  

--?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pesquisando sobre como podemos enviar os dados ao Sql Server no formato de tipo de dado esperado pelas colunas, encontrei uma forma que acho que nem todos sabem, que é o uso de cursor.setinputsizes que permite definir os campos do insert conforme os tipos de dados de cada coluna da sua tabela de destino.&lt;br&gt;
Aqui o link que encontrei essa informação: &lt;a href="https://techcommunity.microsoft.com/t5/azure-database-support-blog/lesson-learned-354-why-is-python-using-a-lot-of-cpu-of-azure-sq/ba-p/3830506" rel="noopener noreferrer"&gt;Clique aqui&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cursor.setinputsizes([( po.SQL_INTEGER), (po.SQL_VARCHAR, 10, 0), \
                                (po.SQL_VARCHAR, 60, 0), (po.SQL_CHAR, 2, 0)])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Abaixo o script python ajustado para envio dos dados no formato correto de cada coluna da tabela evitando conversão implícita:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# -*- coding: utf-8 -*-

import os
import dotenv
import pyodbc as po
from datetime import datetime


## Carrega os valores do .env que contem os dados sensíveis de conexao
dotenv.load_dotenv()


## funcao de formacao da connString Sql
def strConnectionSql():

    #variaveis de conexao SQL
    server   = os.getenv("SERVER_TARGET_SQL")
    port     = os.getenv("PORT_TARGET_SQL")
    database = os.getenv("DATABASE_TARGET_SQL")
    username = os.getenv("USERNAME_TARGET_SQL")
    password = os.getenv("PASSWORD_TARGET_SQL")

    strConnection = 'DRIVER={{ODBC Driver 17 for SQL Server}};\
        SERVER={v_server};\
        PORT={v_port};\
        DATABASE={v_database};\
        UID={v_username};\
        PWD={v_password}'.format(v_server = server, v_port = port, v_database = database, v_username = username, v_password = password)

    return strConnection


def gravaDadosSqlTable():

    try:
        ## Connection string
        connString = str(strConnectionSql())
        cnxn = po.connect(connString)
        cnxn.autocommit = False
        cursor = cnxn.cursor()

        RowCount = 0

        ## sql insert 
        sqlcmd = """  
        INSERT INTO [dbo].[Cliente] 
            (
                [Id], [Codigo], [Cidade], [Estado]
            ) VALUES (?, ?, ?, ?);  
        """

        listSqlSource = [  (1, 'A12345', 'São Paulo', 'SP'), 
                    (2, 'A12346', 'São Paulo', 'SP'),
                    (3, 'A12347', 'Campinas', 'SP'),
                    (4, 'B12348', 'Curitiba', 'PR'),
                    (5, 'C12349', 'Rio de Janeiro', 'RJ')

        ]

        ## define os tipos de dados de inputs no sql server
        # isso evita que o Sql Server realize conversão implícita que afeta a performance
        # foi necessário devido o pyodbc por padrão mandar dados no format nvarchar causando a conversão implícita
        cursor.setinputsizes([( po.SQL_INTEGER), (po.SQL_VARCHAR, 10, 0), \
                                (po.SQL_VARCHAR, 60, 0), (po.SQL_CHAR, 2, 0)])

        for params in listSqlSource:
            cursor.execute(sqlcmd, params)
            RowCount = RowCount + cursor.rowcount


    except Exception as e:
        msgException = "Error: {0}".format(e)
        msgLog = 'Fim inserção de dados no destino [Erro]: {0}'.format(msgException)
        print(msgLog)
        cnxn.rollback()

    else:
        cnxn.commit()

    finally:
        ## Close the database connection
        cursor.close()
        del cursor
        cnxn.close()
        msgLog = 'Quantidade de Registros Inseridos no destino: {0}'.format(RowCount)
        print(msgLog)


## funcao inicial criada para iniciar as chamadas das demais funcoes
def main():
    ## log do início da aplicacao
    datahora = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    msgLog = '\n***** Início da aplicação: {0}'.format(datahora)
    print(msgLog)

    gravaDadosSqlTable()

    ## log do final da aplicacao
    datahora = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    msgLog = '***** Final da aplicação: {0}'.format(datahora)
    print(msgLog)

#### inicio da aplicacao ####
if __name__ == "__main__":
    ## chamada da função inicial
    main()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Abaixo como está a execução do insert no Sql Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?query --
(@P1 int,@P2 varchar(6),@P3 varchar(9),@P4 char(2))  
        INSERT INTO [dbo].[Teste] 
            (
                [Id], [Codigo], 
                [Cidade], [Estado]
            ) VALUES (@P1, @P2, @P3, @P4);  

--?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inicialmente pode não parecer que isso faz alguma diferença, porém uma comparação simples dos planos de execução sem conversão implícita e com conversão implícita apresenta planos de execução diferentes e com menos custo de execução do Sql Server quando não ocorre a conversão, isso é válido tanto para instruções de insert, quanto select, update e delete.&lt;/p&gt;

&lt;p&gt;Documentação oficial pyodbc: &lt;a href="https://github.com/mkleehammer/pyodbc/wiki/Cursor#setinputsizeslist_of_value_tuples" rel="noopener noreferrer"&gt;https://github.com/mkleehammer/pyodbc/wiki/Cursor#setinputsizeslist_of_value_tuples&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Artigo detalhado sobre conversão implícita: &lt;a href="https://portosql.wordpress.com/2018/10/25/os-perigos-da-conversao-implicita-1/" rel="noopener noreferrer"&gt;https://portosql.wordpress.com/2018/10/25/os-perigos-da-conversao-implicita-1/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bom, por enquanto é isso. Espero que ajude a mais alguém.&lt;br&gt;
Fiquem com Deus.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Criar uma partição SWAP para uma VM Linux do Azure</title>
      <dc:creator>MagnoRSantos</dc:creator>
      <pubDate>Sat, 13 Jan 2024 14:53:33 +0000</pubDate>
      <link>https://dev.to/magnorsantos/create-a-swap-partition-for-an-azure-linux-vm-98g</link>
      <guid>https://dev.to/magnorsantos/create-a-swap-partition-for-an-azure-linux-vm-98g</guid>
      <description>&lt;p&gt;Criar Swap de forma automática em disco /mnt nas Vms do Azure.&lt;/p&gt;

&lt;p&gt;Bom dia pessoal, venho contribuir um pouco com uma solução para um problema que temos com memória Swap para sistemas Linux em Vms do Azure quando direcionamos o arquivo Swap para o disco de recursos padrão das Vms do Azure que são montados automaticamente no /mnt.&lt;/p&gt;

&lt;p&gt;O conteúdo desse disco é temporário. Quando uma VM é movida para outro host ou interrompida/desalocada, todos os dados gravados neste disco serão apagados.&lt;/p&gt;

&lt;p&gt;Porém a Microsoft recomenda criar o arquivo de Swap nesse disco, agora um problema chato que sempre tinha que ficar atento é lembrar de recriar a swap manualmente quando a máquina era desligada e ligada novamente por alguma necessidade.&lt;/p&gt;

&lt;p&gt;Pesquisando uma forma de automatizar isso na inicialização da máquina encontrei o seguinte artigo do Microsoft Learn explicando 3 formas de fazer isso de forma simples e automatizada "Por isso gosto tanto deles kkkkkkkk".&lt;/p&gt;

&lt;p&gt;Bom o link oficial é esse: &lt;a href="https://learn.microsoft.com/en-us/troubleshoot/azure/virtual-machines/create-swap-file-linux-vm"&gt;Acesse&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vou demonstrar aqui apenas como fazer por meio de um arquivo swap.sh conforme descrito no link acima:&lt;/p&gt;

&lt;p&gt;1 - Criar uma partição SWAP no recurso ou caminho de disco personalizado usando scripts. &lt;/p&gt;

&lt;p&gt;Crie um script de criação SWAP chamado swap.sh em /var/lib/cloud/scripts/per-boot com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/sh

# Percent of space on the ephemeral disk to dedicate to swap. Here 30% is being used. Modify as appropriate.
PCT=0.3

# Location of the swap file. Modify as appropriate based on the location of the ephemeral disk.
LOCATION=/mnt

if [ ! -f ${LOCATION}/swapfile ]
then

    # Get size of the ephemeral disk and multiply it by the percent of space to allocate
    size=$(/bin/df -m --output=target,avail | /usr/bin/awk -v percent="$PCT" -v pattern=${LOCATION} '$0 ~ pattern {SIZE=int($2*percent);print SIZE}')
    echo "$size MB of space allocated to swap file"

     # Create an empty file first and set correct permissions
    /bin/dd if=/dev/zero of=${LOCATION}/swapfile bs=1M count=$size
    /bin/chmod 0600 ${LOCATION}/swapfile

    # Make the file available to use as swap
    /sbin/mkswap ${LOCATION}/swapfile
fi

# Enable swap
/sbin/swapon ${LOCATION}/swapfile
/sbin/swapon -a

# Display current swap status
/sbin/swapon -s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O script será executado a cada inicialização e alocará 30% do espaço disponível no disco de recursos. Você pode personalizar os valores com base na sua situação.&lt;/p&gt;

&lt;p&gt;2 - Torne o script executável:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;chmod +x /var/lib/cloud/scripts/per-boot/swap.sh&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;3 - Pare e inicie a VM. Parar e iniciar a VM só é necessário na primeira vez após a criação do arquivo SWAP.&lt;/p&gt;

&lt;p&gt;Espero que ajude! Até a próxima, fiquem com Deus.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Obter Http Status Code de uma lista de Function Apps do Azure e enviar para o Zabbix</title>
      <dc:creator>MagnoRSantos</dc:creator>
      <pubDate>Wed, 27 Dec 2023 22:50:46 +0000</pubDate>
      <link>https://dev.to/magnorsantos/obter-http-status-code-de-uma-lista-de-function-apps-do-azure-e-enviar-para-o-zabbix-10l9</link>
      <guid>https://dev.to/magnorsantos/obter-http-status-code-de-uma-lista-de-function-apps-do-azure-e-enviar-para-o-zabbix-10l9</guid>
      <description>&lt;p&gt;No artigo anterior postei um script que obtém o status da function que pertence a determinada function app onde exibe se a mesma está habilitada ou desabilitada &lt;a href="https://dev.to/magnorsantos/como-obter-status-das-functions-que-pertencem-as-function-apps-do-azure-e-enviar-os-status-para-o-zabbix-l0c"&gt;Acesse Aqui&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;No artigo atual iremos obter os status http code das function apps do Azure, assim podemos saber de uma forma facilitada uma lista de functions apps e seus status.&lt;/p&gt;

&lt;p&gt;Bom vamos lá!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pré-requisitos

Python 3.6 ou superior

Zabbix Sender (necessário para envio das informações ao zabbix usando agendamento de execução pelo sistema operacional)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;1 - Criar no diretório home do seu usuário o diretório statusUrlfuncapp e dentro dele os diretórios logstatus e loghistorico:&lt;br&gt;
&lt;code&gt;mkdir -p statusUrlfuncapp/{logstatus,loghistorico}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;O diretório logstatus apenas irá gravar um arquivo texto de log do status atual de cada function verificada.&lt;br&gt;
O diretório loghistorico irá receber o log diário dos status obtidos e adicionar mais informações de log em execuções posteriores no mesmo dia.&lt;/p&gt;

&lt;p&gt;2 - Criar no diretório statusUrlfuncapp o arquivo listfunctionapp.json que conterá as informações sobre as functions apps a serem monitoradas. Digite no terminal conforme abaixo:&lt;br&gt;
&lt;code&gt;nano listfunctionapp.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observações:&lt;/strong&gt;&lt;br&gt;
Abaixo uma explicação sobre cada um dos campos do arquivo json a seguir:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;functionapp&lt;/strong&gt; — Nome da Function App&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;urlfunctionapp&lt;/strong&gt; - Url da Function App&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;zabbixkeyurl&lt;/strong&gt; — Nome único para cada function a ser criado como chave no zabbix (zabbix key)&lt;/p&gt;

&lt;p&gt;Abaixo o conteúdo do arquivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "azurefunction": [
        {
            "functionapp": "namefunctionapp1",
            "urlfunctionapp": "https://namefunctionapp1.azurewebsites.net",
            "zabbixkeyurl": "key.url.namefunctionapp1.function"
        },
        {
            "functionapp": "namefunctionapp2",
            "urlfunctionapp": "https://namefunctionapp2.azurewebsites.net",
            "zabbixkeyurl": "key.url.namefunctionapp2.function"
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podem ser adicionadas várias functions apps a serem monitoradas, apenas seguir esse estrutura acima do json.&lt;/p&gt;

&lt;p&gt;3 - Criar no diretório statusUrlfuncapp o arquivo vfuncapp_urlstatus.py que conterá o script python com a instruções para obter as informações das functions apps conforme existentes no arquivo listfunctionapp.json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import os
import io
#import sys
import socket
import requests
from datetime import datetime


def limpaLog():
    pathLog = r"/home/user/statusUrlfuncapp/logfunctionapp_urlstatus.log"

    ### cria/escreve arquivo de log
    with io.open(pathLog, 'w', encoding='utf-8') as f:
      f.write(str(''))

def gravaLog(msglog): 
    pathLog = r"/home/user/statusUrlfuncapp/logfunctionapp_urlstatus.log"
    datahora = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

    ### cria/escreve arquivo de log MongoDB
    with io.open(pathLog, 'a', encoding='utf-8') as f:
      f.write(str('\n=====================================\n')) 
      f.write(str(datahora + '\n' + msglog))


def gravaLogHistorico(msgloghist):
    dataloghist = datetime.now().strftime('%Y-%m-%d')
    pathLog = r"/home/user/statusUrlfuncapp/loghistorico/logfunctionapp_urlstatusHist_" + dataloghist + ".log"
    datahora = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

    ### cria/escreve arquivo de log MongoDB
    with io.open(pathLog, 'a', encoding='utf-8') as f:
      f.write(str('\n=====================================\n')) 
      f.write(str(datahora + '\n' + msgloghist)) 


def removeLogHistorico():
    ### variaveis dos logs antigos a serem removidos
    pathLog = r"/home/user/statusUrlfuncapp/loghistorico"
    diasRemover = 4


    ### remove os logs acima de XX dias
    ## cmd = 'find ' + pathLog + ' -type f -mtime +' + str(diasRemover) + ' -exec rm -f -r {} +'
    cmd = 'find ' + pathLog + ' -name "*.log" -type f -mtime +' + str(diasRemover) + ' -delete'
    print(cmd)
    os.popen(cmd)


#somente app services worker
def verificaStatusUrlWorker(varUrl):
    p_Url = varUrl

    try: 
        x = requests.get(p_Url)
        #print(x.status_code)
        #print('\nUrl: '+ str(varUrl) + '\nStatus Code Http: ' + str(x.status_code))
        return x.status_code
    except:
        #print('\nUrl: '+ str(varUrl) + '\nOcorreu erro ao obter status da url')
        return 0


def readJson():

    ### variaveis
    msg = ''   
    pathJsonFunction = r'/home/user/statusUrlfuncapp/listfunctionapp.json'
    i = 0

    ### chamada da funcao de limpeza do log file
    limpaLog()

    ### codigo de leitura do json com as functions
    ### nesse trecho ocorre o processamento dos dados
    with open(pathJsonFunction) as file:
        data = json.load(file)

        for functions in data["azurefunction"]:
            v_functionappname = str(data["azurefunction"][i]["functionapp"])
            v_urlfunctionapp = str(data["azurefunction"][i]["urlfunctionapp"])
            v_keyzabbix = str(data["azurefunction"][i]["zabbixkeyurl"])


            statusAux = str(verificaStatusUrlWorker(v_urlfunctionapp))

            #msg = f'FunctionApp : {v_functionApp}, UrlFunctionApp : {v_urlfunctionapp} , Http Status Code: {statusAux}'
            #print(f'FunctionApp: {v_functionappname}, UrlFunctionApp: {v_urlfunctionapp} , Http Status Code: {statusAux}')
            strcsvAux = f'{v_functionappname},{v_urlfunctionapp},{statusAux},{v_keyzabbix}'
            #print(strcsvAux)

            msg = msg + '\n' + strcsvAux + '\n'

            ### chamada das funcoes de gravacao status e envio ao zabbix server
            gravaStatusFunction(v_functionappname, statusAux)
            zabbixSenderStatus(v_keyzabbix, statusAux)

            #print(f"\n****{v_functionappname} - {v_urlfunctionapp} - {statusAux}\n")
            gravaLogHistorico(f"\n****{v_functionappname} - {v_urlfunctionapp} - {statusAux}\n")

            i = i + 1

    gravaLog(msg)



def gravaStatusFunction(functionApp, fstatus):

    ### variaveis do local de gravacao dos status
    strResult = r"/home/user/statusUrlfuncapp/logstatus"
    strResult = os.path.join(strResult, functionApp + '.txt')
    pathCsvResult = strResult

    with open(pathCsvResult, 'w', encoding='utf-8') as csvf: 
        csvf.write(fstatus)

def zabbixSenderStatus(zbxChave, zbxStatus):

    ### variaveis server e host monitorado    
    server = '10.10.9.2' #ip do zabbix server
    myhost = socket.gethostname()

    zbxsendercmd = f'zabbix_sender -z {server} -s {myhost} -k {zbxChave} -o {zbxStatus}'    
    retornozbx = os.popen(zbxsendercmd).read()

    #gravaLog(zbxsendercmd + '\n' + retornozbx)
    print(zbxsendercmd)
    print(retornozbx)


### INICIO DA APLICACAO
if __name__ == "__main__":
   readJson()
   removeLogHistorico()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após a criação dos arquivos na estrutura acima é necessário criar um agendamento para execução do mesmo via cron no Linux.&lt;/p&gt;

&lt;p&gt;Como é um script que não necessita de execução via root pode ser criado o agendamento no cron do usuário logado no Linux mesmo, abaixo um exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## verifica status da function na functionapp azure roda a cada 6 minutos
*/6 * * * * /usr/bin/python3 /home/user/statusUrlfuncapp/vfuncapp_urlstatus.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após o procedimento é necessário criar no zabbix os itens para cada uma das functions apps a serem monitoradas.&lt;/p&gt;

&lt;p&gt;Exemplo item para a function app “namefunctionapp1” e function “namefunction1” .&lt;/p&gt;

&lt;p&gt;Nome do item no zabbix: “key.namefunctionapp1.function”&lt;/p&gt;

&lt;p&gt;Com isso no zabbix pode-se criar um Screen com o tipo de visualização de dados "Data Overview" que irá apresentar os dados em um formato de tabela e os retornos dos status url podem ser mapeados para os tipos http status code conforme esse link: &lt;a href="https://www.httpstatus.com.br"&gt;Acesse aqui&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>zabbix</category>
      <category>azure</category>
      <category>python</category>
      <category>functionapp</category>
    </item>
    <item>
      <title>Backup Lógico MongoDB — Completo, Oplog e Bancos de Dados Específicos</title>
      <dc:creator>MagnoRSantos</dc:creator>
      <pubDate>Sat, 14 Oct 2023 23:43:19 +0000</pubDate>
      <link>https://dev.to/magnorsantos/backup-logico-mongodb-completo-oplog-e-bancos-de-dados-especificos-1n46</link>
      <guid>https://dev.to/magnorsantos/backup-logico-mongodb-completo-oplog-e-bancos-de-dados-especificos-1n46</guid>
      <description>&lt;p&gt;O Script abaixo foi criado para realizar backups lógicos do MongoDB usando o MongoDump para backup completos, oplog e bancos de dados específicos.&lt;/p&gt;

&lt;p&gt;O backup de oplog é realizado por fatias do mesmo e por isso pode ser rodado em intervalos específicos de tempo, por exemplo a cada X horas, que pode ser agendado via cron no Linux.&lt;/p&gt;

&lt;p&gt;Exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## roda diariamente à 01:00 hora da manha
00 01 * * * /usr/bin/python3 /home/user/bkp_mongodb_python/mongo_backup_azcopy.py "ALL"

## roda diariamente a cada 2 horas começando às 04:00hs até às 23:00hs
00 4-23/2 * * * /usr/bin/python3 /home/user/bkp_mongodb_python/mongo_backup_azcopy.py "OPLOG"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O script realiza o envio do backup para um blob storage do azure utilizando o azcopy, mas pode ser implementado outras funções para envio para outros locais.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requisitos: 
Python 3.10
Azcopy 10.16.2 ou superior
Ubuntu 18.04.3 LTS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Abaixo script python, o mesmo faz uso de variáveis de ambiente por meio de arquivo .env que contem as seguintes informações de exemplo:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arquivo .env:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MONGO_HOST = "localhost"
MONGO_PORT = "27017"
DBUSERNAME="user.backup"
DBPASSWORD="pwd.backup"
DBAUTHDB="admin"
str_account_name= "storagename"
str_account_key= "xxxxxxyyyyyyyyyyvvvvvvvvwwwwwwwwwww=="
str_container_name= "containername"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Arquivo mongo_backup_azcopy.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# -*- coding: utf-8 -*-

###################################################################################
# mongo_backup_azcopy.py
# Magno Santos - Criacao
#
# Requerimentos:
### python 3.10  64 bits
### modulos: 
### io, os, datetime, csv, pyodbc, time, fnmatch, socket, 
### pymongo, azure.storage.blob, bson.timestamp, dotenv
###################################################################################

import io
import sys
import os
import time
import calendar
import socket
import logging
from datetime import date, timedelta, datetime
from pymongo import MongoClient
from azure.storage.blob import BlobServiceClient, generate_container_sas, ResourceTypes, ContainerSasPermissions
from bson.timestamp import Timestamp
import dotenv

## Carrega os valores do .env
dotenv.load_dotenv()

TIMESTAMP = datetime.now().strftime('%Y-%m-%d-%H%M')

### User geral
# database host name
MONGO_HOST = os.getenv("MONGO_HOST")
# database port
MONGO_PORT = os.getenv("MONGO_PORT")
# database user name
DBUSERNAME = os.getenv("DBUSERNAME")
# database password
DBPASSWORD = os.getenv("DBPASSWORD")
# authentication database name
DBAUTHDB   = os.getenv("DBAUTHDB")

# ambiente
ENV=socket.gethostname()
# backup diretorio disco local  
BKP_DIR = os.path.join("/backup", ENV)
# backup name
BKP_NAME= f"{ENV}-{TIMESTAMP}"

# variavel global list dbs mongodb
listdbs = []

# variaveis globais do storage account
str_account_name   = os.getenv("str_account_name")
str_account_key    = os.getenv("str_account_key")
str_container_name = os.getenv("str_container_name")

# variavel global do local do app python
datahoraLog = datetime.now().strftime('%Y-%m-%d')
dirapp = os.path.dirname(os.path.realpath(__file__))

dirlogfile = os.path.join(dirapp, "log")
logfile = os.path.join(dirlogfile, f"log_backup_{datahoraLog}.txt")

dirqueryfile = os.path.join(dirapp, "query")
queryfile = os.path.join(dirqueryfile, "query.js")


##cria os diretórios se não existirem
if not os.path.exists(dirlogfile):
    os.makedirs(dirlogfile)

if not os.path.exists(dirqueryfile):
    os.makedirs(dirqueryfile)


## trecho de geração do log
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s | %(levelname)s | %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
    filename=logfile, filemode='a'
)

logger = logging.getLogger(__name__)


def geraSasToken(v_account_name, v_account_key, v_container_name):
    sas_token = generate_container_sas(
    account_name=str(v_account_name),
    account_key=str(v_account_key),
    container_name=str(v_container_name),
    resource_types=ResourceTypes(service=True),
    permission=ContainerSasPermissions(read=True, write=True, delete=True, list=True, add=True,create=True),
    start = datetime.now().strftime('%Y-%m-%dT00:00:00Z'),
    expiry=(datetime.now() + timedelta(days=3)).strftime('%Y-%m-%dT00:00:00Z')
    #expiry=datetime.utcnow() + timedelta(hours=1)
    )

    return sas_token


## obtem timestamp   
def getTimeStamp():
    try:

        connstr = f"mongodb://{DBUSERNAME}:{DBPASSWORD}@{MONGO_HOST}:{MONGO_PORT}/{DBAUTHDB}"

        with MongoClient(connstr) as client:
            v_timestamp = client['local'].oplog.rs.find().sort([("$natural", -1)]).limit(1).next()
            v_timestampts = str(v_timestamp['ts'])

        ### cria/escreve arquivo de script temporario javascript
        logging.info(f"Obtendo timestamp: [{v_timestampts}]")
        v_timestampts = v_timestampts.replace('Timestamp', '')
        v_timestampts = v_timestampts.replace('(', '')
        v_timestampts = v_timestampts.replace(')', '')
        v_timestampts = v_timestampts.replace(' ', '')
        array_timestampts = v_timestampts.split(',')
        t = array_timestampts[0]
        i = array_timestampts[1]
        str_query = '{"ts":{"$gt":{"$timestamp":{"t": ' + str(t) + ' , "i": ' + i + ' }}}}'
        print(f'new: {str_query}')
        with io.open(queryfile, 'w', encoding='utf-8') as f:
            f.write(str(str_query))

    except Exception as e:
        print("Error: %s" % e)
        logging.error("Error: %s" % e)


## funcao de remocao do backup local após envio ao storage
def removeBackupLocal():
    cmdDelete = f"sudo rm -f -r {BKP_DIR}/*"
    print(f"Apagando backup local: {cmdDelete}")
    logging.info(f"Apagando backup local: {cmdDelete}")
    os.popen(cmdDelete)


## funcao de remocao do log do azcopy
def removeLogAzcopy():
    cmdDelete1 = f"sudo rm -rf /home/user/.azcopy/*.log"
    cmdDelete2 = f"sudo rm -rf /root/.azcopy/*.log"
    print(f"Apagando logs do azcopy: {cmdDelete1}\n{cmdDelete2}")
    logging.info(f"Apagando logs do azcopy: {cmdDelete1}")
    logging.info(f"Apagando logs do azcopy: {cmdDelete2}")
    os.popen(cmdDelete1)
    os.popen(cmdDelete2)


# Lista databases no mongodb
def databaseMongodb():
    try:

        connstr = f"mongodb://{DBUSERNAME}:{DBPASSWORD}@{MONGO_HOST}:{MONGO_PORT}/{DBAUTHDB}"

        with MongoClient(connstr) as client:
            cursor = client.list_database_names() #listar todos databases

        for dbcursor in cursor:
            listdbs.append(dbcursor)

        return listdbs

    except Exception as e:
        print("Error: %s" % e)
        logging.error("Error: %s" % e)

## funcao de backup de todos os databases
def BackupAllDbs():

    sas = str(geraSasToken(str_account_name, str_account_key, str_container_name))
    v_localbkp = os.path.join(BKP_DIR, BKP_NAME)
    v_localbkp = f"{v_localbkp}_FULL"
    cmdMongodump = f"sudo mongodump -u {DBUSERNAME} -p {DBPASSWORD} --host {MONGO_HOST} --port {MONGO_PORT} --numParallelCollections 8 --authenticationDatabase {DBAUTHDB} --readPreference=secondary --oplog --out {v_localbkp} --gzip"
    str_cmdMongodump = f"sudo mongodump -u {DBUSERNAME} -p ******** --host {MONGO_HOST} --port {MONGO_PORT} --numParallelCollections 8 --authenticationDatabase {DBAUTHDB} --readPreference=secondary --oplog --out {v_localbkp} --gzip"

    print(f"Executando MongoDump...")
    logging.info(f"MongoDump - Início")
    print(str_cmdMongodump)
    logging.info(str_cmdMongodump)
    os.popen(cmdMongodump).read()
    getTimeStamp()
    logging.info("MongoDump - Fim")

    ## verifica existencia da pasta e chama o azcopy
    pathLocalAux = f"{v_localbkp}/"

    if os.path.isdir(pathLocalAux):
        msg = f"Diretório [{v_localbkp}] a ser enviado para storage. Executando azcopy."
        print(msg)
        logging.info(msg)
        logging.info("Envio de dados pelo azcopy - Início.")
        ExecutaAzcopy(v_localbkp, sas)
        logging.info("Envio de dados pelo azcopy - Fim.")
        removeBackupLocal()
    else:
        msg = f"Diretório {v_localbkp} não existe..."
        logging.info(msg)
        print(msg)

## funcao de backup de databases especificos
def BackupEspecificDbs(p_listdbs):

    sas = str(geraSasToken(str_account_name, str_account_key, str_container_name))
    dbsmongo = databaseMongodb()
    #print(dbsmongo[1:-1])
    #print(p_listdbs)

    listOfstr = list(p_listdbs.split(','))
    for dbs in listOfstr:
        if dbs in dbsmongo:

            print(f"Database: {dbs} existente para Backup.")
            logging.info(f"Database: {dbs} existente para Backup.")
            v_localbkpdb = "backup-" + dbs
            v_localbkp = os.path.join(os.path.join(BKP_DIR, BKP_NAME), v_localbkpdb)

            cmdMongodump = f"sudo mongodump -u {DBUSERNAME} -p {DBPASSWORD} --host {MONGO_HOST} --port {MONGO_PORT} --numParallelCollections 8 --authenticationDatabase {DBAUTHDB} --readPreference=secondary --db={dbs} --out {v_localbkp} --gzip"
            str_cmdMongodump = f"sudo mongodump -u {DBUSERNAME} -p ******** --host {MONGO_HOST} --port {MONGO_PORT} --numParallelCollections 8 --authenticationDatabase {DBAUTHDB} --readPreference=secondary --db={dbs} --out {v_localbkp} --gzip"

            print("Executando MongoDump...")
            logging.info(f"MongoDump {dbs} - Início")
            print(str_cmdMongodump)
            logging.info(str_cmdMongodump)
            os.popen(cmdMongodump).read()
            logging.info(f"MongoDump {dbs} - Fim")
            print("\n===========================================================================\n")

        else:
            print(f"Database: {dbs} não existente para Backup.")
            print("\n===========================================================================\n")
            logging.info(f"Database: {dbs} não existente para Backup.")

    ## verifica existencia da pasta e chama o azcopy
    pathLocal = os.path.join(BKP_DIR, BKP_NAME) 
    pathLocalAux = f"{pathLocal}/"

    if os.path.isdir(pathLocalAux):
        msg = f"Diretório [{pathLocal}] a ser enviado para storage. Executando azcopy."
        print(msg)
        logging.info(f"Diretório [{pathLocal}] a ser enviado para storage.")
        logging.info("Envio de dados pelo azcopy - Início.")
        ExecutaAzcopy(pathLocal, sas)
        logging.info("Envio de dados pelo azcopy - Fim.")
        removeBackupLocal()
    else:
        msg = f"Diretório {pathLocal} não existe..."
        print(msg)
        logging.info(msg)


## funcao de backup somente do OpLog
def BackupOnlyOpLog():

    sas = str(geraSasToken(str_account_name, str_account_key, str_container_name))
    v_localbkp = os.path.join(BKP_DIR, BKP_NAME)
    v_localbkp = v_localbkp + "_OPLOG"
    cmdMongodump = f"sudo mongodump -u {DBUSERNAME} -p {DBPASSWORD} --host {MONGO_HOST} --port {MONGO_PORT} --authenticationDatabase {DBAUTHDB} -d local -c oplog.rs --queryFile {queryfile} --out {v_localbkp} --gzip"
    str_cmdMongodump = f"sudo mongodump -u {DBUSERNAME} -p ******** --host {MONGO_HOST} --port {MONGO_PORT} --authenticationDatabase {DBAUTHDB} -d local -c oplog.rs --queryFile {queryfile} --out {v_localbkp} --gzip"

    print("Executando MongoDump...")
    logging.info("MongoDump - Início")
    print(str_cmdMongodump)
    logging.info(str_cmdMongodump)
    os.popen(cmdMongodump).read()
    getTimeStamp()
    logging.info("MongoDump - Fim")

    ## verifica existencia da pasta e chama o azcop
    pathLocalAux = f"{v_localbkp}/"

    if os.path.isdir(pathLocalAux):
        msg = f"Diretório [{v_localbkp}] a ser enviado para storage. Executando azcopy."
        print(msg)
        logging.info(msg)
        logging.info("Envio de dados pelo azcopy - Início.")
        ExecutaAzcopy(v_localbkp, sas)
        logging.info("Envio de dados pelo azcopy - Fim.")
        removeBackupLocal()
    else:
        msg = f"Diretório {v_localbkp} não existe..."
        print(msg)
        logging.info(msg)

## funcao que identifica o tipo de backup a ser realizado
def TypeBackup(v_typeBackp):
    msgbkp = "Selecionado o tipo de backup: "

    # All - todos os dbs (default)
    # OpLog - backup somente do oplog

    # All - todos os dbs (default)
    if(v_typeBackp == "all"):
        print(f"{msgbkp} {v_typeBackp}")
        print("Escolhido backup de todos os dbs (default)\n")
        logging.info(f"{msgbkp} {v_typeBackp}")
        logging.info("Escolhido backup de todos os dbs (default)")
        BackupAllDbs()

    # OpLog - backup somente do oplog
    elif(v_typeBackp == "oplog"):
        print(f"{msgbkp} {v_typeBackp}")
        print("Escolhido backup somente do Oplog\n")
        logging.info(f"{msgbkp} {v_typeBackp}")
        logging.info("Escolhido backup somente do Oplog")
        BackupOnlyOpLog()

    # backup de Db especifico
    else:
        v_listdbs = v_typeBackp
        v_typeBackp = "Backups de databases especificos"
        print(f"{msgbkp} {v_typeBackp}")
        print(f"Escolhido backup de databases especificos: {v_listdbs}\n")
        logging.info(f"{msgbkp} {v_typeBackp}")
        logging.info(f"Escolhido backup de databases especificos: {v_listdbs}")
        BackupEspecificDbs(v_listdbs)


def ExecutaAzcopy(v_localbkp, sastoken):

    # variaveis de origem e destino para  envio pelo azcopy
    SOURCE = v_localbkp
    TARGET = f"https://{str_account_name}.blob.core.windows.net/{str_container_name}/{ENV}?{sastoken}"

    cmdAzcopy = f'/usr/bin/azcopy copy "{SOURCE}" "{TARGET}" --recursive=true'
    print(cmdAzcopy)

    logCmdBkp = os.popen(cmdAzcopy).read()
    print(logCmdBkp)
    logging.info(logCmdBkp)

def main():

    ## log inicio
    logging.info(f"*****Início Backup MongoDB*****")

    ## chamada de limpeza dos logs do azcopy
    removeLogAzcopy()

    ## recebe o tipo de backup a ser feito
    arg_typeBackp = str(sys.argv[1])

    ### testes
    #arg_typeBackp = "db_000XX" #"db_98000, db_97998, db_000XX"
    #arg_typeBackp = "OPLOG"
    #arg_typeBackp = "ALL"

    arg_typeBackp = (arg_typeBackp.lower()).replace(' ', '')
    TypeBackup(arg_typeBackp)

    ## log fim
    logging.info(f"*****Final Backup MongoDB*****\n")

### INICIO DA APLICACAO
if __name__ == "__main__":
    main()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/MagnoRSantos/Backup_MongoDB_Python/blob/main/mongo_backup_azcopy.py"&gt;Fonte no GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Espero que ajude.&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>backup</category>
      <category>mongodump</category>
      <category>python</category>
    </item>
    <item>
      <title>Verificação de Status ReplicaSet MongoDB com Python</title>
      <dc:creator>MagnoRSantos</dc:creator>
      <pubDate>Wed, 23 Aug 2023 00:51:19 +0000</pubDate>
      <link>https://dev.to/magnorsantos/verificacao-de-status-replicaset-mongodb-com-python-5fmk</link>
      <guid>https://dev.to/magnorsantos/verificacao-de-status-replicaset-mongodb-com-python-5fmk</guid>
      <description>&lt;p&gt;Este artigo apresenta um código simples que pode ser usado para auxiliar na verificação de status do replicaset MongoDB utilizando python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Requisitos: 

- Python 2.7 ou posterior
- Pymongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Abaixo o script Python usado:&lt;br&gt;
Nesse exemplo estamos considerando que temos 3 servidores no conjunto de replicaset com os seguintes nomes &lt;strong&gt;(SVRMONGO030, SVRMONGO031 e SVRMONGO032)&lt;/strong&gt; e usamos um usuário criado somente com a role &lt;strong&gt;clusterMonitor&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/python
# -*- coding: utf-8 -*-

import os
import io   
from sys import exit
from pymongo import MongoClient

#funcao de limpeza do terminal
def cleanScreen():

        # For Windows
        if os.name == 'nt':
                os.system('cls')

        # For macOS and Linux
        else:
                os.system('clear')


#funcao de verificacao do status do replicaset mongodb
def replsetstatus():

        print('\n###############################################################\n')
        #client = MongoClient('mongodb://127.0.0.1:27017/') #localhost
        user = 'user.cluster.monitor' #user cluster monitor
        pwd = 'pwd.user.cluster.monitor' #password user cluster monitor
        client = MongoClient('mongodb://' + user + ':' + pwd + '@ipdns_server01:27017,ipdns_server02:27017,ipdns_server03:27017/')
        db = client ['admin']
        nserver = [0, 1, 2]
        rs_stats = db.command({'replSetGetStatus': 1})

        for x in nserver:
                hostnameSrv = 'SVRMONGO03' + str(x)
                IpSrv = rs_stats["members"][x]["name"]
                health = rs_stats["members"][x]["health"]
                state = rs_stats["members"][x]["state"]
                statusRepl = rs_stats["members"][x]["stateStr"]
                optimeDate = rs_stats["members"][x]["optimeDate"]

                if statusRepl == "PRIMARY":
                        syncSourceHost = "IsPrimary"
                else:
                        syncSourceHost = rs_stats["members"][x]["syncSourceHost"]

                print ("\nHost: {0} \nIP Server: {1} \nstateStr: {2} \nstate: {3} \nhealth: {4} \noptimeDate: {5} \nsyncSourceHost: {6} \n".format(str(hostnameSrv), str(IpSrv), str(statusRepl), str(state), str(health), str(optimeDate), str(syncSourceHost)))

        print('\n###############################################################\n')
        client.close()

## função inicial 
def main():
    cleanScreen()
    replsetstatus()
    input("Pressione Enter para nova verificacao. ")
    main()

#inicio da aplicacao
if __name__ == "__main__":
    main()


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Abaixo uma versão mais simples do script que pode ser usado em conjunto com o Zabbix para monitoramento de status do replicaset e envio de alertas pelo Zabbix em casos que o status do replicaset de determinado servidor for diferente do previamente esperado, por exemplo, casos em que o primário caia ou se torne um secundário e outro assuma como primário, basta fazer a chamada do script pelo Zabbix usando o recurso de UserParameter passando na chamada do script o indicador de cada servidor que no caso do nosso exemplo é:&lt;br&gt;
&lt;strong&gt;0 - (SVRMONGO030)&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;1 - (SVRMONGO031)&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;2 - (SVRMONGO032)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exemplos:&lt;/strong&gt; &lt;br&gt;
python verifyStatusReplSet.py 0&lt;br&gt;
python verifyStatusReplSet.py 1&lt;br&gt;
python verifyStatusReplSet.py 2&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
from pymongo import MongoClient

def replsetstatus(nserver):
    # VARIAVEL DE INDICACAO DO SERVIDOR - nserver
    # 0 - (SVRMONGO030) | 1 - (SVRMONGO031) | - 2 (SVRMONGO032)

    # VARIAVEIS DE CONEXAO
    user = 'user.cluster.monitor'
    pwd = 'pwd.user.cluster.monitor'

    client = MongoClient('mongodb://' + user + ':' + pwd + '@ipdns_server01:27017,ipdns_server02:27017,ipdns_server03:27017/')
    db = client ['admin']
    rs_stats = db.command({'replSetGetStatus': 1})
    statusRepl= rs_stats["members"][nserver]["stateStr"]
    print (str(statusRepl))

if __name__ == "__main__":
    nserver = int(sys.argv[1])
    replsetstatus(nserver)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Até a próxima !!!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Como obter status das Functions que pertencem às Function Apps do Azure e enviar os status para o Zabbix</title>
      <dc:creator>MagnoRSantos</dc:creator>
      <pubDate>Mon, 14 Aug 2023 23:05:44 +0000</pubDate>
      <link>https://dev.to/magnorsantos/como-obter-status-das-functions-que-pertencem-as-function-apps-do-azure-e-enviar-os-status-para-o-zabbix-l0c</link>
      <guid>https://dev.to/magnorsantos/como-obter-status-das-functions-que-pertencem-as-function-apps-do-azure-e-enviar-os-status-para-o-zabbix-l0c</guid>
      <description>&lt;p&gt;Para funcionamento é necessário estar logado no Azure autorizando o dispositivo onde estão os arquivos de monitoramento com um usuário que tenha permissões para verificar status das functions no Azure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az login --use-device-code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Abaixo instruções de realização do login pelo Azure CLI.&lt;br&gt;
&lt;a href="https://learn.microsoft.com/en-us/cli/azure/authenticate-azure-cli"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pré-requisitos

Az cli

Python 3.6 ou superior

Zabbix Sender (necessário para envio das informações ao zabbix usando agendamento de execução pelo sistema operacional)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;1 - Criar no diretório home do seu usuário o diretório statusfuncapp e dentro dele o diretório logstatus:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mkdir -p statusfuncapp/{logstatus}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;O diretório logstatus apenas irá gravar um arquivo texto de log do status atual de cada function verificada.&lt;/p&gt;

&lt;p&gt;2 - Criar no diretório statusfuncapp o arquivo listfunctionapp.json que conterá as informações sobre as functions app e function a serem monitoradas. Digite no terminal conforme abaixo:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nano listfunctionapp.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observações:&lt;/strong&gt;&lt;br&gt;
Abaixo uma explicação sobre cada um dos campos do arquivo json a seguir:&lt;br&gt;
&lt;strong&gt;subscriptionId&lt;/strong&gt; — Id da Subscription no azure que a function app pertence&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;resourcegroup&lt;/strong&gt; — Nome do Resource Group no azure que a function app pertence&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;functionapp&lt;/strong&gt; — Nome da Function App&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;function&lt;/strong&gt; — Nome da Function&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;zabbixkey&lt;/strong&gt; — Nome único para cada function a ser criado como chave no zabbix (zabbix key)&lt;/p&gt;

&lt;p&gt;Abaixo o conteúdo do arquivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "azurefunction": [
        {
            "subscriptionId": "xxxxxxx-yyyy-zzzz-wwww-kkkkkkkkkkkk",
            "resourcegroup": "RGTesteFunc",
            "functionapp": "namefunctionapp1",
            "function": "namefunction1",
            "zabbixkey": "key.nomefunctionapp1.function"
        },
        {
            "subscriptionId": "xxxxxxx-yyyy-zzzz-wwww-kkkkkkkkkkkk",
            "resourcegroup": "RGTesteFunc",
            "functionapp": "namefunctionapp2",
            "function": "namefunction2",
            "zabbixkey": "key.nomefunctionapp2.function"
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podem ser adicionadas várias functions a serem monitoradas, apenas seguir esse estrutura acima do json.&lt;/p&gt;

&lt;p&gt;3 - Criar no diretório statusfuncapp o arquivo vfuncapp.py que conterá o script python com a instruções de obter as informações das functions conforme existentes no arquivo listfunctionapp.json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import os
import io
import sys
import csv
import socket
from datetime import datetime

### Funcao de limpeza do log
def limpaLog():
    pathLog = r"/home/user/statusfuncapp/logfunctionapp.log"

    ### cria/escreve arquivo de log MongoDB
    with io.open(pathLog, 'w', encoding='utf-8') as f:
      f.write(str(''))


### Funcao de gravacao do log
def gravaLog(msglog):
    pathLog = r"/home/user/statusfuncapp/logfunctionapp.log"
    datahora = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

    ### cria/escreve arquivo de log MongoDB
    with io.open(pathLog, 'a', encoding='utf-8') as f:
      f.write(str('\n=====================================\n'))
      f.write(str(datahora + '\n' + msglog))


### Funcao de leitura do arquivo json com as informacoes das functions
def readJson():

    ### variaveis
    msg = ''  
    pathJsonFunction = r'/home/user/statusfuncapp/listfunctionapp.json'
    i = 0

    ### chamada da funcao de limpeza do log file
    limpaLog()

    ### codigo de leitura do json com as functions
    ### nesse trecho ocorre o processamento dos dados
    with open(pathJsonFunction) as file:
         data = json.load(file)

         for functions in data["azurefunction"]:
            v_subscription = str(data["azurefunction"][i]["subscriptionId"])
            v_resourcegroup = str(data["azurefunction"][i]["resourcegroup"])
            v_functionappname = str(data["azurefunction"][i]["functionapp"])
            v_functionname = str(data["azurefunction"][i]["function"])
            v_keyzabbix = str(data["azurefunction"][i]["zabbixkey"])
            #print(f"{v_functionappname} - {v_functionname}")


            try:
              ## Monta o comando de execucao via az cli
              cmd = 'az functionapp function show --subscription ' + v_subscription + ' -g ' + v_resourcegroup + ' -n ' + v_functionappname + ' --function-name ' + v_functionname

              ## Executa e pega o retorno em formato json
              retorno = os.popen(cmd).read()
              #print(retorno)

              ## Monta dicionario do json e exibe somente o valor requerido
              data_dict = json.loads(retorno)
              statusFunction = str(data_dict["isDisabled"])
              #print(data_dict["isDisabled"])

              ### Verifica os status da function
              ### conforme o retorno do campo isDisabled
              if(statusFunction == 'True'):
                statusAux = 'Disable'
              elif (statusFunction == 'False'):
                statusAux = 'Enabled'
              else:
                statusAux = 'Desconhecido'

            except:
              msg = msg + f'Erro functionApp: {v_functionappname} - function name: {v_functionname}\n'
              statusAux = 'Error'

            #msg = f'FunctionApp : {v_functionApp}, FunctionName : {v_functionName} ,Status : {statusAux}'
            strcsvAux = f'{v_functionappname},{v_functionname},{statusAux},{v_keyzabbix}'
            #print(strcsvAux)

            msg = msg + strcsvAux + '\n'

            ### chamada das funcoes de gravacao status e envio ao zabbix server
            gravaStatusFunction(v_functionappname, statusAux)
            zabbixSenderStatus(v_keyzabbix, statusAux)

            i = i + 1

    gravaLog(msg)
    #print(msg)


### Funcao de gravacao do status das functions
def gravaStatusFunction(functionApp, fstatus):

    ### variaveis do local de gravacao dos status
    strResult = r"/home/user/statusfuncapp/logstatus"
    strResult = os.path.join(strResult, functionApp + '.txt')
    pathCsvResult = strResult

    with open(pathCsvResult, 'w', encoding='utf-8') as csvf:
        csvf.write(fstatus)


### Funcao de envio das informacoes de status das functions via zabbix_sender
def zabbixSenderStatus(zbxChave, zbxStatus):

    ### variaveis server e host monitorado   
    server = 'ip_server_zabbix'
    myhost = socket.gethostname() #nome da maquina local

    zbxsendercmd = f'zabbix_sender -z {server} -s {myhost} -k {zbxChave} -o {zbxStatus}'   
    retornozbx = os.popen(zbxsendercmd).read()

    gravaLog(zbxsendercmd + '\n' + retornozbx)
    print(zbxsendercmd)
    print(retornozbx)


### INICIO DA APLICACAO
if __name__ == "__main__":
   readJson()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após a criação dos arquivos na estrutura acima é necessário criar um agendamento para execução do mesmo via cron no Linux.&lt;/p&gt;

&lt;p&gt;Como é um script que não necessita de execução via root pode ser criado o agendamento no cron do usuário logado no Linux mesmo, abaixo um exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## verifica status da function na functionapp azure roda a cada 6 minutos
*/6 * * * * /usr/bin/python3 /home/user/statusfuncapp/vfuncapp.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após o procedimento é necessário criar no zabbix os itens para cada uma das functions a serem monitoradas.&lt;/p&gt;

&lt;p&gt;Exemplo item para a function app “namefunctionapp1” e function “namefunction1” .&lt;/p&gt;

&lt;p&gt;Nome do item no zabbix: “key.namefunctionapp1.function”&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
