08 - Komutlar

June 02, 2018 4 minute read

Web projelerinin en önemli bölümlerinden biri de teknik operasyonlar. Bazen birikmiş sessionları temizlemek, bazen çeviri dosyalarını oluşturmak, bazen de sadece arama motorlarına buradayım demek için komut satırı uygulamalarına ihtiyaç duyarız. Bizim de uptime uygulamasında, ekranları kontrol etmek için benzer bir komut satırı uygulamasına ihtiyacımız var.

Özet

  • URL’lerin erişilebilir olup olmadığını anlayabileceğimiz bir komut yazacağız.
  • Ekran durumlarını admin panelinden ve kullanıcı arayüzünden görünür hale getireceğiz.
  • Bu yazıyla ilgili kodlara buradan erişebilirsiniz.

Ekranları kontrol edelim

Kullanıcılar için Uptime uygulamasını yazdık, kullanıcılar ekranları giriyor, bizim de bu ekranları periyodik olarak kontrol etmemiz gerekiyor. Periyodik kısmını sonraya bırakalım ve önce ekranları kontrol etme kısmına odaklanalım.

En nihayetinde yapacağımız şey, komut satırından komutumuzu url parametreleriyle çalıştırmak ve o url’i kullanan ekran sayısını öğrenmek, url’in erişim durumunu kontrol etmek, ve bilgileri veritabanına işlemek:

# terminal
$ python manage.py checkurls https://blabla.com https://gokmengorgen.net https://radity.com
https://blabla.com - 0 monitor(s): offline
https://gokmengorgen.net - 2 monitor(s): online
https://radity.com - 1 monitor(s): online

Hatırlamak için modelimizi tekrar incelemekte fayda var:

# hello_uptime/models.py
...
class Monitor(models.Model):
    user = models.ForeignKey(
        verbose_name=_("User"), to=settings.AUTH_USER_MODEL, related_name='monitors', on_delete=models.CASCADE,
        null=True)
    url = models.URLField(verbose_name=_("URL"))
    interval = models.PositiveSmallIntegerField(
        verbose_name=_("Monitoring interval"), choices=MonitoringInterval.get_choices(),
        default=MonitoringInterval.get_default())
    status = models.CharField(verbose_name=_("Status"), max_length=9, choices=MonitorStatus.get_choices(), blank=True)
    is_active = models.BooleanField(verbose_name=_("Is active?"), blank=True, default=True)
    checked_at = models.DateTimeField(verbose_name=_("Checked at"), null=True, editable=False)
    created_at = models.DateTimeField(verbose_name=_("Created at"), auto_now_add=True)
...

Burada bazı field’ları henüz tam anlamıyla kullanmadık:

  • status: URL’in erişim durumunu burada saklayacağız.
  • is_active: Aktif olmayan ekranları pas geçeceğiz.
  • checked_at: En son ne zaman kontrol ettiğimiz bilgisini saklayacağız.

Bizim komutumuz, ekran bilgilerini bu fieldları kullanarak saklayacak. Django belgesinde anlatıldığı1 üzere özel bir komut yazabilmemiz için, script’imizi management/commands/ içinde tutmamız gerekiyor2. Komutu hazırlarken bir kuralımız var, bu komutu kullanacak kişinin veya uygulamanın hiçbir şekilde yanlış ve saçmasapan URL girmeyeceğini varsayıyoruz. Bunu varsayabiliriz, çünkü hem uptime uygulamasında, hem admin panelinde URL’ler için bir kontrolümüz var ki geçerli URL girilmemişse veritabanına kaydedilemiyor:

# hello_uptime/management/commands/checkurls.py
import requests
from django.core.management.base import BaseCommand
from django.utils import timezone
from requests.exceptions import SSLError

from hello_uptime.models import Monitor
from hello_uptime.utils import MonitorStatus


