Projet

Général

Profil

0001-manage-availability-check-through-the-UI-29965.patch

Emmanuel Cazenave, 11 février 2019 17:15

Télécharger (14,6 ko)

Voir les différences:

Subject: [PATCH 1/2] manage availability check through the UI (#29965)

 .../migrations/0011_auto_20190205_1126.py     | 30 +++++++++++
 passerelle/base/models.py                     | 30 +++++++++++
 passerelle/base/urls.py                       |  7 ++-
 passerelle/base/views.py                      | 54 ++++++++++++++++++-
 passerelle/contrib/planitech/models.py        |  5 ++
 .../manage/manage_availability_form.html      | 17 ++++++
 .../passerelle/manage/service_view.html       |  3 ++
 passerelle/views.py                           |  7 +++
 tests/test_availability.py                    | 20 +++++++
 tests/test_manager.py                         | 53 +++++++++++++++++-
 10 files changed, 222 insertions(+), 4 deletions(-)
 create mode 100644 passerelle/base/migrations/0011_auto_20190205_1126.py
 create mode 100644 passerelle/templates/passerelle/manage/manage_availability_form.html
passerelle/base/migrations/0011_auto_20190205_1126.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.17 on 2019-02-05 10:26
3
from __future__ import unicode_literals
4

  
5
from django.db import migrations, models
6
import django.db.models.deletion
7

  
8

  
9
class Migration(migrations.Migration):
10

  
11
    dependencies = [
12
        ('contenttypes', '0002_remove_content_type_name'),
13
        ('base', '0010_loggingparameters_trace_emails'),
14
    ]
15

  
16
    operations = [
17
        migrations.CreateModel(
18
            name='AvailabilityParameters',
19
            fields=[
20
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
                ('resource_pk', models.PositiveIntegerField()),
22
                ('run_check', models.BooleanField(default=True, verbose_name='Run regular availability checks')),
23
                ('resource_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
24
            ],
25
        ),
26
        migrations.AlterUniqueTogether(
27
            name='availabilityparameters',
28
            unique_together=set([('resource_type', 'resource_pk')]),
29
        ),
30
    ]
passerelle/base/models.py
176 176
        parameters.log_level = value
177 177
        parameters.save()
178 178

  
179
    @property
180
    def availability_parameters(self):
181
        resource_type = ContentType.objects.get_for_model(self)
182
        try:
183
            return AvailabilityParameters.objects.get(
184
                    resource_type=resource_type,
185
                    resource_pk=self.id)
186
        except AvailabilityParameters.DoesNotExist:
187
            return AvailabilityParameters(
188
                    resource_type=resource_type,
189
                    resource_pk=self.id)
190

  
179 191
    def soap_client(self, **kwargs):
180 192
        return passerelle.utils.SOAPClient(resource=self, **kwargs)
181 193

  
......
387 399
    def check_status(self):
388 400
        # should raise an exception if status is not ok
389 401
        raise NotImplementedError
402
    check_status.not_implemented = True
390 403

  
391 404
    def availability(self):
392 405
        # "availability" cron job to update service statuses
406

  
407
        # eventually skip it
408
        if not self.availability_parameters.run_check:
409
            return
410

  
393 411
        currently_down = self.down()
394 412
        try:
395 413
            self.check_status()
......
477 495
        unique_together = (('resource_type', 'resource_pk'))
478 496

  
479 497

  
498
class AvailabilityParameters(models.Model):
499
    resource_type = models.ForeignKey(ContentType)
500
    resource_pk = models.PositiveIntegerField()
501
    resource = fields.GenericForeignKey('resource_type', 'resource_pk')
502
    run_check = models.BooleanField(
503
        default=True, verbose_name=_('Run regular availability checks'),
504
        help_text=_('Run an availability check every 5 minutes'))
505

  
506
    class Meta:
507
        unique_together = (('resource_type', 'resource_pk'))
508

  
509

  
480 510

  
481 511
class ResourceLog(models.Model):
482 512
    timestamp = models.DateTimeField(auto_now_add=True)
passerelle/base/urls.py
2 2

  
3 3
from .views import ApiUserCreateView, ApiUserUpdateView, ApiUserDeleteView, \
4 4
        ApiUserListView, AccessRightDeleteView, AccessRightCreateView, \
5
        LoggingParametersUpdateView
5
        LoggingParametersUpdateView, ManageAvailabilityView
6 6

  
7 7
access_urlpatterns = [
8 8
    url(r'^$', ApiUserListView.as_view(), name='apiuser-list'),
......
15 15
    url(r'^accessright/add/(?P<resource_type>[\w,-]+)/(?P<resource_pk>[\w,-]+)/(?P<codename>[\w,-]+)/',
16 16
        AccessRightCreateView.as_view(), name='access-right-add'),
17 17
    url(r'logging/parameters/(?P<resource_type>[\w,-]+)/(?P<resource_pk>[\w,-]+)/$',
18
        LoggingParametersUpdateView.as_view(), name='logging-parameters')
18
        LoggingParametersUpdateView.as_view(), name='logging-parameters'),
19
    url(r'manage/availability/(?P<resource_type>[\w,-]+)/(?P<resource_pk>[\w,-]+)/$',
20
        ManageAvailabilityView.as_view(), name='manage-availability')
21

  
19 22
]
passerelle/base/views.py
5 5
from django.views.generic import *
6 6
from django.http import Http404
7 7

  
8
from .models import ApiUser, AccessRight, LoggingParameters
8
from .models import ApiUser, AccessRight, LoggingParameters, AvailabilityParameters, ResourceStatus
9 9
from .forms import ApiUserForm, AccessRightForm
10 10
from ..utils import get_trusted_services
11 11

  
......
30 30
        context['absolute_uri'] = '%s%s' % (
31 31
                context['site_base_uri'],
32 32
                self.request.path)
33

  
33 34
        return context
34 35

  
35 36

  
......
136 137
        parameters.trace_emails = form.cleaned_data['trace_emails']
137 138
        parameters.save()
138 139
        return super(LoggingParametersUpdateView, self).form_valid(form)
140

  
141

  
142
class ManageAvailabilityView(FormView):
143
    template_name = 'passerelle/manage/manage_availability_form.html'
144

  
145
    def get_context_data(self, **kwargs):
146
        context = super(ManageAvailabilityView, self).get_context_data(**kwargs)
147
        connector = self.get_resource()
148
        context['connector'] = connector
149
        context['availability_status'] = connector.get_availability_status()
150
        return context
151

  
152
    def get_form_class(self):
153
        form_class = model_forms.modelform_factory(
154
                AvailabilityParameters,
155
                fields=['run_check'])
156
        return form_class
157

  
158
    def get_initial(self):
159
        d = self.initial.copy()
160
        d['resource_type'] = self.kwargs['resource_type']
161
        d['resource_pk'] = self.kwargs['resource_pk']
162
        d['run_check'] = self.get_resource().availability_parameters.run_check
163
        return d
164

  
165
    def get_resource(self):
166
        content_type = ContentType.objects.get_for_id(self.kwargs['resource_type'])
167
        return content_type.model_class().objects.get(pk=self.kwargs['resource_pk'])
168

  
169
    def get_success_url(self):
170
        return self.get_resource().get_absolute_url()
171

  
172
    def form_valid(self, form):
173
        resource = self.get_resource()
174
        parameters = resource.availability_parameters
175
        run_check = form.cleaned_data['run_check']
176

  
177
        if not run_check and resource.down():
178
            resource_type = ContentType.objects.get_for_model(resource)
179
            ResourceStatus(
180
                resource_type=resource_type,
181
                resource_pk=self.kwargs['resource_pk'],
182
                status='up',
183
                message='').save()
184

  
185
        if parameters.run_check != run_check:
186
            parameters.run_check = run_check
187
            parameters.save()
188
            resource.logger.info(u'availability checks %s', 'enabled' if run_check else 'disabled')
189

  
190
        return super(ManageAvailabilityView, self).form_valid(form)
passerelle/contrib/planitech/models.py
602 602
            }
