Browse Source

Cronjob sincroniza SIGI com Rancher

pull/159/head
Sesostris Vieira 2 years ago
parent
commit
33a20a9867
  1. 19
      sigi/apps/servicos/__init__.py
  2. 3
      sigi/apps/servicos/admin.py
  3. 204
      sigi/apps/servicos/jobs/daily/sincroniza_rancher.py
  4. 53
      sigi/apps/servicos/migrations/0014_servico_instancia_servico_versao_and_more.py
  5. 27
      sigi/apps/servicos/migrations/0015_nomeia_instancias.py
  6. 15
      sigi/apps/servicos/models.py
  7. 34
      sigi/apps/servicos/templates/servicos/emails/report_sincroniza_rancher.rst
  8. 2
      sigi/settings.py

19
sigi/apps/servicos/__init__.py

@ -0,0 +1,19 @@
def generate_instance_name(orgao):
import re
from sigi.apps.utils import to_ascii
# Orgao deve ser uma instância de sigi.apps.casas.models.Orgao #
if orgao.tipo.sigla == "CM":
return (
re.sub("\W+", "", to_ascii(orgao.municipio.nome)).lower()
+ "-"
+ orgao.municipio.uf.sigla.lower()
)
elif orgao.tipo.sigla == "CT":
return "cl-df"
elif orgao.tipo.sigla == "AL":
return f"al-{orgao.municipio.uf.sigla.lower()}"
elif orgao.tipo.sigla in ["CD", "SF"]:
return re.sub("\W+", "", to_ascii(orgao.nome)).lower()
else:
return f"{orgao.tipo.sigla.lower()}-{orgao.municipio.uf.sigla.lower()}"

3
sigi/apps/servicos/admin.py

