#Mejores prácticas

0 Seguidores · 90 Publicaciones

Sugerencias de prácticas recomendadas sobre cómo desarrollar, probar, implementar y administrar más eficientemente las soluciones en InterSystems Data Platform. 

Artículo Ricardo Paiva · ago 6, 2024 11m read

         

Hola Comunidad,
En este artículo voy a enseñar diferentes usos de InterSystems Embedded Python. Vamos a cubrir los siguientes temas:

  • 1-Visión general de Embedded Python
  • 2-Uso de Embedded Python
    • 2.1 - Uso de una biblioteca Python desde ObjectScript
    • 2.2 - Llamando a las API de InterSystems desde Python
    • 2.3 - Uso conjunto de ObjectScript y Python
  • 3-Uso de las funciones incorporadas de python
  • 4-Módulos o bibliotecas de Python 
  • 5-Casos de uso de Embedded Python
  • 6-Resumen

Empecemos con un resumen

 

1-Resumen de Embedded Python

Embedded Python es una característica de la plataforma de datos de InterSystems IRIS que permite a los desarrolladores de Python obtener un acceso completo y directo a los datos y a la funcionalidad de InterSystems IRIS.

InterSystems IRIS incluye con un potente lenguaje de programación incorporado llamado ObjectScript que se interpreta, compila y ejecuta dentro de la plataforma de datos.

Dado que ObjectScript se ejecuta dentro del contexto de InterSystems IRIS, tiene acceso directo a la memoria y a las llamadas a procedimientos de la plataforma de datos.

Embedded Python es una extensión del lenguaje de programación Python que permite la ejecución de código Python dentro del contexto del proceso InterSystems IRIS.

Dado que tanto ObjectScript como Python operan sobre la misma memoria de objetos, puede decirse que los objetos Python no sólo emulan objetos ObjectScript, sino que son objetos ObjectScript.

La coigualdad de estos lenguajes permite que podáis elegir el lenguaje más apropiado para el trabajo, o el lenguaje con el que os sintáis más cómodos para escribir aplicaciones.

 

2-Uso de Embedded Python

Al utilizar Embedded Python, podéis escribir código en tres modalidades diferentes.

2.1 - Uso de una biblioteca Python desde ObjectScript

En primer lugar, podéis escribir un archivo .py ordinario y llamarlo desde el contexto InterSystems IRIS. En este caso, la plataforma de datos lanzará el proceso de Python y os permitirá importar un módulo llamado IRIS, que asocia automáticamente el proceso de Python al núcleo de IRIS y os proporciona acceso a toda la funcionalidad de ObjectScript desde el contexto de vuestro código Python.

2.2 - Llamando a las API de InterSystems desde Python

En segundo lugar, podéis escribir un código ObjectScript ordinario e instanciar un objeto Python utilizando el paquete %SYS.Python. Este paquete ObjectScript os permite importar módulos y bibliotecas de Python y, a continuación, trabajar con esa base de código utilizando la sintaxis ObjectScript.
El paquete %SYS.Python permite a los desarrolladores de ObjectScript sin ningún conocimiento de Python utilizar el rico ecosistema de bibliotecas Python en su código ObjectScript.

2.3 - Uso conjunto de ObjectScript y Python

En tercer lugar, podéis crear una definición de clase de InterSystems y escribir métodos en Python. Cualquier llamada a ese método lanzará el intérprete de Python. Este método tiene la ventaja de poblar la palabra clave self de ese bloque de código Python con una referencia a la instancia de la clase contenedora. Además, al utilizar Python para escribir métodos de clase en las clases de InterSystems, podéis implementar fácilmente métodos que administren diferentes eventos de entrada de datos en SQL, como la adición de una nueva fila a una tabla.
También permite desarrollar rápidamente procedimientos almacenados personalizados en Python.

Como podéis ver, Embedded Python os permite elegir el lenguaje de programación más adecuado para el trabajo sin sacrificar el rendimiento.

3-Uso de las funciones incorporadas de Python

El intérprete de Python tiene una serie de funciones y tipos incorporados que están siempre disponibles. Se enumeran aquí por orden alfabético.

Funciones incorporadas

A

abs()

aiter()

all()

any()

anext()

ascii()

 

B

bin()

bool()

breakpoint()

bytearray()

bytes()

 

C

callable()

chr()

classmethod()

compile()

complex()

 

D

delattr()

dict()

dir()

divmod()

 

E

enumerate()

eval()

exec()

 

F

filter()

float()

format()

frozenset()

 

G

getattr()

globals()

 

H

hasattr()

hash()

help()

hex()

 

I

id()

input()

int()

isinstance()

issubclass()

iter()

L

len()

list()

locals()

 

M

map()

max()

memoryview()

min()

 

N

next()

 

O

object()

oct()

open()

ord()

 

P

pow()

print()

property()

 

 

 

 

R

range()

repr()

reversed()

round()

 

S

set()

setattr()

slice()

sorted()

staticmethod()

str()

sum()

super()

 

T

tuple()

type()

 

V

vars()

 

Z

zip()

 

_

__import__()

Uso de las funciones incorporadas de python

Para utilizar una función incorporada de python tenemos que importar "builtins" y después podremos llamar a la función

set builtins = ##class(%SYS.Python).Import("builtins")

La función print() de python es en realidad un método del módulo incorporado, por lo que ahora podéis utilizar esta función desde ObjectScript:

USER>do builtins.print("hello world!")
hello world!
USER>set list = builtins.list()
 
USER>zwrite list
list=5@%SYS.Python  ; []  ; 

Del mismo modo, podéis utilizar el método help() para obtener ayuda sobre el objeto lista.

USER>do builtins.help(list)
Help on list object:
class list(object)
 |  list(iterable=(), /)
 |
 |  Built-in mutable sequence.
 |
 |  If no argument is given, the constructor creates a new empty list.
 |  The argument must be an iterable if specified.
 |
 |  Methods defined here:
 |
 |  __add__(self, value, /)
 |      Return self+value.
 |
 |  __contains__(self, key, /)
 |      Return key in self.
 |
 |  __delitem__(self, key, /)
 |      Delete self[key].

 

4-Módulos o bibliotecas python

