Artículo Luis Angel Pérez Ramos · ene 2, 2023 5m read

¡Hola desarrolladores!

Comparto con vosotros una plantilla de Python Embebido básica, que recomiendo como punto de partida para cualquier proyecto general con InterSystems IRIS que use Python Embebido.

Funcionalidades:

  • Python Embebido configurado;
  • 3 ejemplos con difentes formas de desarrollar con Python Embebido;
  • VSCode configurado;
  • Docker disponible;
  • Demo online disponible;
  • Desarrollo ZPM First disponible.

Vamos a comentar estas funcionalidades.

0
0 152
Artículo Luis Angel Pérez Ramos · dic 20, 2022 3m read

Llamamos Procesamiento Híbrido Transaccional y Analítico (HTAP por sus siglas en inglés) a la capacidad de recuperar numerosos registros por segundo, mientras que a la vez se permiten consultas simultáneas en tiempo real. También se llama Analítica Transaccional ó Transanalítica y es un elemento muy útil en escenarios en los que disponemos de un flujo constante de datos en tiempo real, como podría ser el caso de datos provenientes de sensores IIOT o información de las fluctuaciones en el mercado bursátil y nos permite satisfacer la necesidad de consultar estos conjuntos de datos en tiempo real o casi en tiempo real.

Os comparto un ejemplo que podréis ejecutar en el que se recibe un conjunto de datos en streaming, con entradas de datos constantes y consultas continuas a la vez. El ejemplo está desarrollado en varias plataformas y podréis comparar cómo reaccionó cada una de ellas, con la velocidad de entrada y salida de datos en cada plataforma y su rendimiento. Las plataformas con las que he probado en esta demo son: InterSystems IRIS, MariaDB y MySQL.

0
0 131
Artículo Luis Angel Pérez Ramos · dic 14, 2022 2m read

En este artículo, mostraré cómo subir y descargar archivos vía http desde los productos de InterSystems.

A menudo desde la comunidad surgen preguntas sobre como trabajar con archivos a través de http y normalmente os emplazamos a revisar el proyecto FileServer, que muestra la subida/descarga de archivos. Pero me gustaría hablar un poco más de cómo podemos enviar y recibir archivos desde los productos de InterSystems.

Cómo descargar un archivo

Si tenéis un archivo en un sistema de archivos y conocéis su ruta, podéis enviarlo vía REST o en el contexto de CSP llamando a este método:

1
0 992
Artículo Luis Angel Pérez Ramos · dic 14, 2022 2m read

Me gustaría compartir con la comunidad un log de datos de un servidor webde un cliente nuestro desde hace muchos años, una compañia operadora.

Su servidor web funciona sobre Apache y contiene datos útiles para analizar la carga y la actividad de los motores de búsqueda.

Tras instalar el proyecto, podrás ver los datos generados durante unos cuantos meses y que muestran la carga y la actividad típica de clientes, bots... también podrás ver cómo dicha carga depende del día de la semana, si son vacaciones o no, así como del momento del día.

El diseño del cubo se encuentra incluido en el paquete.

0
0 94
Artículo Luis Angel Pérez Ramos · nov 23, 2022 13m read

Antecedentes

VersiónFechaCambios
V108/02/2022Lanzamiento Inicial
V1.106/04/2022Generación de certificados con un archivo sh en vez de un pki-script
Uso de variables de entorno en los archivos de configuración

¡Hola Comunidad!

¿Ya habéis configurado un entorno en mirror? ¿Tenéis una red privada, una dirección IP virtual y una configuración SSL? Después de hacer esto un par de veces, me di cuenta de que es muy largo, y hay muchos pasos que hay que realizar manualmente para generar certificados y configurar cada instancia de IRIS. Es un dolor de cabeza para cualquiera que tenga que hacer esto a menudo.

Por ejemplo, un equipo de control de calidad podría necesitar un nuevo entorno por cada nueva versión de la aplicación que tenga que probar mientras que el equipo de soporte puede necesitar crear un entorno para reproducir un problema complejo.

Definitivamente, necesitamos herramientas para crearlos rápidamente.

En este artículo crearemos una muestra para configurar un mirror con:

  • Arbiter.
  • Primary.
  • Miembro failover del backup.
  • Miembro asíncrono de lectura-escritura de informes.
  • Configuración SSL para transferencias de journal entre nodos.
  • Red privada para el mirror.
  • Dirección IP virtual.
  • Una base de datos en mirror.

network-schema

A primera vista, parece un poco complejo y parece que hace falta una gran cantidad de código, pero no te preocupes. Hay librerías en OpenExchange para realizar fácilmente la mayoría de las operaciones.

El propósito de este artículo es ofrecer un ejemplo de cómo adaptar el proceso a vuestras necesidades, pero no es una guía de prácticas recomendadas en materia de seguridad.

Así que vamos a crear nuestra muestra.

Herramientas y librerías

  • config-api: Esta librería se utilizará para configurar IRIS. Es compatible con la configuración del mirroring desde la versión 1.1.0. No vamos a dar una descripción detallada de cómo utilizar esta librería. Ya hay varios artículos aquí. En resumen, config-api se utilizará para crear archivos de configuración de plantillas IRIS (formato JSON) y cargarlos fácilmente.

  • ZPM.

  • Docker.

  • OpenSSL.

Página de Github

Podéis encontrar todos los archivos de recursos necesarios en el repositorio iris-mirroring-samples.

Preparación del sistema

Clonad el repositorio existente:

git clone https://github.com/lscalese/iris-mirroring-samples
cd iris-mirroring-samples