603 603
        }
604 604

  
605
    def check_status(self):
606
        auth_url = urlparse.urljoin(self.url, 'auth')
607
        response = self.requests.get(auth_url, headers={'MH-LOGIN': self.username})
608
        response.raise_for_status()
609

  
605 610

  
606 611
class Pairing(models.Model):
607 612

  
passerelle/templates/passerelle/manage/manage_availability_form.html
1
{% extends "passerelle/manage.html" %}
2
{% load i18n %}
3

  
4
{% block appbar %}
5
<h2>{% trans 'Connector Status' %} : {{availability_status.status}}</h2>
6
{% endblock %}
7

  
8
{% block content %}
9
<form method="post">
10
  {% csrf_token %}
11
  {{ form.as_p }}
12
  <div class="buttons">
13
    <button class="submit-button">{% trans 'Save' %}</button>
14
    <a href="{{ connector.get_absolute_url }}" class="cancel">{% trans 'Cancel' %}</a>
15
  </div>
16
</form>
17
{% endblock %}
passerelle/templates/passerelle/manage/service_view.html
15 15
    {% endwith %}
16 16
</h2>
17 17
<span class="actions">
18
{% if object|can_edit:request.user and has_check_status %}
19
<a rel="popup" href="{% url 'manage-availability' resource_type=object|resource_type resource_pk=object.id %}">{% trans 'availability check' %}</a>
20
{% endif %}
18 21
{% if object|can_edit:request.user %}
19 22
<a rel="popup" href="{% url 'logging-parameters' resource_type=object|resource_type resource_pk=object.id %}">{% trans 'logging parameters' %}</a>
20 23
{% endif %}
passerelle/views.py
138 138

  
139 139

  
140 140
class GenericConnectorView(GenericConnectorMixin, DetailView):
141

  
142
    def get_context_data(self, slug=None, **kwargs):
