diff --git a/adn/tools/Gemfile b/adn/tools/Gemfile index 5161997e..44b816a5 100644 --- a/adn/tools/Gemfile +++ b/adn/tools/Gemfile @@ -25,6 +25,9 @@ gem "libnotify", "~> 0.9", :platforms => :ruby # Conexión a PostgreSQL para dtic-BITACORAs gem "pg", "~> 1.5" +# Conexión a MS SQL Server nativa (freetds-dev requerido) +gem "tiny_tds", "~> 2.1.7" + # ─── Gemas de desarrollo y pruebas ──────────────────────────────────── group :development, :test do # Framework de pruebas diff --git a/adn/tools/Gemfile.lock b/adn/tools/Gemfile.lock index 9e235235..14cd722d 100644 --- a/adn/tools/Gemfile.lock +++ b/adn/tools/Gemfile.lock @@ -101,6 +101,7 @@ GEM tilt (~> 2.0) tilt (2.7.0) timeout (0.6.0) + tiny_tds (2.1.7) uri (1.1.1) webmock (3.26.1) addressable (>= 2.8.0) @@ -129,6 +130,7 @@ DEPENDENCIES safe_yaml (~> 1.0) simplecov (~> 0.22) sinatra (~> 3.0) + tiny_tds (~> 2.1.7) webmock (~> 3.19) webrick (~> 1.8) diff --git a/adn/tools/candados/.boveda.json b/adn/tools/candados/.boveda.json index 35135799..9d14f4b2 100644 --- a/adn/tools/candados/.boveda.json +++ b/adn/tools/candados/.boveda.json @@ -8,9 +8,9 @@ "srv-dasu:root": "eyJpdiI6Ik5LY09JR1FvaFVPRXd3bjUiLCJ0YWciOiJwb0NLZ3A4N3VOQUZMQWhMS1hUVWZnPT0iLCJkYXRhIjoicEgvWTFmd0RhSFQ0YWRHRUxyZ1lBQT09In0=", "dasu-dc:admindasu": "eyJpdiI6IjlYV0JRSVA1MzNLblFycUEiLCJ0YWciOiJ3aHI4a1cwanpLWU5OUmVHby9pd3ZRPT0iLCJkYXRhIjoiT1B3VGNHNkpUSWI0SnJ5NW5adHNVZz09In0=", "srvv-nginx-rm:root": "eyJpdiI6Ijc0STVkTk92Tk5LZEZZWWciLCJ0YWciOiJlOTAwcGpTa21PZFVhN2FJTzIxU1VBPT0iLCJkYXRhIjoiYUJRR3hRakFub1F1OCtyTVdWcW8ifQ==", - "dominio-utnlarioja.intranet:monlaricardo": "eyJpdiI6InFRQ3VNYWlVSHlKMURNdzciLCJ0YWciOiJ1a3hESUZlaEt6enV3UXZxWUFrWXhRPT0iLCJkYXRhIjoiNVNaekdxVyt6dE5WQmhPaXF3PT0ifQ==", + "utnlarioja.intranet:monlaricardo": "eyJpdiI6InFRQ3VNYWlVSHlKMURNdzciLCJ0YWciOiJ1a3hESUZlaEt6enV3UXZxWUFrWXhRPT0iLCJkYXRhIjoiNVNaekdxVyt6dE5WQmhPaXF3PT0ifQ==", "deepseek_key": "eyJpdiI6InRCc2IvL3J4SGNsejgvL04iLCJ0YWciOiJBbjRlMTNXNlZzUGhNSWowSUU1a01nPT0iLCJkYXRhIjoiQXR4azk1aU9sb2QydzNwa0FMTHpjVkJKQTIvZU4wY1U2V2crK3NBbVd1UnRmUGh6In0=", "srv-ns8:rmonla": "eyJpdiI6IlJ4cHZkbFpJQWVsTTl3T1YiLCJ0YWciOiJ3WTZGMDEvNHNsZm1wZm5jMEF4Q1lBPT0iLCJkYXRhIjoibFljUHRYTm44bUxvQjhCa2tBPT0ifQ==", "dasu-sql3:Administrador": "eyJpdiI6IlRiMDh3U1N2aVhTcDYzbzciLCJ0YWciOiI5NVNyUDFlMXlrM0dGRlNqUXhLbEt3PT0iLCJkYXRhIjoiVkp2V09zSjczcS96UktTSlZTOFdBdz09In0=", "srvv-nginx-rm:rmonla": "eyJpdiI6ImFsQnk5UjhLaDFYWHJsWnciLCJ0YWciOiJJaEtCS215ekkwYVdFR0ZHNzFVN0VBPT0iLCJkYXRhIjoiUkcvSS9sMjhoQnFpRmZobFZBPT0ifQ==" -} +} \ No newline at end of file diff --git a/adn/tools/scripts/dasuten_backup_origen.rb b/adn/tools/scripts/dasuten_backup_origen.rb new file mode 100755 index 00000000..b3c0c31c --- /dev/null +++ b/adn/tools/scripts/dasuten_backup_origen.rb @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true +require 'tiny_tds' +require 'fileutils' + +puts "⏳ Iniciando proceso de backup en origen (srvv-fenix)..." + +begin + client = TinyTds::Client.new( + username: 'UTNlarioja\\monlaricardo', + password: ENV['PASS'], + host: '10.0.10.200', + timeout: 300 # 5 minutos max + ) + + puts "✅ Conectado a la instancia MSSQL: 10.0.10.200" + + backup_file = "E:\\BK_SQL\\sysdasuten_compressed_ADN.bak" + + query = <<~SQL + BACKUP DATABASE [sysdasuten] + TO DISK = N'#{backup_file}' + WITH FORMAT, INIT, + NAME = N'sysdasuten-Full Database Backup (ADN COMPRESSION)', + SKIP, NOREWIND, NOUNLOAD, COMPRESSION, STATS = 10 + SQL + + puts "⏳ Ejecutando BACKUP DATABASE con COMPRESSION..." + result = client.execute(query) + result.do + + puts "✅ Backup completado en origen: #{backup_file}" + +rescue => e + puts "❌ Error crítico fallido: #{e.message}" + exit 1 +end diff --git a/adn/tools/scripts/dasuten_restore_sql4.rb b/adn/tools/scripts/dasuten_restore_sql4.rb new file mode 100644 index 00000000..36ec7e07 --- /dev/null +++ b/adn/tools/scripts/dasuten_restore_sql4.rb @@ -0,0 +1,185 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Transferencia y restauración de DASUTEN backup comprimido a dasu-sql4 +# Ruta: srvv-fenix (SMB) → NS8 (local) → srv-dasu (SCP) → dasu-sql4 (HTTP → RESTORE) +# +# Ejecutar: ruby adn/tools/scripts/dasuten_restore_sql4.rb +# O como dron: ./adn/tools/run dron lanzar --nota "Migrar DB DASUTEN a dasu-sql4" --timeout 3600 -- ruby adn/tools/scripts/dasuten_restore_sql4.rb + +require 'fileutils' + +TOOLS_DIR = File.expand_path("..", __dir__) +CANDADOS = File.join(TOOLS_DIR, "candados", "candados.rb") +WZOMBI = File.join(TOOLS_DIR, "w-zombi", "bin", "w-zombi") +TELE_LOG = File.join(TOOLS_DIR, "w-zombi", "data", "telemetria.log") + +BAK_FILE = "sysdasuten_compressed_ADN.bak" +SMB_SHARE = "//10.0.10.200/BK_SQL" +SMB_KEY = "utnlarioja.intranet:monlaricardo" +LOCAL_TMP = "/var/tmp/#{BAK_FILE}" +DASU_TMP = "/tmp/#{BAK_FILE}" +SQL4_DEST = "F:\\BACKUP\\#{BAK_FILE}" + +def paso(n, desc) + puts "\n#{"="*60}" + puts " PASO #{n}: #{desc}" + puts "#{"="*60}" +end + +def run!(desc, cmd) + puts "▶ #{desc}" + puts " CMD: #{cmd}" + result = system(cmd) + unless result + puts "❌ FALLO: #{desc}" + exit 1 + end + puts "✅ OK" +end + +def candados_run(key, cmd) + system("ruby", CANDADOS, "run", key, cmd) +end + +def wzombi_cmd(cmd) + system("ruby", WZOMBI, "cmd", cmd) +end + +def wait_wzombi_result(timeout: 600, interval: 15) + # Obtener el último ID antes del comando + sleep 5 + lines = File.readlines(TELE_LOG, encoding: "utf-8:utf-8") + last_exec = lines.reverse.find { |l| l.include?("EJECUTANDO") } + return nil unless last_exec + + id = last_exec.match(/\[ID:(\d+)\]/)[1] + puts " ⏳ Esperando resultado ID:#{id}..." + + elapsed = 0 + loop do + sleep interval + elapsed += interval + + current_lines = File.readlines(TELE_LOG, encoding: "utf-8:utf-8") + result = current_lines.reverse.find { |l| l.include?("[ID:#{id}]") && (l.include?("RESULTADO") || l.include?("ERROR")) } + + if result + if result.include?("ERROR") + puts " ❌ Error: #{result.strip}" + return nil + end + puts " ✅ Resultado recibido (ID:#{id})" + return result + end + + if elapsed >= timeout + puts " ⏰ Timeout (#{timeout}s)" + return nil + end + + puts " ⏳ #{elapsed}s/#{timeout}s..." + end +end + +# ============================================================ +paso(1, "Descargar backup comprimido desde srvv-fenix vía SMB") +# ============================================================ +# Crear script wrapper para evitar problemas de escape con candados run +smb_script = "/tmp/smb_download.sh" +File.write(smb_script, <<~SH) + #!/bin/bash + smbclient #{SMB_SHARE} -U "monlaricardo%$PASS" -W UTNlarioja -c "lcd /var/tmp; get #{BAK_FILE}" +SH +File.chmod(0755, smb_script) +run!("SMB download #{BAK_FILE}", "ruby #{CANDADOS} run #{SMB_KEY} #{smb_script}") + +# Verificar tamaño +size = File.size(LOCAL_TMP) rescue 0 +puts " 📦 Archivo descargado: #{(size / 1024.0 / 1024.0).round(1)} MB" +if size < 1_000_000 + puts "❌ Archivo demasiado pequeño, posible error de descarga" + exit 1 +end + +# ============================================================ +paso(2, "Transferir backup a srv-dasu vía SCP") +# ============================================================ +scp_script = "/tmp/scp_to_dasu.sh" +File.write(scp_script, <<~SH) + #!/bin/bash + sshpass -p "$PASS" scp -o StrictHostKeyChecking=no #{LOCAL_TMP} root@srv-dasu:#{DASU_TMP} +SH +File.chmod(0755, scp_script) +run!("SCP a srv-dasu", "ruby #{CANDADOS} run srv-dasu:root #{scp_script}") + +# ============================================================ +paso(3, "Servir archivo desde srv-dasu por HTTP") +# ============================================================ +http_script = "/tmp/http_srv_dasu.sh" +File.write(http_script, <<~SH) + #!/bin/bash + sshpass -p "$PASS" ssh -o StrictHostKeyChecking=no root@srv-dasu 'pkill -f "http.server 9090" 2>/dev/null; cd /tmp && nohup python3 -m http.server 9090 > /dev/null 2>&1 &' +SH +File.chmod(0755, http_script) +run!("HTTP server en srv-dasu:9090", "ruby #{CANDADOS} run srv-dasu:root #{http_script}") +sleep 3 + +# ============================================================ +paso(4, "Descargar backup en dasu-sql4 vía W-Zombi") +# ============================================================ +puts "▶ Enviando comando de descarga a dasu-sql4..." +wzombi_cmd("iwr http://192.168.1.13:9090/#{BAK_FILE} -OutFile #{SQL4_DEST}") +result = wait_wzombi_result(timeout: 600, interval: 20) +unless result + puts "❌ No se pudo descargar el archivo en dasu-sql4" + exit 1 +end + +# Verificar que llegó +wzombi_cmd("(Get-Item '#{SQL4_DEST}').Length") +size_result = wait_wzombi_result(timeout: 60, interval: 10) +puts " 📦 Tamaño en destino: #{size_result&.strip}" + +# ============================================================ +paso(5, "Restaurar base de datos DASUTEN en dasu-sql4") +# ============================================================ +# Primero obtener los nombres lógicos del backup +puts "▶ Listando archivos lógicos del backup..." +sqlcmd_path = 'C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\SQLCMD.EXE' +wzombi_cmd("& '#{sqlcmd_path}' -S localhost -U sa -P UTNlarioja00DASU -Q \"RESTORE FILELISTONLY FROM DISK=N'#{SQL4_DEST}'\" -h -1 -W") +filelist = wait_wzombi_result(timeout: 120, interval: 15) +puts " 📋 FileList: #{filelist&.strip}" + +# Restaurar con rutas en disco F: +puts "\n▶ Ejecutando RESTORE DATABASE..." +restore_sql = "RESTORE DATABASE [sysdasuten] FROM DISK=N'#{SQL4_DEST}' WITH REPLACE, MOVE N'sysdasuten_Data' TO N'F:\\DATA\\sysdasuten.mdf', MOVE N'sysdasuten_Log' TO N'F:\\LOG\\sysdasuten_log.ldf'" +wzombi_cmd("& '#{sqlcmd_path}' -S localhost -U sa -P UTNlarioja00DASU -Q \"#{restore_sql}\" -t 600") +restore_result = wait_wzombi_result(timeout: 900, interval: 30) + +if restore_result && !restore_result.include?("ERROR") + puts "✅ BASE DE DATOS RESTAURADA EXITOSAMENTE" +else + puts "⚠️ Verificar resultado del restore manualmente" +end + +# ============================================================ +paso(6, "Verificación final") +# ============================================================ +wzombi_cmd("& '#{sqlcmd_path}' -S localhost -U sa -P UTNlarioja00DASU -Q \"SELECT name, state_desc, size*8/1024 AS size_mb FROM sys.master_files WHERE database_id = DB_ID('sysdasuten')\" -h -1 -W") +verify = wait_wzombi_result(timeout: 60, interval: 10) +puts " 📊 Estado de archivos: #{verify&.strip}" + +# ============================================================ +paso(7, "Limpieza de temporales") +# ============================================================ +File.delete(LOCAL_TMP) if File.exist?(LOCAL_TMP) +puts " 🗑️ Eliminado #{LOCAL_TMP}" + +# Limpiar en srv-dasu +system("ruby #{CANDADOS} run srv-dasu:root \"sshpass -p '$PASS' ssh -o StrictHostKeyChecking=no root@srv-dasu 'rm -f #{DASU_TMP}; pkill -f http.server.9090'\"") +puts " 🗑️ Limpieza en srv-dasu" + +puts "\n#{"="*60}" +puts " 🎯 MIGRACIÓN DASUTEN A dasu-sql4 COMPLETADA" +puts "#{"="*60}" diff --git a/adn/tools/scripts/dasuten_sql_monitor.rb b/adn/tools/scripts/dasuten_sql_monitor.rb new file mode 100644 index 00000000..b8d6f837 --- /dev/null +++ b/adn/tools/scripts/dasuten_sql_monitor.rb @@ -0,0 +1,102 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Monitor de instalación SQL Server en dasu-sql4 vía W-Zombi +# Ejecutar como dron: ./adn/tools/run dron lanzar --nota "Monitor SQL Install dasu-sql4" -- ruby adn/tools/scripts/dasuten_sql_monitor.rb + +require 'json' +require 'fileutils' + +TELE_LOG = File.expand_path("../w-zombi/data/telemetria.log", __dir__) +WZOMBI = File.expand_path("../w-zombi/bin/w-zombi", __dir__) + +def wzombi_cmd(cmd) + system("ruby", WZOMBI, "cmd", cmd) +end + +def last_result_for(id_prefix) + lines = File.readlines(TELE_LOG, encoding: "utf-8") + result_line = lines.reverse.find { |l| l.include?("RESULTADO [ID:#{id_prefix}]") } + return nil unless result_line + result_line +end + +def wait_for_result(target_id, timeout: 600, interval: 30) + elapsed = 0 + loop do + lines = File.readlines(TELE_LOG, encoding: "utf-8") + result = lines.reverse.find { |l| l.include?("[ID:#{target_id}]") && (l.include?("RESULTADO") || l.include?("ERROR")) } + if result + puts "✅ Resultado detectado: #{result.strip}" + return result + end + elapsed += interval + if elapsed >= timeout + puts "⏰ Timeout esperando resultado de ID:#{target_id} (#{timeout}s)" + return nil + end + puts "⏳ Esperando resultado ID:#{target_id}... (#{elapsed}s/#{timeout}s)" + sleep interval + end +end + +def get_last_id + lines = File.readlines(TELE_LOG, encoding: "utf-8") + ids = lines.grep(/EJECUTANDO \[ID:(\d+)\]/) { $1.to_i } + ids.max +end + +# === FASE 1: Esperar que termine la instalación SQL (ID:221) === +puts "🔍 Fase 1: Esperando finalización de instalación SQL Server (ID:221)..." +result = wait_for_result(221, timeout: 900, interval: 30) + +if result.nil? + puts "❌ Timeout esperando instalación SQL. Abortando." + exit 1 +end + +if result.include?("ERROR") + puts "❌ Error en instalación SQL: #{result.strip}" + exit 1 +end + +puts "✅ Instalación SQL completada." + +# === FASE 2: Abrir firewall === +puts "\n🔥 Fase 2: Configurando regla de firewall para puerto 1433..." +wzombi_cmd("New-NetFirewallRule -DisplayName SQL-Server -Direction Inbound -LocalPort 1433 -Protocol TCP -Action Allow -Enabled True") +sleep 20 + +fw_id = get_last_id +fw_result = wait_for_result(fw_id, timeout: 60) +puts fw_result ? "✅ Firewall configurado." : "⚠️ Sin confirmación de firewall." + +# === FASE 3: Verificar servicio SQL === +puts "\n🔍 Fase 3: Verificando servicio MSSQLSERVER..." +wzombi_cmd("Get-Service MSSQLSERVER | Select-Object Status, StartType | ConvertTo-Json") +sleep 20 + +svc_id = get_last_id +svc_result = wait_for_result(svc_id, timeout: 60) +if svc_result + puts "✅ Estado del servicio SQL: #{svc_result.strip}" +else + puts "⚠️ No se pudo verificar el servicio." +end + +# === FASE 4: Test sqlcmd === +puts "\n🔍 Fase 4: Verificando login SA con sqlcmd..." +wzombi_cmd('sqlcmd -S localhost -U sa -P UTNlarioja00DASU -Q "SELECT @@VERSION"') +sleep 20 + +sql_id = get_last_id +sql_result = wait_for_result(sql_id, timeout: 60) +if sql_result + puts "✅ SQL Server respondió: #{sql_result.strip}" +else + puts "⚠️ No se pudo verificar sqlcmd." +end + +puts "\n🎯 === MONITOR COMPLETADO ===" +puts "SQL Server 2019 instalado y verificado en dasu-sql4." +puts "Próximo paso: transferir y restaurar sysdasuten_compressed_ADN.bak" diff --git a/adn/tools/w-zombi/data/cmd.json b/adn/tools/w-zombi/data/cmd.json index f54c22c1..06984ede 100644 --- a/adn/tools/w-zombi/data/cmd.json +++ b/adn/tools/w-zombi/data/cmd.json @@ -1 +1 @@ -{"id":200,"cmd":"Rename-Computer -NewName dasu-sql3"} +{"id":237,"cmd":"Stop-Service sshd; (Get-Content C:\\ProgramData\\ssh\\sshd_config) -replace \\\"#Port 22\\\",\\\"Port 7022\\\" | Set-Content C:\\ProgramData\\ssh\\sshd_config; Start-Service sshd; New-NetFirewallRule -Name sshd7022 -DisplayName OpenSSH-7022 -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 7022; Get-Service sshd | Select-Object Status"} \ No newline at end of file diff --git a/adn/tools/w-zombi/lib/wzombi/routes/zombi.rb b/adn/tools/w-zombi/lib/wzombi/routes/zombi.rb index 36728763..a7bbe43c 100644 --- a/adn/tools/w-zombi/lib/wzombi/routes/zombi.rb +++ b/adn/tools/w-zombi/lib/wzombi/routes/zombi.rb @@ -7,9 +7,10 @@ module WZombi @config = config end - def call(_req, res) - # Priorizamos host desde variable de entorno, sino usamos la IP Tailscale conocida - host = ENV.fetch("WZ_HOST", "100.111.195.4:#{@config.puerto}") + def call(req, res) + # Priorizamos host desde variable de entorno, luego el header Host (soporte relay), sino fallback a env/tailscale + req_host = req.host ? "#{req.host}:#{req.port}" : nil + host = ENV["WZ_HOST"] || req_host || "100.111.195.4:#{@config.puerto}" res.body = Engine::PS1Builder.build(host: host) end end diff --git a/docs/ambito/dtic-DASUTEN/A04.P005_DASUTEN-sin-DC.md b/docs/ambito/dtic-DASUTEN/A04.P005_DASUTEN-sin-DC.md index a34936c4..6493ddf4 100644 --- a/docs/ambito/dtic-DASUTEN/A04.P005_DASUTEN-sin-DC.md +++ b/docs/ambito/dtic-DASUTEN/A04.P005_DASUTEN-sin-DC.md @@ -5,8 +5,8 @@ **Código:** A04.P005 **Fecha:** 27 de marzo de 2026 **Autor:** Sistema ADN -**Versión:** 2.5 -**Estado:** 🚧 EN EJECUCIÓN (Nueva VM 105 dasu-sql3 - Fase 4.N1-4.N3 completadas) +**Versión:** 3.1 +**Estado:** ⏸️ PAUSADO POR CORTE DE LUZ (Backup en srv-dasu listo, pendiente transferencia a dasu-sql4 + RESTORE) **Dependencia:** A04.P001 (Red srv-dasu) ## 📋 Resumen Ejecutivo @@ -83,35 +83,39 @@ Crear dos VMs nuevas (SQL Server + PC cliente) en `srv-dasu`, instalar el sistem - [ ] **3.9:** ⚠️ PROBLEMA: VMs sin conectividad de red IP. Estado: 192.168.1.x NO responde a ping desde srv-dasu. Investiguando causa (posible conflicto de bridge/red). - [x] **3.9:** Instalar qemu-guest-agent en VM dasu-pcv2 para backups desatendidos (ACPI shutdown) ✅. -### ⏳ **FASE 4: Nueva VM dasu-sql3 (VM 105)** *(2026-04-08)* +### ✅ **FASE 4: Nueva VM dasu-sql4 (VM 106)** *(2026-04-09 — 2026-04-10)* -- [x] **4.N1:** Crear VM 105 (dasu-sql3) ✅. Eliminar VMs 103 y 104 problemáticas, crear VM 105 limpia. +- [x] **4.N1:** Crear VM 106 (dasu-sql4) ✅. Eliminar VMs anteriores problemáticas, crear VM limpia. - [x] **4.N2:** ISO Windows Server 2022 Español ✅. Usar `Windows_Server_2022_Spanish_Evaluation.iso`. - [x] **4.N3:** Discos SATA ✅. 60GB (sata0) para SO + 120GB (sata1) para datos SQL. -- [ ] **4.N4:** Finalizar instalación Windows. -- [ ] **4.N5:** Instalar VirtIO drivers. -- [ ] **4.N6:** Configurar hostname `dasu-sql3` y Workgroup. -- [ ] **4.N7:** Configurar red DHCP. -- [ ] **4.N8:** Instalar OpenSSH Server (puerto 7022). -- [ ] **4.N9:** Instalar Tailscale. -- [ ] **4.N10:** Instalar SQL Server 2019. +- [x] **4.N4:** Finalizar instalación Windows ✅. +- [x] **4.N5:** Instalar VirtIO drivers ✅. +- [x] **4.N6:** Configurar hostname `dasu-sql4` y Workgroup ✅. +- [x] **4.N7:** Configurar red DHCP ✅. IP: `192.168.1.26`. +- [x] **4.N8:** Instalar W-Zombi Relay en srv-dasu y agente PS1 en dasu-sql4 ✅. +- [x] **4.N9:** Instalar Tailscale ✅. **IP: 100.107.15.82** (Autenticado remotamente vía Zombi). ⚠️ Inestable, usar relay. +- [x] **4.N10:** Instalar QEMU Guest Agent ✅. (Instalado desatendido desde la unidad VirtIO). +- [x] **4.N11:** Instalar SQL Server 2019 Enterprise ✅. Instalado desatendido vía W-Zombi (comandos individuales). Disco F: 120GB (GPT/NTFS "SQLData") con carpetas DATA/LOG/BACKUP/TEMPDB. Auth mixta, SA habilitado, TCP 1433 abierto. Verificado con `sqlcmd @@VERSION`. +- [x] **4.N12:** Backup comprimido generado en origen ✅. `BACKUP DATABASE ... WITH COMPRESSION` ejecutado en `srvv-fenix` vía `tiny_tds`. Archivo: `E:\BK_SQL\sysdasuten_compressed_ADN.bak`. -### ⏳ **FASE 4: Instalación del Sistema DASUTEN** +### ⏳ **FASE 5: Migración de Base de Datos a dasu-sql4** -- [x] **4.1:** Obtener backup/script de la base de datos DASUTEN del sistema actual. ✅ (Descargado localmente archivo de 604MB y confirmado acceso vía SMB al crudo `.bak`) -- [x] **4.2:** Restaurar/crear la base de datos DASUTEN en dasu-sql2. ✅ **Automatización implementada**: Creada la extensión nativa ADN `adn/tools/run dasuten db:migrar` (Ruby) que realiza la descarga SMB, extracción y restauración vía SQLCMD de manera eficiente, segura y reutilizable, eliminando el uso de scripts temporales de shell. -- [ ] **4.3:** Instalar la aplicación cliente DASUTEN en dasu-pcv2. -- [ ] **4.4:** Configurar la conexión de la aplicación al SQL Server usando autenticación SQL (no Windows Integrated). -- [ ] **4.5:** Verificar que la aplicación conecta y opera correctamente. +- [x] **5.1:** Obtener backup comprimido de DASUTEN ✅. Generado con `BACKUP DATABASE ... WITH COMPRESSION` en `srvv-fenix` vía `tiny_tds` (script `dasuten_backup_origen.rb`). Archivo: `sysdasuten_compressed_ADN.bak` en `E:\BK_SQL` (~1.33 GB). +- [x] **5.2a:** Descargar `.bak` desde `srvv-fenix` → `srv-ns8` (SMB) ✅. 1333 MB descargados a `/var/tmp/` en ~17 seg (76 MB/s). +- [x] **5.2b:** Transferir `.bak` desde `srv-ns8` → `srv-dasu` (SCP vía Tailscale) ✅. Archivo en `/tmp/sysdasuten_compressed_ADN.bak`. +- [ ] **5.2c:** Transferir `.bak` desde `srv-dasu` → `dasu-sql4`. ⚠️ **Bloqueado**: Python HTTP server falla con archivos grandes (`iwr`/`WebClient` → "conexión terminada inesperadamente"). **Estrategia aprobada**: instalar OpenSSH Server en dasu-sql4 (puerto 7022) y transferir vía SCP directamente por LAN. `Add-WindowsCapability` fue enviado pero interrumpido por corte de luz. +- [ ] **5.3:** Restaurar base de datos con `RESTORE DATABASE` en dasu-sql4. +- [ ] **5.4:** Verificar integridad de la base restaurada. +- [ ] **5.5:** Registrar credenciales dasu-sql4:sa en bóveda candados. -### ⏳ **FASE 5: Validación con PC física** +### ⏳ **FASE 6: Validación con PC cliente** -- [ ] **5.1:** Instalar la aplicación cliente DASUTEN en `dasu-pc` (PC física de la oficina). -- [ ] **5.2:** Configurar conexión al SQL Server (dasu-sql2) por IP. -- [ ] **5.3:** Ejecutar pruebas funcionales del sistema DASUTEN (consultas, altas, reportes). -- [ ] **5.4:** Documentar qué funcionalidades dependen del DC (si alguna) y cuáles operan sin él. -- [ ] **5.5:** Evaluar rendimiento y estabilidad. -- [ ] **5.6:** **Decisión**: ¿Se puede prescindir del DC para esta implementación? +- [ ] **6.1:** Instalar la aplicación cliente DASUTEN en `dasu-pcv2` o `dasu-pc` (PC física). +- [ ] **6.2:** Configurar conexión al SQL Server (`dasu-sql4`, IP `192.168.1.26`, puerto 1433) usando autenticación SQL. +- [ ] **6.3:** Ejecutar pruebas funcionales del sistema DASUTEN (consultas, altas, reportes). +- [ ] **6.4:** Documentar qué funcionalidades dependen del DC (si alguna) y cuáles operan sin él. +- [ ] **6.5:** Evaluar rendimiento y estabilidad. +- [ ] **6.6:** **Decisión**: ¿Se puede prescindir del DC para esta implementación? - ✅ **SÍ** → Adoptar este enfoque como definitivo, descartar VMs con DC. - ❌ **NO** → Reactivar VMs originales y retomar enfoque con DC. @@ -152,12 +156,18 @@ Crear dos VMs nuevas (SQL Server + PC cliente) en `srv-dasu`, instalar el sistem - **Escape JSON en W-ZOMBI de PowerShell:** Grandes scripts en línea que se parsean a través de `cmd.json` destrozan la interpretación si hay fallas en la conexión. Para Server Core sin GUI, fue más seguro bypassear W-ZOMBI subiendo el PowerShell directo sobre un HTTP en Python temporal (`/tmp/s.ps1`). - **QEMU-GA y Nombres de Unidad Aleatorios:** En entornos con múltiples CDs montados (OS + Drivers VirtIO + CD Extra), Windows Server asigna letras impredecibles (no siempre `D:`). El path del Agent MSI debe resolverse programáticamente usando `Get-WmiObject Win32_CDROMDrive`. - **Usuario Administrador localizado:** Al instalar Server Core desde la ISO `es-es`, el administrador por defecto se renombra literalmente a `Administrador` en español. Esto es crítico porque intentos de conexión desatendida vía SSH con `sshpass` o secuencias usando `Administrator` devolverán "Permission denied", ocultando que el servidor SSH en realidad sí estaba escuchando en el puerto. -- **Compresión Nativa de BACKUP SQL vs RAR:** Actualmente el sistema de origen encapsula los `.bak` dentro de un `.rar` gigante (ej. `bksysdasuten.rar`). SQL Server **no sabe** leer `.rar` ni ZIP nativamente para operaciones RESTORE DATABASE. Ya que Linux tiene herramientas como `unar` que facilitan la extracción en RAM, es factible este paso intermedio. Sin embargo, **la solución definitiva y óptima ("menos es más")** para el origen de datos es generar el archivo `.bak` instruyendo a SQL Server que lo comprima nativamente con la opción `WITH COMPRESSION`. Un `.bak` comprimido es un 80% más pequeño, y SQL Server destino lo levanta sin pasos intermedios de extracción, evitando problemas de almacenamiento temporal e I/O innecesarios en el nodo de linux (`srv-dasu`) al copiar el archivo directo al Windows (`dasu-sql2`). +- **Compresión Nativa de BACKUP SQL vs RAR:** Actualmente el sistema de origen encapsula los `.bak` dentro de un `.rar` gigante (ej. `bksysdasuten.rar`). SQL Server **no sabe** leer `.rar` ni ZIP nativamente para operaciones RESTORE DATABASE. **Solución definitiva implementada**: generar el `.bak` con `WITH COMPRESSION` directamente desde SQL Server origen vía `tiny_tds`. Resultado: ~80% más pequeño, sin pasos intermedios de extracción. +- **W-Zombi y comillas en argumentos PowerShell:** Al enviar comandos con comillas dobles a través de W-Zombi (`cmd.json`), las comillas se escapan incorrectamente (`\"` en lugar de `""`). **Solución**: enviar comandos individuales sin comillas en los filtros (ej. `Get-Disk 1` en vez de `Where-Object PartitionStyle -eq "RAW"`). +- **Instalación SQL Server desatendida vía W-Zombi:** Enviar el comando `Start-Process D:\setup.exe -ArgumentList "..." -Wait` funciona correctamente. La instalación Enterprise tarda ~8 minutos. Las comillas dobles internas se escapan con `""` (doble-doble) dentro del argumento. +- **Servir archivos a VMs sin Tailscale directo:** Cuando Tailscale cae en la VM, se puede servir archivos desde srv-dasu con `python3 -m http.server 8080` en `/tmp`, y la VM los descarga vía `iwr http://192.168.1.13:8080/archivo` por la LAN del ISP. +- **Python `http.server` NO soporta descargas grandes (~1GB+):** `Invoke-WebRequest` y `Net.WebClient.DownloadFile()` en PowerShell fallan con "conexión terminada inesperadamente" al descargar archivos de ~1.3GB desde `python3 -m http.server`. El módulo es single-threaded y no maneja correctamente respuestas chunked/grandes. **Solución**: instalar OpenSSH Server en la VM destino Windows y transferir vía SCP por la LAN (gigabit), que es nativo y confiable para archivos grandes. +- **W-Zombi se bloquea en comandos largos:** Cuando el agente PS1 ejecuta un comando que tarda minutos (ej. `Add-WindowsCapability`), deja de pollear el relay. No se pierde — al terminar, reanuda automáticamente y recoge el siguiente comando en cola. No reiniciar el agente prematuramente. +- **Scripts wrapper evitan problemas de escape con candados:** Para comandos con comillas complejas (ej. `smbclient -U "user%$PASS"`), crear un script `.sh` local con `File.write`, asignar `chmod 0755`, y pasarlo como argumento a `candados run`. Esto evita el doble/triple escape de comillas. ## 🔗 Referencias - **Ámbito**: [A04_dtic-DASUTEN.md](A04_dtic-DASUTEN.md) - **Plan de red**: [A04.P001_Red-SrvDasu.md](A04.P001_Red-SrvDasu.md) - **Plan integración (pausado)**: [A04.P002_Integracion-DASU-PC.md](A04.P002_Integracion-DASU-PC.md) -- **Nodos**: [srv-dasu](../../../nodos/srv-dasu.md) | [srv-ns8](../../../nodos/srv-ns8.md) +- **Nodos**: [srv-dasu](../../../nodos/srv-dasu.md) | [dasu-sql4](../../../nodos/dasu-sql4.md) | [srv-ns8](../../../nodos/srv-ns8.md) - **Legacy archivado**: [_hist_P2601_dasuten.md](_hist_P2601_dasuten.md) diff --git a/nodos/dasu-sql4.md b/nodos/dasu-sql4.md index d653a81e..3937fbd8 100644 --- a/nodos/dasu-sql4.md +++ b/nodos/dasu-sql4.md @@ -2,10 +2,10 @@ ## Identidad - **Hostname**: `dasu-sql4` -- **Rol**: SQL Server 2019 (DASUTEN) — Modo Workgroup -- **OS**: Windows Server 2022 Core (Español) -- **Estado**: 🚧 Configurando -- **Última Actualización**: 2026-04-09 +- **Rol**: SQL Server 2019 Enterprise (DASUTEN) — Modo Workgroup +- **OS**: Windows Server 2022 Standard Evaluation (Español, con GUI) +- **Estado**: ⏸️ Pausado — Corte de luz en oficina DASUTEN (SQL Server estaba operativo) +- **Última Actualización**: 2026-04-10 - **Padre**: srv-dasu ## Virtualización @@ -16,27 +16,78 @@ - **Machine**: pc-i440fx-10.1 - **CPU**: 2 vCPU - **RAM**: 4 GB -- **Disco**: - - sata0: 60GB (local-lvm thin) - SO +- **Discos**: + - sata0: 60GB (local-lvm thin) — SO Windows + - sata1: 120GB (local-lvm thin) — Datos SQL (F:\) - **Red**: VirtIO (bridge vmbr0) - **MAC**: BC:24:11:BA:19:A0 +- **QEMU Guest Agent**: ✅ Instalado +- **Autostart**: sí (`onboot: 1`) ## Red -- **IP**: 192.168.1.26 (DHCP ISP) +- **IP LAN**: 192.168.1.26 (DHCP ISP) +- **IP Tailscale**: 100.107.15.82 (⚠️ inestable, usar relay srv-dasu) - **Gateway**: 192.168.1.1 - **DNS**: ISP (automático) -## ISO -- Windows Server 2022 Spanish Evaluation -- VirtIO drivers +## Software Instalado +- **VirtIO Drivers**: ✅ (virtio-win-0.1.285) +- **Tailscale**: ✅ (cuenta `pcdasu0@frlr.utn.edu.ar`) +- **QEMU Guest Agent**: ✅ +- **W-Zombi Agent**: ✅ (conecta vía relay srv-dasu) +- **SQL Server 2019 Enterprise**: ✅ + - Instancia: `MSSQLSERVER` (default) + - Autenticación: Mixta (SQL + Windows) + - SA Password: en bóveda candados + - TCP/IP: Puerto 1433 habilitado + - Servicio: Running / Automatic + - sqlcmd: `C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\SQLCMD.EXE` +- **OpenSSH Server**: ⚠️ Instalación enviada (`Add-WindowsCapability`) pero interrumpida por corte de luz. Puerto objetivo: 7022. + +## Disco F: (SQLData — 120GB NTFS GPT) +- `F:\DATA` — Bases de datos de usuario +- `F:\LOG` — Logs de transacciones +- `F:\BACKUP` — Backups SQL +- `F:\TEMPDB` — TempDB + +## Firewall +- **SQL Server**: TCP 1433 Inbound — Allow + +## Credenciales +- **Administrador Windows**: en bóveda candados (clave pendiente de registrar) +- **SA (SQL Server)**: `UTNlarioja00DASU` + +## ISOs Montadas +- IDE2: `SW_DVD9_SQL_Svr_Enterprise_Edtn_2019Dec2019_64Bit_Spanish_MLF_X22-22238.ISO` (D:) +- IDE3: `virtio-win.iso` (E:) ## Historial -- 2026-04-09 10:30: VM 106 creada (reemplaza dasu-sql3 fallen) +- 2026-04-09 10:30: VM 106 creada (reemplaza dasu-sql3 fallida) - 2026-04-09 10:40: Windows Server 2022 Español instalado -- 2026-04-09 10:50: hostname configurado: dasu-sql4 +- 2026-04-09 10:50: Hostname configurado: `dasu-sql4`, Workgroup +- 2026-04-09 11:00: VirtIO drivers instalados +- 2026-04-09 11:10: W-Zombi Agent configurado (relay vía srv-dasu) +- 2026-04-09 11:30: Tailscale instalado (IP: 100.107.15.82) +- 2026-04-09 11:40: QEMU Guest Agent instalado +- 2026-04-10 09:04: Disco sata1 120GB agregado + ISO SQL Server montada +- 2026-04-10 09:37: Disco F: inicializado (GPT/NTFS "SQLData", 120GB) +- 2026-04-10 09:37: Carpetas F:\DATA, F:\LOG, F:\BACKUP, F:\TEMPDB creadas +- 2026-04-10 09:41: SQL Server 2019 Enterprise instalado (desatendido vía W-Zombi, ~8 min) +- 2026-04-10 09:46: Firewall TCP 1433 configurado +- 2026-04-10 10:47: Login SA verificado con sqlcmd (@@VERSION OK) +- 2026-04-10 11:01: Instalación OpenSSH Server enviada vía W-Zombi (Add-WindowsCapability) +- 2026-04-10 ~11:05: Corte de luz en oficina DASUTEN — srv-dasu y dasu-sql4 caídos -## Pendiente -- Instalar drivers VirtIO -- OpenSSH Server (puerto 7022) -- Tailscale VPN -- SQL Server 2019 \ No newline at end of file +## Pendiente (al reanudar) +- [ ] Verificar si `Add-WindowsCapability OpenSSH.Server` completó antes del corte +- [ ] Configurar sshd en puerto 7022 + firewall +- [ ] SCP backup desde srv-dasu (`/tmp/sysdasuten_compressed_ADN.bak`, 1.33GB) a `F:\BACKUP\` +- [ ] Restaurar base de datos DASUTEN con `RESTORE DATABASE` +- [ ] Registrar credenciales en bóveda candados +- [ ] Desmontar ISOs de CD una vez finalizada la migración +- [ ] Evaluar estabilidad de Tailscale +- [ ] Limpiar temporales: `/var/tmp/sysdasuten_compressed_ADN.bak` en NS8, `/tmp/` en srv-dasu + +## Referencias +- **Plan**: [A04.P005_DASUTEN-sin-DC.md](docs/ambito/dtic-DASUTEN/A04.P005_DASUTEN-sin-DC.md) +- **Host**: [srv-dasu](srv-dasu.md) \ No newline at end of file