Si preferís crear una muestra desde cero, en vez de clonar el repositorio, simplemente cread un nuevo directorio con subdirectorios: backup, y config-files. Descargad irissession.sh:

mkdir -p iris-mirroring-samples/backup iris-mirroring-samples/config-files
cd  iris-mirroring-samples
wget -O session.sh https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/session.sh

Para evitar la incidencia "permiso rechazado" más tarde, tenemos que crear el grupo irisowner, el usuario irisowner, y cambiar el grupo del directorio del backup a irisowner

sudo useradd --uid 51773 --user-group irisowner
sudo groupmod --gid 51773 irisowner
sudo chgrp irisowner ./backup

Este directorio se utilizará como volumen para compartir una copia de seguridad de la base de datos después de que se configure el primer miembro mirror con los otros nodos.

Obtención de una licencia de IRIS

Mirroring no está disponible con la Edición Community de IRIS. Si aún no tenéis una licencia válida para el contenedor de IRIS, conectaos al Centro de Soporte Internacional (WRC) con vuestras credenciales. Haced clic en "Actions" --> "Online distribtion", después en el botón "Evaluations" y seleccionad "Evaluation License". Completad el formulario. Copiad vuestro archivo de licencia iris.key en este directorio.

Inicio de sesión en el Registro de Contenedores de Intersystems

Por comodidad, utilizamos Intersystems Containers Registry (ICR) para extraer imágenes de Docker. Si no sabéis vuestro nombre de usuario\contraseña de Docker, conectaos a SSO.UI.User.ApplicationTokens.cls con vuestras credenciales del Centro de Soporte Internacional (WRC), y podréis recuperar vuestro Token ICR.

docker login -u="YourWRCLogin" -p="YourICRToken" containers.intersystems.com

Creación de la base de datos myappdata y un mapeo de globales

De momento no vamos a crear la base de datos myappdata, únicamente estamos preparando la configuración para crearla al momento de crear el Docker. Para ello, simplemente creamos un archivo sencillo utilizando el formato JSON. La librería config-api se utilizará para cargarlo en las instancias de IRIS.

Cread el archivo config-files/simple-config.json

{
   "Defaults":{
       "DBDATADIR" : "${MGRDIR}myappdata/",
       "DBDATANAME" : "MYAPPDATA"

   },
   "SYS.Databases":{
       "${DBDATADIR}" : {}
   },
   "Databases":{
       "${DBDATANAME}" : {
           "Directory" : "${DBDATADIR}"
       }
   },
   "MapGlobals":{
       "USER": [{
           "Name" : "demo.*",
           "Database" : "${DBDATANAME}"
       }]
   },
   "Security.Services" : {
       "%Service_Mirror" : {                      /* Enable the mirror service on this instance */
           "Enabled" : true
       }
   }
}

Este archivo de configuración permite crear una nueva base de datos con la configuración predeterminada y hacer un mapeo del global demo.* en el namespace USER.

Para más información sobre las funciones del archivo de configuración config-api consulta el artículo, relacionado o la página de github.

El archivo Docker

El archivo Docker se basa en la plantilla existente de Docker, pero necesitamos hacer algunos cambios para crear un directorio de trabajo, instalar las herramientas para el uso de la IP virtual, instalar ZPM, etc…

Nuestra imagen IRIS es la misma para cada miembro mirror. El mirroring se establecerá en el contenedor empezando con la configuración correcta dependiendo de su función (primer miembro, backup de respaldo/failover o informe de lectura-escritura). Observa los comentarios en el Dockerfile:

ARG IMAGE=containers.intersystems.com/intersystems/iris:2021.1.0.215.0
# No es necesario descargar la imagen desde WRC, se hará automáticamente desde ICR cuando se despliegue el contenedor.

FROM $IMAGE

USER root

COPY session.sh /
COPY iris.key /usr/irissys/mgr/iris.key

# /opt/demo será nuestro directorio de trabajo y en el que almacenaremos nuestros archivos de configuración así como otros archivos de instalación.
# Instalamos iputils-arping para hacer uso del comando arping. Es necesario para configurar una IP Virtual.
# Descargamos la última versión de ZPM (o IPM, incluida en las versiones a partir de la 2023.1). 
RUN mkdir /opt/demo && \
    chown ${ISC_PACKAGE_MGRUSER}:${ISC_PACKAGE_IRISGROUP} /opt/demo && \
    chmod 666 /usr/irissys/mgr/iris.key && \
    apt-get update && apt-get install iputils-arping gettext-base && \
    wget -O /opt/demo/zpm.xml https://pm.community.intersystems.com/packages/zpm/latest/installer

USER ${ISC_PACKAGE_MGRUSER}

WORKDIR /opt/demo

# Configuramos el rol del mirror por defecto a master.
# Se sobreescribirá en el archivo docker-compose en el momento de la ejecución (master para la primera instancia, backup, y report)
ARG IRIS_MIRROR_ROLE=master

# Copiamos el contenido del directorio de archivos de configuración en /opt/demo.
# Únicamente hemos creado una configuración simple de nuestra base de datos y los mapeos de globales.
# Posteriormente en este mismo artículo incluiremos otros archivos de configuración para desplegar el mirror.
ADD config-files .

SHELL [ "/session.sh" ]

# Instalamos el ZPM (no será necesario para versiones a partir de 2023.1)
# Usamos ZPM para instalar config-api
# Cargamos el archivo de configuración simple-config.json con config-api para:
#  - crear la base de datos "myappdata",
#  - añadirmos un mapeo de globales en el namespace "USER" para los globales "demo.*" a la base de datos "myappdata".
# Basicamente, el punto de entrada para instalar tu aplicación de ObjectScript es este. 
# Para este ejemplo cargaremos simple-config.json para crear una base de datos simple y un mapeo de globals.
RUN \
Do $SYSTEM.OBJ.Load("/opt/demo/zpm.xml", "ck") \
zpm "install config-api" \
Set sc = ##class(Api.Config.Services.Loader).Load("/opt/demo/simple-config.json")

