Cliente SDK Python
El apartado actual detalla el cliente SDK Python de Jotelulu para el acceso simplificado al API Pública de Jotelulu.
📖 Documentación de API
Endpoints Principales
| Servicio | Endpoint | Descripción |
|---|---|---|
| Core | /core/v1/organizations | Gestión de organizaciones |
| Core | /core/v1/organizations/{id}/users | Gestión de usuarios |
| Servers | /servers/v1/subscriptions/{id}/instances | Instancias de servidor |
| Servers | /servers/v1/subscriptions/{id}/networks | Gestión de redes |
| Remote Desktops | /remote-desktops/v1/subscriptions/{id}/instances | Instancias de escritorio remoto |
| Remote Desktops | /remote-desktops/v1/subscriptions/{id}/applications | Aplicaciones |
Modelos Principales
- Organization: Representa una organización
- User: Usuario del sistema
- Subscription: Suscripción a servicios
- Instance: Instancia de servidor o escritorio remoto
- Application: Aplicación instalable
- NetworkInterface: Interfaz de red
- FirewallInboundRule: Regla de firewall
🚀 Instalación en Entorno Virtual (Recomendado)
# Crear entorno virtual
python -m venv jotelulu-env
# Activar entorno virtual
# En Windows:
jotelulu-env\Scripts\activate
# En Linux/macOS:
source jotelulu-env/bin/activate
# Instalar el SDK
pip install generated-client-python🔐 Autenticación con JWT
Configuración Básica
import openapi_client
from openapi_client.configuration import Configuration
from openapi_client.api_client import ApiClient
# Configurar el cliente con JWT
configuration = Configuration(
host="https://connect-eu1.jotelulu.com", # Producción
access_token="your_jwt_token_here"
)
# Crear cliente API
api_client = ApiClient(configuration)Configuración de Entornos
# Entornos disponibles
ENVIRONMENTS = {
'production': 'https://connect-eu1.jotelulu.com'
}
# Configuración por entorno
def create_jotelulu_client(environment='production', token=None):
configuration = Configuration(
host=ENVIRONMENTS[environment],
access_token=token
)
return ApiClient(configuration)Manejo Seguro de Tokens
import os
from openapi_client import Configuration, ApiClient
class JoteluluClient:
def __init__(self, environment='production'):
# Obtener token desde variables de entorno
token = os.getenv('JOTELULU_JWT_TOKEN')
if not token:
raise ValueError("JOTELULU_JWT_TOKEN environment variable is required")
self.configuration = Configuration(
host=ENVIRONMENTS.get(environment, ENVIRONMENTS['production']),
access_token=token
)
self.api_client = ApiClient(self.configuration)
def get_organizations_api(self):
from openapi_client.api.organizations_api import OrganizationsApi
return OrganizationsApi(self.api_client)
def get_instances_api(self):
from openapi_client.api.instances_api import InstancesApi
return InstancesApi(self.api_client)🐍 Integración con Django
1. Instalación en Proyecto Django
# Crear nuevo proyecto Django
django-admin startproject mi_proyecto_jotelulu
cd mi_proyecto_jotelulu
# Crear aplicación
python manage.py startapp jotelulu_integration
# Instalar dependencias
pip install django
pip install generated-client-python2. Configuración en settings.py
# settings.py
import os
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'jotelulu_integration', # Tu aplicación
]
# Configuración Jotelulu
JOTELULU_SETTINGS = {
'ENVIRONMENT': os.getenv('JOTELULU_ENV', 'production'),
'JWT_TOKEN': os.getenv('JOTELULU_JWT_TOKEN'),
'API_ENDPOINTS': {
'production': 'https://connect-eu1.jotelulu.com'
}
}3. Servicio Django para Jotelulu
# jotelulu_integration/services.py
from django.conf import settings
import openapi_client
from openapi_client.api.organizations_api import OrganizationsApi
from openapi_client.api.instances_api import InstancesApi
from openapi_client.api.applications_api import ApplicationsApi
from openapi_client.exceptions import ApiException
class JoteluluService:
def __init__(self):
jotelulu_config = settings.JOTELULU_SETTINGS
environment = jotelulu_config['ENVIRONMENT']
self.configuration = openapi_client.Configuration(
host=jotelulu_config['API_ENDPOINTS'][environment],
access_token=jotelulu_config['JWT_TOKEN']
)
self.api_client = openapi_client.ApiClient(self.configuration)
def get_user_organizations(self):
"""Obtener organizaciones del usuario actual"""
try:
api_instance = OrganizationsApi(self.api_client)
response = api_instance.list_user_organizations()
return response.data
except ApiException as e:
print(f"Error al obtener organizaciones: {e}")
return []
def get_organization_users(self, organization_id):
"""Obtener usuarios de una organización"""
try:
api_instance = OrganizationsApi(self.api_client)
response = api_instance.list_organization_users(organization_id)
return response.data
except ApiException as e:
print(f"Error al obtener usuarios: {e}")
return []
def get_remote_desktop_instances(self, subscription_id):
"""Obtener instancias de escritorio remoto"""
try:
api_instance = InstancesApi(self.api_client)
response = api_instance.list_remote_desktop_subscription_instances(subscription_id)
return response.data
except ApiException as e:
print(f"Error al obtener instancias: {e}")
return []4. Views Django
# jotelulu_integration/views.py
from django.shortcuts import render
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from .services import JoteluluService
def dashboard(request):
"""Vista principal del dashboard"""
jotelulu = JoteluluService()
organizations = jotelulu.get_user_organizations()
context = {
'organizations': organizations,
'user': request.user
}
return render(request, 'jotelulu_integration/dashboard.html', context)
def organization_detail(request, organization_id):
"""Detalle de una organización"""
jotelulu = JoteluluService()
users = jotelulu.get_organization_users(organization_id)
return JsonResponse({
'organization_id': organization_id,
'users': [{'id': user.id, 'name': user.name, 'email': user.email} for user in users]
})
@csrf_exempt
def create_organization_user(request, organization_id):
"""Crear usuario en organización"""
if request.method == 'POST':
import json
from openapi_client.model.create_organization_user_request import CreateOrganizationUserRequest
from openapi_client.model.create_organization_user_request_data import CreateOrganizationUserRequestData
data = json.loads(request.body)
jotelulu = JoteluluService()
try:
api_instance = jotelulu.get_organizations_api()
user_request = CreateOrganizationUserRequest(
data=CreateOrganizationUserRequestData(
email=data['email'],
name=data['name'],
last_name=data['lastName'],
password=data.get('password')
)
)
response = api_instance.create_organization_user(organization_id, user_request)
return JsonResponse({'success': True, 'user_id': response.data.user_id})
except Exception as e:
return JsonResponse({'success': False, 'error': str(e)})
return JsonResponse({'error': 'Method not allowed'}, status=405)5. URLs Django
# jotelulu_integration/urls.py
from django.urls import path
from . import views
app_name = 'jotelulu_integration'
urlpatterns = [
path('', views.dashboard, name='dashboard'),
path('organization/<str:organization_id>/', views.organization_detail, name='organization_detail'),
path('organization/<str:organization_id>/users/', views.create_organization_user, name='create_user'),
]
# urls.py principal del proyecto
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('jotelulu/', include('jotelulu_integration.urls')),
]📚 Ejemplos de Uso
Gestión de Organizaciones
from openapi_client.api.organizations_api import OrganizationsApi
from openapi_client.model.create_organization_user_request import CreateOrganizationUserRequest
# Inicializar cliente
client = JoteluluClient()
org_api = client.get_organizations_api()
# Listar organizaciones del usuario
organizations = org_api.list_user_organizations()
print(f"Organizaciones encontradas: {len(organizations.data)}")
# Listar usuarios de una organización
org_id = "ORG-123"
users = org_api.list_organization_users(org_id)
for user in users.data:
print(f"Usuario: {user.name} ({user.email})")
# Crear nuevo usuario
new_user_request = CreateOrganizationUserRequest(
data={
'email': '[email protected]',
'name': 'Nuevo',
'lastName': 'Usuario',
'password': 'password123',
'twoFactorAuthRequired': True
}
)
result = org_api.create_organization_user(org_id, new_user_request)
print(f"Usuario creado con ID: {result.data.user_id}")Gestión de Instancias y Servidores
from openapi_client.api.instances_api import InstancesApi
from openapi_client.api.instance_api import InstanceApi
# APIs de instancias
instances_api = InstancesApi(api_client)
instance_api = InstanceApi(api_client)
# Listar instancias de escritorio remoto
subscription_id = "SUB-456"
rd_instances = instances_api.list_remote_desktop_subscription_instances(subscription_id)
for instance in rd_instances.data:
print(f"Instancia: {instance.name} - Estado: {instance.state}")
# Obtener progreso de despliegue
if instance.state == 'creating':
progress = instance_api.get_instance_progress(subscription_id, instance.id)
print(f"Progreso: {progress.data.progress}")
# Listar instancias de servidor
server_instances = instance_api.list_server_subscription_instances(subscription_id)
print(f"Instancias de servidor: {len(server_instances.data)}")Gestión de Aplicaciones
from openapi_client.api.applications_api import ApplicationsApi
from openapi_client.model.create_instance_application_request import CreateInstanceApplicationRequest
apps_api = ApplicationsApi(api_client)
# Listar aplicaciones disponibles
available_apps = apps_api.list_applications()
print("Aplicaciones disponibles:")
for app in available_apps.data:
print(f"- {app.name} (ID: {app.id})")
# Listar aplicaciones de una suscripción
subscription_apps = apps_api.list_subscription_applications(subscription_id)
print(f"Aplicaciones instaladas: {len(subscription_apps.data)}")
# Instalar nueva aplicación
install_request = CreateInstanceApplicationRequest(
data={
'instanceId': 'INST-789',
'applicationId': 'APP-123'
}
)
result = apps_api.create_instance_application(subscription_id, install_request)
print(f"Aplicación instalada: {result.data[0].id}")Gestión de Redes y Firewall
from openapi_client.api.networks_api import NetworksApi
from openapi_client.api.firewall_api import FirewallApi
from openapi_client.model.create_isolated_network_request import CreateIsolatedNetworkRequest
from openapi_client.model.create_firewall_inbound_rule_request import CreateFirewallInboundRuleRequest
networks_api = NetworksApi(api_client)
firewall_api = FirewallApi(api_client)
# Crear red aislada
network_request = CreateIsolatedNetworkRequest(
data={
'name': 'Mi Red Privada',
'network': '192.168.100.0',
'netmask': '255.255.255.0',
'gateway': '192.168.100.1'
}
)
network = networks_api.create_isolated_network(subscription_id, network_request)
print(f"Red creada: {network.data.name}")
# Crear regla de firewall
firewall_rule = CreateFirewallInboundRuleRequest(
data={
'protocol': 'TCP',
'publicNetworkId': 'NET-456',
'privateNicAddressIpId': 'NIC-789',
'publicPort': 80,
'privatePort': 8080,
'origin': '0.0.0.0/0'
}
)
rule = firewall_api.create_firewall_inbound_rule(
subscription_id,
network.data.main_network_id,
firewall_rule
)
print(f"Regla de firewall creada: {rule.data.id}")🛠️ Manejo de Errores
from openapi_client.exceptions import ApiException
import logging
# Configurar logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def safe_api_call(api_function, *args, **kwargs):
"""Wrapper para llamadas seguras a la API"""
try:
return api_function(*args, **kwargs)
except ApiException as e:
logger.error(f"Error de API: {e.status} - {e.reason}")
logger.error(f"Cuerpo de respuesta: {e.body}")
# Manejo específico por código de error
if e.status == 401:
logger.error("Token JWT inválido o expirado")
elif e.status == 403:
logger.error("Permisos insuficientes")
elif e.status == 404:
logger.error("Recurso no encontrado")
elif e.status >= 500:
logger.error("Error interno del servidor")
return None
except Exception as e:
logger.error(f"Error inesperado: {str(e)}")
return None
# Ejemplo de uso
organizations = safe_api_call(org_api.list_user_organizations)
if organizations:
print(f"Organizaciones obtenidas: {len(organizations.data)}")
else:
print("No se pudieron obtener las organizaciones")🔧 Configuración Avanzada
Cliente Personalizado con Retry
import time
from functools import wraps
def retry_on_failure(max_retries=3, delay=1):
"""Decorador para reintentar llamadas fallidas"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except ApiException as e:
if e.status >= 500 and attempt < max_retries - 1:
time.sleep(delay * (2 ** attempt)) # Backoff exponencial
continue
raise
return None
return wrapper
return decorator
class RobustJoteluluClient(JoteluluClient):
@retry_on_failure(max_retries=3)
def get_organizations_with_retry(self):
api = self.get_organizations_api()
return api.list_user_organizations()Configuración de Timeout
from openapi_client.configuration import Configuration
from openapi_client.api_client import ApiClient
# Configuración con timeouts personalizados
configuration = Configuration(
host="https://connect-eu1.jotelulu.com",
access_token="your_token"
)
# Configurar timeouts
api_client = ApiClient(configuration)
api_client.rest_client.pool_manager.connection_pool_kw['timeout'] = 30Updated 4 months ago