import yaml import uuid import sys # Source files SOURCE_TEMPLATE = r"c:\Users\joao.goncalves\Downloads\custom_zabbix_templates\base_sources\template_exchange_source.yaml" GOLD_TEMPLATE = r"c:\Users\joao.goncalves\Downloads\custom_zabbix_templates\gold_edition\template_exchange_gold.yaml" TARGET_FILE = r"c:\Users\joao.goncalves\Downloads\custom_zabbix_templates\gold_edition\template_exchange_gold.yaml" # Target Group UUID GROUP_UUID = "a571c0d144b14fd4a87a9d9b2aa9fcd6" # Portuguese Translations Map (English substring -> Portuguese replacement) TRANSLATIONS = { "Microsoft Exchange Server 2016 by Zabbix agent": "Microsoft Exchange Gold Edition", "Databases total mounted": "Exchange: Bancos de Dados Montados (Total)", "ActiveSync: ping command pending": "ActiveSync: Comandos Ping Pendentes", "ActiveSync: requests per second": "ActiveSync: Requisições por segundo", "ActiveSync: sync commands per second": "ActiveSync: Comandos Sync por segundo", "Autodiscover: requests per second": "Autodiscover: Requisições por segundo", "Availability Service: availability requests per second": "Availability Service: Requisições por segundo", "Outlook Web App: current unique users": "OWA: Usuários Únicos Atuais", "Outlook Web App: requests per second": "OWA: Requisições por segundo", "MSExchangeWS: requests per second": "WebServices: Requisições por segundo", "Databases discovery": "Descoberta de Bancos de Dados", "Active Manager [{#INSTANCE}]: Database copy role": "Banco [{#INSTANCE}]: Função da Cópia", "Information Store [{#INSTANCE}]: Page faults per second": "Banco [{#INSTANCE}]: Page Faults/seg", "Information Store [{#INSTANCE}]: Log records stalled": "Banco [{#INSTANCE}]: Logs Travados (Stalled)", "Information Store [{#INSTANCE}]: Log threads waiting": "Banco [{#INSTANCE}]: Threads de Log Aguardando", "Active database read operations per second": "Leitura: Operações/seg (Ativo)", "Active database read operations latency": "Leitura: Latência Média (Ativo)", "Passive database read operations latency": "Leitura: Latência Média (Passivo)", "Active database write operations per second": "Escrita: Operações/seg (Ativo)", "Active database write operations latency": "Escrita: Latência Média (Ativo)", "Passive database write operations latency": "Escrita: Latência Média (Passivo)", "Information Store [{#INSTANCE}]: Active mailboxes count": "Banco [{#INSTANCE}]: Mailboxes Ativas", "Information Store [{#INSTANCE}]: Database state": "Banco [{#INSTANCE}]: Status", "Information Store [{#INSTANCE}]: RPC requests latency": "Banco [{#INSTANCE}]: Latência RPC Média", "Information Store [{#INSTANCE}]: RPC requests per second": "Banco [{#INSTANCE}]: Requisições RPC/seg", "Information Store [{#INSTANCE}]: RPC requests total": "Banco [{#INSTANCE}]: Total Requisições RPC", "LDAP discovery": "Descoberta de LDAP", "Domain Controller [{#INSTANCE}]: Read time": "DC [{#INSTANCE}]: Tempo de Leitura LDAP", "Domain Controller [{#INSTANCE}]: Search time": "DC [{#INSTANCE}]: Tempo de Busca LDAP", "Web services discovery": "Descoberta de Web Services", "Web Service [{#INSTANCE}]: Current connections": "Web Service [{#INSTANCE}]: Conexões Atuais", # Description Translations "Shows the number of active database copies on the server.": "Mostra o número de cópias ativas de banco de dados no servidor.", "Shows the number of ping commands currently pending in the queue.": "Mostra o número de comandos de ping pendentes na fila.", "Shows the number of HTTP requests received from the client via ASP.NET per second. Determines the current Exchange ActiveSync request rate. Used only to determine current user load.": "Mostra o número de requisições HTTP recebidas via ASP.NET/seg. Determina a carga atual de usuários ActiveSync.", "Shows the number of sync commands processed per second. Clients use this command to synchronize items within a folder.": "Mostra o número de comandos de sincronização processados/seg.", "Shows the number of Autodiscover service requests processed each second. Determines current user load.": "Mostra o número de requisições Autodiscover processadas/seg.", "Shows the number of requests serviced per second. The request can be only for free/ busy information or include suggestions. One request may contain multiple mailboxes. Determines the rate at which Availability service requests are occurring.": "Mostra o número de requisições de disponibilidade (Free/Busy) atendidas/seg.", "Shows the number of unique users currently logged on to Outlook Web App. This value monitors the number of unique active user sessions, so that users are only removed from this counter after they log off or their session times out. Determines current user load.": "Número de usuários únicos logados no OWA. Monitora sessões ativas (só reduz após logoff ou timeout).", "Shows the number of requests handled by Outlook Web App per second. Determines current user load.": "Número de requisições OWA processadas/seg.", "Shows the number of requests processed each second. Determines current user load.": "Número de requisições processadas/seg.", "Database copy active or passive role.": "Função da cópia do banco (Ativa ou Passiva).", "Indicates the rate of page faults that can't be serviced because there are no pages available for allocation from the database cache. If this counter is above 0, it's an indication that the MSExchange Database\\I/O Database Writes (Attached) Average Latency is too high.": "Taxa de falhas de página (Page Faults) não atendidas pelo cache. Se maior que 0, indica latência de disco alta.", "Too much page faults stalls for database \"{#INSTANCE}\". This counter should be 0 on production servers.": "Muitos Page Faults no banco \"{#INSTANCE}\". Deveria ser 0 em produção.", "Indicates the number of log records that can't be added to the log buffers per second because the log buffers are full. The average value should be below 10 per second. Spikes (maximum values) shouldn't be higher than 100 per second.": "Número de logs que não puderam ser gravados no buffer (Stalled). Média deve ser < 10/seg.", "Stalled log records too high. The average value should be less than 10 threads waiting.": "Muitos logs travados (stalled). A média deve ser menor que 10.", "Indicates the number of threads waiting to complete an update of the database by writing their data to the log.": "Número de threads aguardando para gravar no log do banco.", "Shows the number of database read operations.": "Número de operações de leitura no banco.", "Shows the average length of time per database read operation. Should be less than 20 ms on average.": "Tempo médio por operação de leitura. Deve ser menor que 20ms.", "Should be less than 20ms on average.": "Deve ser menor que 20ms em média.", "Shows the average length of time per passive database read operation. Should be less than 200ms on average.": "Tempo médio por leitura passiva. Deve ser menor que 200ms.", "Should be less than 200ms on average.": "Deve ser menor que 200ms em média.", "Shows the number of database write operations per second for each attached database instance.": "Número de operações de escrita/seg por instância.", "Shows the average length of time per database write operation. Should be less than 50ms on average.": "Tempo médio por operação de escrita. Deve ser menor que 50ms.", "Should be less than 50ms on average.": "Deve ser menor que 50ms em média.", "Shows the average length of time, in ms, per passive database write operation. Should be less than the read latency for the same instance, as measured by the MSExchange Database ==> Instances({#INF.STORE}/_Total)\\I/O Database Reads (Recovery) Average Latency counter.": "Tempo médio (ms) por escrita passiva.", "Should be less than the read latency for the same instance, as measured by the MSExchange Database ==> Instances({#INF.STORE}/_Total)\\I/O Database Reads (Recovery) Average Latency counter.": "Deve ser menor que a latência de leitura.", "Number of active mailboxes in this database.": "Número de mailboxes ativas neste banco.", "Database state. Possible values:\n0: Database without any copy and dismounted.\n1: Database is a primary database and mounted.\n2: Database is a passive copy and the state is healthy.": "Estado do banco (0=Desmontado, 1=Montado/Primário, 2=Saudável/Passivo).", "RPC Latency average is the average latency of RPC requests per database. Average is calculated over all RPCs since exrpc32 was loaded. Should be less than 50ms at all times, with spikes less than 100ms.": "Latência média de RPC por banco. Deve ser menor que 50ms.", "Should be less than 50ms at all times, with spikes less than 100ms.": "Deve ser menor que 50ms sempre.", "Shows the number of RPC operations per second for each database instance.": "Número de operações RPC/seg.", "Indicates the overall RPC requests currently executing within the information store process. Should be below 70 at all times.": "Total de requisições RPC em execução. Deve ser menor que 70.", "Should be below 70 at all times.": "Deve ser menor que 70 sempre.", "Time that it takes to send an LDAP read request to the domain controller in question and get a response. Should ideally be below 50 ms; spikes below 100 ms are acceptable.": "Tempo para enviar/receber leitura LDAP do DC. Ideal < 50ms.", "Time that it takes to send an LDAP search request and get a response. Should ideally be below 50 ms; spikes below 100 ms are acceptable.": "Tempo para busca LDAP. Ideal < 50ms.", "Shows the current number of connections established to the each Web Service.": "Número atual de conexões para cada Web Service.", "The template to monitor Microsoft Exchange Server 2016 by Zabbix that works without any external scripts.": "Template Gold para monitoramento do Exchange.", "The metrics are collected by Zabbix agent.": "Métricas coletadas pelo Zabbix Agent.", "Recommended to use it with \"OS Windows by Zabbix agent\" template.": "Recomendado usar junto com o template de SO Windows." } def load_yaml(path): with open(path, 'r', encoding='utf-8') as f: return yaml.safe_load(f) def clean_tags_and_fix_uuids(data): generated_uuids = set() def process_node(node): if isinstance(node, dict): # Clean tags for tag in ['wizard_ready', 'readme', 'vendor', 'config']: if tag in node: del node[tag] # Fix UUIDs if 'uuid' in node: # Keep group UUID if matches target if node['uuid'] == GROUP_UUID: pass else: new_uuid = uuid.uuid4().hex while new_uuid in generated_uuids: new_uuid = uuid.uuid4().hex node['uuid'] = new_uuid generated_uuids.add(new_uuid) # Translate Strings for key in ['name', 'description', 'event_name', 'comment']: if key in node and isinstance(node[key], str): for eng, pt in TRANSLATIONS.items(): node[key] = node[key].replace(eng, pt) for k, v in list(node.items()): process_node(v) elif isinstance(node, list): for item in node: process_node(item) process_node(data) def main(): print("Loading Source Template...") source = load_yaml(SOURCE_TEMPLATE) # 1. Update Header source['zabbix_export']['version'] = '7.0' template = source['zabbix_export']['templates'][0] template['name'] = "Microsoft Exchange Gold Edition" template['description'] = "Template Gold Edition para Microsoft Exchange (2016/2019).\n\nFuncionalidades:\n- Monitoramento Completo de Bancos de Dados (I/O, Latência, RPC)\n- Filas de Transporte e Back Pressure (Anti-Spam)\n- Serviços Críticos\n- Backup Age (PowerShell)\n- Acesso Web (OWA/ActiveSync)" # 2. Update Group source['zabbix_export']['template_groups'][0]['uuid'] = GROUP_UUID template['groups'][0]['name'] = "Templates/Applications" # 3. Add Custom "Gold" Items (Backup & New Metrics) # Backup Item (from Old Gold) backup_item = { 'uuid': '', # Will be generated 'name': 'Exchange: Horas desde o último Backup Full', 'key': 'system.run[powershell -NoProfile -Command "((Get-Date) - (Get-MailboxDatabase -Status | Sort-Object LastFullBackup | Select-Object -First 1 -ExpandProperty LastFullBackup)).TotalHours"]', 'delay': '4h', 'value_type': 'FLOAT', 'units': 'h', 'tags': [{'tag': 'component', 'value': 'backup'}], 'triggers': [{ 'uuid': '', 'expression': 'last(/Microsoft Exchange Gold Edition/system.run[powershell -NoProfile -Command "((Get-Date) - (Get-MailboxDatabase -Status | Sort-Object LastFullBackup | Select-Object -First 1 -ExpandProperty LastFullBackup)).TotalHours"])>30', 'name': '🚨 Exchange: Backup Atrasado (> 30 Horas)', 'priority': 'HIGH', 'description': 'O backup Full não roda há mais de 30 horas. Risco de Log Transactional encher o disco.' }] } template['items'].append(backup_item) # Back Pressure: Private Bytes private_bytes_item = { 'uuid': '', 'name': 'EdgeTransport: Consumo de RAM (Private Bytes)', 'key': 'perf_counter_en["\\Process(EdgeTransport)\\Private Bytes"]', 'delay': '1m', 'value_type': 'FLOAT', 'units': 'B', 'tags': [{'tag': 'component', 'value': 'back_pressure'}], 'triggers': [{ 'uuid': '', 'expression': 'min(/Microsoft Exchange Gold Edition/perf_counter_en["\\Process(EdgeTransport)\\Private Bytes"],15m)>{$EXCHANGE.EDGE.MEM.MAX}', 'name': '🚨 Exchange: EdgeTransport consumindo muita RAM (Possível Back Pressure)', 'priority': 'AVERAGE', 'description': 'O processo EdgeTransport está consumindo muita memória. Isso pode ativar o Back Pressure e rejeitar e-mails.' }] } template['items'].append(private_bytes_item) # Back Pressure / Spam: Submission Queue submission_queue_item = { 'uuid': '', 'name': 'Exchange: Fila de Submissão (Submission Queue)', 'key': 'perf_counter_en["\\MSExchangeTransport Queues(_Total)\\Submission Queue Length"]', 'delay': '1m', 'value_type': 'FLOAT', 'tags': [{'tag': 'component', 'value': 'spam_protection'}], 'triggers': [{ 'uuid': '', 'expression': 'min(/Microsoft Exchange Gold Edition/perf_counter_en["\\MSExchangeTransport Queues(_Total)\\Submission Queue Length"],10m)>500', 'name': '🚨 Exchange: Fila de Submissão Crítica (>500) - Possível SPAM', 'priority': 'HIGH', 'description': 'A fila de submissão está alta. Pode indicar um ataque de SPAM massivo entrando no servidor.' }] } template['items'].append(submission_queue_item) # Database Size (Inject into Discovery Rule) # Finding "Databases discovery" rule db_discovery = next((d for d in template['discovery_rules'] if "Databases discovery" in d['name'] or "Descoberta de Bancos de Dados" in d['name']), None) if db_discovery: db_size_item = { 'uuid': '', 'name': 'Banco [{#INSTANCE}]: Tamanho do Arquivo (Bytes)', 'key': 'system.run[powershell -NoProfile -Command "(Get-MailboxDatabase -Identity \'{#INSTANCE}\' -Status).DatabaseSize.ToBytes()"]', 'delay': '1h', 'value_type': 'FLOAT', 'units': 'B', 'description': 'Tamanho físico do arquivo do banco de dados (EDB).', 'tags': [{'tag': 'component', 'value': 'database'}, {'tag': 'database', 'value': '{#INSTANCE}'}] } db_discovery['item_prototypes'].append(db_size_item) else: print("WARNING: Could not find Database Discovery rule to inject DB Size item.") # Macros for new items template['macros'].append({'macro': '{$EXCHANGE.EDGE.MEM.MAX}', 'value': '20G', 'description': 'Máximo de RAM para EdgeTransport'}) # 4. Clean and Fix print("Cleaning tags, translating and regenerating UUIDs...") clean_tags_and_fix_uuids(source) # 5. Save print(f"Saving to {TARGET_FILE}...") with open(TARGET_FILE, 'w', encoding='utf-8') as f: yaml.dump(source, f, sort_keys=False, indent=2, width=float("inf"), allow_unicode=True) print("Done!") if __name__ == "__main__": main()