# Copiamos el script de arranque del mirror. 
COPY init_mirror.sh /

Creación de la imagen IRIS

El Dockerfile está listo, podemos crear la imagen:

docker build --no-cache --tag mirror-demo:latest .

Esta imagen se utilizará para ejecutar los nodos primarios, los de copias de seguridad y los de informes.

El archivo .env

Los archivos de configuración JSON y docker-compose utilizan variables de entorno. Sus valores se almacenan en un archivo llamado .env. Para este ejemplo, nuestro archivo env es:

APP_NET_SUBNET=172.16.238.0/24
MIRROR_NET_SUBNET=172.16.220.0/24

IRIS_HOST=172.16.238.100
IRIS_PORT=1972
IRIS_VIRTUAL_IP=172.16.238.100

ARBITER_IP=172.16.238.10

MASTER_APP_NET_IP=172.16.238.20
MASTER_MIRROR_NET_IP=172.16.220.20

BACKUP_APP_NET_IP=172.16.238.30
BACKUP_MIRROR_NET_IP=172.16.220.30

REPORT_APP_NET_IP=172.16.238.40
REPORT_MIRROR_NET_IP=172.16.220.40

Preparación del archivo de configuración del primer miembro del mirror

La librería config-api permite configurar un mirror, por lo que debemos crear un archivo de configuración específico para el primer miembro mirrorconfig-files/mirror-master.json

Para mayor comodidad, los comentarios se sitúan directamente en el JSON. Podéis descargar el mirror-master.json sin comentarios aquí.

{
    "Security.Services" : {
        "%Service_Mirror" : {
            "Enabled" : true
        }
    },
    "SYS.MirrorMaster" : {
        "Demo" : {
            "Config" : {
                "Name" : "Demo",                                /* El nombre de nuestro mirror */
                "SystemName" : "master",                        /* El nombre de esta instancia en el mirror */
                "UseSSL" : true,                
                "ArbiterNode" : "${ARBITER_IP}|2188",           /* Dirección IP y puerto para el nodo del arbiter */
                "VirtualAddress" : "${IRIS_VIRTUAL_IP}/24",     /* Dirección IP Virtual IP */
                "VirtualAddressInterface" : "eth0",             /* Interfaz de red usada para la dirección IP Virtual. */
                "MirrorAddress": "${MASTER_MIRROR_NET_IP}",     /* Dirección IP de este nodo en la red privada del mirror */
                "AgentAddress": "${MASTER_APP_NET_IP}"          /* Dirección IP de este nodo (Agent está instalado en la misma máquina) */
            },
            "Databases" : [{                                    /* Lista de bases de datos añadidas al mirror */
                "Directory" : "/usr/irissys/mgr/myappdata/",
                "MirrorDBName" : "MYAPPDATA"
            }],
            "SSLInfo" : {                                       /* Configuración SSL */
                "CAFile" : "/certificates/CA_Server.cer",
                "CertificateFile" : "/certificates/master_server.cer",
                "PrivateKeyFile" : "/certificates/master_server.key",
                "PrivateKeyPassword" : "",
                "PrivateKeyType" : "2"
            }
        }
    }
}

Preparación del archivo de configuración del miembro failover

Creamos un archivo de configuración para los miembros de backup de respaldo (failover) config-files/mirror-backup.json.

Se parece al primer miembro del mirror:

{
    "Security.Services" : {
        "%Service_Mirror" : {
            "Enabled" : true
        }
    },
    "SYS.MirrorFailOver" : {
        "Demo" : {                                          /* Datos del mirror al que se va a unir */
            "Config": {
                "Name" : "Demo",
                "SystemName" : "backup",                    /* Nombre de esta instancia en el mirror */
                "InstanceName" : "IRIS",                    /* Nombre de la instancia de IRIS del primer miembro del mirror */
                "AgentAddress" : "${MASTER_APP_NET_IP}",    /* Dirección IP del Agent del primer miembro del mirror */
                "AgentPort" : "2188",
                "AsyncMember" : false,
                "AsyncMemberType" : ""
            },  
            "Databases" : [{                                /* Base de datos en mirror */
                 "Directory" : "/usr/irissys/mgr/myappdata/"    
            }],
            "LocalInfo" : {
                "VirtualAddressInterface" : "eth0",         /* Interfaz de red usada por la dirección IP Virtual. */
                "MirrorAddress": "${BACKUP_MIRROR_NET_IP}"  /* Dirección IP de este nodo en la red privada del mirror */
            },
            "SSLInfo" : {
                "CAFile" : "/certificates/CA_Server.cer",
                "CertificateFile" : "/certificates/backup_server.cer",
                "PrivateKeyFile" : "/certificates/backup_server.key",
                "PrivateKeyPassword" : "",
                "PrivateKeyType" : "2"
            }
        }
    }
}

Preparación del archivo de configuración del miembro en modo lectura-escritura asíncrono

Es bastante similar al archivo de configuración de failover. Las diferencias son los valores de AsyncMember, AsyncMemberType, y MirrorAddress. Creamos el archivo ./config-files/mirror-report.json:

