Procesado de un tabular a través de PowerShell
Powershell es una consola de sistema más avanzado y completo que MS-DOS o CMD. En nuestro caso, a través de PowerShell, crearemos un script que nos permitirá procesar un modelo tabular (Microsoft Analysis Services Tabular Model) de forma autónoma.
Asumimos que vamos a tener el siguiente código en un fichero .ps1, es decir, crearemos un fichero de nombre ProcesaTabular.ps1 con el código que viene a continuación:
#┌────────────────────────────────────────┬────┐
#│ PARAMETROS │ │
#├────────────────────────────────────────┼────┤
#│ IN - Nombre del fichero de parámetros │ │
#│ IN - Nombre del fichero de Log │ │
#└────────────────────────────────────────┴────┘
Param
(
[string]$pFicheroParam,
[string]$pFicheroLog
)
#Variable debugging, si vale 1 da salida por pantalla del log (Verbose) y en caso de fallo no sale del debugger (PowerShell ISE)
#$Debugging= 0
$Debugging = 1
#$pFicheroParam = 'Config.Param'
#$pFicheroLog = 'Log.out'
#┌─────────────────────────────────────────┐
#│ CONSTANTES │
#├─────────────────────────────────────────┤
#│ Ubicación de fichero de Log │
#│ Ubicación de fichero de parámetros │
#└─────────────────────────────────────────┘
$cRutaScript = split-path $SCRIPT:MyInvocation.MyCommand.Path -parent
$cRutaFicheroLog = $cRutaScript + "\LOG\"
$cRutaFicheroParam = $cRutaScript + "\CONFIG\"
$cRtdoOk = '<return xmlns="urn:schemas-microsoft-com:xml-analysis"><root xmlns="urn:schemas-microsoft-com:xml-analysis:empty"></root></return>'
$cBKdoOk = ''
########################################################################
# FUNCIONES DEL SCRIPT
########################################################################
#┌─────────────────────────────────────────┐
#│Funciones para escribir en el Log │
#└─────────────────────────────────────────┘
Function LogWrite
# Ecribe en el log el string que se pasa como parámetro y el valor de DateTime
{
Param ([string]$logString)
$vLog = $cRutaFicheroLog + $pFicheroLog
$vFecha = Get-Date -format "yyyy-MM-dd HH:mm:ss"
$logString = $vFecha + " AST-TABULAR " + $logString
if ($Debugging -eq 1)
{
echo $logString
}
Add-content $vLog -value $logString
}
Function LogWriteSeparador
# Ecribe en el log una línea de separación
{
$vLog = $cRutaFicheroLog + $pFicheroLog
Add-content $vLog -value "---------------------------------------------------------------------------------------------------------------------------------"
}
#┌────────────────────────────────────────────────────────────────────────┐
#│ Función para salir del script y devolver un código a Control-M │
#│ o al proceso desde el que se invoca el script. │
#│ El valor devuelto es un parámetro que recibe la función │
#└────────────────────────────────────────────────────────────────────────┘
function ExitScriptCodigo
{
Param ($ExitCode)
if ($Debugging -eq 1)
{
break
}
else
{
$Host.SetShouldExit($ExitCode)
Exit
}
}
#┌────────────────────────────────────────────────────────────────────────┐
#│ Función que importa el script contenido en un fichero en una variable │
#│ y sustituye las variables ProcServer y ModelID por los valores del │
#│ archivo de configuración. │
#│ Devuelve una cadena con el comando listo para ejecutar │
#└────────────────────────────────────────────────────────────────────────┘
function GetCommandFromFile
{
Param($FileProcess)
Try
{
#importa el script a ejecutar a una variable para poder cambiar las variables
$SyncQuery = Get-Content -Path $FileProcess
$SyncQuery = $SyncQuery.Replace("ProcServer", $ConfigParam.Get_Item("ProcServer").Trim())
$SyncQuery = $SyncQuery.Replace("ModelID", $ConfigParam.Get_Item("ModelID").Trim())
return [String]$SyncQuery
}
Catch
{
LogWrite "ERROR leyendo el comando del archivo XMLA.Salida del script con código 1"
ExitScriptCodigo(1)
}
}
#┌───────────────────────────────────────────────────────────────────┐
#│ Función que importa el script que se usa para generar un BACKUP │
#│ de la base de datos │
#└───────────────────────────────────────────────────────────────────┘
function GetCommandFromFileBackup
{
Param($FileProcess)
Try
{
#componemos el backup file
$rutaBckp = $ConfigParam.Get_Item("BackupFile").Trim() + "\" + $ConfigParam.Get_Item("ModelID").Trim() + ".ABF"
#importa el script a ejecutar a una variable para poder cambiar las variables
$BackupQuery = Get-Content -Path $FileProcess
$BackupQuery = $BackupQuery.Replace("ModelID", $ConfigParam.Get_Item("ModelID").Trim())
$BackupQuery = $BackupQuery.Replace("BackupFile", $rutaBckp)
return [String]$BackupQuery
}
Catch
{
LogWrite "ERROR leyendo el comando del archivo BackupFile del script con código 2"
ExitScriptCodigo(2)
}
}
#┌───────────────────────────────────────────────────────────────────┐
#│ Función que importa la query que se usará para crear nuevas │
#│ particiones y la devuelve como resultado │
#└───────────────────────────────────────────────────────────────────┘
function GetPartitionQuery
{
Param($FileQuery)
Try
{
#importa la query en una variable y la devuelve como salida
$Fichero = ($cRutaFicheroParam + $FileQuery.Trim())
$QueryParticion = Get-Content -Path $Fichero
return [String]$QueryParticion
}
Catch
{
LogWrite "ERROR leyendo el comando del archivo QueryParticion del script con código 3"
ExitScriptCodigo(3)
}
}
#┌─────────────────────────────────────────────────┐
#│ Función que crea un backup de la base de datos │
#└─────────────────────────────────────────────────┘
function CreateBackUp
{
Try
{
LogWrite "INICIO Backup SSAS Tabular --> $db"
$pathBackup = $cRutaFicheroParam + $ConfigParam.Get_Item("BackupQuery").Trim()
$BackupCommand = GetCommandFromFileBackup ($pathBackup)
Invoke-ASCmd -Server $svr -OutVariable vSalida -Query $BackupCommand
if ($cRtdoOk -eq $vSalida)
{
LogWrite "FIN Backup SSAS Tabular --> $db"
}
else
{
LogWrite "ERROR backup modelo tabular ($db). Salida con código 4"
ExitScriptCodigo(4)
}
}
Catch
{
$ErrorMessage = $_.Exception.Message
InterpretaError($ErrorMessage.ToString())
}
}
#┌──────────────────────────────────────────────────────────────┐
#│ Funcion que procesa las tablas no particionadas del tabular │
#└──────────────────────────────────────────────────────────────┘
function ProcesaNoParticionadas
{
Try
{
$ServerName = $ConfigParam.Get_Item("ProcServer").Trim()
$DatabaseName = $ConfigParam.Get_Item("ModelID").Trim()
$ProcessType = $ConfigParam.Get_Item("TipoProcesado").Trim()
$CubeName = $ConfigParam.Get_Item("CubeName").Trim()
#[Reflection.Assembly]::LoadWithPartialName("Microsoft.AnalysisServices")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.AnalysisServices") >$NULL
$server = New-Object Microsoft.AnalysisServices.Server
$server.connect($ServerName)
$db = $server.Databases.Item($DatabaseName)
$DBTabla = @() #array
$DBTabla = $ConfigParam.Get_Item("TablasNoParticion").ToString().Split(";").Trim()
LogWrite "PROCESO : INICIO PROCESADO ***********************"
ForEach ($T in $DBTabla)
{
#if ($DBTabla.Name -ne $Particionada)
#{
$cube = $db.Cubes.GetByName($CubeName)
$measureGroup = $cube.MeasureGroups.GetByName($T)
LogWrite ("PROCESO : INICIO PROCESADO {0}" -f $measureGroup.Name)
$measureGroup.Process($ProcessType)
LogWrite ("PROCESO : FIN PROCESADO {0}" -f $measureGroup.Name)
#}
}
LogWrite "PROCESO : FIN PROCESADO ***********************"
}
Catch
{
LogWrite "ERROR 5 - Error procesando"
LogWrite $_.Exception.Message
ExitScriptCodigo(5)
}
}
function CheckListLog
{
$ServerName = $ConfigParam.Get_Item("ProcServer").Trim()
$DatabaseName = $ConfigParam.Get_Item("ModelID").Trim()
## Add the AMO namespace
$loadInfo = [Reflection.Assembly]::LoadWithPartialName("Microsoft.AnalysisServices")
$server = New-Object Microsoft.AnalysisServices.Server
$server.connect($ServerName)
if ($server.name -eq $null) {
LogWrite ("Server '{0}' not found" -f $ServerName)
break
}
$db = $server.Databases.Item($DatabaseName)
LogWrite ( "Database: {0}; Status: {1}; Size: {2}MB" -f $db.Name, $db.State, ($db.EstimatedSize/1024/1024).ToString("#,##0") )
foreach ($cube in $db.Cubes)
{
LogWrite ( " Modelo : {0}" -f $Cube.Name )
foreach ($mg in $cube.MeasureGroups)
{
LogWrite ( " Tabla : {0}; Status: {1}" -f $mg.Name.PadRight(35), $mg.State)
}
}
}
#┌────────────────────────────────────────────────────────────────┐
#│ Función que devuelve si el error en el procesado │
#│ es un problema en el origen o un problema de la plataforma │
#└────────────────────────────────────────────────────────────────┘
function InterpretaError
{
param ([string]$Mens)
Try {
$DBError = @() #array con los posibles errores relacionados con BBDD
$DBError = $ConfigParam.Get_Item("DBError").ToString().Split(";").Trim()
$flag = 0
foreach ($texto in $DBError) {
If ($Mens.Contains($texto)) {
LogWrite $texto
$flag = 1
break;
}
else {
$flag = 0
}
}
If ($flag -eq 1) {
LogWrite "ERROR en procesado del modelo tabular en el origen de datos. Salida del script con código 8 ($Mens)"
write-host $Mens
ExitScriptCodigo(8)
}
else {
LogWrite "ERROR en procesado del modelo tabular. Salida del script con código 9 ($Mens)"
ExitScriptCodigo(9)
}
}
Catch {
LogWrite "ERROR interpretando el error de procesado. Salida del script con código 10"
ExitScriptCodigo(10)
}
}
########################################################################
# INICIO DEL SCRIPT
########################################################################
write-host "................en ejecución...."
# Marcar inicio de script en Fichero de Log
# Se captura posible error en parámetro de log o en escritura del mismo
########################################################################
Try
{
LogWriteSeparador
LogWrite "INICIO -> Script Procesado Microsoft SSAS Tabular Model"
LogWrite "Fichero Parámetros : $pFicheroParam "
LogWrite "Fichero Log : $pFicheroLog "
}
Catch
{
LogWrite "ERROR en parámetro de Log o en fichero de Log. Salida del script con código 11"
ExitScriptCodigo(11)
}
# Lectura de parámetros del fichero
# IMPORTANTE: Se lee un par parámetro valor en una tabla hash
# En el fichero los pares parámetro valor tienen que estar separados por un =
########################################################################
$vFile = $cRutaFicheroParam.Trim() + $pFicheroParam.Trim()
$ConfigParam = @{} # Tabla de hash para guardar los pares Parametro - Valor
Try
{
LogWrite "INICIO Lectura Fichero Parámetros"
Get-Content $vFile | foreach {
$line = $_.split("=")
$ConfigParam.($line[0].Trim()) = $line[1].Trim()
}
}
Catch
{
LogWrite "ERROR en lectura de fichero de parámetros. Salida del script con código 12"
ExitScriptCodigo(12)
}
# carga en variables algunos valores de la configuración con los que se
# necesita iterar posteriormente:
# - servidores "query"
########################################################################
$QueryServers = @() # array con los servidores de query en los que desplegar el cubo
$QueryServers = $ConfigParam.Get_Item("QueryServers").ToString().Split(";").Trim()
$QueryStorage = @() # array con los directorios de almacenamiento
$QueryStorage = $ConfigParam.Get_Item("QueryStorage").ToString().Split(";").Trim()
# Importación de módulos con los cmdlets de Analysis Services
########################################################################
Try
{
LogWrite "INICIO Carga Módulos Analysis Services"
Import-Module -DisableNameChecking sqlascmdlets
Import-Module -DisableNameChecking SQLPS
}
Catch
{
LogWrite "ERROR en importación de módulos (cmdlets) Analysis Services. Salida del script con código 13"
ExitScriptCodigo(13)
}
# CONEXIÓN CON EL SERVIDOR DE PROCESADO
########################################################################
Try
{
## load the AMO and XML assemblies into the current runspace
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.AnalysisServices") > $null
[System.Reflection.Assembly]::LoadWithPartialName("System.Xml") > $null
LogWrite "INICIO Conexión Servidor de Procesado"
# se conecta con el servidor de procesado
$svr = new-Object Microsoft.AnalysisServices.Server
$Err = New-Object Microsoft.AnalysisServices.ErrorConfiguration
$svr.Connect($ConfigParam.Get_Item("ProcServer").Trim())
}
Catch
{
LogWrite "ERROR en conexión con el servidor de procesado ($svr). Salida del script con código 14"
ExitScriptCodigo(14)
}
###################################################################################
# INICIO DE LAS ACCIONES (PROCESADO DE DIMENSIONES, PARTICIONES, SINCRONIZADO...)
###################################################################################
Try
{
# obtiene la bbdd a procesar
$db = $svr.Databases.Item($ConfigParam.Get_Item("ModelID").Trim())
#Crea un backup de la BBDD
CreateBackUp
# procesado del cubo
LogWrite "INICIO Procesado SSAS Tabular --> $db"
$TipoProcesado = $ConfigParam.Get_Item("TipoProcesado").Trim()
LogWrite "INICIO Procesado $TipoProcesado SSAS Tabular --> $db"
$db.Process($TipoProcesado)
LogWrite "FIN Procesado $TipoProcesado SSAS Tabular --> $db"
CheckListLog
LogWrite "FIN Procesado SSAS Tabular --> $db"
}
Catch
{
$ErrorMessage = $_.Exception.Message
InterpretaError($ErrorMessage.ToString())
}
LogWrite "FIN -> Script Procesado Microsoft SSAS Tabular Model -> Estado [OK]"
$svr.Disconnect()
break
Ahora crearemos la siguiente árbol de carpetas:
c:\Script
c:\Script\Config
c:\Script\Log
En la ruta c:\Script dejaremos el archivo ProcesaTabular.ps1, y crearemos un fichero .bat con la siguiente información:
cls
powershell -command .\ProcesaTabular.ps1 Config.Param Log.out
powershell -command .\ProcesaTabular.ps1 Config.Param Log.out
Una vez guardado el .bat en la ruta c:\Script nos vamos a la carpeta c:\Script\Config y creamos los siguientes archivos:
Config.Param
El fichero estará compuesto por la siguiente información:
ProcServer = <NOMBRE DEL SERVIDOR>
ModelID = <Nombre de la BBDD a Procesar>
SyncQUERY = TabularSync.xmla
RestoreQUERY = RestoreSync.xmla
BackupQuery = BackupSync.xmla
BackupFile = <RUTA DE BACKUP>
DBError = Error de OLE DB u ODBC;ORA-;SQLSTATE;[DB2/AIX64];valor duplicado
CubeName = <Nombre del Cubo>
TipoProcesado = ProcessFull
RestoreQUERY = RestoreSync.xmla
BackupQuery = BackupSync.xmla
BackupFile = <RUTA DE BACKUP>
DBError = Error de OLE DB u ODBC;ORA-;SQLSTATE;[DB2/AIX64];valor duplicado
CubeName = <Nombre del Cubo>
TipoProcesado = ProcessFull
Simplemente hay que sustituir los parámetros en color verde por los adecuados.
BackupSync.xmla
Con la siguiente información:
<Backup xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">
<Object>
<DatabaseID>ModelID</DatabaseID>
</Object>
<File>BackupFile</File>
<AllowOverwrite>true</AllowOverwrite>
</Backup>
<Object>
<DatabaseID>ModelID</DatabaseID>
</Object>
<File>BackupFile</File>
<AllowOverwrite>true</AllowOverwrite>
</Backup>
RestoreSync.xmla
Con la siguiente información:
<Restore xmlns="http://schemas.microsoft.com/analysisservices/2003/engine">
<File>BackupFile</File>
<DatabaseName>ModelID</DatabaseName>
<AllowOverwrite>true</AllowOverwrite>
</Restore>
<File>BackupFile</File>
<DatabaseName>ModelID</DatabaseName>
<AllowOverwrite>true</AllowOverwrite>
</Restore>
Una vez realizado esto simplemente ejecutaríamos el .bat y el modelo se procesaría.
Comentarios