0001-manage-availability-check-through-the-UI-29965.patch
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 |
- |