{
    "Security.Services" : {
        "%Service_Mirror" : {
            "Enabled" : true
        }
    },
    "SYS.MirrorFailOver" : {
        "Demo" : {
            "Config": {
                "Name" : "Demo",
                "SystemName" : "report",
                "InstanceName" : "IRIS",
                "AgentAddress" : "${MASTER_APP_NET_IP}",
                "AgentPort" : "2188",
                "AsyncMember" : true,
                "AsyncMemberType" : "rw"
            },
            "Databases" : [{
                 "Directory" : "/usr/irissys/mgr/myappdata/"
            }],
            "LocalInfo" : {
                "VirtualAddressInterface" : "eth0",
                "MirrorAddress": "${REPORT_MIRROR_NET_IP}"
            },
            "SSLInfo" : {
                "CAFile" : "/certificates/CA_Server.cer",
                "CertificateFile" : "/certificates/report_server.cer",
                "PrivateKeyFile" : "/certificates/report_server.key",
                "PrivateKeyPassword" : "",
                "PrivateKeyType" : "2"
            }
        }
    }
} 

Generación de certificados y configuración de los nodos IRIS y

¡Todos los archivos de configuración están listos!

Ahora tenemos que añadir un script para generar certificados para asegurar la comunicación entre cada uno de los nodos. En el repositorio gen-certificates.sh hay un script listo para ser usado

# sudo es obligatorio debido al uso de chown, chgrp chmod.
sudo ./gen-certificates.sh

Para configurar cada nodo init_mirror.sh se realizará al iniciar los contenedores. Se configurará posteriormente en docker-compose.yml en la sección de comandos command: ["-a", "/init_mirror.sh"] :

#!/bin/bash

# Base de datos usada para probar el mirror.
DATABASE=/usr/irissys/mgr/myappdata

# Directorio que contiene myappdata copiada por el master para restaurar en los otros nodos y hacer el mirror.
BACKUP_FOLDER=/opt/backup

# Archivo de configuración del mirror en json con formato de config-api para el nodo master.
MASTER_CONFIG=/opt/demo/mirror-master.json

# Archivo de configuración del mirror en json con formato de config-api para el nodo de backup.
BACKUP_CONFIG=/opt/demo/mirror-backup.json

# Archivo de configuración del mirror en json con formato de config-api para el nodo asíncrono.
REPORT_CONFIG=/opt/demo/mirror-report.json

# El nombre del mirror...
MIRROR_NAME=DEMO

# Lista de miembros del mirror.
MIRROR_MEMBERS=BACKUP,REPORT

# Ejecutado en el master.
# Carga de la configuración del mirror usando config-api con el archivo /opt/demo/simple-config.json.
# Iniciamos un Job para auto-aceptar otros miembros llamados "backup" y "report" se unan al mirror (evitando la validación manual desde el portal de gestión).
master() {
rm -rf $BACKUP_FOLDER/IRIS.DAT
envsubst < ${MASTER_CONFIG} > ${MASTER_CONFIG}.resolved
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS <<- END
Set sc = ##class(Api.Config.Services.Loader).Load("${MASTER_CONFIG}.resolved")
Set ^log.mirrorconfig(\$i(^log.mirrorconfig)) = \$SYSTEM.Status.GetOneErrorText(sc)
Job ##class(Api.Config.Services.SYS.MirrorMaster).AuthorizeNewMembers("${MIRROR_MEMBERS}","${MIRROR_NAME}",600)
Hang 2
Halt
END
}

# Ejecutado por el master, hacemos un backup de /usr/irissy
make_backup() {
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS "##class(SYS.Database).DismountDatabase(\"${DATABASE}\")"
md5sum ${DATABASE}/IRIS.DAT
cp ${DATABASE}/IRIS.DAT ${BACKUP_FOLDER}/IRIS.TMP
mv ${BACKUP_FOLDER}/IRIS.TMP ${BACKUP_FOLDER}/IRIS.DAT
chmod 777 ${BACKUP_FOLDER}/IRIS.DAT
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS "##class(SYS.Database).MountDatabase(\"${DATABASE}\")"
}

# Restauramos la base datos en mirror "myappdata".  Esta restauración es ejecutada en los nodos "backup" y "report".
restore_backup() {
sleep 5
while [ ! -f $BACKUP_FOLDER/IRIS.DAT ]; do sleep 1; done
sleep 2
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS "##class(SYS.Database).DismountDatabase(\"${DATABASE}\")"
cp $BACKUP_FOLDER/IRIS.DAT $DATABASE/IRIS.DAT
md5sum $DATABASE/IRIS.DAT
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS "##class(SYS.Database).MountDatabase(\"${DATABASE}\")"
}

# Configuramos el miembro "backup"
#  - Cargamos el archivo de configuración /opt/demo/mirror-backup.json si esta instancia es la de backup o 
#    /opt/demo/mirror-report.json si esta instancia es la de report (nodo mirror con lecturas/escrituras asíncronas).
other_node() {
sleep 5
envsubst < $1 > $1.resolved
iris session $ISC_PACKAGE_INSTANCENAME -U %SYS <<- END
Set sc = ##class(Api.Config.Services.Loader).Load("$1.resolved")
Halt
END
}

if [ "$IRIS_MIRROR_ROLE" == "master" ]
then
  master
  make_backup
elif [ "$IRIS_MIRROR_ROLE" == "backup" ]
then
  restore_backup
  other_node $BACKUP_CONFIG
else
  restore_backup
  other_node $REPORT_CONFIG
fi

exit 0

Archivo Docker-compose

Tenemos cuatro contenedores para empezar. El archivo Docker-compose es perfecto para organizar nuestro ejemplo.

version: '3.7'

