manage availability check through the UI (#29965)

This commit is contained in:
Emmanuel Cazenave 2019-01-22 14:34:56 +01:00
parent d501c6055b
commit fa76fbc8d7
10 changed files with 222 additions and 4 deletions

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.17 on 2019-02-05 10:26
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('base', '0010_loggingparameters_trace_emails'),
]
operations = [
migrations.CreateModel(
name='AvailabilityParameters',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('resource_pk', models.PositiveIntegerField()),
('run_check', models.BooleanField(default=True, verbose_name='Run regular availability checks')),
('resource_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
],
),
migrations.AlterUniqueTogether(
name='availabilityparameters',
unique_together=set([('resource_type', 'resource_pk')]),
),
]

View File

@ -176,6 +176,18 @@ class BaseResource(models.Model):
parameters.log_level = value
parameters.save()
@property
def availability_parameters(self):
resource_type = ContentType.objects.get_for_model(self)
try:
return AvailabilityParameters.objects.get(
resource_type=resource_type,
resource_pk=self.id)
except AvailabilityParameters.DoesNotExist:
return AvailabilityParameters(
resource_type=resource_type,
resource_pk=self.id)
def soap_client(self, **kwargs):
return passerelle.utils.SOAPClient(resource=self, **kwargs)
@ -387,9 +399,15 @@ class BaseResource(models.Model):
def check_status(self):
# should raise an exception if status is not ok
raise NotImplementedError
check_status.not_implemented = True
def availability(self):
# "availability" cron job to update service statuses
# eventually skip it
if not self.availability_parameters.run_check:
return
currently_down = self.down()
try:
self.check_status()
@ -477,6 +495,18 @@ class LoggingParameters(models.Model):
unique_together = (('resource_type', 'resource_pk'))
class AvailabilityParameters(models.Model):
resource_type = models.ForeignKey(ContentType)
resource_pk = models.PositiveIntegerField()
resource = fields.GenericForeignKey('resource_type', 'resource_pk')
run_check = models.BooleanField(
default=True, verbose_name=_('Run regular availability checks'),
help_text=_('Run an availability check every 5 minutes'))
class Meta:
unique_together = (('resource_type', 'resource_pk'))
class ResourceLog(models.Model):
timestamp = models.DateTimeField(auto_now_add=True)

View File

@ -2,7 +2,7 @@ from django.conf.urls import url
from .views import ApiUserCreateView, ApiUserUpdateView, ApiUserDeleteView, \
ApiUserListView, AccessRightDeleteView, AccessRightCreateView, \
LoggingParametersUpdateView
LoggingParametersUpdateView, ManageAvailabilityView
access_urlpatterns = [
url(r'^$', ApiUserListView.as_view(), name='apiuser-list'),
@ -15,5 +15,8 @@ access_urlpatterns = [
url(r'^accessright/add/(?P<resource_type>[\w,-]+)/(?P<resource_pk>[\w,-]+)/(?P<codename>[\w,-]+)/',
AccessRightCreateView.as_view(), name='access-right-add'),
url(r'logging/parameters/(?P<resource_type>[\w,-]+)/(?P<resource_pk>[\w,-]+)/$',
LoggingParametersUpdateView.as_view(), name='logging-parameters')
LoggingParametersUpdateView.as_view(), name='logging-parameters'),
url(r'manage/availability/(?P<resource_type>[\w,-]+)/(?P<resource_pk>[\w,-]+)/$',
ManageAvailabilityView.as_view(), name='manage-availability')
]

View File

@ -5,7 +5,7 @@ from django.forms import models as model_forms
from django.views.generic import *
from django.http import Http404
from .models import ApiUser, AccessRight, LoggingParameters
from .models import ApiUser, AccessRight, LoggingParameters, AvailabilityParameters, ResourceStatus
from .forms import ApiUserForm, AccessRightForm
from ..utils import get_trusted_services
@ -30,6 +30,7 @@ class ResourceView(DetailView):
context['absolute_uri'] = '%s%s' % (
context['site_base_uri'],
self.request.path)
return context
@ -136,3 +137,54 @@ class LoggingParametersUpdateView(FormView):
parameters.trace_emails = form.cleaned_data['trace_emails']
parameters.save()
return super(LoggingParametersUpdateView, self).form_valid(form)
class ManageAvailabilityView(FormView):
template_name = 'passerelle/manage/manage_availability_form.html'
def get_context_data(self, **kwargs):
context = super(ManageAvailabilityView, self).get_context_data(**kwargs)
connector = self.get_resource()
context['connector'] = connector
context['availability_status'] = connector.get_availability_status()
return context
def get_form_class(self):
form_class = model_forms.modelform_factory(
AvailabilityParameters,
fields=['run_check'])
return form_class
def get_initial(self):
d = self.initial.copy()
d['resource_type'] = self.kwargs['resource_type']
d['resource_pk'] = self.kwargs['resource_pk']
d['run_check'] = self.get_resource().availability_parameters.run_check
return d
def get_resource(self):
content_type = ContentType.objects.get_for_id(self.kwargs['resource_type'])
return content_type.model_class().objects.get(pk=self.kwargs['resource_pk'])
def get_success_url(self):
return self.get_resource().get_absolute_url()
def form_valid(self, form):
resource = self.get_resource()
parameters = resource.availability_parameters
run_check = form.cleaned_data['run_check']
if not run_check and resource.down():
resource_type = ContentType.objects.get_for_model(resource)
ResourceStatus(
resource_type=resource_type,
resource_pk=self.kwargs['resource_pk'],
status='up',
message='').save()
if parameters.run_check != run_check:
parameters.run_check = run_check
parameters.save()
resource.logger.info(u'availability checks %s', 'enabled' if run_check else 'disabled')
return super(ManageAvailabilityView, self).form_valid(form)

