feat(A07_dtic-DNS): Ámbito DNS operativo + CLI para gestión de CoreDNS
- Crear ámbito A07_dtic-DNS con manifiesto y plan A07.P001 - Implementar CLI dns.rb con 7 subcomandos (status, zona, registros, buscar, backup, reload, corefile) - Detectar servidor srvv-dns: CoreDNS en Docker (10.0.10.2, VM 112 en srv-pmox1) - Documentar zona frlr.utn.edu.ar con 20+ registros (A, CNAME, MX, TXT, SRV) - Actualizar ficha nodos/srvv-dns.md con información técnica completa - Agregar script reparar.sh para ns8-apps (pendiente de ejecución) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
be3a78c50a
commit
a013811aa2
@@ -0,0 +1,285 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# DNS CLI — Gestión del servidor DNS (srvv-dns)
|
||||
#
|
||||
# Responsabilidad: Comandos para administración de CoreDNS
|
||||
#
|
||||
# Uso:
|
||||
# ./adn/tools/run dns help # Ayuda
|
||||
# ./adn/tools/run dns status # Estado del servidor
|
||||
# ./adn/tools/run dns zona # Listar zona activa
|
||||
# ./adn/tools/run dns registros # Listar todos los registros
|
||||
# ./adn/tools/run dns buscar <nombre>
|
||||
# ./adn/tools/run dns backup # Crear backup de zona
|
||||
# ./adn/tools/run dns reload # Forzar reload de CoreDNS
|
||||
|
||||
require 'open3'
|
||||
require 'time'
|
||||
require_relative '../core/colores'
|
||||
|
||||
module ADN
|
||||
class SubcomandoDNS
|
||||
SSH_HOST = 'rmonla@10.0.10.2'
|
||||
SSH_OPTS = '-o StrictHostKeyChecking=no -o ConnectTimeout=10'
|
||||
ZONA_PATH = '/docker/coreDNS/zones/frlr.utn.edu.ar.db'
|
||||
COREFILE_PATH = '/docker/coreDNS/Corefile'
|
||||
ZONA_NAME = 'frlr.utn.edu.ar'
|
||||
|
||||
def initialize(args, logger)
|
||||
@args = args
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
def ejecutar
|
||||
if @args.empty? || @args[0] == 'help'
|
||||
mostrar_ayuda
|
||||
return
|
||||
end
|
||||
|
||||
comando = @args[0]
|
||||
|
||||
case comando
|
||||
when 'status'
|
||||
status
|
||||
when 'zona'
|
||||
zona
|
||||
when 'registros'
|
||||
registros
|
||||
when 'buscar'
|
||||
buscar(@args[1])
|
||||
when 'backup'
|
||||
backup
|
||||
when 'reload'
|
||||
reload
|
||||
when 'corefile'
|
||||
corefile
|
||||
else
|
||||
puts "#{Color::RED}✗ Comando DNS desconocido: #{comando}#{Color::RESET}"
|
||||
mostrar_ayuda
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mostrar_ayuda
|
||||
puts <<~HELP
|
||||
#{Color::CYAN}🌐 DNS CLI — Gestión del servidor DNS (srvv-dns)#{Color::RESET}
|
||||
|
||||
#{Color::GREEN}Uso:#{Color::RESET} ./adn/tools/run dns <subcomando> [args]
|
||||
|
||||
#{Color::YELLOW}━━━ Comandos de Consulta ━━━#{Color::RESET}
|
||||
#{Color::BOLD}status#{Color::RESET} Estado del servidor CoreDNS
|
||||
#{Color::BOLD}zona#{Color::RESET} Información de la zona #{ZONA_NAME}
|
||||
#{Color::BOLD}registros#{Color::RESET} Listar todos los registros DNS
|
||||
#{Color::BOLD}buscar <nombre>#{Color::RESET} Buscar registros por nombre
|
||||
|
||||
#{Color::YELLOW}━━━ Comandos de Mantenimiento ━━━#{Color::RESET}
|
||||
#{Color::BOLD}backup#{Color::RESET} Crear backup de la zona
|
||||
#{Color::BOLD}reload#{Color::RESET} Forzar reload de CoreDNS
|
||||
#{Color::BOLD}corefile#{Color::RESET} Mostrar configuración CoreDNS
|
||||
|
||||
#{Color::YELLOW}Ejemplos:#{Color::RESET}
|
||||
#{Color::DIM}# Ver estado del servidor#{Color::RESET}
|
||||
./adn/tools/run dns status
|
||||
|
||||
#{Color::DIM}# Listar todos los registros#{Color::RESET}
|
||||
./adn/tools/run dns registros
|
||||
|
||||
#{Color::DIM}# Buscar registros de ns8#{Color::RESET}
|
||||
./adn/tools/run dns buscar ns8
|
||||
|
||||
#{Color::DIM}# Crear backup antes de cambios#{Color::RESET}
|
||||
./adn/tools/run dns backup
|
||||
HELP
|
||||
end
|
||||
|
||||
def status
|
||||
puts "#{Color::CYAN}🌐 Estado del servidor DNS (srvv-dns)#{Color::RESET}\n\n"
|
||||
|
||||
# Verificar conectividad
|
||||
print " Conectividad SSH... "
|
||||
if ssh_exec_simple('true')
|
||||
puts "#{Color::GREEN}✅ OK#{Color::RESET}"
|
||||
else
|
||||
puts "#{Color::RED}❌ Fallido#{Color::RESET}"
|
||||
return
|
||||
end
|
||||
|
||||
# Verificar contenedor CoreDNS
|
||||
print " Contenedor CoreDNS... "
|
||||
output = ssh_exec('docker ps --filter "name=coredns" --format "{{.Status}}"')
|
||||
if output&.include?('Up')
|
||||
puts "#{Color::GREEN}✅ Activo (#{output.strip})#{Color::RESET}"
|
||||
else
|
||||
puts "#{Color::RED}❌ Inactivo#{Color::RESET}"
|
||||
end
|
||||
|
||||
# Verificar puerto 53
|
||||
print " Puerto 53... "
|
||||
output = ssh_exec('ss -tuln | grep ":53" | head -1')
|
||||
if output&.include?('53')
|
||||
puts "#{Color::GREEN}✅ Escuchando#{Color::RESET}"
|
||||
else
|
||||
puts "#{Color::RED}❌ Cerrado#{Color::RESET}"
|
||||
end
|
||||
|
||||
# Verificar zona
|
||||
print " Zona #{ZONA_NAME}... "
|
||||
output = ssh_exec("test -f #{ZONA_PATH} && echo 'exists' || echo 'missing'")
|
||||
if output&.include?('exists')
|
||||
puts "#{Color::GREEN}✅ Existente#{Color::RESET}"
|
||||
else
|
||||
puts "#{Color::RED}❌ No encontrada#{Color::RESET}"
|
||||
end
|
||||
|
||||
# Serial de zona
|
||||
print " Serial de zona... "
|
||||
serial = obtener_serial
|
||||
if serial
|
||||
puts "#{Color::GREEN}#{serial}#{Color::RESET}"
|
||||
else
|
||||
puts "#{Color::RED}❌ No se pudo leer#{Color::RESET}"
|
||||
end
|
||||
|
||||
puts "\n #{Color::DIM}Última consulta: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}#{Color::RESET}"
|
||||
end
|
||||
|
||||
def zona
|
||||
puts "#{Color::CYAN}📋 Zona DNS: #{ZONA_NAME}#{Color::RESET}\n\n"
|
||||
|
||||
serial = obtener_serial
|
||||
puts " SOA Serial: #{serial || 'N/A'}"
|
||||
|
||||
output = ssh_exec("cat #{ZONA_PATH}")
|
||||
return unless output
|
||||
|
||||
# Contar registros por tipo
|
||||
registros = { 'A' => 0, 'CNAME' => 0, 'MX' => 0, 'TXT' => 0, 'NS' => 0, 'SRV' => 0 }
|
||||
output.each_line do |line|
|
||||
next if line.start_with?('$', ';')
|
||||
registros.each_key do |tipo|
|
||||
registros[tipo] += 1 if line.include?(" IN #{tipo} ")
|
||||
end
|
||||
end
|
||||
|
||||
puts "\n Registros por tipo:"
|
||||
registros.each do |tipo, count|
|
||||
puts " #{tipo}: #{count}" if count > 0
|
||||
end
|
||||
|
||||
# Nameservers
|
||||
puts "\n Nameservers:"
|
||||
output.each_line do |line|
|
||||
puts " #{line.strip}" if line.include?(' IN NS ')
|
||||
end
|
||||
end
|
||||
|
||||
def registros
|
||||
output = ssh_exec("cat #{ZONA_PATH}")
|
||||
return unless output
|
||||
|
||||
puts "#{Color::CYAN}📋 Registros de zona #{ZONA_NAME}#{Color::RESET}\n\n"
|
||||
puts "```\n"
|
||||
|
||||
# Imprimir solo líneas de registros (no comentarios ni directivas)
|
||||
output.each_line do |line|
|
||||
line = line.strip
|
||||
next if line.empty? || line.start_with?('$', ';', '(', ')')
|
||||
puts line if line.include?(' IN ')
|
||||
end
|
||||
|
||||
puts "```"
|
||||
end
|
||||
|
||||
def buscar(nombre)
|
||||
return puts "#{Color::RED}✗ Falta nombre a buscar#{Color::RESET}" unless nombre
|
||||
|
||||
output = ssh_exec("cat #{ZONA_PATH}")
|
||||
return unless output
|
||||
|
||||
puts "#{Color::CYAN}🔍 Buscando '#{nombre}' en #{ZONA_NAME}#{Color::RESET}\n\n"
|
||||
|
||||
encontrados = []
|
||||
output.each_line do |line|
|
||||
line = line.strip
|
||||
next if line.empty? || line.start_with?('$', ';')
|
||||
encontrados << line if line.include?(nombre)
|
||||
end
|
||||
|
||||
if encontrados.empty?
|
||||
puts " #{Color::RED}No se encontraron registros#{Color::RESET}"
|
||||
else
|
||||
puts "```\n"
|
||||
encontrados.each { |l| puts l }
|
||||
puts "```"
|
||||
end
|
||||
end
|
||||
|
||||
def backup
|
||||
timestamp = Time.now.strftime('%y%m%d-%H%M')
|
||||
backup_path = "/docker/coreDNS/zones/bkp_#{timestamp}_frlr.utn.edu.ar.db"
|
||||
|
||||
puts " Creando backup: #{File.basename(backup_path)}..."
|
||||
|
||||
output = ssh_exec("cp #{ZONA_PATH} #{backup_path} && ls -la #{backup_path}")
|
||||
if output
|
||||
puts " #{Color::GREEN}✅ Backup creado#{Color::RESET}"
|
||||
else
|
||||
puts " #{Color::RED}❌ Error al crear backup#{Color::RESET}"
|
||||
end
|
||||
end
|
||||
|
||||
def reload
|
||||
puts " Forzando reload de CoreDNS..."
|
||||
|
||||
output = ssh_exec("touch #{ZONA_PATH} && docker kill --signal=HUP coredns 2>/dev/null || echo 'reload manual'")
|
||||
|
||||
if output
|
||||
puts " #{Color::GREEN}✅ Reload solicitado#{Color::RESET}"
|
||||
puts " #{Color::DIM}CoreDNS recargará la zona en ~30s#{Color::RESET}"
|
||||
else
|
||||
puts " #{Color::RED}❌ Error al solicitar reload#{Color::RESET}"
|
||||
end
|
||||
end
|
||||
|
||||
def corefile
|
||||
puts "#{Color::CYAN}⚙️ Corefile de CoreDNS#{Color::RESET}\n\n"
|
||||
puts "```\n"
|
||||
output = ssh_exec("cat #{COREFILE_PATH}")
|
||||
puts output if output
|
||||
puts "```"
|
||||
end
|
||||
|
||||
def ssh_exec(cmd)
|
||||
full_cmd = "ssh #{SSH_OPTS} #{SSH_HOST} '#{cmd}'"
|
||||
begin
|
||||
stdout, stderr, status = Open3.capture3(full_cmd)
|
||||
if status.success?
|
||||
stdout.strip
|
||||
else
|
||||
$stderr.puts "#{Color::RED}SSH error: #{stderr.strip}#{Color::RESET}"
|
||||
nil
|
||||
end
|
||||
rescue => e
|
||||
$stderr.puts "#{Color::RED}Error en SSH: #{e.message}#{Color::RESET}"
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def ssh_exec_simple(cmd)
|
||||
full_cmd = "ssh #{SSH_OPTS} #{SSH_HOST} '#{cmd}'"
|
||||
begin
|
||||
_, _, status = Open3.capture3(full_cmd)
|
||||
status.success?
|
||||
rescue
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def obtener_serial
|
||||
output = ssh_exec("grep -A5 'IN SOA' #{ZONA_PATH} | grep -oE '[0-9]{10}' | head -1")
|
||||
output
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user