services:
  arbiter:
    image: containers.intersystems.com/intersystems/arbiter:2021.1.0.215.0
    init: true
    container_name: mirror-demo-arbiter
    command: 
      - /usr/local/etc/irissys/startISCAgent.sh 2188
    networks:
      app_net:
        ipv4_address: ${ARBITER_IP}
    extra_hosts:
      - "master:${MASTER_APP_NET_IP}"
      - "backup:${BACKUP_APP_NET_IP}"
      - "report:${REPORT_APP_NET_IP}"
    cap_add:
      - NET_ADMIN

  master:
    build: .
    image: mirror-demo
    container_name: mirror-demo-master
    networks:
      app_net:
        ipv4_address: ${MASTER_APP_NET_IP}
      mirror_net:
        ipv4_address: ${MASTER_MIRROR_NET_IP}
    environment: 
      - IRIS_MIRROR_ROLE=master
      - WEBGATEWAY_IP=${WEBGATEWAY_IP}
      - MASTER_APP_NET_IP=${MASTER_APP_NET_IP}
      - MASTER_MIRROR_NET_IP=${MASTER_MIRROR_NET_IP}
      - ARBITER_IP=${ARBITER_IP}
      - IRIS_VIRTUAL_IP=${IRIS_VIRTUAL_IP}
    ports:
      - 81:52773
    volumes: 
      - ./backup:/opt/backup
      - ./init_mirror.sh:/init_mirror.sh
      # Mount certificates
      - ./certificates/master_server.cer:/certificates/master_server.cer
      - ./certificates/master_server.key:/certificates/master_server.key
      - ./certificates/CA_Server.cer:/certificates/CA_Server.cer
      #- ~/iris.key:/usr/irissys/mgr/iris.key
    hostname: master
    extra_hosts:
      - "backup:${BACKUP_APP_NET_IP}"
      - "report:${REPORT_APP_NET_IP}"
    cap_add:
      - NET_ADMIN
    command: ["-a", "/init_mirror.sh"]

  backup:
    image: mirror-demo
    container_name: mirror-demo-backup
    networks:
      app_net:
        ipv4_address: ${BACKUP_APP_NET_IP}
      mirror_net:
        ipv4_address: ${BACKUP_MIRROR_NET_IP}
    ports:
      - 82:52773
    environment: 
      - IRIS_MIRROR_ROLE=backup
      - WEBGATEWAY_IP=${WEBGATEWAY_IP}
      - BACKUP_MIRROR_NET_IP=${BACKUP_MIRROR_NET_IP}
      - MASTER_APP_NET_IP=${MASTER_APP_NET_IP}
      - BACKUP_APP_NET_IP=${BACKUP_APP_NET_IP}
    volumes: 
      - ./backup:/opt/backup
      - ./init_mirror.sh:/init_mirror.sh
      # Mount certificates
      - ./certificates/backup_server.cer:/certificates/backup_server.cer
      - ./certificates/backup_server.key:/certificates/backup_server.key
      - ./certificates/CA_Server.cer:/certificates/CA_Server.cer
      #- ~/iris.key:/usr/irissys/mgr/iris.key
    hostname: backup
    extra_hosts:
      - "master:${MASTER_APP_NET_IP}"
      - "report:${REPORT_APP_NET_IP}"
    cap_add:
      - NET_ADMIN
    command: ["-a", "/init_mirror.sh"]

  report:
    image: mirror-demo
    container_name: mirror-demo-report
    networks:
      app_net:
        ipv4_address: ${REPORT_APP_NET_IP}
      mirror_net:
        ipv4_address: ${REPORT_MIRROR_NET_IP}
    ports:
      - 83:52773
    environment: 
      - IRIS_MIRROR_ROLE=report
      - WEBGATEWAY_IP=${WEBGATEWAY_IP}
      - MASTER_APP_NET_IP=${MASTER_APP_NET_IP}
      - REPORT_MIRROR_NET_IP=${REPORT_MIRROR_NET_IP}
      - REPORT_APP_NET_IP=${REPORT_APP_NET_IP}
    volumes: 
      - ./backup:/opt/backup
      - ./init_mirror.sh:/init_mirror.sh
      # Mount certificates
      - ./certificates/report_server.cer:/certificates/report_server.cer
      - ./certificates/report_server.key:/certificates/report_server.key
      - ./certificates/CA_Server.cer:/certificates/CA_Server.cer
      #- ~/iris.key:/usr/irissys/mgr/iris.key
    hostname: report
    extra_hosts:
      - "master:${MASTER_APP_NET_IP}"
      - "backup:${BACKUP_APP_NET_IP}"
    cap_add:
      - NET_ADMIN
    command: ["-a", "/init_mirror.sh"]

networks:
  app_net:
    ipam:
      driver: default
      config:
        - subnet: "${APP_NET_SUBNET}"
  mirror_net:
    ipam:
      driver: default
      config:
        - subnet: "${MIRROR_NET_SUBNET}" 

El archivo docker-compose.yml contiene una gran cantidad de variables de entorno. Observa el tipo de archivo que devuelve en el terminal:

docker-compose config

Despliegue de los contenedores

docker-compose up

Esperamos a que cada instancia tenga su estado correcto en el mirror:

  • nodo maestro (master) con estado Primary.
  • nodo de la copia de seguridad (backup) con estado Backup.
  • nodo de informes (report) con el estado Connected.

Finalmente, deberíais ver estos mensajes en los inicio de sesión de Docker:

mirror-demo-master | 01/09/22-11:02:08:227 (684) 1 [Utility.Event] Becoming primary mirror server
...
mirror-demo-backup | 01/09/22-11:03:06:398 (801) 0 [Utility.Event] Found MASTER as primary, becoming backup
...
mirror-demo-report | 01/09/22-11:03:10:745 (736) 0 [Generic.Event] MirrorClient: Connected to primary: MASTER (ver 4)

