0001-general-add-service-availability-basics-9416.patch
debian/passerelle.cron.d | ||
---|---|---|
1 | 1 |
MAILTO=root |
2 | 2 | |
3 |
17 * * * * passerelle /usr/bin/passerelle-manage tenant_command cron --all-tenants hourly |
|
4 |
25 1 * * * passerelle /usr/bin/passerelle-manage tenant_command cron --all-tenants daily |
|
5 |
47 2 * * 7 passerelle /usr/bin/passerelle-manage tenant_command cron --all-tenants weekly |
|
6 |
52 3 1 * * passerelle /usr/bin/passerelle-manage tenant_command cron --all-tenants monthly |
|
3 |
*/5 * * * * passerelle /usr/bin/passerelle-manage tenant_command cron --all-tenants availability |
|
4 |
17 * * * * passerelle /usr/bin/passerelle-manage tenant_command cron --all-tenants hourly |
|
5 |
25 1 * * * passerelle /usr/bin/passerelle-manage tenant_command cron --all-tenants daily |
|
6 |
47 2 * * 7 passerelle /usr/bin/passerelle-manage tenant_command cron --all-tenants weekly |
|
7 |
52 3 1 * * passerelle /usr/bin/passerelle-manage tenant_command cron --all-tenants monthly |
passerelle/apps/base_adresse/models.py | ||
---|---|---|
120 | 120 | |
121 | 121 |
return {'data': result} |
122 | 122 | |
123 |
def check_status(self): |
|
124 |
result = self.search(None, '169 rue du chateau, paris') |
|
125 |
if len(result) == 0: |
|
126 |
raise Exception('no results') |
|
127 | ||
123 | 128 |
def daily(self): |
124 | 129 |
super(BaseAdresse, self).daily() |
125 | 130 |
if not self.zipcode: |
passerelle/apps/feeds/models.py | ||
---|---|---|
37 | 37 |
response.raise_for_status() |
38 | 38 |
feed = feedparser.parse(response.content) |
39 | 39 |
return {'data': feed} |
40 | ||
41 |
def check_status(self): |
|
42 |
if not self.url: |
|
43 |
return |
|
44 |
feed = self.json(None) |
|
45 |
if not feed['data'].get('feed'): |
|
46 |
raise Exception('empty feed') |
passerelle/apps/opengis/models.py | ||
---|---|---|
73 | 73 |
def get_wfs_service_version(self, renew=False): |
74 | 74 |
return self.get_service_version('wfs', self.wfs_service_url, renew=renew) |
75 | 75 | |
76 |
def check_status(self): |
|
77 |
if self.wms_service_url: |
|
78 |
response = self.requests.get(self.wms_service_url, |
|
79 |
params={'service': 'WMS', 'request': 'GetCapabilities'}) |
|
80 |
response.raise_for_status() |
|
81 |
if self.wfs_service_url: |
|
82 |
response = self.requests.get(self.wfs_service_url, |
|
83 |
params={'service': 'WFS', 'request': 'GetCapabilities'}) |
|
84 |
response.raise_for_status() |
|
76 | 85 | |
77 | 86 |
@endpoint(perm='can_access', description='Get features', |
78 | 87 |
parameters={'type_names': {'description': _('Type of feature to query'), 'example_value':'feature'}, |
passerelle/base/management/commands/cron.py | ||
---|---|---|
34 | 34 | |
35 | 35 | |
36 | 36 |
def handle(self, frequency, **options): |
37 |
if frequency not in ('hourly', 'daily', 'weekly', 'monthly'): |
|
37 |
if frequency not in ('hourly', 'daily', 'weekly', 'monthly', 'availability'):
|
|
38 | 38 |
raise CommandError('unknown frequency') |
39 | 39 |
errors = [] |
40 | 40 |
for app in get_all_apps(): |
passerelle/base/migrations/0006_resourcestatus.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('contenttypes', '0002_remove_content_type_name'), |
|
11 |
('base', '0005_resourcelog'), |
|
12 |
] |
|
13 | ||
14 |
operations = [ |
|
15 |
migrations.CreateModel( |
|
16 |
name='ResourceStatus', |
|
17 |
fields=[ |
|
18 |
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), |
|
19 |
('resource_pk', models.PositiveIntegerField()), |
|
20 |
('start_timestamp', models.DateTimeField(auto_now_add=True)), |
|
21 |
('status', models.CharField(default=b'unknown', max_length=20, choices=[(b'unknown', 'Unknown'), (b'up', 'Up'), (b'down', 'Down')])), |
|
22 |
('message', models.CharField(max_length=500, blank=True)), |
|
23 |
('resource_type', models.ForeignKey(to='contenttypes.ContentType')), |
|
24 |
], |
|
25 |
options={ |
|
26 |
'ordering': ['-start_timestamp'], |
|
27 |
}, |
|
28 |
), |
|
29 |
] |
passerelle/base/models.py | ||
---|---|---|
225 | 225 |
_('Access (%s) is limited to the following API users:') % permission) |
226 | 226 |
return [{'key': x[0], 'label': x[1]} for x in perms.items()] |
227 | 227 | |
228 |
def get_availabity_status(self): |
|
229 |
resource_type = ContentType.objects.get_for_model(self) |
|
230 |
current_status = ResourceStatus.objects.filter( |
|
231 |
resource_type=resource_type, |
|
232 |
resource_pk=self.pk).first() |
|
233 |
return current_status |
|
234 | ||
228 | 235 |
def export_json(self): |
229 | 236 |
d = { |
230 | 237 |
'@type': 'passerelle-resource', |
... | ... | |
355 | 362 |
slug=self.slug, |
356 | 363 |
timestamp__lt=timestamp).delete() |
357 | 364 | |
365 |
def check_status(self): |
|
366 |
# should raise an exception if status is not ok |
|
367 |
raise NotImplementedError |
|
368 | ||
369 |
def availability(self): |
|
370 |
# "availability" cron job to update service statuses |
|
371 |
try: |
|
372 |
self.check_status() |
|
373 |
status = 'up' |
|
374 |
message = '' |
|
375 |
except NotImplementedError: |
|
376 |
return |
|
377 |
except Exception as e: |
|
378 |
status = 'down' |
|
379 |
message = unicode(e) |
|
380 | ||
381 |
resource_type = ContentType.objects.get_for_model(self) |
|
382 |
current_status = ResourceStatus.objects.filter( |
|
383 |
resource_type=resource_type, |
|
384 |
resource_pk=self.pk).first() |
|
385 |
if not current_status or status != current_status.status: |
|
386 |
ResourceStatus( |
|
387 |
resource_type=resource_type, |
|
388 |
resource_pk=self.pk, |
|
389 |
status=status, |
|
390 |
message=message).save() |
|
391 |
elif status == 'down': |
|
392 |
current_status.message = message |
|
393 |
current_status.save() |
|
394 | ||
358 | 395 |
def hourly(self): |
359 | 396 |
pass |
360 | 397 | |
... | ... | |
409 | 446 |
return '%s %s %s %s' % (self.timestamp, self.levelno, self.appname, self.slug) |
410 | 447 | |
411 | 448 | |
449 |
STATUS_CHOICES = ( |
|
450 |
('unknown', _('Unknown')), |
|
451 |
('up', _('Up')), |
|
452 |
('down', _('Down')), |
|
453 |
) |
|
454 | ||
455 |
class ResourceStatus(models.Model): |
|
456 |
resource_type = models.ForeignKey(ContentType) |
|
457 |
resource_pk = models.PositiveIntegerField() |
|
458 |
start_timestamp = models.DateTimeField(auto_now_add=True) |
|
459 |
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='unknown') |
|
460 |
message = models.CharField(max_length=500, blank=True) |
|
461 | ||
462 |
class Meta: |
|
463 |
ordering = ['-start_timestamp'] |
|
464 | ||
465 |
def up(self): |
|
466 |
return self.status == 'up' |
|
467 | ||
468 |
def down(self): |
|
469 |
return self.status == 'down' |
|
470 | ||
471 | ||
412 | 472 |
class ProxyLogger(object): |
413 | 473 | |
414 | 474 |
def __init__(self, level, appname=None, slug=None): |
passerelle/static/css/style.css | ||
---|---|---|
4 | 4 |
font-style: normal; |
5 | 5 |
} |
6 | 6 | |
7 |
span.down { |
|
8 |
background: #CD2026; |
|
9 |
color: white; |
|
10 |
padding: 0 0.5ex; |
|
11 |
font-size: 70%; |
|
12 |
border-radius: 5px; |
|
13 |
cursor: help; |
|
14 |
} |
|
15 | ||
7 | 16 |
div#queries, |
8 | 17 |
div#security, |
9 | 18 |
div#logs, |
passerelle/templates/passerelle/manage/service_view.html | ||
---|---|---|
7 | 7 |
{% endblock %} |
8 | 8 | |
9 | 9 |
{% block appbar %} |
10 |
<h2>{{ view.model.get_verbose_name }} - {{ object.title }}</h2> |
|
10 |
<h2>{{ view.model.get_verbose_name }} - {{ object.title }} |
|
11 |
{% with status=object.get_availabity_status %} |
|
12 |
{% if status.down %}<span class="down" title="{{status.message}} {% trans 'since:' %} {{status.start_timestamp|date:"SHORT_DATETIME_FORMAT"}} ">{% trans 'Down' %}</span>{% endif %} |
|
13 |
{% endwith %} |
|
14 |
</h2> |
|
11 | 15 |
{% if object|can_edit:request.user %} |
12 | 16 |
<a rel="popup" href="{{ object.get_edit_url }}">{% trans 'edit' %}</a> |
13 | 17 |
{% endif %} |
passerelle/views.py | ||
---|---|---|
141 | 141 |
class GenericCreateConnectorView(GenericConnectorMixin, CreateView): |
142 | 142 |
template_name = 'passerelle/manage/service_form.html' |
143 | 143 | |
144 |
def form_valid(self, form): |
|
145 |
response = super(GenericCreateConnectorView, self).form_valid(form) |
|
146 |
self.object.availability() |
|
147 |
return response |
|
148 | ||
144 | 149 | |
145 | 150 |
class GenericEditConnectorView(GenericConnectorMixin, UpdateView): |
146 | 151 |
template_name = 'passerelle/manage/service_form.html' |
147 | 152 | |
153 |
def form_valid(self, form): |
|
154 |
response = super(GenericEditConnectorView, self).form_valid(form) |
|
155 |
self.object.availability() |
|
156 |
return response |
|
157 | ||
148 | 158 | |
149 | 159 |
class GenericDeleteConnectorView(GenericConnectorMixin, DeleteView): |
150 | 160 |
template_name = 'passerelle/manage/service_confirm_delete.html' |
151 |
- |