143
        context = super(GenericConnectorView, self).get_context_data(**kwargs)
144
        context['has_check_status'] = not hasattr(
145
            context['object'].check_status, 'not_implemented')
146
        return context
147

  
141 148
    def get_template_names(self):
142 149
        template_names = super(DetailView, self).get_template_names()[:]
143 150
        if self.model.manager_view_template_name:
tests/test_availability.py
73 73
        connector.availability()
74 74
    assert connector.get_availability_status().down()
75 75
    assert '500' in connector.get_availability_status().message
76

  
77

  
78
def test_availability_checks_disabled(app, connector):
79
    with HTTMock(up_mock):
80
        connector.availability()
81
    assert connector.get_availability_status().up()
82

  
83
    av = connector.availability_parameters
84
    av.run_check = False
85
    av.save()
86

  
87
    with HTTMock(down_mock):
88
        connector.availability()
89
    assert connector.get_availability_status().up()
90

  
91
    av.run_check = True
92
    av.save()
93
    with HTTMock(down_mock):
94
        connector.availability()
95
    assert connector.get_availability_status().down()
tests/test_manager.py
7 7
from django.core.files import File
8 8
import pytest
9 9

  
10
from passerelle.base.models import ApiUser, AccessRight, ResourceLog
10
from passerelle.base.models import ApiUser, AccessRight, ResourceLog, ResourceStatus
11 11
from passerelle.apps.csvdatasource.models import CsvDataSource, Query
12 12

  
13 13
pytestmark = pytest.mark.django_db
......
197 197
    resp = app.get(csv.get_absolute_url())
198 198
    resp = resp.click('logging parameters')
199 199
    assert resp.form['trace_emails'].value == 'fred@localhost'
200

  
201

  
202
def test_availability_parameters(app, admin_user, monkeypatch):
203
    data = StringIO('1;Foo\n2;Bar\n3;Baz')
204
    csv = CsvDataSource.objects.create(
205
        csv_file=File(data, 't.csv'),
206
        columns_keynames='id, text', slug='test', title='a title', description='a description')
207
    app = login(app)
208
    resp = app.get(csv.get_absolute_url())
209

  
210
    assert csv.availability_parameters.run_check
211
    # csv connector has the default check_status which does nothing
212
    # so availability check is hidden
213
    assert 'availability check' not in resp.text
214

  
215
    def check_status(*args, **kwargs):
216
        return True
217

  
218
    monkeypatch.setattr(CsvDataSource, 'check_status', check_status)
219

  
220
    resp = app.get(csv.get_absolute_url())
221
    assert 'availability check' in resp.text
222

  
223
    resp = resp.click('availability check')
224
    assert 'up' in resp.text
225
    resp.form['run_check'] = False
226
    resp = resp.form.submit()
227
    # Connector status not changed, availability parameters changed
228
    assert not csv.availability_parameters.run_check
229

  
230
    resp = app.get(csv.get_absolute_url())
231
    resp = resp.click('availability check')
232
    resp.form['run_check'] = True
233
    resp = resp.form.submit()
234

  
235
    # Connector down
236
    resource_type = ContentType.objects.get_for_model(csv)
237
    status = ResourceStatus(
238
        resource_type=resource_type, resource_pk=csv.pk,
239
        status='down', message='')
240
    status.save()
241
    assert csv.down()
242
    resp = app.get(csv.get_absolute_url())
243
    resp = resp.click('availability check')
244
    resp.form['run_check'] = False
245
    resp = resp.form.submit()
246
    # Connector is put back up
247
    assert not csv.availability_parameters.run_check
248
    assert not csv.down()
249
    status = csv.get_availability_status()
250
    assert status.status == 'up'
200
-