También se puede verificar el estado del mirror mediante el portal http://localhost:81/csp/sys/utilhome.csp

Mirror-Status

Acceso a los portales

En Docker-compose mapeamos los puertos 81, 82 y 83 para tener un acceso a cada portal de administración. Esta es la contraseña de acceso predeterminada para todas las instancias:

Prueba

Comprobad el monitor del mirror (portal de administración, este es el usuario y la contraseña predeterminada): http://localhost:81/csp/sys/op/%25CSP.UI.Portal.Mirror.Monitor.zenMirror-Monitor

Verifica la configuración del mirror: http://localhost:81/csp/sys/mgr/%25CSP.UI.Portal.Mirror.EditFailover.zen?$NAMESPACE=%25SYS

Mirror-Configuration

Podemos iniciar una prueba simplemente configurando un global que comience por demo. Recuerda que hemos configurado un mapeo del global demo.* en el namespace USER.

Abrid una sesión del terminal en el servidor primario:

docker exec -it mirror-demo-master irissession iris
Set ^demo.test = $zdt($h,3,1)

Comprobad si los datos están disponibles en el nodo de la copia de seguridad:

docker exec -it mirror-demo-backup irissession iris
Write ^demo.test

Comprobamos si los datos están disponibles en el nodo del informe:

docker exec -it mirror-demo-report irissession iris
Write ^demo.test

¡Bien! Tenemos un entorno de mirror listo, creado por completo programáticamente. Para rizar el rizo, deberíamos añadir un web gateway con https y encriptación entre el web gateway e IRIS, pero lo dejaremos para el próximo artículo.

Espero que este artículo os haya resultado útil si decidís crear vuestro propio script.

Fuentes

El contenido de este artículo está inspirado en:

0
1 246
Artículo Luis Angel Pérez Ramos · nov 22, 2022 5m read

Últimamente me han preguntado en varias ocasiones cómo hacer que el Framework Laravel funcione con InterSystems IRIS Data Platform. Ha pasado un tiempo desde la publicación de este anuncio sobre Laravel e InterSystems Caché. En este artículo mostraremos unas breves instrucciones sobre cómo configurar un proyecto Laravel para usarlo con InterSystems IRIS a través de ODBC.

¿Qué es Laravel?

0
1 144
Artículo Luis Angel Pérez Ramos · oct 28, 2022 8m read

Acceso a Producción Programática

Para editar producciones programáticamente (interfaces) puedes usar una combinación de APIs de Interoperabilidad y consultas SQL.

Namespace actual

Es importante conocer el namespace y la producción en la que estás trabajando.

// Object script 
// El namespace activo se guarda en esta variable
$$$NAMESPACE 
// Imprimir el namespace
Write $$$NAMESPACE
# Python
import iris
# Este método retorna el namespace activo
iris.utils._OriginalNamespace()
# Imprimir el namespace
print(iris.utils._OriginalNamespace())
>>> DEMONSTRACIÓN

Producción activa

También, es importante conocer el nombre de tu producción. Puedes recuperar la producción activa en un namespace usando las siguientes APIs

// ObjectScript
USER>ZN "DEMONSTRATION"
// Obtener la última producción en ejecución
DEMONSTRATION>W ##class(Ens.Director).GetActiveProductionName()
>>> Hospital.HospitalProduction
#  Python
import os
os.environ['IRISNAMESPACE'] = 'DEMONSTRATION'
import iris
active_production = iris.cls('Ens.Director').GetActiveProductionName()
print(active_production)
>>> Hospital.HospitalProduction

Cómo encontrar elementos en una producción

Puedes usar Objectscript o Python para encontrar elementos activos en la producción

1. Consulta SQL para obtener elementos en producción

SELECT Name FROM Ens_Config.Item Where Production = 'Hospital.HospitalProduction'
-- 
['From_Athena_Multi']
['From_Athena_Multi_Router']
['From_Cerner_ADT']
['From_Cerner_ADT_Router']
['From_Cerner_Orders']
['From_Cerner_Orders_Router']
['From_Dictaphone_Results']
['From_Dictaphone_Results_Router']
['From_Lab_Results']
['From_Lab_Results_Router']
['From_Radiology_Results']
['From_Radiology_Results_Router']
['HS.IHE.XDSb.DocumentSource.Operations']
['HS.IHE.XDSb.Repository.Operations']
['To_Cerner_Results']
['To_Dictaphone']
['To_Intellilab']
['To_Lab']
['To_Radiology']
-- 

2. Consulta SQL para obtener elementos activos en producción

SELECT Name, ClassName 
FROM Ens_Config.Item 
WHERE Production = 'Hospital.HospitalProduction' 
  AND Enabled = 1

-- 
Name	                                ClassName
To_Radiology	                        EnsLib.HL7.Operation.FileOperation
To_Lab	                                EnsLib.HL7.Operation.FileOperation
To_Dictaphone	                        EnsLib.HL7.Operation.FileOperation
From_Cerner_ADT	                        EnsLib.HL7.Service.FileService
From_Cerner_ADT_Router	                EnsLib.HL7.MsgRouter.RoutingEngine
From_Radiology_Results_Router	        EnsLib.HL7.MsgRouter.RoutingEngine
From_Lab_Results_Router	                EnsLib.HL7.MsgRouter.RoutingEngine
From_Dictaphone_Results_Router	        EnsLib.HL7.MsgRouter.RoutingEngine
To_Intellilab	                        EnsLib.HL7.Operation.FileOperation
To_Cerner_Results	                    EnsLib.HL7.Operation.FileOperation
From_Cerner_Orders_Router	            EnsLib.HL7.MsgRouter.RoutingEngine
From_Athena_Multi_Router	            EnsLib.HL7.MsgRouter.RoutingEngine
HS.IHE.XDSb.DocumentSource.Operations	HS.IHE.XDSb.DocumentSource.Operations
-- 