Algunos módulos o bibliotecas de Python se instalan de forma predeterminada y ya están disponibles para su uso. Utilizando la función help("module" podemos ver estos módulos:
Python libraries list output 1

Instalación de un módulo o biblioteca de Python

Además de estos módulos en Python existen cientos de módulos o bibliotecas, que se pueden consultar en pypi.org(The Python Package Index (PyPI) es un repositorio de software para el lenguaje de programación Python)

Si necesitamos algunas otras bibliotecas, entonces necesitamos instalar las bibliotecas usando el comando intersystems irispip

Por ejemplo, Pandas es una biblioteca de análisis de datos de Python. El siguiente comando utiliza el instalador de paquetes irispip para instalar pandas en un sistema Windows:

C:\InterSystems\IRIS\bin>irispip install --target C:\InterSystems\IRIS\mgr\python pandas

Tened en cuenta que C:\InterSystems será sustituido por el directorio de instalación de Intersystems

5-Casos de uso de Embedded Python

5.1-Imprimir PDF utilizando la biblioteca Reportlab de python

Necesitamos instalar la biblioteca Reportlab usando el comando irispip, luego simplemente crear la función objectcript.

Dada una ubicación del archivo, el siguiente método ObjectScript, CreateSamplePDF(), crea un archivo PDF de muestra y lo guarda en esa ubicación.

Class Demo.PDF
{

ClassMethod CreateSamplePDF(fileloc As%String) As%Status
{
    set canvaslib = ##class(%SYS.Python).Import("reportlab.pdfgen.canvas")
    set canvas = canvaslib.Canvas(fileloc)
    do canvas.drawImage("C:\Sample\isc.png", 150, 600)
    do canvas.drawImage("C:\Sample\python.png", 150, 200)
    do canvas.setFont("Helvetica-Bold", 24)
    do canvas.drawString(25, 450, "InterSystems IRIS & Python. Perfect Together.")
    do canvas.save()
}

}

La primera línea del método importa el archivo canvas.py del subpaquete pdfgen de ReportLab. La segunda línea del código instancia un objeto Canvas y procede a llamar a sus métodos, de forma muy similar a como llamaría a los métodos de cualquier objeto InterSystems IRIS.

A continuación, podéis llamar al método de la forma habitual:.

do ##class(Demo.PDF).CreateSamplePDF("C:\Sample\hello.pdf")

Se genera el siguiente PDF y se guarda en la ubicación especificada:
PDF de una página con el logotipo de InterSystems, el logotipo de Python y el texto: InterSystems IRIS y Python. Juntos somos perfectos.

 

5.2-Generar código QR utilizando la librería Qrcode de Python

Para generar un código QR, necesitamos instalar la librería Qrcode usando el comando irispip, luego usando el siguiente código podemos generar el Código QR:

** 

5.3-Obtener la geolocalización utilizando la biblioteca Folium de Python

Para obtener los datos geográficos, necesitamos instalar la biblioteca Folium utilizando el comando irispip, luego crear la siguiente función de script del objeto:

Class dc.IrisGeoMap.Folium Extends%SwizzleObject
{

// Function to print Latitude, Longitude and address details ClassMethod GetGeoDetails(addr As%String) [ Language = python ]
{
    from geopy.geocoders import Nominatim
    geolocator = Nominatim(user_agent="IrisGeoApp")
    try:
    	location = geolocator.geocode(addr)
    	print("Location:",location.point)
    	print("Address:",location.address)
    	point = location.point
    	print("Latitude:", point.latitude)
    	print("Longitude:", point.longitude)
    except:
    	print("Not able to find location")
}
}

Conectaos a la Terminal IRIS y ejecutad el siguiente código

do ##class(dc.IrisGeoMap.Folium).GetGeoDetails("Cambridge MA 02142")

A continuación se muestra el resultado:

image

 

5.4-Generar y marcar localizaciones en un Mapa interactivo utilizando la Biblioteca de Python Folium

Utilizaremos la misma biblioteca Folium de Python para generar y marcar localizaciones en un Mapa interactivo, Abajo la función de script objeto hará lo deseado :

ClassMethod MarkGeoDetails(addr As%String, filepath As%String) As%Status [ Language = python ]
{
    import folium
    from geopy.geocoders import Nominatim
    
    geolocator = Nominatim(user_agent="IrisGeoMap")
    #split address in order to mark on the map
    locs = addr.split(",")
    if len(locs) == 0:
    	print("Please enter address")
    elif len(locs) == 1:
    	location = geolocator.geocode(locs[0])
    	point = location.point
    	m = folium.Map(location=[point.latitude,point.longitude], tiles="OpenStreetMap", zoom_start=10)
    else:
    	m = folium.Map(location=[20,0], tiles="OpenStreetMap", zoom_start=3)
    
    for loc in locs:
    	try:
    		location = geolocator.geocode(loc)
    		point = location.point
    		folium.Marker(
	    			location=[point.latitude,point.longitude],
	    	  		popup=addr,
	    		).add_to(m)    		
    	except:
    		print("Not able to find location : ",loc) 
    		  
    map_html = m._repr_html_()
    iframe = m.get_root()._repr_html_()
    fullHtml = """
      	     <!DOCTYPE html>
        	    <html>
        	        <head></head>
        	        <body> """
    fullHtml = fullHtml + iframe            
    fullHtml = fullHtml + """                                             
       	        </body>
        	    </html>
    """try:
    	f = open(filepath, "w")
    	f.write(fullHtml)
    	f.close()
    except:
    	print("Not able to write to a file")
}

Conectaos a la Terminal IRIS y llamad a la función MarkGeoDetails

Llamaremos a la función MarkGeoDetails() de la clase dc.IrisGeoMap.Folium.
Esta función requiere dos parámetros:

  1. ubicación/ubicaciones(Podemos pasar varias ubicaciones añadiendo "," entre ellas)
  2. ruta del archivo HTML

Ejecutemos el siguiente comando para marcar Cambridge MA 02142, NY, Londres, EAU, Jeddah, Lahore y Glasgow en el Mapa y guardémoslo como archivo "irisgeomap_locations.html

do ##class(dc.IrisGeoMap.Folium).MarkGeoDetails("Cambridge MA 02142,NY,London,UAE,Jeddah,Lahore,Glasgow","d:\irisgeomap_locations.html")

El código anterior generará el siguiente archivo HTML interactivo:

image

 

5.5-Analítica de datos mediante el uso de la biblioteca Pandas de Python

Necesitamos instalar la biblioteca Pandas usando el comando irispip, entonces podemos usar el siguiente código para ver los datos

 

6-Resumen

InterSystems Embedded Python (IEP) es una potente función que os permite integrar el código Python a la perfección con vuestras aplicaciones de InterSystems. Con IEP, podéis aprovechar las amplias bibliotecas y estructuras disponibles en Python para mejorar la funcionalidad de sus aplicaciones de InterSystems. En este artículo, exploraremos las principales características y ventajas de IEP.

IEP se implementa como un conjunto de bibliotecas que os permiten interactuar con objetos Python y ejecutar código Python desde dentro de las aplicaciones de InterSystems. Esto proporciona una forma sencilla y eficaz de integrar el código Python en vuestras aplicaciones de InterSystems, permitiéndoos realizar análisis de datos, aprendizaje automático, procesamiento del lenguaje natural y otras tareas que pueden resultar difíciles de implementar en InterSystems ObjectScript.

Una de las principales ventajas de utilizar IEP es que proporciona una forma de tender puentes entre los mundos de Python e InterSystems. Esto facilita el uso de los puntos fuertes de ambos lenguajes para crear aplicaciones potentes que combinen lo mejor de ambos mundos.

IEP también proporciona una forma de ampliar la funcionalidad de sus aplicaciones InterSystems aprovechando las capacidades de Python. Esto significa que podéis aprovechar el gran número de bibliotecas y estructura de trabajo disponibles en Python para realizar tareas que pueden resultar difíciles de implementar en InterSystems ObjectScript.

InterSystems Embedded Python proporciona una potente forma de ampliar la funcionalidad de sus aplicaciones de InterSystems aprovechando las capacidades de Python. Al integrar el código Python en sus aplicaciones de InterSystems, podéis aprovechar el gran número de bibliotecas y estructura de trabajo disponibles en Python para realizar tareas complejas que pueden resultar difíciles de implementar en InterSystems ObjectScript.

¡Gracias!

0
0 112
Artículo Alberto Fuentes · ene 11, 2024 2m read

¿Alguna vez habéis estado editando ficheros en VS Code, pero necesitábais comprobar el valor de un global o ejecutar algunos comandos de ObjectScript? Ahora podéis hacerlo, sin necesidad de configuración! Si tenéis la versión 2.10.0 o posterior de la extensiónvscode-objectscript y estáis conectados a InterSystems IRIS 2023.2 o posterior, ahora podéis abrir una conexión del terminal a vuestro servidor, sin importar dónde se encuentre.

Hay tres formas de abrir este nuevo terminal:

1
0 262
Artículo Nancy Martínez · mar 31, 2021 2m read

Este es el ejemplo de un código para visualizar JSON o un objeto dinámico.

Muestra cómo iterar sobre object, get property values y sus rutas.

Class JSON.Test
{

/// do ##class(JSON.Test).Test()
ClassMethod Test()
{
    set json = "{""a"":1,""b"":2,""c"":{""c1"":3,""c2"":4}, ""d"": [5, {""e_e"":6}, 7]}"

    set obj = {}.%FromJSON(json)

    do ..Iterate(obj)
}

ClassMethod Iterate(obj As %DynamicAbstractObject, level = 0, path = "obj")
{
    set indent = $j("", level * 4)
    #dim iterator As %Iterator.Array
    set iterator = obj.%GetIterator()

    while iterator.%GetNext(.key, .value) {
        set type = obj.%GetTypeOf(key)
        write indent, "Key: ", key, !
        write indent, "Type: ", type, !

        if $classname(obj) = "%Library.DynamicArray" {
            set newPath = path _ ".%GetAt(" _ key _ ")"
        } else {
            if $zname(key, 6) = 1 {
                set newPath = path _  "." _ key
            } else {
                set newPath = path _  ".""" _ key _ """"
            }
        }

        write indent, "Path: ", newPath, !
        if $isObject(value) {
            write indent, "Value: ", !
            do ..Iterate(value, level + 1, newPath)
        } else {
            write indent, "Value: ", value, !
        }
        write !
    }
}

}

Esta es la salida después de ejecutar el método de prueba (Test method):

Key: a
Type: number
Path: obj.a
Value: 1

Key: b
Type: number
Path: obj.b
Value: 2

Key: c
Type: object
Path: obj.c
Value:
    Key: c1
    Type: number
    Path: obj.c.c1
    Value: 3

    Key: c2
    Type: number
    Path: obj.c.c2
    Value: 4


Key: d
Type: array
Path: obj.d
Value:
    Key: 0
    Type: number
    Path: obj.d.%GetAt(0)
    Value: 5

    Key: 1
    Type: object
    Path: obj.d.%GetAt(1)
    Value:
        Key: e_e
        Type: number
        Path: obj.d.%GetAt(1)."e_e"
        Value: 6


    Key: 2
    Type: number
    Path: obj.d.%GetAt(2)
    Value: 7
1
0 401
Artículo Luis Angel Pérez Ramos · feb 26, 2024 5m read

He estado desarrollando una aplicación web que utiliza IRIS como back-end. Trabajé en ella con acceso no autenticado. Estoy llegando al punto en el que me gustaría implementarla para los usuarios, pero primero necesito añadir la autenticación. En vez de utilizar la contraseña para la autenticación predeterminada de IRIS (con contraseña), me gustaría que los usuarios iniciaran sesión con el Inicio de Sesión Único (SSO) de mi organización, o con algún otro proveedor de identidad popular como Google o GitHub. He leído que OpenID Connect es un estándar de autenticación común, y que es admitido por IRIS. ¿Cuál es la forma más sencilla de ponerlo en marcha?

Ejemplo 1: una aplicación CSP simple

La documentación muestra una opción bastante sencilla para utilizar una aplicación CSP como cliente de OpenID Connect.

Los pasos para ello son los siguientes:

  1. Configurar el servidor OAuth 2.0 y el cliente en IRIS. Consultad la sección "Configuración de Caché" del estupendo artículo de Daniel Kutac para obtener más información.

  2. Copie la rutina OAUTH2.ZAUTHENTICATE del repositorio de ejemplos en GitHub en el Namespace %SYS y renombrarlo como ZAUTHENTICATE.

  3. Habilitar la autenticación delegada en todo el sistema.

  4. Crear una página de inicio de sesión personalizada que se extienda desde %OAuth2.Login, y anule el método DefineParameters para especificar el nombre de la aplicación OAuth 2.0 y los ámbitos:

Class MyOAuth2.Login Extends %OAuth2.Login
{

ClassMethod DefineParameters(Output application As %String, Output scope As %String, Output responseMode As %String)
{
    Set application="my application name"
    Set scope="openid profile email"
    Set responseMode=..#RESPONSEMODE
    Quit
}

}
  1. Habilitar la aplicación web para la autenticación delegada y establecer la página de inicio de sesión personalizada en MyOAuth2.Login.cls.

  2. Un truco final: Para que la página de inicio de sesión personalizada funcione, el usuario CSPSystem en IRIS necesita que se le conceda específicamente acceso de LECTURA (READ) a la base de datos en la que vive MyOAuth2.Login.cls.

En ese momento, el inicio de sesión debería "simplemente funcionar": visitar una página CSP en esa aplicación web redirigirá a la página de inicio de sesión en el proveedor de identidad. Después de iniciar sesión, el usuario tendrá una sesión CSP autenticada. Su $username será igual a su identificador de sujeto de SSO/Google/GitHub/donde sea, así que puedo utilizar la autorización incorporada de IRIS para determinar a qué cosas dar acceso.

Ejemplo 2: el problema con REST

¿Qué ocurre si la aplicación web utiliza un controlador REST? El proceso anterior no funciona. Si una aplicación web está habilitada para REST, no hay forma de definir una página de inicio de sesión personalizada. He descubierto que se necesitan algunos pasos más para solucionar este problema.

  1. Crear una aplicación web independiente que no tenga REST habilitado. La ruta de esa aplicación debe comenzar con la ruta de la aplicación REST. Por ejemplo, si la aplicación REST se llama "/csp/api", podríamos llamar a esta nueva aplicación "/csp/api/login". Hay que habilitar la autenticación delegada y establecer la página de inicio de sesión personalizada en la página MyOAuth2.Login.cls.

  2. Establecer la Ruta de la Cookie de Sesión en esta nueva aplicación como la misma que la de la aplicación REST: por ejemplo, "/csp/api". Esto permitirá que ambas aplicaciones compartan una sesión CSP.

  3. Añadir una página CSP a esta nueva aplicación que actuará como "página de inicio". Un usuario deberá acceder primero a esta página para establecer su sesión. Este es un ejemplo que redirige a un endpoint en la API REST tras el inicio de sesión:

Class App.Home Extends %CSP.Page
{

ClassMethod OnPage() As %Status [ ServerOnly = 1 ]
{
    &html<;>
    return $$$OK
}

}
  1. Asegurarsese de que la clase del controlador REST tiene el parámetro UseSession anulado a true.
Class API.REST Extends %CSP.REST
{

Parameter UseSession As BOOLEAN = 1;

XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{

}

ClassMethod Test() As %Status
{
    write { "username": ($username) }.%ToJSON()
    return $$$OK
}

}

En este punto, el inicio de sesión en la aplicación REST también "simplemente funcionará". El usuario visitará la página de inicio, será redirigido al inicio de sesión SSO y, finalmente, volverá a la aplicación REST, donde tendrá una sesión CSP autenticada. Hasta donde yo sé, ésta es la forma más sencilla de añadir OpenID Connect a una aplicación IRIS REST.

Otra opción es utilizar la muestra "REST.ZAUTHENTICATE" del repositorio de muestras de seguridad. Esto espera que el front-end adjunte un token de portador OAuth 2.0 a cada solicitud. Sin embargo, no hay una forma definida para que el front-end obtenga este token de acceso. Habrá que implementar ese flujo OAuth en JavaScript (o usar una librería como angular-oauth2-oidc.) También hay que asegurarse de que la aplicación JavaScript y el back-end de IRIS coinciden en todos los elementos de la configuración como el endpoint emisor del servidor de autorización, el id de cliente OAuth 2.0, etc. He descubierto que esto no es una tarea sencilla.

Tengo curiosidad por saber si alguien más utiliza OpenID Connect para autenticar una aplicación IRIS. ¿Existe una forma aún más sencilla? ¿O merece la pena utilizar el enfoque más complicado con tokens de portador? Podéis comentarlo más abajo.

0
0 290
Artículo Luis Angel Pérez Ramos · oct 10, 2023 3m read

Seguramente todos habéis oido hablar de FHIR como la panacea y solución a todos los problemas de interoperabilidad y compatibilidad entre sistemas. Aquí mismo podemos ver a uno de sus clásicos defensores sujetando un recurso FHIR con su mano y disfrutando desaforadamente:

Pero para el resto de los mortales vamos a hacer una pequeña introducción.

¿Qué es FHIR?

2
0 193
InterSystems Official Jose-Tomas Salvador · ene 26, 2024 2m read

Para vuestra comodidad, InterSystems está publicando los pasos de instalación característicos de los sistemas operativos que son compatibles con InterSystems IRIS.

Para Microsoft Windows, consultad por favor la documentación de producto de InterSystems.

El instalador de IRIS detectará si hay un servidor web instalado en la misma máquina, lo que da la opción de tener configurado automáticamente el servidor web.

Todas las instalaciones de Apache requerirán permiso de sudo (recomendado) o de root para instalar el servidor web. Este requisito es compatible con las mejores prácticas recomendadas.

0
0 156
Artículo Luis Angel Pérez Ramos · dic 31, 2023 5m read

csp-log-tutorial

Requisitos previos

Aseguraos de tener git instalado.

Creé una carpeta git dentro del directorio mgr de IRIS. Hice clic derecho en la carpeta git y elegí "Git Bash Here" en el menú contextual.

git clone https://github.com/oliverwilms/csp-log-tutorial.git

Clonad mi repositorio de GitHub csp-log-tutorial si queréis probarlo vosotros mismos.

En este tutorial, describiré cómo intento utilizar los archivos access.log y CSP.log en los pods de WebGateway para rastrear las solicitudes y respuestas.

Mi equipo trabaja con contenedores IRIS que se ejecutan en la plataforma de contenedores Red Hat OpenShift (Kubernetes) en AWS. Implementamos tres pods con el WebGateway que reciben solicitudes a través de un Balanceador de carga de red (Network Load Balancer). Las solicitudes se procesan en una producción de Interoperabilidad que se ejecuta en tres pods de computación. Usamos una producción como banco de mensajes que se ejecuta en pods de datos replicados (mirrored) como un lugar para revisar todos los mensajes procesados por cualquier pod de computación.

Probamos nuestras interfaces usando pods de alimentación para enviar muchos mensajes de solicitud al Balanceador de carga de red. Enviamos 100 000 mensajes y probamos cuánto tiempo llevaría procesar todos los mensajes en el banco de mensajes y registrar las respuestas en los alimentadores. El recuento de mensajes almacenados en el banco de mensajes y las respuestas recibidas por los alimentadores coinciden con el número de mensajes entrantes.

Recientemente nos pidieron que probáramos la tolerancia ante fallos de los pods y de la Zona de Disponibilidad. Borramos pods individuales con o sin fuerza y periodo de gracia. Simulamos un fallo en la zona de disponibilidad al negar todo el tráfico entrante y saliente a una subred (una de las tres zonas de disponibilidad) a través de la consola de AWS mientras los alimentadores envían muchos mensajes de solicitud al Balanceador de carga de red. Ha sido bastante difícil contabilizar todos los mensajes enviados por los alimentadores.

El otro día, mientras un alimentador enviaba 5 000 mensajes, simulamos el fallo de la Zona de disponibilidad. El alimentador recibió 4 933 respuestas. El banco de mensajes almacenó 4 937 mensajes.

Almacenamos access.log y CSP.log en el directorio de datos de los pods de WebGateway. Añadimos esta línea al Dockerfile de nuestra imagen de WebGateway:

RUN sed -i 's|APACHE_LOG_DIR=/var/log/apache2|APACHE_LOG_DIR=/irissys/data/webgateway|g' /etc/apache2/envvars

Configuramos el Nivel de Registro de Eventos (Event Log Level) en Web Gateway en Ev9r.!

captura de pantalla

Creé los subdirectorios data0, data1 y data2 para nuestros tres pods de webgateway. Copié los archivos CSP.log y access.log de nuestros tres pods de webgateway almacenados en volúmenes persistentes:

oc cp iris-webgateway-0:/irissys/data/webgateway/access.log data0/access.log
oc cp iris-webgateway-1:/irissys/data/webgateway/access.log data1/access.log
oc cp iris-webgateway-2:/irissys/data/webgateway/access.log data2/access.log
oc cp iris-webgateway-0:/irissys/data/webgateway/CSP.log data0/CSP.log
oc cp iris-webgateway-1:/irissys/data/webgateway/CSP.log data1/CSP.log
oc cp iris-webgateway-2:/irissys/data/webgateway/CSP.log data2/CSP.log

Termino con tres subdirectorios, cada uno conteniendo archivos access.log y CSP.log.

Contamos el número de solicitudes procesadas en cualquier pod de webgateway usando este comando:

cat access.log | grep InComingOTW | wc -l

Contamos el número de solicitudes y respuestas registradas en CSP.log usando este comando:

cat CSP.log | grep InComingOTW | wc -l

Normalmente esperamos el doble de líneas en CSP.log en comparación con access.log. A veces encontramos más líneas en CSP.log que el doble esperado de líneas en access.log. Recuerdo haber visto menos líneas de las esperadas en CSP.log en comparación con lo que había en access.log al menos una vez. Sospechamos que esto se debió a una respuesta 500 registrada en access.log, que no se registró en CSP.log, correctamente porque el pod de webgateway se terminó.

¿Cómo puedo analizar muchas líneas de solicitudes y explicar lo sucedido?

Creé las clases persistentes otw.wgw.apache y otw.wgw.csp para importar líneas desde access.log y CSP.log. access.log contiene una línea por solicitud e incluye el estado de la respuesta. CSP.log contiene líneas separadas para solicitudes y respuestas.

Abrid el terminal de IRIS e importad las clases en el directorio src desde el repositorio csp-log-tutorial que habíamos clonado anteriormente:

Set src="C:\InterSystems\IRISHealth\mgr\git\csp-log-tutorial\src"
Do $system.OBJ.LoadDir(src,"ck",,1)

Si no tenéis vuestro propio archivo CSP.log para analizar, podéis importar el que se proporciona en el repositorio csp-log-tutorial:

Set pFile="C:\InterSystems\IRISHealth\mgr\git\csp-log-tutorial\wg2_20230314_CSP.log"
Do ##class(otw.wgw.csp).ImportMessages(pFile,.pLines,.pFilter,1)

pLines y pFilter son parámetros de salida que no son tan importantes en este momento. pImport controla si el Extent se elimina antes de importar los datos.

Después de importar los datos podemos ejecutar consultas SQL como esta:

SELECT count(*) FROM otw_wgw.csp where wgEvent='WebGateway.ProcessRequest'

Si el recuento devuelto es un número impar, indica que hay un desajuste entre las solicitudes y las respuestas.

Podemos ejecutar la siguiente consulta para identificar los nombres de archivo en las solicitudes que no tienen una respuesta coincidente:

select zFilename, count(*) from otw_wgw.csp group by zFilename having count(*) = 1

Tened en cuenta que utilizo el método CalcFilename para establecer la propiedad zFilename antes de guardar una línea importada del archivo CSP.log.

También incluí un archivo access.log de muestra, que podemos importar así: Si no tenéis vuestro propio archivo CSP.log para analizar, podéis importar el que se proporciona en el repositorio csp-log-tutorial:

Set pFile="C:\InterSystems\IRISHealth\mgr\git\csp-log-tutorial\wg2_20230314_access.log"
Do ##class(otw.wgw.apache).ImportMessages(pFile,.pLines,.pFilter,1)

Podemos ejecutar esta consulta para obtener el recuento de mensajes relacionados con este tutorial:

SELECT count(*) FROM otw_wgw.apache where Text like '%tutorial%'

¿Qué más me gustaría hacer si encuentro tiempo?

Me gustaría combinar los datos de las tablas apache y csp y posiblemente incluir datos recogidos de los pods de computación.

Creo que puedo utilizar los datos de access.log para determinar cuándo hubo una interrupción en un pod de webgateway.

0
0 108
Artículo Heloisa Paiva · sep 23, 2022 4m read

En este artículo vas a encontrar un sencillo programa con Python en un entorno IRIS y otro sencilloprograma con ObjectScript en un entorno Python. Además, me gustaría compartir algunos de los errores que tuve cuando empecé la implementación de estos códigos.

Python en entorno IRIS

Supongamos, por ejemplo, que estás en un entorno IRIS y quieres resolver un problema que crees más fácil o más eficiente de resolver en Python.

Puedes simplemente cambiar el entorno: crea tu método como cualquier otro, y al final del nombre y sus especificaciones, añade [ Language = python ]:

1
0 917
Artículo Alberto Fuentes · dic 20, 2023 4m read
       Cómo incluir IRIS Data en vuestro almacén de datos de Google Big Query y en vuestras exploraciones de datos de Data Studio. En este artículo utilizaremos Google Cloud Dataflow para conectarnos a nuestro Servicio de InterSystems Cloud SQL y crear un trabajo para persistir los resultados de una consulta de IRIS en Big Query en un intervalo. 
Si tuvisteis la suerte de obtener acceso a Cloud SQL en el Global Summit 2022, como se menciona en "InterSystems IRIS: What's New, What's Next" (InterSystems IRIS: Lo nuevo, lo siguiente), el ejemplo será pan comido, pero se puede realizar con cualquier punto de acceso público o vpc que hayáis provisionado.
 
Provisión de InterSystems Cloud SQL para uso temporal
Para probar InterSystems Cloud SQL es posible que tengáis que hacer algunas llamadas telefónicas o solicitar acceso a través del portal como hice yo, pero es una forma muy rápida de ponerse en marcha en cuestión de segundos para realizar esta demostración o vuestras cargas de trabajo en IRIS.

Al examinar vuestra implementación, podéis dirigiros al panel "External Connections" (Conexiones externas) en la pestaña "Overview" y crear vosotros mismos una URL de conexión y conservar vuestras credenciales. Optamos por permitir acceso público (0.0.0.0/0) al punto de conexión y no cifrarlo.
 
Desde arriba, tendréis que difundir la siguiente información...
ConnectionURL: 

jdbc:IRIS://k8s-c5ce7068-a4244044-265532e16d-2be47d3d6962f6cc.elb.us-east-1.amazonaws.com:1972/USER
User/Pass:
SQLAdmin/Testing12!
DriverClassName:
com.intersystems.jdbc.IRISDriver
 
Configuración de Google Cloud
  • Provisionar un proyecto GCP
gcloud projects create iris-2-datastudio --set-as-default
  • Habilitar Big Query
  • Habilitar DataFlow
  • Habilitar el almacenamiento en la nube
gcloud services enable  bigquery.googleapis.com
gcloud services enable dataflow.googleapis.com
gcloud services enable storage.googleapis.com
  • Crear un cubo de almacenamiento en la nube
gsutil mb gs://iris-2-datastudio
wget https://github.com/intersystems-community/iris-driver-distribution/raw/main/intersystems-jdbc-3.3.0.jar
gsutil cp intersystems-jdbc-3.3.0.jar gs://iris-2-datastudio
  • Crear un conjunto de datos o DataSet de Big Query
bq --location=us mk \
--dataset \
--description "sqlaas to big query" \
iris-2-datastudio:irisdata
  • Crear una tabla de destino en Big Query

Aquí es donde una ventaja súper potente se convierte en algo molesto para nosotros. Big Query puede crear tablas en tiempo real si se le suministra un esquema junto con una carga útil. Esto es genial dentro de pipelines y soluciones pero, en nuestro caso, necesitamos establecer la tabla con antelación. El proceso es sencillo, ya que se puede exportar un archivo CSV desde la base de datos IRIS con bastante facilidad con herramientas como DBeaver etc. y, cuando se tenga, se puede invocar el diálogo "Create table" (crear tabla) debajo del conjunto de datos que se creó y utilizar el archivo CSV para crear la tabla. Hay que asegurarse de tener marcada la opción "auto generate schema" ("autogenerar esquema") en la parte inferior del cuadro de diálogo. Esto debería completar la configuración de Google Cloud, y deberíamos estar listos para configurar y ejecutar nuestro trabajo Dataflow.

Google Dataflow Job

Si seguisteis los pasos anteriores, deberíais tener lo siguiente en vuestro inventario para ejecutar el trabajo para leer vuestros datos de InterSystems IRIS e ingerirlos en Google Big Query utilizando Google Dataflow.

En Google Cloud Console, id a Dataflow y seleccionad "Create Job from Template" (Crear trabajo a partir de plantilla)


Esta es una imagen bastante innecesaria sobre cómo rellenar un formulario con los requisitos previos generados, pero destaca la fuente de los componentes...!

 
 ... para completarlo, aseguraos de expandir la sección inferior y facilitar vuestras credenciales para IRIS.

Para los que encontrasteis esas capturas de pantalla ofensivas a vuestra inteligencia, esta es la ruta alternativa a seguir para manteneros en vuestra zona de confort en la CLI para ejecutar el trabajo:

gcloud dataflow jobs run iris-2-bq-dataflow \
--gcs-location gs://dataflow-templates-us-central1/latest/Jdbc_to_BigQuery \
--region us-central1 --num-workers 2 \
--staging-location gs://iris-2-datastudio/tmp \
--parameters connectionURL=jdbc:IRIS://k8s-c5ce7068-a4244044-265532e16d-2be47d3d6962f6cc.elb.us-east-1.amazonaws.com:1972/USER,driverClassName=com.intersystems.jdbc.IRISDriver,query=SELECT TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, SELF_REFERENCING_COLUMN_NAME, REFERENCE_GENERATION, USER_DEFINED_TYPE_CATALOG, USER_DEFINED_TYPE_SCHEMA, USER_DEFINED_TYPE_NAME, IS_INSERTABLE_INTO, IS_TYPED, CLASSNAME, DESCRIPTION, OWNER, IS_SHARDED FROM INFORMATION_SCHEMA.TABLES;,outputTable=iris-2-datastudio:irisdata.dataflowtable,driverJars=gs://iris-2-datastudio/intersystems-jdbc-3.3.0.jar,bigQueryLoadingTemporaryDirectory=gs://iris-2-datastudio/input,username=SQLAdmin,password=Testing12!

Una vez que hayáis iniciado vuestra tarea, podéis disfrutar de la gloria de un trabajo bien hecho:

Resultados

Echemos un vistazo a nuestros datos de origen y a la consulta en InterSystems Cloud SQL...

...y, después, examinando los resultados en Big Query, parece que, de hecho, tenemos InterSystems IRIS Data en Big Query.


Cuando tengamos los datos en Big Query, es sencillo incluir nuestros datos de IRIS en Data Studio, seleccionando Big Query como fuente de datos... a este ejemplo de abajo le falta algo de estilo, pero se pueden ver rápidamente los datos de IRIS listos para su manipulación en vuestros proyectos en Data Studio.


 

0
0 118
Artículo Alberto Fuentes · dic 7, 2023 4m read

Introducción

Con frecuencia nos encontramos con problemas de conectividad en las implementaciones de HealthShare (HS) en Microsoft Azure que tienen varios componentes de HealthShare (instancias o namespaces) instalados en la misma máquina virtual, especialmente cuando es necesario comunicarse con otros componentes de HS mientras se utiliza el balanceador de carga interno de Azure (ILB) para proporcionar la funcionalidad VIP (Virtual IP) de Mirroring. Los detalles sobre cómo y por qué se usa un balanceador de carga con Mirroring los podéis encontrar en este artículo de la Comunidad.

Según la documentación del Balanceador de Carga de Azure, el comportamiento predeterminado es el siguiente:

0
0 128
Artículo Daniel Aguilar · nov 25, 2023 1m read

Preguntas frecuentes de InterSystems

Puedes establecer el tamaño máximo de la base de datos IRISTemp en el arranque de IRIS configurando el parámetro MaxIRISTempSizeAtStart.

Después de configurarlo, el sistema truncará IRISTemp al valor establecido (MB) en el siguiente inicio de IRIS. Si el tamaño actual es inferior al especificado MaxIRISTempSizeAtStart, no realizará el truncado. Si se especifica el valor 0 no se realizará el truncado nunca.

Puedes configurarlo en el siguiente menú.

1
0 94
InterSystems Official Luis Angel Pérez Ramos · mar 27, 2023 3m read

Me gustaría adelantaros una mejora sobre cómo generamos e invocamos el código de los métodos en IRIS 2023.1.

Una clase en IRIS se compone de dos componentes de runtime principales:

  1. Descriptor de Clase (Class Descriptor) - Una lista de métodos muy optimizada, propiedades, parámetros de clase que configura y parametriza la clase, junto con atributos asociados con cada uno de estos, por ejemplo configuración pública/privada.
  2. Código de ObjectScript (ObjectScript code) - Un conjunto de rutinas que contienen el código de ObjectScript para ser ejecutado cuando se invoca un método.
1
0 145
Artículo Alberto Fuentes · jul 19, 2023 3m read

Este artículo es un sencillo ejemplo para probar SqlDatabaseChain pidiéndole a OpenAI cierta información y que escriba consultas SQL sobre una base de datos IRIS.

Quizá despierte el interés de alguno de vosotros.

Muchas gracias a sqlalchemy-iris (autor @Dmitry Maslennikov). Ese proyecto ha sido indispensable para esta prueba. 

El script de este artículo usa la API de OpenAI así que tenedlo en cuenta para no compartir la información de vuestras tablas externamente en el caso de que no queráis hacerlo. Podría llegar a implementarse un modelo local en caso que lo necesitaseis.

1
0 489
Artículo Luis Angel Pérez Ramos · jul 11, 2023 3m read

¡Hola Comunidad!

El otro día vi un artículo sobre el uso del paquete %ZEN cuando se trabaja con JSON y he decidido escribir un artículo para describir un enfoque más actualizado. Hace no mucho se dio el paso de usar %ZEN.Auxiliary.* a clases JSON dedicadas. Esto permite trabajar con JSONs de forma más orgánica.

Llegados a este punto hay básicamente 3 clases principales para trabajar con JSON:

  • %Library.DynamicObject - proporciona una manera simple y eficiente de encapsular y trabajar con documentos JSON estándar. También nos da la posibilidad de, en vez de escribir el código habitual para crear la instancia de una clase como
set obj = ##class(%Library.DynamicObject).%New()

usar la siguiente sintaxis

set obj = {}
  • %Library.DynamicArray - proporciona una manera simple y eficiente de encapsular y trabajar con matrices JSON estándar. Con los arrays se puede usar el mismo enfoque que con los objetos, lo que significa que o bien se puede crear una  instancia de la clase
set array = ##class(%DynamicArray).%New()

o se puede hacer usando corchetes []

set array = []
  • %JSON.Adaptor es una utilidad para mapear objetos de ObjectScript (registrados, serial o persistentes) a textos JSON o entidades dinámicas.
1
0 239
Artículo Alberto Fuentes · oct 18, 2023 3m read

Hoy os traigo otro ejemplo de aplicación de LangChain.

Inicialmente buscaba generar una "chain" o cadena para lograr hacer búsquedas dinámicas en la documentación en HTML, pero al final resultó más sencillo utilizar la versión en PDF de la documentación .

Crear un nuevo entorno virtual

mkdir chainpdf

cd chainpdf

python -m venv .

scripts\activate 

pip install openai
pip install langchain
pip install wget
pip install lancedb
pip install tiktoken
pip install pypdf

set OPENAI_API_KEY=[ Your OpenAI Key ]

python

Preparar los documentos

0
0 323
Artículo Muhammad Waseem · dic 1, 2021 3m read

En este artículo explicaré cómo, mediante programación, creé el usuario, concedí privilegios, habilité/deshabilité y autentifiqué/invalidé una aplicación web en mi aplicación Data_APP_Security (https://openexchange.intersystems.com/package/Data_APP_Security)
 

Empecemos por la autentificación

La autenticación verifica la identidad de cualquier usuario o entidad que intente conectarse a InterSystems IRIS®. Como se dice a menudo, la autentificación es la forma de demostrar que eres quien dices ser.

1
0 289
Artículo Alberto Fuentes · oct 5, 2023 9m read

En el vasto y variado mercado de las bases de datos SQL, InterSystems IRIS destaca como una plataforma que va mucho más allá de SQL, ofreciendo una experiencia multimodelo perfecta, y siendo compatible con un amplio conjunto de paradigmas de desarrollo. Especialmente el avanzado motor objeto-relacional ha ayudado a organizaciones a utilizar el enfoque de desarrollo más adecuado para cada una de sus cargas de trabajo intensivas en datos; por ejemplo, ingerir datos a través de Objetos y consultarlos simultáneamente mediante SQL. Las Clases Persistentes corresponden a tablas SQL, sus propiedades a columnas de la tabla y se accede fácilmente a la lógica de negocio utilizando Funciones Definidas por el Usuario o Procedimientos Almacenados. En este artículo, nos centraremos un poco en la magia que se encuentra justo debajo de la superficie y discutiremos cómo puede afectar vuestras prácticas de desarrollo e implementación. Esta es un área del producto que tenemos planificado evolucionar y mejorar, así que no dudéis en compartir vuestras opiniones y experiencias en los comentarios al artículo.

Cómo guardar la Definición de Almacenamiento

Escribir una nueva lógica de negocio es fácil, y suponiendo que tenéis APIs y especificaciones bien definidas, también lo es adaptarla o extenderla, generalmente. Pero cuando no se trata solo de lógica de negocio, sino que también involucra datos persistentes, cualquier cambio que se realice desde la versión inicial deberá poder utilizar los datos que se ingirieron a través de esa versión anterior.

En InterSystems IRIS, los datos y el código coexisten en un único motor de alto rendimiento, sin las numerosas capas de abstracción que se pueden encontrar en otros frameworks de programación 3GL o 4GL. Esto significa que solo hay un mapeo muy fino y transparente para convertir las propiedades de una clase a posiciones de $list en un nodo global por fila de datos cuando se utiliza el almacenamiento predeterminado. Si se añaden o eliminan propiedades, no se quiere que los datos de una propiedad eliminada aparezcan bajo una nueva propiedad. De este mapeo de las propiedades de clase es de lo que se encarga la Definición de Almacenamiento, un bloque XML algo críptico que se puede ver al final de la definición de clase. La primera vez que se compila una clase, se genera una nueva Definición de Almacenamiento basada en las propiedades y parámetros de la clase. Cuando se hacen cambios en la definición de clase, en el momento de la recompilación esos cambios se reconcilian con la Definición de Almacenamiento existente y se modifica para que siga siendo compatible con los datos existentes. Así que mientras uno se arriesga a refactorizar sus clases, la Definición de Almacenamiento considera cuidadosamente su creatividad anterior y se asegura de que tanto los datos antiguos como los nuevos sigan siendo accesibles. A esto le llamamos evolución del esquema.

En la mayoría de las otras bases de datos SQL, el almacenamiento físico de las tablas es mucho más opaco, si es que es visible, y los cambios solo se pueden realizar mediante sentencias ALTER TABLE. Son comandos DLL (Lenguaje de Definición de Datos) estándar, pero generalmente son mucho menos expresivos de lo que se puede lograr modificando directamente una definición de clase y código de procedimiento en IRIS. 

En InterSystems, nos esforzamos para ofrecer a los desarrolladores de IRIS la capacidad de separar de manera limpia su código y sus datos, ya que esto es crucial para garantizar un empaquetado e implementación sencilla de sus aplicaciones. La Definición de Almacenamiento juega un papel único en esto, ya que captura cómo se mapea uno al otro. Por eso, merece la pena analizarla más de cerca en el contexto de las prácticas generales de desarrollo y en particular en los flujos de CI/CD (Integración Continua/Entrega Continua).

Exportando a UDL

En el siglo actual, la administración del código fuente se basa en archivos, así que veamos primero el formato principal de exportación de archivos de IRIS. El Lenguaje de Descripción Universal (UDL) es, como su nombre indica, un formato de archivos universal para cualquier código que se escriba en InterSystems IRIS. Es el formato de exportación predeterminado cuando se trabaja con el plug-in ObjectScript-VS Code y genera archivos de fácil lectura que se asemejan casi exactamente a lo que se vería en un Entorno de Desarrollo Integrado (IDE), con un archivo .cls individual para cada clase (tabla) en la aplicación. Se puede utilizar $SYSTEM.OBJ.Export() para crear archivos UDL explícitamente, o simplemente aprovechar la integración con VS Code.

Desde la época de Studio, es posible que también recordéis un formato XML que capturaba la misma información que UDL y permitía agrupar varias clases en una sola exportación. Si bien esta última parte es conveniente en algunos escenarios, es mucho menos práctica para leer y rastrear diferencias entre versiones, por lo que lo ignoraremos por ahora.

Como UDL está diseñado para capturar todo lo que IRIS puede expresar sobre una clase, incluirá todos los elementos de una definición de clase, incluida la Definición de Almacenamiento completa. Al importar una definición de clase que ya incluye una Definición de Almacenamiento, IRIS verificará si esa Definición de Almacenamiento cubre todas las propiedades e índices de la clase y, en caso afirmativo, la tomará tal cual y sobrescribirá la Definición de Almacenamiento anterior para esa clase. Esto hace que UDL sea un formato práctico para la gestión de versiones de clases y sus Definiciones de Almacenamiento, ya que mantiene esa compatibilidad hacia atrás para los datos ingresados mediante versiones anteriores de la clase, sin importar dónde se implemente. 

Si eres un desarrollador aplicado, es posible que te preguntes si estas Definiciones de Almacenamiento siguen creciendo y si esta "carga" debe llevarse indefinidamente. El propósito de las Definiciones de Almacenamiento es preservar la compatibilidad con los datos preexistentes, por lo que si sabes que no hay ninguno de ellos y quieres deshacerte de una genealogía larga, puedes "reiniciar" tu Definición de Almacenamiento eliminándola de la definición de clase y permitiendo que el compilador de clase la genere de nuevo. Por ejemplo, puedes usar esto para aprovechar prácticas recomendadas más recientes, como el uso de Conjuntos de extensión, que implementan nombres globales hash y separan cada índice en su propio global, mejorando la eficiencia a bajo nivel. Para garantizar la compatibilidad con versiones anteriores dentro de las aplicaciones de los clientes, no podemos cambiar universalmente dichos valores predeterminados en la superclase %Persistent (aunque los aplicaremos al crear una tabla desde cero utilizando el comando DDL CREATE TABLE), por lo que vale la pena realizar una revisión periódica de tus clases y tu almacenamiento. También es posible editar directamente el XML de la Definición de Almacenamiento, pero los usuarios deben tener extrema precaución, ya que esto puede hacer que los datos existentes sean inaccesibles.

Hasta ahora, todo bien. Las Definiciones de Almacenamiento ofrecen un mapeo inteligente entre tus clases y se adaptan automáticamente a medida que el esquema evoluciona. ¿Qué más hay ahí?

¿Estático vs. Estadísticas?

Como probablemente sabes, el motor SQL de InterSystems IRIS hace uso avanzado de las estadísticas de tablas para identificar el plan de consulta óptimo para cualquier sentencia que el usuario ejecute. Las estadísticas de tablas incluyen métricas sobre el tamaño de una tabla, cómo se distribuyen los valores dentro de una columna y mucho más. Esta información ayuda al optimizador SQL de IRIS a decidir qué índice es más beneficioso, en qué orden unir tablas, etcétera. Así que, intuitivamente, cuanto más actualizadas estén las estadísticas, habrá más posibilidades de obtener planes de consulta óptimos. Desafortunadamente, hasta que introdujimos el muestreo rápido de bloques en IRIS 2021.2, recopilar estadísticas precisas de las tablas era una operación computacionalmente costosa. Por lo tanto, cuando los clientes implementaban la misma aplicación en muchos entornos en los que los patrones de datos eran en gran medida los mismos, tenía sentido considerar las estadísticas de las tablas como parte del código de la aplicación e incluirlas con las definiciones de las tablas.

Por eso en IRIS actualmente las estadísticas de las tablas se encuentran integradas dentro de la Definición de Almacenamiento. Cuando se recopilan estadísticas de tablas mediante una llamada manual a TUNE TABLE o de forma implícita mediante una consulta (ver más abajo), las nuevas estadísticas se escriben en la Definición de Almacenamiento, y los planes de consulta existentes para esta tabla se invalidan para que puedan aprovechar las nuevas estadísticas en la próxima ejecución. Como son parte de la Definición de Almacenamiento, estas estadísticas formarán parte de las exportaciones de clase UDL y, por lo tanto, pueden terminar en el repositorio de código fuente. En el caso de las estadísticas cuidadosamente revisadas para una aplicación empaquetada, esto es aconsejable, ya que se querrá que estas estadísticas específicas impulsen la generación de planes de consulta para todas las implementaciones de la aplicación.

Desde la versión 2021.2, IRIS recopila automáticamente estadísticas de tabla al inicio del plan de consultas cuando consulta una tabla que no tiene ningún tipo de estadísticas y es apta para el muestreo rápido de bloques. En nuestras pruebas, los beneficios de trabajar con estadísticas actualizadas frente a no tener estadísticas claramente superaron el coste de recopilar estadísticas en tiempo real. Sin embargo, para algunos clientes, esto ha tenido el desafortunado efecto secundario de que las estadísticas recopiladas automáticamente en la instancia del desarrollador terminan en la Definición de Almacenamiento en el sistema de control del código fuente y, finalmente, en la aplicación empaquetada. Obviamente, los datos en ese entorno de desarrollo y, por tanto, las estadísticas sobre ellos, pueden no ser representativas para una implementación real del cliente y conducir a planes de consulta no óptimos.

Este escenario es fácil de evitar. Las estadísticas de tablas se pueden excluir de la exportación de la definición de clase utilizando el calificador /exportselectivity=0 al llamar a $SYSTEM.OBJ.Export(). La configuración predeterminada del sistema para esta bandera se puede establecer utilizando [$SYSTEM.OBJ.SetQualifiers("/exportselectivity=0]. Luego se puede dejar que la recopilación automática, en la implementación final, recoja estadísticas representativas, haga que la recopilación explícita de estadísticas sea parte del proceso de implementación, lo que sobrescribirá cualquier cosa que pueda haber sido empaquetada con la aplicación, o administre las estadísticas de tablas por separado mediante sus propias funciones de importación/exportación: $SYSTEM.SQL.Stats.Table.Export() y Import().  

A largo plazo, tenemos la intención de trasladar las estadísticas de tablas para que convivan con los datos en vez de ser parte del código, y diferenciar de manera más clara entre las estadísticas configuradas explícitamente por un desarrollador y las recolectadas de los datos reales. Además, estamos planeando automatizar más el proceso de actualizar periódicamente esas estadísticas, en función de cuánto cambien los datos de la tabla con el tiempo. 

Conclusiones

En este artículo, hemos explicado el papel de una Definición de Almacenamiento en el motor IRIS ObjectRelational, cómo es compatible con la evolución del esquema y lo que implica incluirla en el sistema de control de código fuente. También hemos descrito por qué las estadísticas de tablas se almacenan actualmente en esa Definición de Almacenamiento y hemos sugerido prácticas de desarrollo para asegurarnos de que las implementaciones de las aplicaciones obtienen estadísticas que sean representativas para los datos reales del cliente. Como se mencionó anteriormente, estamos planeando mejorar aún más estas funciones, por lo que esperamos vuestros comentarios sobre la funcionalidad actual y la planificada, y mejoraremos nuestro diseño según corresponda.

0
0 170
Artículo Pierre-Yves Duquesnoy · sep 18, 2023 10m read

Power BI

Conectar a una Fuente de Datos

Para conectarse a AtScale, utilizaremos la base de datos de SQL Server Analysis Services. Vamos a abrirla en el editor de Power Query. Para hacerlo, hay que seleccionar Transform Data en la pestaña Home.

En la ventana que aparece, id a Home,New Source y seleccionad Analysis Services.

1
0 297
Artículo Alberto Fuentes · ago 9, 2023 5m read

Cada vez es más común ver coloridas insignias o badges en el archivo README.MD, con información útil sobre un proyecto en repositorios como GitHub, GitLab y otros.

Por ejemplo:

imageimage

Incluso la iniciativa sobre calidad del código que está en marcha en la comunidad actualmente también ofrece su propia insignia. Esta insignia muestra el estado de validación de código del proyecto.

Para incluirla, debes añadir la siguiente línea en el README.MD:

 [![Quality Gate Status](https://community.objectscriptquality.com/api/project_badges/measure?project=intersystems_iris_community%2Fappmsw-zpm-shields&metric=alert_status)](https://community.objectscriptquality.com/dashboard?id=intersystems_iris_community%2Fappmsw-zpm-shields)

Y en el directorio del proyecto en GitHub /.github/workflows/, añadir el archivo objectscript-quality.yml. Tras eso, podrás ver esta insignia:

image

Existen diferentes servicios que otorgan estas insignias.

Un ejemplo es Shield.io Simplifica la creación de enlaces, incluyendo Markdown image

Aquí tenéis ejemplos de insignias excelentes que se han utilizado en algunos proyectos.

A medida que el proyecto del gestor de paquetes ZPM crece, los recursos necesarios para almacenar los módulos de los diferentes paquetes también crecen.

Cada vez es más frecuente que necesites información detallada de un paquete sin tener que abrir los archivos del proyecto, preferiblemente visible de forma sencilla en la primera página. Datos como:

  • ¿Qué versión del proyecto se almacena en el repositorio de paquetes? Necesito verlo sin abrir el archivo module.xml
  • ¿Cómo se relaciona esa versión con la que está en el repositorio público? ¿Es el momento de actualizar la versión o no?
  • ¿Qué puertos se mapean en la configuración de docker?

Todo esa información se puede consultar con insignias a través del servicio shields.io.

Mostrar la versión del proyecto zpm tomado del archivo module.xml

image

![Repo-GitHub](https://img.shields.io/badge/dynamic/xml?color=gold&label=GitHub%20module.xml&prefix=ver.&query=%2F%2FVersion&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsergeymi37%2Fzapm%2Fmaster%2Fmodule.xml)

Se puede complicar el enlace añadiendo la posibilidad de hacer click para abrir el archivo correspondiente module.xml:

[![Repo-GitHub](https://img.shields.io/badge/dynamic/xml?color=gold&label=GitHub%20module.xml&prefix=ver.&query=%2F%2FVersion&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsergeymi37%2Fzapm%2Fmaster%2Fmodule.xml)](https://raw.githubusercontent.com/sergeymi37/zapm/master/module.xml)

Mostrar la versión del proyecto zpm tomado del servicio

image

![OEX-zapm](https://img.shields.io/badge/dynamic/json?url=https:%2F%2Fpm.community.intersystems.com%2Fpackages%2Fzapm%2F&label=ZPM-pm.community.intersystems.com&query=$.version&color=green&prefix=zapm)

Ejemplo de un enlace con una solicitud de un servicio:

[![OEX-zapm](https://img.shields.io/badge/dynamic/json?url=https:%2F%2Fpm.community.intersystems.com%2Fpackages%2Fzapm%2F&label=ZPM-pm.community.intersystems.com&query=$.version&color=green&prefix=zapm)](https://pm.community.intersystems.com/packages/zapm)

Mostrar qué puertos son mapeados en la configuración de docker

image

 ![Docker-ports](https://img.shields.io/badge/dynamic/yaml?color=blue&label=docker-compose&prefix=ports%20-%20&query=%24.services.iris.ports&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsergeymi37%2Fzapm%2Fmaster%2Fdocker-compose.yml)

Por ejemplo, un link para abrir el fichero docker-compose.yml:

[![Docker-ports](https://img.shields.io/badge/dynamic/yaml?color=blue&label=docker-compose&prefix=ports%20-%20&query=%24.services.iris.ports&url=https%3A%2F%2Fraw.githubusercontent.com%2Fsergeymi37%2Fzapm%2Fmaster%2Fdocker-compose.yml)](https://raw.githubusercontent.com/sergeymi37/zapm/master/docker-compose.yml)

Sin embargo, para métricas más complejas o sus combinaciones, e incluso para proyectos dentro de una red local privada, se ha decidido crear este servicio REST, que muestra la versión del módulo ZPM desde el archivo del repositorio y desde https://pm.community.intersystems.com/

Tras la instalación, se tiene un servicio zpm-shields al que hay que proporcionar acceso sin autenticación.

Enlaces para obtener un archivo svg que puede ser insertado en el README.MD:

Por ejemplo:

![Repo](http://localhost:52773/zpm-shields/repo/mode?module=https:%2F%2Fgithub.com%2FSergeyMi37%2Fzapm&color=blue)

donde los valores del parámetro son: zpm-shields/repo - extracción desde el archivo module.xml para obtener la versión module - enlace al repositorio color - por ejemplo #00987

![Registry](http://localhost:52773/zpm-shields/registry/mode?project=appmsw-dbdeploy&color=gold)

donde los valores del parámetro son: zpm-shields/registry - obtener la versión por petición desde el servicio project - nombre del proyecto

![Repo+Registry](http://localhost:52773/zpm-shields/both/mode?module=sergeymi37%2Fappmsw-dbdeploy&project=appmsw-dbdeploy&color=FFA07A)

donde los valores del parámetro son: zpm-shields/both - extracción desde el archivo module.xml, obtiene la versión desde el servicio project - nombre del proyecto module - enlace al repositorio

El servicio también se puede usar para recursos ZPM locales. Para hacerlo, hay que utilizar la ruta completa del repositorio local y un registro privado.

A mí me gustan mucho estas insignias. Y creo que también podrían seros útiles a vosotros.

0
1 195
Artículo Ricardo Paiva · jul 25, 2023 9m read

Antes de empezar a hablar de bases de datos y de los distintos modelos de datos que existen, primero explicaré qué es una base de datos y cómo se utiliza.

Una base de datos es una colección organizada de datos, almacenados y accesibles de forma electrónica. Se utiliza para almacenar y recuperar datos estructurados, semiestructurados o sin procesar, que normalmente están relacionados con un tema o una actividad.

En el corazón de toda base de datos hay al menos un modelo utilizado para describir sus datos. Y según el modelo que utilice, una base de datos puede tener características ligeramente diferentes y almacenar distintos tipos de datos.

Para escribir, recuperar, modificar, ordenar, transformar o imprimir la información de la base de datos, se utiliza un software llamado Sistema de Gestión de Bases de Datos (DBMS, por sus siglas en inglés).

El tamaño, la capacidad y el rendimiento de las bases de datos y sus respectivos DBMS ha aumentado de forma significativa. Esto ha sido posible gracias a los avances tecnológicos en varios ámbitos, como los procesadores, la memoria y almacenamiento de los ordenadores y las redes informáticas. En general, el desarrollo de la tecnología de bases de datos puede dividirse en cuatro generaciones basadas en los modelos o la estructura de los datos: navegacional, relacional, de objetos y post-relacionales.

A diferencia de las tres primeras generaciones, que se caracterizan por un modelo de datos específico, la cuarta generación incluye muchas bases de datos diferentes basadas en distintos modelos, como columnas, gráficos, documentos, componentes, multidimensiones, clave-valor, almacenamiento en memoria, etc. Todas estas bases de datos están unidas por un único nombre NoSQL (NoSQL o ahora es más preciso decir No sólo SQL).

Además, ahora aparece una nueva clase, que se llama NewSQL. Se trata de bases de datos relacionales modernas que tienen como objetivo ofrecer el mismo rendimiento escalable que los sistemas NoSQL para cargas de trabajo de procesamiento de transacciones en línea (lectura-escritura), a la vez que utilizan SQL y mantienen ACID.

Por casualidad, entre estas bases de datos de cuarta generación se encuentran las que admiten los modelos de datos múltiples mencionados anteriormente. Se llaman bases de datos multimodelo. Un buen ejemplo de este tipo de base de datos es InterSystems IRIS. Por ello, la utilizaré para dar ejemplos de distintos tipos de modelos, según vayan presentándose.

<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" src="https://www.youtube.com/embed/EAOfw09xTrM" title="YouTube video player" width="560" height="315" frameborder="0"></iframe>

La primera generación de bases de datos utilizaba modelos jerárquicos o modelos en red. El núcleo de los primeros es una estructura en forma de árbol en la que cada registro tiene un solo propietario. Podéis ver cómo funciona utilizando el ejemplo de InterSystems IRIS, porque su modelo principal es jerárquico y todos los datos se almacenan en globals (que son árboles B). Podéis leer más sobre los globals aquí.

Podemos crear este árbol en IRIS:

Set^a("+7926X", "city") = "Moscow"Set^a("+7926X", "city", "street") = "Req Square"Set^a("+7926X", "age") = 25Set^a("+7916Y", "city") = "London"Set^a("+7916Y", "city", "street") = "Baker Street"Set^a("+7916Y", "age") = 36

Y verlo en la base de datos:

Después, Edgar F. Codd propuso su álgebra relacional y su teoría del almacenamiento de datos, basada en principios relacionales, en 1969. Y tras ello se crearon las bases de datos relacionales. El uso de relaciones (tablas), atributos (columnas), tuplas (filas) y, lo que es más importante, transacciones y requisitos ACID, hizo que estas bases de datos fueran muy populares y lo siguen siendo ahora.

Por ejemplo, tenemos el siguiente esquema:

Podemos crear y completar tablas:

Y si escribimos la consulta:

select p.ID, p.Name, a.Country, A.City
  from My.Person p left join My.Address a
    on p.Address = a.ID

recibiremos la respuesta:

A pesar de las significativas ventajas de las bases de datos relacionales, con la difusión de los lenguajes de objetos se hizo necesario almacenar datos orientados a objetos en bases de datos. Por eso, en los años 90 aparecieron las primeras bases de datos orientadas a objetos y objeto-relacionales. Estas últimas se crearon a partir de bases de datos relacionales y añadiéndole complementos para simular el trabajo con objetos. Las primeras se desarrollaron desde cero a partir de las recomendaciones del consorcio OMG (Object Management Group) y después del ODMG (Object Data Management Group).

Las ideas clave de estas bases de datos orientadas a objetos son las siguientes.

El almacén de datos único es accesible usando:

  • object definition language - schema definition, permite definir clases, sus atributos, relaciones y métodos
  • object-query language - declarative, es un lenguaje similar a SQL, que permite obtener objetos de la base de datos
  • object manipulation language - permite modificar y guardar datos en la base de datos, admite transacciones y la invocación de métodos.

Este modelo permite obtener datos de bases de datos mediante lenguajes orientados a objetos.

Si tomamos la misma estructura que en el ejemplo anterior, pero en la forma orientada a objetos, tendremos las siguientes clases:

Class My.Person Extends%Persistent
{
Property Name As%Name;Property Address As My.Address;
}
Class My.Address Extends%Persistent
{
Property Country;Property City;
}

Y podemos crear los objetos utilizando el lenguaje orientado a objetos:

 set address = ##class(My.Address).%New()
 set address.Country = "France"
 set address.City = "Marseille"
 do address.%Save()
 set person = ##class(My.Person).%New()
 set person.Address = address
 set person.Name = "Quouinaut, Martin"
 do person.%Save()

Desafortunadamente, las bases de datos de objetos no lograron competir con las bases de datos relacionales a pesar de su posición dominante, y como resultado, surgieron muchos ORM.

En cualquier caso, con la expansión de Internet en la década de los 2000 y la aparición de nuevos requisitos para el almacenamiento de datos, empezaron a surgir otros modelos de datos y DBMS. Dos de estos modelos que se utilizan en IRIS son los de documentos y columnas.

Las bases de datos orientadas a documentos se utilizan para gestionar datos semiestructurados. Se trata de datos que no siguen una estructura fija y llevan la estructura dentro. Cada unidad de información en una base de datos de este tipo es un par simple: una clave y un documento específico. Este documento normalmente tiene un formato JSON y contiene la información. Como la base de datos no requiere un esquema particular, también es posible integrar distintos tipos de documentos en el mismo almacén.

Si tomamos el ejemplo anterior, podemos tener documentos como estos:

{
   "Name":"Quouinaut, Martin",
   "Address":{
      "Country":"France",
      "City":"Paris"
   }
}
{
   "Name":"Merlingue, Luke",
   "Address":{
      "Country":"France",
      "City":"Nancy"
   },
   "Age":26
}

Estos dos documentos con un distinto número de campos se almacenan en la base de datos de IRIS sin ningún problema.

Y el último ejemplo de un modelo que estará disponible en la versión 2022.2 es el modelo de columnas. En este caso, el DBMS almacena las tablas de datos por columnas y no por filas.

La orientación por columnas permite un acceso más eficiente a los datos para consultar un subconjunto de columnas (eliminando la necesidad de leer columnas que no son relevantes), y más opciones para comprimir datos. Asimismo, comprimir por columnas es más eficaz cuando los datos de la columna son similares. No obstante, generalmente son menos eficaces para insertar nuevos datos.

Podéis crear esta tabla:

Create Table My.Address (
  city varchar(50),
  zip varchar(5),
  country varchar(15)
) WITH STORAGETYPE = COLUMNAR

En este caso, la clase es la siguiente:

 
Spoiler
 
Class My.Address Extends%Persistent [ ClassType = persistent, DdlAllowed, Final, Owner = {UnknownUser}, ProcedureBlock, SqlRowIdPrivate, SqlTableName = Address ]
{
Property city As%Library.String(COLLATION = "EXACT", MAXLEN = 50) [ SqlColumnNumber = 2 ];Property zip As%Library.String(COLLATION = "EXACT", MAXLEN = 5) [ SqlColumnNumber = 3 ];Property country As%Library.String(COLLATION = "EXACT", MAXLEN = 15) [ SqlColumnNumber = 4 ];Parameter STORAGEDEFAULT = "columnar";Parameter USEEXTENTSET = 1;/// Bitmap Extent Index auto-generated by DDL CREATE TABLE statement.  Do not edit the SqlName of this index.
Index DDLBEIndex [ Extent, SqlName = "%%DDLBEIndex", Type = bitmap ];
Storage Default
{
<Data name="_CDM_city">
<Attribute>city</Attribute>
<ColumnarGlobal>^q3AW.DZLd.1.V1</ColumnarGlobal>
<Structure>vector</Structure>
</Data>
<Data name="_CDM_country">
<Attribute>country</Attribute>
<ColumnarGlobal>^q3AW.DZLd.1.V2</ColumnarGlobal>
<Structure>vector</Structure>
</Data>
<Data name="_CDM_zip">
<Attribute>zip</Attribute>
<ColumnarGlobal>^q3AW.DZLd.1.V3</ColumnarGlobal>
<Structure>vector</Structure>
</Data>
<DataLocation>^q3AW.DZLd.1</DataLocation>
<ExtentLocation>^q3AW.DZLd</ExtentLocation>
<ExtentSize>3</ExtentSize>
<IdFunction>sequence</IdFunction>
<IdLocation>^q3AW.DZLd.1</IdLocation>
<Index name="DDLBEIndex">
<Location>^q3AW.DZLd.2</Location>
</Index>
<Index name="IDKEY">
<Location>^q3AW.DZLd.1</Location>
</Index>
<IndexLocation>^q3AW.DZLd.I</IndexLocation>
<Property name="%%ID">
<AverageFieldSize>3</AverageFieldSize>
<Selectivity>1</Selectivity>
</Property>
<Property name="city">
<AverageFieldSize>7</AverageFieldSize>
<Selectivity>33.3333%</Selectivity>
</Property>
<Property name="country">
<AverageFieldSize>7</AverageFieldSize>
<Selectivity>33.3333%</Selectivity>
</Property>
<Property name="zip">
<AverageFieldSize>7</AverageFieldSize>
<Selectivity>33.3333%</Selectivity>
</Property>
<SQLMap name="%%DDLBEIndex">
<BlockCount>-4</BlockCount>
</SQLMap>
<SQLMap name="IDKEY">
<BlockCount>-4</BlockCount>
</SQLMap>
<SQLMap name="_CDM_city">
<BlockCount>-4</BlockCount>
</SQLMap>
<SQLMap name="_CDM_country">
<BlockCount>-4</BlockCount>
</SQLMap>
<SQLMap name="_CDM_zip">
<BlockCount>-4</BlockCount>
</SQLMap>
<StreamLocation>^q3AW.DZLd.S</StreamLocation>
<Type>%Storage.Persistent</Type>
}
}
 

Después insertamos los datos:

insert into My.Address values ('London', '47000', 'UK')
insert into My.Address values ('Paris', '54000', 'France')
insert into My.Address values ('Kyiv', '03000', 'Ukraine')

En los globals podemos ver:

Si abrimos el global con los nombres de las ciudades, podremos ver:

Y si escribimos una consulta:

select City
  from My.Address

recibimos los datos:

En este caso, el DBMS se limita únicamente a leer un global para obtener el resultado completo. Además, ahorra tiempo y recursos en la lectura.

Así pues, hemos hablado de 5 modelos de datos diferentes compatibles con la base de datos InterSystems IRIS: los modelos jerárquicos, relacionales, de objetos, de documentos y de columnas.

Espero que este artículo os resulte útil para saber qué modelos están disponibles. Si tenéis alguna pregunta, podéis escribirla en los comentarios.

2
0 427
Artículo Alberto Fuentes · oct 27, 2022 8m read

1. Interoperability-embedded-python

Esta prueba de concepto pretende mostrar cómo el framework de interoperabilidad de IRIS puede utilizarse con Python Embebido.

1.1. Índice

1.2. Ejemplo

import grongier.pex
import iris
import MyResponse

class MyBusinessOperation(grongier.pex.BusinessOperation):

    def OnInit(self):
        print("[Python] ...MyBusinessOperation:OnInit() is called")
        self.LOGINFO("Operation OnInit")
        return

    def OnTeardown(self):
        print("[Python] ...MyBusinessOperation:OnTeardown() is called")
        return

    def OnMessage(self, messageInput):
        if hasattr(messageInput,"_IsA"):
            if messageInput._IsA("Ens.StringRequest"):
                self.LOGINFO(f"[Python] ...This iris class is a Ens.StringRequest with this message {messageInput.StringValue}")
        self.LOGINFO("Operation OnMessage")
        response = MyResponse.MyResponse("...MyBusinessOperation:OnMessage() echos")
        return response

1.3. Registrar un componente

No es necesario código en ObjectScript. Gracias al método Grongier.PEX.Utils.RegisterComponent():

Inicia un Embedded Python Shell:

/usr/irissys/bin/irispython

A continuación, utiliza este método de clase para añadir un nuevo archivo py a la lista de componentes de interoperabilidad.

iris.cls("Grongier.PEX.Utils").RegisterComponent(<ModuleName>,<ClassName>,<PathToPyFile>,<OverWrite>,<NameOfTheComponent>)

por ejemplo:

iris.cls("Grongier.PEX.Utils").RegisterComponent("MyCombinedBusinessOperation","MyCombinedBusinessOperation","/irisdev/app/src/python/demo/",1,"PEX.MyCombinedBusinessOperation")

Esto es un truco, no es para producción.

2. Demo

La producción tiene cuatro componentes en Pure Python:

  • Dos Business Services:
    • Grongier.PEX.MyCombinedBusinessService, que envía continuamente mensajes de sincronización a una Business Operation
      • Esos mensajes son objetos de Python en formato JSON y almacenados en Grongier.PEX.Message.
      • Código de Python: src/python/demo/MyCombinedBusinessService.py
  • Grongier.PEX.MyBusinessService, que básicamente no hace nada, es un Business Service en bruto que escribe registros de mensajes
    • Código de Python: src/python/demo/MyBusinessService.py
  • Dos Business Operations:
    • Grongier.PEX.BusinessOperation, que recibe el mensaje de Grongier.PEX.MyCombinedBusinessService
      • Código de Python: src/python/demo/MyBusinessOperation.py
    • Grongier.PEX.CombinedBusinessOperation, puede recibir el mensaje Ens.StringRequest y la respuesta con Ens.StringResponse
      • Código de Python: src/python/demo/MyCombinedBusinessOperation.py
interop-screenshot

Nueva traza json para los mensajes nativos de Python:

json-message-trace

3. Requisitos previos

Asegúrate de tener instalado git y el escritorio Docker.

4. Instalación con Docker

Clona el repositorio a cualquier directorio local

git clone https://github.com/grongierisc/interpeorability-embedded-python

Abre el terminal en este directorio y ejecuta:

docker-compose build

Ejecuta el contenedor IRIS con tu proyecto:

docker-compose up -d

5. Instalación sin Docker

Instala el grongier_pex-1.0.0-py3-none-any.whl en tu instancia local de iris:

/usr/irissys/bin/irispython -m pip install grongier_pex-1.0.0-py3-none-any.whl

A continuación, carga las clases de ObjectScript:

do $System.OBJ.LoadDir("/opt/irisapp/src","cubk","*.cls",1)

6. Cómo ejecutar el ejemplo

Abre la producción y comienza a utilizarla. Se comenzará a ejecutar el código del ejemplo.

7. Qué hay dentro del repositorio

7.1. Dockerfile

Un dockerfile que instala algunas dependencias de Python (pip, venv) y sudo en el contenedor por conveniencia. Después, crea el directorio dev y copia el repositorio git.

Inicia IRIS e importa los archivos csv de Titanic, después activa %Service_CallIn para Python Shell. Utiliza el docker-compose.yml relacionado para configurar fácilmente parámetros adicionales como el número de puerto y dónde mapear contraseñas y carpetas del host.

Este dockerfile termina con la instalación de los requisitos para los módulos de python.

La última parte es sobre la instalación de Jupyter Notebook y sus kernels.

Utiliza el archivo .env/ para ajustar el dockerfile que se utiliza en docker-compose.

7.2. .vscode/settings.json

Archivo de configuración para poder codificar en VSCode con el plugin VSCode ObjectScript

7.3. .vscode/launch.json

Archivo de configuración si quieres depurar con VSCode ObjectScript

Lee sobre todos los archivos en este artículo

7.4. .vscode/extensions.json

Archivo de recomendación para añadir extensiones si quieres ejecutar VSCode en el contenedor.

Más información aquí

Arquitectura

Esto es muy útil para trabajar con Python Embebido.

7.5. src folder

src
├── Grongier
│   └── PEX // ObjectScript classes that wrap python code
│       ├── BusinessOperation.cls
│       ├── BusinessProcess.cls
│       ├── BusinessService.cls
│       ├── Common.cls
│       ├── Director.cls
│       ├── InboundAdapter.cls
│       ├── Message.cls
│       ├── OutboundAdapter.cls
│       ├── Python.cls
│       ├── Test.cls
│       └── Utils.cls
├── PEX // Some example of wrapped classes
│   ├── MyBusinessOperationWithAdapter.cls
│   ├── MyBusinessOperationWithIrisAdapter.cls
│   ├── MyBusinessOperationWithPythonAdapter.cls
│   ├── MyBusinessService.cls
│   ├── MyOutboundAdapter.cls
│   └── Production.cls
└── python
    ├── demo // Actual python code to run this demo
    │   ├── MyBusinessOperation.py
    │   ├── MyBusinessOperationWithAdapter.py
    │   ├── MyBusinessOperationWithIrisAdapter.py
    │   ├── MyBusinessProcess.py
    │   ├── MyBusinessService.py
    │   ├── MyCombinedBusinessOperation.py
    │   ├── MyCombinedBusinessProcess.py
    │   ├── MyCombinedBusinessService.py
    │   ├── MyInboundAdapter.py
    │   ├── MyLoggingOperation.py
    │   ├── MyNonPollingStarter.py
    │   ├── MyOutboundAdapter.py
    │   ├── MyRequest.py
    │   ├── MyResponse.py
    │   ├── MySyncBusinessProcess.py
    │   └── SimpleObject.py
    ├── dist // Wheel used to implement python interoperability components
    │   └── grongier_pex-1.0.0-py3-none-any.whl
    ├── grongier
    │   └── pex // Helper classes to implement interoperability components
    │       ├── _BusinessHost.py
    │       ├── _BusinessOperation.py
    │       ├── _BusinessProcess.py
    │       ├── _BusinessService.py
    │       ├── _Common.py
    │       ├── _Director.py
    │       ├── _InboundAdapter.py
    │       ├── _Message.py
    │       ├── _OutboundAdapter.py
    │       └── __init__.py
    └── setup.py // setup to build the wheel

8. Cómo añadir un nuevo componente

8.1. InboundAdapter

Para implementar InboundAdapter en Python:

Subclase de grongier.pex.InboundAdapter en Python. Sobreescribe el método OnTask().

8.2. OutboundAdapter

Para implementar OutboundAdapter en Python:

Subclase de grongier.pex.OutboundAdapter en Python. Implementar los métodos de acción requeridos.

8.3. BusinessService

Para implementar BusinessService en Python:

Subclase de grongier.pex.BusinessService en Python. Sobreescribe el método OnProcessInput().

8.4. BusinessProcess

Para implementar BusinessProcess en Python:

Subclase de grongier.pex.BusinessProcess en Python. Sobreescribe los métodos OnRequest(), OnResponse() y OnComplete().

8.5. BusinessOperation

Para implementar BusinessOperation en Python:

Subclase de grongier.pex.BusinessOperation en Python. Sobreescribe el método OnMessage().

8.6. Registrar un componente

Iniciar un Embedded Python Shell:

/usr/irissys/bin/irispython

A continuación, utiliza este método de clase para añadir un nuevo archivo py a la lista de componentes de interoperabilidad.

iris.cls("Grongier.PEX.Utils").RegisterComponent(<ModuleName>,<ClassName>,<PathToPyFile>,<OverWrite>,<NameOfTheComponent>)

por ejemplo:

iris.cls("Grongier.PEX.Utils").RegisterComponent("MyCombinedBusinessOperation","MyCombinedBusinessOperation","/irisdev/app/src/python/demo/",1,"PEX.MyCombinedBusinessOperation")

8.7. Uso directo de Grongier.PEX

Si no quieres utilizar la utilidad RegisterComponent. Puedes añadir un componente Grongier.PEX.Business* y configurar las propiedades:

  • %module :
  • Nombre del módulo correspondiente a tu código de python
  • %classname :
  • Nombre de la clase de tu componente
  • %classpaths
  • Ruta donde se encuentra tu componente.
  • Puede ser una o más Rutas de clase (separadas por el carácter '|') necesarias además de PYTHON_PATH

Por ejemplo:

component-config

9. Trabajo futuro

  • Solo se ha probado Service y Operation.
  • Trabajo en curso sobre el adaptador

10. Créditos

La mayor parte del código procede de PEX para Python, de Mo Cheng y Summer Gerry.

La parte del registro procede del formulario de características no liberado de IRIS 2021.3.

2
1 215
Artículo Luis Angel Pérez Ramos · mar 9, 2023 3m read
   _________ ___ ____  
  |__  /  _ \_ _|  _ \ 
    / /| |_) | || |_) |
   / /_|  __/| ||  __/ 
  /____|_|  |___|_|    

Desde la versión 2021.1, InterSystems IRIS empezó a distribuirse con un runtime de Python en el motor del kernel. Sin embargo, no había forma de instalar paquetes desde dentro de la instancia. La principal ventaja de Python es su enorme ecosistema de paquetes. Con ello en mente, os presento mi proyecto zpip, un empaquetador pip que se puede invocar desde el terminal de iris.

¿Qué es zpip?

zpip es un empaquetador para python pip que permite a los desarrolladores añadir de forma rápida paquetes a una instancia, a través del terminal de InterSystems IRIS.

Características

  • Empaquetador pip de python para InterSystems IRIS
  • Instalación/Desinstalación paquetes python
  • La instalación añade la palabra clave zpip al lenguaje

Instalación de zpip

%SYS> zpm "install zpip"

Lista de tareas

  • API invocable con retorno de estados

Uso de zpip

Todos los comandos* pip están soportados. Sin embargo, cualquier comando interactivo requerirá que uses la versión no-interactiva del comando. Por ejemplo, para desinstalar un package, tendrás que usar -y en el comando para confirmar el proceso.

Instalación de los paquetes python con zpip

// Install multiple packages
// beautiful soup and requests libraries
%SYS> zpip "install requests bs4"

... en acción:

%SYS>zpip "install emoji"

Processing /home/irisowner/.cache/pip/wheels/ae/80/43/3b56e58669d65ea9ebf38b9574074ca248143b61f45e114a6b/emoji-2.1.0-py3-none-any.whl
Installing collected packages: emoji
Successfully installed emoji-2.1.0

%SYS>

Especificación de un directorio de instalación diferente:

// Install to some other python package target
$SYS> zpip "install --target '/durable/iconfig/lib/python' emoji"

Desinstalación de un paquete python

// Requires -y!
%SYS>zpip "uninstall -y emoji"
Found existing installation: emoji 2.1.0
Uninstalling emoji-2.1.0:
  Successfully uninstalled emoji-2.1.0

Otros comandos de pip útiles

lista de paquetes

// List Packages
%SYS> zpip "list"
Package                      Version    
---------------------------- -----------
absl-py                      1.1.0      
argon2-cffi                  21.3.0     
argon2-cffi-bindings         21.2.0     
asttokens                    2.0.5      
astunparse                   1.6.3      
attrs                        21.4.0     
backcall                     0.2.0      
beautifulsoup4               4.11.1     
bleach                       5.0.0      
bs4                          0.0.1   
...

Limitaciones

  • Los comandos interactivos no están soportados.
  • Uso de -y para desinstalar.
  • La búsqueda puede no funcionar dependiendo de la configuración del sistema.
  • Usa la infraestructura pip subyacente del sistema operativo, por lo que la instalación depende de la versión pip del sistema operativo.
1
0 143
Artículo Dmitry Maslennikov · feb 17, 2023 4m read

Os presento mi nuevo proyecto: irissqlcli, REPL (Read-Eval-Print Loop) para InterSystems IRIS SQL.

  • Resaltado de sintaxis
  • Sugerencias (tablas, funciones)
  • +20 formatos de salida
  • Soporte a stdin
  • Salida a ficheros 

Instalación con pip

pipinstallirissqlcli

O se puede ejecutar con docker

dockerrun-itcaretdev/irissqlcliirissqlcliiris://_SYSTEM:SYS@host.docker.internal:1972/USER
1
0 131
Artículo Daniel Aguilar · jun 12, 2023 3m read

ZPM está diseñado para funcionar con aplicaciones y módulos de InterSystems IRIS. Está formado por dos  componentes: el Cliente ZPM, que es un CLI para gestionar módulos; y El Registro, que es una base de datos de módulos y meta-información. Podemos utilizar ZPM para buscar, instalar, actualizar, eliminar y publicar módulos. Con ZPM se pueden instalar clases de ObjectScript, aplicaciones Frontend, producciones de Interoperabilidad, soluciones de BI de IRIS, conjuntos de datos de IRIS o cualquier otro archivo, como paquetes wheel de Python Embebido. 

En este artículo repasaremos tres secciones:

  1. Instalación de ZPM
  2. Generación del Módulo
  3. Búsqueda, Instalación y Publicación de módulos dentro del Registro
2
0 282
Artículo Mathew Lambert · mar 25, 2020 6m read

¡Hola Comunidad!

Creo que hoy en día todo el mundo guarda el código fuente de sus proyectos en repositorios como Github, GitLab, bitbucket, etc. Lo mismo sucede con proyectos de InterSystems IRIS, se pueden ver algunos ejemplos en Open Exchange.

¿Qué hacemos cada vez que empezamos o continuamos nuestro trabajo con un repositorio en particular con la plataforma de datos InterSystems?

1
1 909
Artículo Bernardo Linarez · mar 30, 2020 6m read

¡Hola Comunidad!

En este artículo hablaré sobre las pruebas y la depuración de las aplicaciones web de Caché (principalmente REST) con herramientas externas. La segunda parte trata sobre las herramientas de Caché.

Usted escribió el código del lado del servidor y quiere probarlo con un cliente, o ya tiene una aplicación web pero no funciona. Aquí es donde entra la depuración. En este artículo abarcaré desde las herramientas más fáciles de utilizar (el navegador), hasta las más completas (el analizador de paquetes), pero primero conversemos un poco sobre los errores más comunes y cómo pueden resolverse.

1
0 674
Artículo Ricardo Paiva · mar 11, 2021 6m read

Quería escribirlo como comentario alartículo de @Evgeny.Shvarov. Pero resultó demasiado largo, así que decidí publicarlo por separado.

Imagen que resulta de Docker cuando se limpian todas las imágenes

Me gustaría añadir una pequeña aclaración sobre cómo utiliza Docker el espacio en disco y como limpiarlo. Yo uso macOS, por lo tanto todo lo que explico aplica principalmente a macOS, pero los comandos de Docker se adaptan a cualquier plataforma.

Cuando Docker se incluye en Linux, por defecto funciona en el mismo sistema de archivos. Pero en Windows y macOS, funciona en una pequeña máquina virtual con su propio Linux dentro. Y el espacio del disco está limitado por mi configuración en Docker. En mi caso, lo configuré para utilizar hasta 112 GB.

Por lo tanto, cuando trabajes de forma activa con Docker, tu espacio interior dejará de usarse. Puedes comprobar como Docker emplea todo ese espacio con el comando:

$ docker system df
TYPE                TOTAL               ACTIVE              SIZE                RECLAIMABLE
Images              84                  6                   66.02GB             55.6GB (84%)
Containers          6                   5                   4.914GB             0B (0%)
Local Volumes       19                  4                   1.812GB             342.7MB (18%)
Build Cache         0                   0                   0B                  0B

En macOS con las últimas versiones de Docker, se utiliza el formato en bruto del disco (anteriormente era qcow2). Y junto con el sistema de archivos APFS en macOS, este archivo puede ocupar menos espacio físico que el propio tamaño del archivo. Observa estos dos comandos. 

$ ls -lh ~/Library/Containers/com.docker.docker/Data/vms/0/Docker.raw
-rw-r--r--@ 1 daimor  staff   104G Jul 13 15:49 /Users/daimor/Library/Containers/com.docker.docker/Data/vms/0/Docker.raw

$ du -h ~/Library/Containers/com.docker.docker/Data/vms/0/Docker.raw
 88G    /Users/daimor/Library/Containers/com.docker.docker/Data/vms/0/Docker.raw

El comando ls muestra el tamaño de mi archivo Docker.raw como 104Gb, mientras que el comando du muestra el tamaño real en el disco, que es de 88Gb.

Bien, docker system df me mostró que puedo recuperar algo de espacio. Vamos a hacerlo.

$ docker system prune -f
Deleted Containers:
79b3d54ae5a881e37771cfdc1d651db9ce036abc297dc55bdd454eb287f0e329

Deleted Images:
deleted: sha256:298d555976effb112428ed3f6bcc2f4d77ab02b4f287a230d9535001184078f5
deleted: sha256:adb2c64ce6e44d837fce8067c7498574822bff90ed599d1671c126539fe652ac
deleted: sha256:9695172139cec16f1071449daf29bd1c424353044088b92b8acbf33f59952e67
deleted: sha256:24d834b252e25e645b8b5d9194360f5ab1a26ffd2b5c03b6593b9a2c468f59fa
deleted: sha256:1b4e3e73fe0b7d88d5ec718bdc6dc6d17d9fe8ba00988eb72690d76f2da3d1a3
deleted: sha256:9f218f6c7aca9c21760ae43590a2d73b35110e10b6575125ed3ccd12c4495d6e
deleted: sha256:b2fa3335d672a0dc60ea7674c45ee3c85b9fc86584a0e21cc7f1900c368ceec3
deleted: sha256:2ecace396ab65fd393dfb2e330bece974cd952e7a41364352f9c867d9ea4c34e
deleted: sha256:16b894351fe53b95dd43d7437bbbcd5104b8613bc1fa8480826e843d65fc92a3
deleted: sha256:b00d9c05035eac62f3ed99a814cd6feea3e4b68797b6d1203e2f41538c78c2aa
deleted: sha256:5a3d0d9f36b356cb47d3838585da6450c60e2860ef143d1406b48d3a5e72b92b
deleted: sha256:998e719368ff74d13b3a8c096ce81f8f2c4bb28bd1ccd169bfa173b4a78d2e74
deleted: sha256:a74d7ff2ca7d623134f9ce1db40da476134a733935a3f322ba34b99653c6273d
deleted: sha256:4d0dcd2bdad2cf0cb91d13313afff29326771bdac27fcb8780545687dbd39ae4
deleted: sha256:29a8989eed3d4002053f98bf562654910ee5f8836940daaa2f2344a8f29a52a2
deleted: sha256:12d34fbf938d19b193199ea6cce5d690fd0d57ec3f4d1630e1d4b3790379c9ec
deleted: sha256:75aba481bb5ccaa52a3aadf311ae22485fb2a82d69be864fe2f45f2834c5e515
deleted: sha256:326efafee9b92e06876878b21a2931ba771bc0e0b2b359f906ef6cca1d297905
deleted: sha256:913937f4ea932fcb00b6c6b3007970296955aa4f488d6fbaa1a575a5aa4ff5ab
deleted: sha256:f3fc0c75858a36ff9d3f4e8eb7a96f511158bbac92d128760b0d3340d828c5da
deleted: sha256:c002dde1ea6a02ae3e3037442a5c556a925e3e4750a6b2aa923c51fa3d11f5ac
deleted: sha256:e763f6e226613c67aaf5984e4c74b9f6e9e28e0490a4f3286885f498a57d3fa0
deleted: sha256:e7daf0a1574376c8602515dc70e44392d16e1b79013d6e81a9b697794432e660
deleted: sha256:ce33670f78109dcacc73a7c6d70f4b5cd4a13bcfe7878b9df5e4e16b812e5df4
deleted: sha256:95bf79e86f83ed16943f9c34035bf8167a4b89466a05d6793c2957d6d46bab2d
deleted: sha256:056d184391613b33303ccf3818a95a17398e9da813d093a6ee5d87952539380c

Total reclaimed space: 5.537GB

Este comando elimina cualquier contenedor detenido y cualquier imagen no etiquetada que no se esté utilizando por cualquier imagen etiquetada. Y se puede eliminar de forma segura.

Tal vez has notado que solo se recuperaron 5.5 GB, mientras que docker system df hablaba de unos 55 GB. Eso es porque df cuenta todas las imágenes no activas, no solo las activas. Si también quieres eliminar todas esas imágenes, puedes utilizar este comando, lo que elimina cualquier imagen que no se utilice en los contenedores que estén ejecutándose en este momento. Por lo tanto, si no tienes ningún contenedor funcionando, eliminará todas las imágenes locales.

docker system prune -a

Acabo de recuperar solo las imágenes activas y los contenedores detenidos. Cuánto espacio utiliza mi docker ahora.

$ docker system df
TYPE                TOTAL               ACTIVE              SIZE                RECLAIMABLE
Images              83                  5                   60.48GB             50.1GB (82%)
Containers          5                   5                   4.914GB             0B (0%)
Local Volumes       19                  3                   1.812GB             342.7MB (18%)
Build Cache         0                   0                   0B                  0B

Como puedes ver, ya utiliza menos tamaño. ls mostrará el mismo resultado. El tamaño del archivo principalmente crece.

$ ls -lh ~/Library/Containers/com.docker.docker/Data/vms/0/Docker.raw
-rw-r--r--@ 1 daimor  staff   104G Jul 13 16:07 /Users/daimor/Library/Containers/com.docker.docker/Data/vms/0/Docker.raw

Pero para macOS es más importante cuánto espacio se utiliza en un disco físico.

$ du -h ~/Library/Containers/com.docker.docker/Data/vms/0/Docker.raw
 69G    /Users/daimor/Library/Containers/com.docker.docker/Data/vms/0/Docker.raw

Y como puedes ver ahora son 69 GB, que son aproximadamente 19 GB menos de los que eran anteriormente.

Así que, para los usuarios de macOS, realmente no importa el tamaño del archivo, con las optimizaciones de APFS en realidad puede ser menor.

Otra forma es reducir las imágenes antiguas con algún filtro por fecha de creación. Al igual que este ejemplo, se eliminarán todas las imágenes creadas hace más de 10 días, pero se mantendrán las imágenes que actualmente utilizan los contenedores.

$ docker image prune --all --filter until=240h
1
0 4823
Artículo Luis Angel Pérez Ramos · abr 3, 2023 9m read

¡Hola Comunidad!

En este artículo configuraremos mediante programación un Apache Web Gateway con Docker, utilizando:

  • El Protocolo HTTPS.
  • TLS\SSL para asegurar la comunicación entre el Web Gateway y la instancia de IRIS.

imagen

Utilizaremos dos imágenes: una para el Web Gateway y la segunda para la instancia de IRIS.

Todos los archivos necesarios están disponibles en este repositorio de GitHub.

Comencemos clonando el proyecto de git:

git clone https://github.com/lscalese/docker-webgateway-sample.git
cd docker-webgateway-sample

Preparación del sistema

Para evitar problemas con los permisos, el sistema necesita un usuario y un grupo:

  • www-data
  • irisowner

Es necesario para compartir archivos de certificados con los contenedores. Si no existen en tu sistema, simplemente ejecútalos:

sudo useradd --uid 51773 --user-group irisowner
sudo groupmod --gid 51773 irisowner
sudo useradd –user-group www-data

Generación de certificados

En este ejemplo, utilizaremos tres certificados:

  1. Uso del servidor web HTTPS
  2. Cifrado TLS\SSL en el cliente Web Gateway
  3. Cifrado TLS\SSL en la Instancia de IRIS

Para generarlos, está disponible un script listo para usarse.

Sin embargo, hay que personalizar el asunto del certificado; simplemente edita el archivo gen-certificates.sh.

Esta es la estructura del argumento OpenSSL subj:

  1. C: Código del país
  2. ST: Estado
  3. L: Ubicación
  4. O: Organización
  5. OU: Unidad de la organización
  6. CN: Nombre común (básicamente el nombre de dominio o el nombre del host)

Se pueden cambiar estos valores.

# sudo is needed due chown, chgrp, chmod ...
sudo ./gen-certificates.sh

Si todo está bien, deberías ver dos nuevos directorios ./certificados/ y ~/webgateway-apache-certificados/ con certificados:

ArchivoContenedorDescripción
./certificates/CA_Server.cerwebgateway,irisCertificado del servidor de autoridad
./certificates/iris_server.ceririsCertificado para la instancia de IRIS (utilizado para el cifrado de la comunicación mirror y de webgateway)
./certificates/iris_server.keyirisClave privada relacionada
~/webgateway-apache-certificates/apache_webgateway.cerwebgatewayCertificado para el servidor web de apache
~/webgateway-apache-certificates/apache_webgateway.keywebgatewayClave privada relacionada
./certificates/webgateway_client.cerwebgatewayCertificado para cifrar la comunicación entre webgateway e IRIS
./certificates/webgateway_client.keywebgatewayClave privada relacionada

Ten en cuenta que si hay certificados autofirmados, los navegadores web mostrarán alertas de seguridad. Obviamente, si tienes un certificado emitido por una autoridad certificadora, puedes utilizarlo en vez de uno autofirmado (especialmente para el certificado del servidor de Apache).

Archivos de configuración del Web Gateway

Echa un vistazo a los archivos de la configuración.

CSP.INI

Puedes ver un archivo CSP.INI en el directorio webgateway-config-files.
Será introducido en la imagen, pero el contenido puede ser modificado durante el tiempo de ejecución. Considera este archivo como una plantilla.

En este ejemplo, los siguientes parámetros se sobrescribirán cuando se inicie el contenedor:

  • Ip_Address
  • TCP_Port
  • System_Manager

Consulta startUpScript.sh para más información. A grandes rasgos, la sustitución se realiza con la línea de comandos sed.

Además, este archivo contiene la configuración SSL\TLS para asegurar la comunicación con la instancia de IRIS:

SSLCC_Certificate_File=/opt/webgateway/bin/webgateway_client.cer
SSLCC_Certificate_Key_File=/opt/webgateway/bin/webgateway_client.key
SSLCC_CA_Certificate_File=/opt/webgateway/bin/CA_Server.cer

Estas líneas son importantes. Debemos asegurarnos de que los archivos del certificado estarán disponibles para el contenedor.
Lo haremos más tarde en el archivo docker-compose con un volumen.

000-default.conf

Se trata de un archivo de configuración de Apache. Permite utilizar el protocolo HTTPS y redirige las llamadas HTTP a HTTPS.
Los archivos del certificado y la clave privada se configuran en este archivo:

SSLCertificateFile /etc/apache2/certificate/apache_webgateway.cer
SSLCertificateKeyFile /etc/apache2/certificate/apache_webgateway.key

Instancia de IRIS

Para nuestra instancia de IRIS, configuramos únicamente los requisitos mínimos para permitir la comunicación de SSL\TLS con el Web Gateway; esto incluye:

  1. Configuración SSL %SuperServer
  2. Habilitar la configuración de seguridad SSLSuperServer
  3. Restringir la lista de IPs que pueden utilizar el servicio del Web Gateway

Para facilitar la configuración, se utiliza config-api con un archivo de configuración JSON sencillo.

{
   "Security.SSLConfigs": {
       "%SuperServer": {
           "CAFile": "/usr/irissys/mgr/CA_Server.cer",
           "CertificateFile": "/usr/irissys/mgr/iris_server.cer",
           "Name": "%SuperServer",
           "PrivateKeyFile": "/usr/irissys/mgr/iris_server.key",
           "Type": "1",
           "VerifyPeer": 3
       }
   },
   "Security.System": {
       "SSLSuperServer":1
   },
   "Security.Services": {
       "%Service_WebGateway": {
           "ClientSystems": "172.16.238.50;127.0.0.1;172.16.238.20"
       }
   }
}

No es necesario realizar ninguna acción. La configuración se cargará automáticamente al iniciar el contenedor.

Imagen tls-ssl-webgateway

dockerfile

ARG IMAGEWEBGTW=containers.intersystems.com/intersystems/webgateway:2021.1.0.215.0
FROM ${IMAGEWEBGTW}
ADD webgateway-config-files /webgateway-config-files
ADD buildWebGateway.sh /
ADD startUpScript.sh /
RUN chmod +x buildWebGateway.sh startUpScript.sh && /buildWebGateway.sh
ENTRYPOINT ["/startUpScript.sh"]

De forma predeterminada, el punto de entrada es /startWebGateway, pero necesitamos realizar algunas operaciones antes de iniciar el servidor web. Recuerda que nuestro archivo CSP.ini es una plantilla, y necesitamos cambiar algunos parámetros (IP, puerto, administrador del sistema) durante el arranque. startUpScript.sh realizará estos cambios y después ejecutará el script del punto de entrada inicial /startWebGateway.

Arranque de los contenedores

Archivo Docker-compose

Antes de iniciar los contenedores, se debe modificar el archivo docker-compose.yml:

  • **SYSTEM_MANAGER** debe estar configurado con la IP autorizada para tener acceso a Web Gateway Managementhttps://localhost/csp/bin/Systems/Module.cxw Básicamente, es tu dirección IP (Puede ser una lista separada por comas).

  • **IRIS_WEBAPPS** debe configurarse con la lista de tus aplicaciones CSP. La lista está separada por espacios, por ejemplo: IRIS_WEBAPPS=/csp/sys /swagger-ui. De forma predeterminada, solo se expone /csp/sys.

  • Los puertos 80 y 443 están mapeados. Adáptalos a otros puertos si ya se utilizan en tu sistema.

version: '3.6'
services:

 webgateway:
   image: tls-ssl-webgateway
   container_name: tls-ssl-webgateway
   networks:
     app_net:
       ipv4_address: 172.16.238.50
   ports:
     # change the local port already used on your system.
     - "80:80"
     - "443:443"
   environment:
     - IRIS_HOST=172.16.238.20
     - IRIS_PORT=1972
     # Replace by the list of ip address allowed to open the CSP system manager
     # https://localhost/csp/bin/Systems/Module.cxw 
     # see .env file to set environement variable.
     - "SYSTEM_MANAGER=${LOCAL_IP}"
     # the list of web apps
     # /csp allow to the webgateway to redirect all request starting by /csp to the iris instance
     # You can specify a list separate by a space : "IRIS_WEBAPPS=/csp /api /isc /swagger-ui"
     - "IRIS_WEBAPPS=/csp/sys"
   volumes:
     # Mount certificates files.
     - ./volume-apache/webgateway_client.cer:/opt/webgateway/bin/webgateway_client.cer
     - ./volume-apache/webgateway_client.key:/opt/webgateway/bin/webgateway_client.key
     - ./volume-apache/CA_Server.cer:/opt/webgateway/bin/CA_Server.cer
     - ./volume-apache/apache_webgateway.cer:/etc/apache2/certificate/apache_webgateway.cer
     - ./volume-apache/apache_webgateway.key:/etc/apache2/certificate/apache_webgateway.key
   hostname: webgateway
   command: ["--ssl"]

 iris:
   image: intersystemsdc/iris-community:latest
   container_name: tls-ssl-iris
   networks:
     app_net:
       ipv4_address: 172.16.238.20
   volumes:
     - ./iris-config-files:/opt/config-files
     # Mount certificates files.
     - ./volume-iris/CA_Server.cer:/usr/irissys/mgr/CA_Server.cer
     - ./volume-iris/iris_server.cer:/usr/irissys/mgr/iris_server.cer
     - ./volume-iris/iris_server.key:/usr/irissys/mgr/iris_server.key
   hostname: iris
   # Load the IRIS configuration file ./iris-config-files/iris-config.json
   command: ["-a","sh /opt/config-files/configureIris.sh"]

networks:
 app_net:
   ipam:
     driver: default
     config:
       - subnet: "172.16.238.0/24"

Ejecutamos el build e iniciamos:


docker-compose up -d --build

Los contenedores tls-ssl-iris y tls-ssl-webgateway deben iniciarse.

Prueba del acceso web

Página predeterminada de Apache

Abre la página http://localhost. Serás redirigido automáticamente a https://localhost.
Los navegadores muestran alertas de seguridad. Este es el comportamiento habitual con un certificado autofirmado, acepta el riesgo y continua.

imagen

Página de administración del Web Gateway

Abre https://localhost/csp/bin/Systems/Module.cxw y prueba la conexión con el servidor. imagen

Portal de administración

Abre https://localhost/csp/sys/utilhome.csp

imagen

¡Excelente! ¡El ejemplo del Web Gateway funciona!

IRIS Mirror con Web Gateway

En el artículo anterior construimos un entorno mirror, pero faltaba el Web Gateway. Ahora podemos solucionar eso.
Un nuevo repositorio iris-miroring-with-webgateway está disponible, incluyendo el Web Gateway y algunas mejoras más:

  1. Los certificados ya no se generan sobre la marcha, sino en un proceso separado.
  2. Las direcciones IP se sustituyen por variables de entorno en docker-compose y archivos de configuración JSON. Las variables se definen en el archivo ".env".
  3. El repositorio puede utilizarse como plantilla.

Consulta el archivo README.md del repositorio para ejecutar un entorno como este:

imagen

0
1 334