View File

@ -602,6 +602,11 @@ class PlanitechConnector(BaseResource):
}
}
def check_status(self):
auth_url = urlparse.urljoin(self.url, 'auth')
response = self.requests.get(auth_url, headers={'MH-LOGIN': self.username})
response.raise_for_status()
class Pairing(models.Model):

View File

@ -0,0 +1,17 @@
{% extends "passerelle/manage.html" %}
{% load i18n %}
{% block appbar %}
<h2>{% trans 'Connector Status' %} : {{availability_status.status}}</h2>
{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans 'Save' %}</button>
<a href="{{ connector.get_absolute_url }}" class="cancel">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -15,6 +15,9 @@
{% endwith %}
</h2>
<span class="actions">
{% if object|can_edit:request.user and has_check_status %}
<a rel="popup" href="{% url 'manage-availability' resource_type=object|resource_type resource_pk=object.id %}">{% trans 'availability check' %}</a>
{% endif %}
{% if object|can_edit:request.user %}
<a rel="popup" href="{% url 'logging-parameters' resource_type=object|resource_type resource_pk=object.id %}">{% trans 'logging parameters' %}</a>
{% endif %}

View File

@ -138,6 +138,13 @@ class GenericConnectorMixin(object):
class GenericConnectorView(GenericConnectorMixin, DetailView):
def get_context_data(self, slug=None, **kwargs):
context = super(GenericConnectorView, self).get_context_data(**kwargs)
context['has_check_status'] = not hasattr(
context['object'].check_status, 'not_implemented')
return context
def get_template_names(self):
template_names = super(DetailView, self).get_template_names()[:]
if self.model.manager_view_template_name:

View File

@ -73,3 +73,23 @@ def test_feed_availability(app, connector):
connector.availability()
assert connector.get_availability_status().down()
assert '500' in connector.get_availability_status().message
def test_availability_checks_disabled(app, connector):
with HTTMock(up_mock):
connector.availability()
assert connector.get_availability_status().up()
av = connector.availability_parameters
av.run_check = False
av.save()
with HTTMock(down_mock):
connector.availability()
assert connector.get_availability_status().up()
av.run_check = True
av.save()
with HTTMock(down_mock):
connector.availability()
assert connector.get_availability_status().down()

View File

@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.files import File
import pytest
from passerelle.base.models import ApiUser, AccessRight, ResourceLog
from passerelle.base.models import ApiUser, AccessRight, ResourceLog, ResourceStatus
from passerelle.apps.csvdatasource.models import CsvDataSource, Query
pytestmark = pytest.mark.django_db
@ -197,3 +197,54 @@ def test_logging_parameters(app, admin_user):
resp = app.get(csv.get_absolute_url())
resp = resp.click('logging parameters')
assert resp.form['trace_emails'].value == 'fred@localhost'
def test_availability_parameters(app, admin_user, monkeypatch):
data = StringIO('1;Foo\n2;Bar\n3;Baz')
csv = CsvDataSource.objects.create(
csv_file=File(data, 't.csv'),
columns_keynames='id, text', slug='test', title='a title', description='a description')
app = login(app)
resp = app.get(csv.get_absolute_url())
assert csv.availability_parameters.run_check
# csv connector has the default check_status which does nothing
# so availability check is hidden
assert 'availability check' not in resp.text
def check_status(*args, **kwargs):
return True
monkeypatch.setattr(CsvDataSource, 'check_status', check_status)
resp = app.get(csv.get_absolute_url())
assert 'availability check' in resp.text
resp = resp.click('availability check')
assert 'up' in resp.text
resp.form['run_check'] = False
resp = resp.form.submit()
# Connector status not changed, availability parameters changed
assert not csv.availability_parameters.run_check
resp = app.get(csv.get_absolute_url())
resp = resp.click('availability check')
resp.form['run_check'] = True
resp = resp.form.submit()
# Connector down
resource_type = ContentType.objects.get_for_model(csv)
status = ResourceStatus(
resource_type=resource_type, resource_pk=csv.pk,
status='down', message='')
status.save()
assert csv.down()
resp = app.get(csv.get_absolute_url())
resp = resp.click('availability check')
resp.form['run_check'] = False
resp = resp.form.submit()
# Connector is put back up
assert not csv.availability_parameters.run_check
assert not csv.down()
status = csv.get_availability_status()
assert status.status == 'up'