3. Acceso por ObjectScript a elementos de la Producción

// ObjectScript 
// Acceso para obtener todos los elementos de una producción activa
// Devuelve la lista de elementos
ClassMethod ListItemsInProduction()
{
    Set productionName =  ##class(Ens.Director).GetActiveProductionName()
    Set items = []
    &sql(Declare curr cursor FOR Select Name into :newId from Ens_Config.Item Where Production = :productionName)
    &sql(OPEN curr)
    For {
        &sql(FETCH curr)
        Quit:SQLCODE
        Do items.%Push(newId)
    }
    &sql(CLOSE curr)
    quit items
}

>>> zw ##class(ISC.SE.ProductionTools).ListItemsInProduction()

["From_Athena_Multi","From_Athena_Multi_Router","From_Cerner_ADT","From_Cerner_ADT_Router","From_Cerner_Orders","From_Cerner_Orders_Router","From_Dictaphone_Results","From_Dictaphone_Results_Router"
,"From_Lab_Results","From_Lab_Results_Router","From_Radiology_Results","From_Radiology_Results_Router","HS.IHE.XDSb.DocumentSource.Operations","HS.IHE.XDSb.Repository.Operations","To_Cerner_Results"
,"To_Dictaphone","To_Intellilab","To_Lab","To_Radiology"]  ; <DYNAMIC ARRAY>
# Python
# Obtención del entorno de datos de los elementos activos de la producción

import os
# Definición de variables de entorno
os.environ['IRISNAMESPACE'] = 'DEMONSTRATION'
import iris

def getActiveProductionItems():
    productionName = iris.cls('Ens.Director').GetActiveProductionName()
    df = iris.sql.exec("SELECT Name FROM Ens_Config.Item Where Production = '{}'".format(productionName))
    return df

production_items_df = getActiveProductionItems().dataframe()

#                                      name
# 0                       From_Athena_Multi
# 1                From_Athena_Multi_Router
# 2                         From_Cerner_ADT
# 3                  From_Cerner_ADT_Router
# 4                      From_Cerner_Orders
# 5               From_Cerner_Orders_Router
# 6                 From_Dictaphone_Results
# 7          From_Dictaphone_Results_Router
# 8                        From_Lab_Results
# 9                 From_Lab_Results_Router
# 10                 From_Radiology_Results
# 11          From_Radiology_Results_Router
# 12  HS.IHE.XDSb.DocumentSource.Operations
# 13      HS.IHE.XDSb.Repository.Operations
# 14                      To_Cerner_Results
# 15                          To_Dictaphone
# 16                          To_Intellilab
# 17                                 To_Lab
# 18                           To_Radiology

Cómo manipular producciones vía API

1. Añadir componentes

// ObjectScript
set productionName = ##class(Ens.Director).GetActiveProductionName()
//crear un nuevo servicio de archivos xml
set classname="EnsLib.XML.FileService"	//clase de este elemento
set name="NewService"			//nombre de la configuración
set item=##class(Ens.Config.Item).%New(classname)

set item.Name=name
set item.Comment = "Test Service"
set item.PoolSize = "1"
set item.Enabled = 1
do item.%Save()
//	
// Abrir la clase de la producción
// set prod="Test.configtest"	//el nombre de la producción se define manualmente
// O
set prod = productionName
set prodObj=##class(Ens.Config.Production).%OpenId(prod)
//se graba el nuevo elemento
set tSC=prodObj.Items.Insert(item)
set tSC=prodObj.SaveToClass(item)
set tSC=prodObj.%Save()

// ELIMINACIÓN del elemento anterior
set tSC = prodObj.RemoveItem(item)
set tSC = prodObj.SaveToClass()
set tSC=prodObj.%Save()
# Python
import os
os.environ['IRISNAMESPACE'] = 'DEMONSTRATION'
import iris
active_production = iris.cls('Ens.Director').GetActiveProductionName()
print("Current Production {}".format(active_production))

# Metadata sobre el componente
classname="EnsLib.XML.FileService"	 # clase de esta elemento
name="NewService"			         # nombre de la configuración
item=iris.cls('Ens.Config.Item')._New(classname) # Creación del nuevo componente
item.Name=name
item.Comment = "Test Service"
item.PoolSize = "1"
item.Enabled = 1
item._Save()

# Se carga la clase de la producción
# prod="Test.configtest"	# el nombre de la producción se define manualmente
# O se usa la producción activa anterior
prod = active_production

prodObj=iris.cls('Ens.Config.Production')._OpenId(prod)
# grabamos la producción después de insertar el elemento en ella
tSC=prodObj.Items.Insert(item)
tSC=prodObj.SaveToClass(item)
tSC=prodObj._Save()

# ELIMINAMOS el elemento anterior
tSC = prodObj.RemoveItem(item)
tSC = prodObj.SaveToClass()
tSC=prodObj._Save()

2. Deshabilitar / Habilitar Componentes

// ObjectScript
set productionName = ##class(Ens.Director).GetActiveProductionName()
set itemName = "My.Inbound.HL7"
// Obligatorio para habilitar el elemento
Set componentName = productionName _ "||" _ itemName _ "|"
// Habilitar or deshabilitar
Set enable = 1 // or 0
Do ##class(Ens.Director).EnableConfigItem(componentName, enable, 1)