@ -69,6 +69,7 @@ class ServicoAdmin(CartExportMixin, admin.ModelAdmin):
] ]
list_display = ( list_display = (
"tipo_servico", "tipo_servico",
"versao",
"casa_legislativa", "casa_legislativa",
"get_uf", "get_uf",
"hospedagem_interlegis", "hospedagem_interlegis",
@ -83,8 +84,10 @@ class ServicoAdmin(CartExportMixin, admin.ModelAdmin):
fields = [ fields = [
"casa_legislativa", "casa_legislativa",
"tipo_servico", "tipo_servico",
"versao",
"url", "url",
"hospedagem_interlegis", "hospedagem_interlegis",
"instancia",
"data_ativacao", "data_ativacao",
"data_alteracao", "data_alteracao",
"data_desativacao", "data_desativacao",

204
sigi/apps/servicos/jobs/daily/sincroniza_rancher.py

@ -0,0 +1,204 @@
import datetime
import docutils.core
import json
from pathlib import Path
from django.conf import settings
from django.core.mail import mail_admins
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.translation import gettext as _
from django_extensions.management.jobs import DailyJob
from sigi.apps.servicos import generate_instance_name
from sigi.apps.servicos.models import Servico, TipoServico
from sigi.apps.casas.models import Orgao
class Job(DailyJob):
help = _("Sincronização dos Serviços SEIT na infraestrutura")
_nomes_gerados = None
_errors = {}
_infos = {}
def execute(self):
print(
_(
"Sincroniza os serviços SEIT a partir da infraestrutura."
f" Início: {datetime.datetime.now(): %d/%m/%Y %H:%M:%S}"
)
)
self._nomes_gerados = {
generate_instance_name(o): o
for o in Orgao.objects.filter(tipo__legislativo=True)
}
print(f"\t{len(self._nomes_gerados)} órgãos que podem ter instâncias.")
for tipo in TipoServico.objects.filter(modo="H").exclude(
tipo_rancher=""
):
print(
_(
f"\tProcessando {tipo.nome}."
f" Início: {datetime.datetime.now():%H:%M:%S}."
),
end="",
)
self.process(tipo)
print(f" Término: {datetime.datetime.now():%H:%M:%S}.")
print("Relatório final:\n================")
self.report()
print(_(f"Término: {datetime.datetime.now(): %d/%m/%Y %H:%M:%S}"))
def process(self, tipo):
NAO_CONSTA = "*não-consta-no-rancher*"
self._errors[tipo] = []
self._infos[tipo] = []
file_path = settings.HOSPEDAGEM_PATH / tipo.arquivo_rancher
if not file_path.exists() or not file_path.is_file():
self._errors[tipo].append(_(f"Arquivo {file_path} não encontado."))
return
with open(file_path, "r") as f:
json_data = json.load(f)
portais = [
item
for item in json_data["items"]
if item["spec"]["chart"]["metadata"]["name"] == tipo.tipo_rancher
]
encontrados = 0
novos = 0
desativados = 0
self._infos[tipo].append(
_(f"{len(portais)} {tipo.nome} encontrados no Rancher")
)
# Atualiza portais existentes e cria novos #
for p in portais:
iname = p["metadata"]["name"]
if tipo.spec_rancher in p["spec"]["values"]:
if "hostname" in p["spec"]["values"][tipo.spec_rancher]:
hostname = p["spec"]["values"][tipo.spec_rancher][
"hostname"
]
elif "domain" in p["spec"]["values"][tipo.spec_rancher]:
hostname = p["spec"]["values"][tipo.spec_rancher]["domain"]
else:
hostname = NAO_CONSTA
self._errors[tipo].append(
_(
f"Instância {iname} de {tipo.nome} sem URL no "
"rancher"
)
)
if "hostprefix" in p["spec"]["values"][tipo.spec_rancher]:
prefix = p["spec"]["values"][tipo.spec_rancher][
"hostprefix"
]
hostname = f"{prefix}.{hostname}"
elif tipo.prefixo_padrao != "":
hostname = f"{tipo.prefixo_padrao}.{hostname}"
else:
hostname = NAO_CONSTA
self._errors[tipo].append(
_(f"Instância {iname} de {tipo.nome} sem URL no rancher")
)
try:
portal = Servico.objects.get(instancia=iname, tipo_servico=tipo)
encontrados += 1
except Servico.DoesNotExist:
if iname in self._nomes_gerados:
orgao = self._nomes_gerados[iname]
portal = Servico(
casa_legislativa=orgao,
tipo_servico=tipo,
instancia=iname,
data_ativacao=p["spec"]["info"]["firstDeployed"][:10],
)
self._infos[tipo].append(
_(
f"Criada instância {iname} de {tipo.nome} para "
f"{orgao.nome} ({orgao.municipio.uf.sigla})"
)
)
novos += 1
else:
self._errors[tipo].append(
_(
f"{iname} ({hostname}) não parece pertencer a "
"nenhum órgão."
)
)
continue
# atualiza o serviço no SIGI
portal.versao = (
p["spec"]["values"]["image"]["tag"]
if "image" in p["spec"]["values"]
else ""
)
if NAO_CONSTA in hostname:
portal.url = ""
else:
portal.url = f"https://{hostname}/"
portal.hospedagem_interlegis = True
portal.save()
# Desativa portais registrados no SIGI que não estão no Rancher #
nomes_instancias = [p["metadata"]["name"] for p in portais]
for portal in Servico.objects.filter(
tipo_servico=tipo, data_desativacao=None, hospedagem_interlegis=True
):
if (
portal.instancia == ""
or portal.instancia not in nomes_instancias
):
portal.data_desativacao = timezone.localdate()
portal.motivo_desativacao = _("Não encontrado no Rancher")
portal.save()
self._infos[tipo].append(
f"{portal.instancia} ({portal.url}) de "
f"{portal.casa_legislativa.nome} desativado pois não "
"foi encontrado no Rancher."
)
desativados += 1
self._infos[tipo].append(
_(f"{encontrados} {tipo.nome} do Rancher encontrados no SIGI")
)
self._infos[tipo].append(
_(f"{novos} novos {tipo.nome} criados no SIGI")
)
self._infos[tipo].append(
_(f"{desativados} {tipo.nome} desativados no SIGI")
)
def report(self):
rst = render_to_string(
"servicos/emails/report_sincroniza_rancher.rst",
{
"erros": self._errors,
"infos": self._infos,
"title": _("Resultado da sincronização do SIGI com o Rancher"),
},
)
html = docutils.core.publish_string(
rst,
writer_name="html5",
settings_overrides={
"input_encoding": "unicode",
"output_encoding": "unicode",
},
)
mail_admins(
subject=self.help,
message=rst,
html_message=html,
fail_silently=True,
)
print(rst)

53
sigi/apps/servicos/migrations/0014_servico_instancia_servico_versao_and_more.py

@ -0,0 +1,53 @@
# Generated by Django 4.1.2 on 2022-10-20 15:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("servicos", "0013_alter_logservico_data_alter_servico_data_ativacao"),
]
operations = [
migrations.AddField(
model_name="servico",
name="instancia",
field=models.CharField(
blank=True, max_length=100, verbose_name="nome da instância"
),
),
migrations.AddField(
model_name="servico",
name="versao",
field=models.CharField(blank=True, max_length=20, verbose_name="versão"),
),
migrations.AddField(
model_name="tiposervico",
name="arquivo_rancher",
field=models.CharField(
blank=True,
max_length=100,
verbose_name="nome do arquivo gerado no rancher",
),
),
migrations.AddField(
model_name="tiposervico",
name="prefixo_padrao",
field=models.CharField(blank=True, max_length=20),
),
migrations.AddField(
model_name="tiposervico",
name="spec_rancher",
field=models.CharField(
blank=True, max_length=100, verbose_name="spec do serviço no Rancher"
),
),
migrations.AddField(
model_name="tiposervico",
name="tipo_rancher",
field=models.CharField(
blank=True, max_length=100, verbose_name="tipo de objeto no Rancher"
),
),
]

27
sigi/apps/servicos/migrations/0015_nomeia_instancias.py

@ -0,0 +1,27 @@
# Generated by Django 4.1.1 on 2022-10-03 21:00
from django.db import migrations
from sigi.apps.servicos import generate_instance_name
def instance_names_fw(apps, schema_editor):
Servico = apps.get_model("servicos", "Servico")
for s in Servico.objects.filter(data_desativacao=None):
s.instancia = generate_instance_name(s.casa_legislativa)
s.save()
def instance_names_rw(apps, schema_editor):
Servico = apps.get_model("servicos", "Servico")
Servico.objects.all().update(instancia="")
class Migration(migrations.Migration):
dependencies = [
("servicos", "0014_servico_instancia_servico_versao_and_more"),
]
operations = [
migrations.RunPython(instance_names_fw, instance_names_rw),
]

15
sigi/apps/servicos/models.py

@ -1,3 +1,4 @@
import black
from django.utils import timezone from django.utils import timezone
from django.db import models from django.db import models
from sigi.apps.casas.models import Orgao, Funcionario from sigi.apps.casas.models import Orgao, Funcionario
@ -18,6 +19,16 @@ class TipoServico(models.Model):
modo = models.CharField( modo = models.CharField(
_("modo de prestação do serviço"), max_length=1, choices=MODO_CHOICES _("modo de prestação do serviço"), max_length=1, choices=MODO_CHOICES
) )
tipo_rancher = models.CharField(
_("tipo de objeto no Rancher"), max_length=100, blank=True
)
spec_rancher = models.CharField(
_("spec do serviço no Rancher"), max_length=100, blank=True
)
arquivo_rancher = models.CharField(
_("nome do arquivo gerado no rancher"), max_length=100, blank=True
)
prefixo_padrao = models.CharField(max_length=20, blank=True)
string_pesquisa = models.TextField( string_pesquisa = models.TextField(
_("string de pesquisa"), blank=True, help_text=string_pesquisa_help _("string de pesquisa"), blank=True, help_text=string_pesquisa_help
) )
@ -63,9 +74,13 @@ class Servico(models.Model):
TipoServico, on_delete=models.PROTECT, verbose_name=_("tipo de serviço") TipoServico, on_delete=models.PROTECT, verbose_name=_("tipo de serviço")
) )
url = models.URLField(_("URL do serviço"), blank=True) url = models.URLField(_("URL do serviço"), blank=True)
versao = models.CharField(_("versão"), max_length=20, blank=True)
hospedagem_interlegis = models.BooleanField( hospedagem_interlegis = models.BooleanField(
_("Hospedagem no Interlegis?"), default=False _("Hospedagem no Interlegis?"), default=False
) )
instancia = models.CharField(
_("nome da instância"), max_length=100, blank=True
)
data_ativacao = models.DateField( data_ativacao = models.DateField(
_("Data de ativação"), default=timezone.localdate _("Data de ativação"), default=timezone.localdate
) )

34
sigi/apps/servicos/templates/servicos/emails/report_sincroniza_rancher.rst

@ -0,0 +1,34 @@
{% extends 'emails/base_email.rst' %}
{% load i18n %}
{% block content %}
{% trans "Resultado da sincronização dos dados de serviços do SIGI com as instâncias instaladas no Rancher." %}
* {% trans "Data/hora de execução" %}: {% now 'SHORT_DATETIME_FORMAT' %}
**{% trans "ERROS ENCONTRADOS" %}**
=====================
{% for tipo, mensagens in erros.items %}
**{{ tipo.nome|upper }} - {{ tipo.sigla|upper }}**
{% for m in mensagens %}
* {{ m }}
{% endfor %}
{% empty %}
*{% trans "Nenhum erro encontrado" %}*
{% endfor %}
**{% trans "INFORMAÇÕES ADICIONAIS" %}**
==========================
{% for tipo, mensagens in infos.items %}
{{ tipo.nome|upper }} - {{ tipo.sigla|upper }}
{% for m in mensagens %}
* {{ m }}
{% endfor %}
{% empty %}
*{% trans "Nenhuma informação adicional gerada" %}*
{% endfor %}
{% endblock content %}

2
sigi/settings.py

@ -253,3 +253,5 @@ TINYMCE_DEFAULT_CONFIG = {
# SIGI specific settings # SIGI specific settings
MENU_FILE = BASE_DIR / "menu_conf.yaml" MENU_FILE = BASE_DIR / "menu_conf.yaml"
HOSPEDAGEM_PATH = Path(env("HOSPEDAGEM_PATH", default="/tmp/HOSP/"))
REGISTRO_PATH = Path(env("REGISTRO_PATH", default="/tmp/DNS/"))

Loading…
Cancel
Save