class Command(BaseCommand):
    help = "Checks the urls for monitors"

    def add_arguments(self, parser):
        # Komutumuz çoklu parametre alıyor ve zorunlu. URL'leri komut satırında yazacağız.
        parser.add_argument('urls', nargs='+', type=str)

    def handle(self, *args, **options):
        # Komutumuz çalıştırıldığında bu fonksiyonu çalıştıracak. Parametrelerimize de buradan erişebileceğiz.
        urls = options['urls']

        for url in urls:
            monitors = Monitor.objects.filter(is_active=True, url=url)
            # Komut satırından ekranlar hakkında bilgi almak için birkaç satır ekliyoruz.
            self.stdout.write(self.style.WARNING("{} - {} monitor(s)".format(url, monitors.count())), ending=': ')

            try:
                response = requests.get(url)
                status = MonitorStatus.ONLINE if response.status_code == 200 else MonitorStatus.OFFLINE
            except SSLError:
                status = MonitorStatus.OFFLINE

            monitors.update(status=status, checked_at=timezone.now())  # Sonucu saklıyoruz.
            status_style = self.style.ERROR if status == MonitorStatus.OFFLINE else self.style.SUCCESS
            self.stdout.write(status_style(status))

Kullanıcı ekran durumlarını nasıl görecek?

Komutumuzu çalıştırdıktan sonra sonuçları iki yerde göreceğiz. Birincisi admin paneli:

Monitor status

Ama admin paneli sadece yetkili kullanıcılar için. Normal kullanıcılar ne yapacak? main.css içine birkaç satır stil ekleyelim ve sonra dashboard.html dosyasını düzenleyelim:

/* static/main.css */
...
table.monitor-form {
    margin-top: 40px;
}

table.monitor-form th,
table.monitor-buttons th {
    padding-right: 10px;
    text-align: right;
    width: 200px;
}

table.monitor-form td.status-online,
table.monitor-form td.status-offline {
    padding: 0 5px;
    color: white;
}

table.monitor-form td.status-online {
    background-color: green;
}

table.monitor-form td.status-offline {
    background-color: red;
}

table.monitor-buttons {
    margin-top: 20px;
}
<!-- templates/uptime/dashboard.html -->
...
<form action="{% url 'uptime:dashboard' %}" method="POST">
    {% csrf_token %}
    {{ formset.management_form }}

    {% for form in formset %}
        <table class="monitor-form">
            <tr>
                <th></th>
                <td>{% blocktrans with number=forloop.counter %}Monitor {{ number }}{% endblocktrans %}</td>
            </tr>
            {% if form.instance.pk %}
                <tr>
                    <th>{% trans "Status" %}:</th>
                    <td class="status-{{ form.instance.status }}">{{ form.instance.get_status_display }}</td>
                </tr>
                <tr>
                    <th>{% trans "Checked at" %}:</th>
                    <td>{{ form.instance.checked_at }}</td>
                </tr>
            {% endif %}
            {{ form.as_table }}
        </table>
    {% endfor %}
    <table class="monitor-buttons">
        <tr>
            <th></th>
            <td><button type="submit">{% trans "Save" %}</button></td>
        </tr>
    </table>
</form>
...

Monitor status in frontend

Peki nasıl komutu periyodik çalıştıracağız?

Bunu anlatabilmem için sunucu tarafına biraz değinmemiz gerekiyor. Bu zamana kadar Django projelerini hep Linux dağıtımlarını kullanarak servis ettim ve periyodik çalıştırılacak işler konusunda projemizi barındırdığımız sistemden destek almamız gerekiyor. Birkaç yolu var, en basiti crontab kullanmak, kullanımı çok basit, bir shell script’i hazırladıktan sonra sunucuda crontab -e komutunu çalıştırıp buna benzer bir satır eklememiz gerekiyor:

# terminal
5 * * * * django-notes /home/django-notes/sync_monitors.sh

Böylece 5 dakikada bir ekranları kontrol ederdik. Ama bana bir hafta daha izin verin, size bunu başka bir yöntem olan Celery3 ile nasıl yapılacağını göstereceğim. İyi günler!


  1. Komutlar çok detaylı bir konu olduğu için, bu belgeye mutlaka bakmanızı öneriyorum. Ben burada sadece Uptime uygulamasında gerekli olduğu kadarıyla ele aldım. [return]
  2. Daha önce de belirttiğimiz gibi, bir dizinin python modülü olarak algılanabilmesi için __init__.py dosyasına ihtiyacımız var. Bu her dizin için geçerlidir, iç içe olsa bile. [return]
  3. Celery için mutlaka sitesine bir göz atın. [return]