/// Habilitar o deshabilitar un ConfigItem en una producción. La producción puede estar activa o no.
/// El argumento pConfigItemName proporciona el nombre del elemento de configuración para habilitar o deshabilitar
/// En el caso de múltiples coincidencias de elementos con el mismo nombre de configuración, si alguno ya está habilitado entonces
///  la opción pEnable=1 no hará nada y la opción pEnable=0 deshabilitará el elemento de la producción activa o, si no está activa, 
/// el primer elemento coincidente que esté habilitado que encuentre.
///   
/// Ver el método Ens.Director.ParseConfigName() para ver la sintáxis del string de especificación del nombre del ConfigItem.
ClassMethod EnableConfigItem(pConfigItemName As %String, pEnable As %Boolean = 1, pDoUpdate As %Boolean = 1)
# Python
import os
os.environ['IRISNAMESPACE'] = 'DEMONSTRATION'
import iris

active_production = iris.cls('Ens.Director').GetActiveProductionName()
item_name = "My.Inbound.HL7"
componentName = active_production + "||" + item_name + "|"

enable = 1 # or 0
iris.cls('Ens.Director').EnableConfigItem(componentName, enable, 1)

Estado de la producción a través de API

// ObjectScript
/// Este método devuelve el estado de la producción por los parámetros de salida.
/// pProductionName: Devuelve el nombre de la producción cuando el estado es en ejecución, suspendida o con problemas.
/// pState: Salidas del estado de la producción. Los valores válidos son:
///          $$$eProductionStateRunning == 1
///          $$$eProductionStateStopped == 2
///          $$$eProductionStateSuspended == 3
///          $$$eProductionStateTroubled == 4
Set sc = ##class(Ens.Director).GetProductionStatus(.productionName, .productionState) 
Write productionName, " -- ", productionState
import os
# Configurar el namespace a las malas
os.environ['IRISNAMESPACE'] = 'DEMONSTRATION'

import iris

# TEST 2 con variables de salida
productionName, productionState = iris.ref('productionName'), iris.ref('productionState')
status = iris.cls('Ens.Director').GetProductionStatus(productionName, productionState) 

print("Status: {}".format(status))
# ver .value
print("Production: {}".format(productionName.value))
# ver .value
print("Production State: {}".format(productionState.value))
0
1 169
Anuncio Luis Angel Pérez Ramos · oct 21, 2022

Quiero anunciar el lanzamiento de algo muy interesante - y revolucionario, de hecho. Puede sonar exagerado, pero no creo que hayáis visto nada como esto, ¡ni si quiera imaginar que sería posible!

Hemos sacado un nuevo módulo JavaScript/Node.js llamado glsdb del que podéis leer todo aquí:

https://github.com/robtweed/glsdb

No obstante, para el propósito de este anuncio, me voy a centrar en una parte de glsdb: sus APIs que abstraen las Clases de IRIS (o Cache) como Objetos JavaScript equivalentes.

Con esto quiero decir que los Objetos de JavaScript serán en realidad ¡Objetos IRIS persistidos en la base de datos! 

0
0 99
Artículo Luis Angel Pérez Ramos · oct 17, 2022 2m read

Acabo de redactar un ejemplo rápido para ayudar a un colega a cargar datos en IRIS desde R usando RJDBC y pensé que sería útil compartirlo aquí para futuras consultas.

Fue bastante sencillo, aparte de que a IRIS no le gusta el uso de puntos "." en los nombres de las columnas; la solución alternativa es simplemente renombrar las columnas. Alguien con más conocimientos que yo en R seguramente pueda ofrecer un enfoque más amplio smiley

# Es necesario un valor válido para el JAVA_HOME antes de cargar la librería (RJDBC)
Sys.setenv(JAVA_HOME="C:\\Java\\jdk-8.0.322.6-hotspot\\jre")
library(RJDBC)
library(dplyr)
# Conexión a IRIS – se requiere la ruta a la librería JAR de InterSystems JDBC JAR de tu instalación
drv <- JDBC("com.intersystems.jdbc.IRISDriver", "C:\\InterSystems\\IRIS\\dev\\java\\lib\\1.8\\intersystems-jdbc-3.3.0.jar","\"")
conn <- dbConnect(drv, "jdbc:IRIS://localhost:1972/USER", "IRIS Username", "IRIS Password")
dbListTables(conn)
# Para mayor confusión, cargar el dataset de IRIS:)
data(iris)
# A IRIS no le gustan los puntos "." en el nombre de las columnas, así que los renombramos. (Probablemente se pueda codificar de una forma más genérica, pero no soy muy bueno con R.)
iris <- iris %>% rename(sepal_length = Sepal.Length, sepal_width = Sepal.Width, petal_length = Petal.Length, petal_width = Petal.Width)

# dbWriteTable/dbGetQuery/dbReadTable funcionan
dbWriteTable(conn, "iris", iris, overwrite = TRUE)
dbGetQuery(conn, "select count(*) from iris")
d <- dbReadTable(conn, "iris")
0
0 127
Artículo Luis Angel Pérez Ramos · sep 29, 2022 2m read

Como sabéis, el namespace EnsDemo de Ensemble ya no está disponible en IRIS.

Esto es algo positivo, ya que IRIS está orientada a la nube y por lo tanto debe ser lo más ligera y rápida posible. La nueva forma de compartir ejemplos de código o módulos es a través de git, integración continua y OpenExchange.

Pero no os preocupéis, somos conscientes de que, en algunos casos, querréis volver a recuperar ejemplos y código presente en EnsDemo, ya sea como fuente de inspiración o para ayudaros a revisar buenas prácticas.

0
0 157