Tag archieven: script

Scheduling jobs

Vanuit de Linux wereld bestaat de term ‘cron’. Deze term komt vanuit het oude Grieks en het betekend tijd. Vanuit oudsher was op Linux de mogelijkheid om op een paar verschillende manieren een scheduled job te starten, zijnde een table (vandaar crontab):

  • user cron
  • /etc/crontab
  • /etc/cron.d/<bestand>
  • /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly

Op deze verschillende plekken, met uitzondering van de eerste, eenvoudig qua syntax een job starten

# minute hour day month day-of-week username command/script
1 12 * * 1 j.doe /opt/myscript.py

Dus elke maandag om 1 minuut over 12 uur word onder de naam j.doe het script uitgevoerd. Het enige verschil met de user cron (crontab -e (edit) of crontab -l (list) is dat de username niet opgevoerd hoeft te worden omdat het in die userspace opereert.

Sinds al een lange poos is de Linux wereld over aan het stappen naar systemd (nouja, is al overgestapt). Deze architectuur en manier van werken is in diverse opzichten een flinke verbetering van de oude init.d scripts/services en dus crontab. Nu is dat geen nieuwtje maar het gebeurt gewoon te makkelijk om “even een crontabje” te zetten.

Systemd staat je toe zelf eenvoudig services te schrijven maar ook targets én timers. De timer functie zoom ik iets meer op in, daar ik vind het een veel betere plek is voor scheduled jobs. Hier komen namelijk ook meer voordelen bij, te denken aan logging/auditing, controle van de jobs, error afhandeling en complexe situaties om pas te starten als ‘iets’ beschikbaar is, of nadat een ander proces succesvol is gestart. De oude cron methode is meer als UDP op het netwerk: fire and forget.

Een voorbeeld is een soort disk-check die elk half uur de vrije ruimte opvraagt en boven, zeg, 75% gebruik een email uitstuurt naar de beheerder. Dit kan eenvoudig in een cron maar we maken er nu een service van.

/usr/lib/systemd/system/diskcheck.service

[Unit]
Description=Disk space check

[Service]
ExecStart=/usr/local/bin/syschk -d

De timer binnen systemd. Deze naam moet overeenkomen met de service.

/usr/lib/systemd/system/diskcheck.timer

[Unit]
Description=Disk space check every 30 minutes

[Timer]
OnCalendar=*:0/30
Persistent=true

[Install]
WantedBy=timers.target

En deze timer schakelen we in: systemctl daemon-reload && systemctl enable diskcheck.timer

Om te zien of alles goed werkt kan je eenvoudig een paar commando’s uitvoeren:

  • systemctl status diskcheck.service
  • systemctl status diskcheck.timer

En zo heb je dus een heel simpel iets als nog heel simpel gemaakt binnen de huidige systemd wereld met voordelen van logging en uiteraard als je wil dat de service uit gezet moet worden:

  • systemctl disable diskcheck.timer

Een andere optie is een service te maken die alleen actief is bij opstarten (on-boot). Dit voorbeeld start een script (met 2 command line opties) dat pas start als het netwerk online is én postfix gestart is. Waarom? Als de ‘syschk’ iets afwijkends detecteert moet het een email kunnen uitsturen.

[Unit]
Description=HouseKeeping at boot
After=network-online.target
After=postfix.service

[Service]
Type=simple
ExecStart=/usr/local/bin/syschk -bp

[Install]
WantedBy=multi-user.target

Met name de After regels zijn in dit geval waarmee het gestuurd word.

Als je besluit dat het een background daemon of iets dergelijk moet worden, dan kan dat ook op deze manier maar dan moet je in jouw proces of script wel het altijd aan laten. Denk aan een lowlevel ‘while true’ loop (maar denk aan je cpu cylcles ;).

Het nakijken van statussen en logs is bijvoorbeeld met eerder genoemde systemctl commando. Echter is er ook vanuit het Linux eco systeem een andere optie: journalctl.

  • journalctl -eu diskcheck.service

De opties zijn flink en de gekozen 2 zijn eenvoudig de eerste om mee te beginnen. De ‘-e’ is pager-end, of wel het laatste stukje en de ‘-u’ is de betreffende unit. In dit voorbeeld de eerder gemaakte diskcheck.service.

Uiteraard zijn er veel meer opties met journalctl om in te zoomen op bijv. tijdsvakken en nog meer. Zie ook de ‘manpages’.

Zo makkelijk als het lijkt om een service, met timer of niet, te maken is, een veelal vergeten aspect, natuurlijk wel even denken aan veiligheid. De systemd services kunnen en mogen veel, ‘out-of-the-box’. Dat is voor een lokaal servertje thuis niet direct een probleem maar een webserver of waar gevoelige data verwerkt kan worden is dan een interessant object. Als je daar, op welke manier dan ook, gebruik kan maken van zwakheden dan is dat als kwaadwillende top. Ik ga hier in dit artikel niet dieper op in maar verdiep gerust even in de materie:

https://linux-audit.com/systemd/systemd-features-to-secure-units-and-services

Informatie ophalen via API

De Internet wereld hangt al geruime tijd flink aan elkaar met API’s. Een API is een “Application Programming Interface”. Daar kan je dus veel dingen mee.

Zoals een klein scriptje dat ik gemaakt heb voor wat basis statistieken omtrent Crypto currency. Ik heb een aantal coins van, jammergenoeg, lage waarde maar toch wil ik niet elke keer zelf opzoeken wat de huidige prijs verloop is. Ook had ik eerst een App op de mobiel maar na een update van de leverancier was mijn overzicht weer leeg. Fijn.

Coincapmarket.com is een hele goede website om snel inzicht te krijgen in het verloop, de prijs en nog meer informatie omtrent een coin. Blijkbaar ook, via een eenmalige registratie, een API functionaliteit. Hè presto.

Een API call is niets meer dan “iets” opvragen en dat krijg je, vaak, terug als JSON data. Deze structuur is prima te verwerken. Omgekeerd kan ook, via de juiste structuur data aanleveren aan het systeem. Hiermee is dus veel automatisering mogelijk tussen verschillende systemen.

#
# Created 9-Feb 2019
# @author: bartjan@pc-mania.nl
# https://github.com/barreljan/cc-stats
# 
import requests
import sys
import smtplib

assets = {
    'BTC': 2,
    'ETH': 1,
    'MNR': 10
          }


def sendmail(msg):
    msg = "Subject: Latest coin prices" + "\n\n" + msg
    server = smtplib.SMTP('smtp.somehost.com', 25)
    server.sendmail('noreply@yourdomain.org', 'john@doe.net', msg)


def cryptodata(assets):
    coins = str()
    for coin, qty in assets.items():
        if coins is "":
            coins += coin
        else:
            coins += ",{}".format(coin)

    link = 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol={}'.format(coins)
    api_key = 'your Coinmarketcap Pro key'
    headers = {
        'X-CMC_PRO_API_KEY': api_key,
        'Accept': 'application/json'
    }

    try:
        data = requests.get(link, headers=headers)
    except Exception:
        print("Could not connect")
        sys.exit(1)

    jdata = data.json()

    try:
        if jdata['data']:
            return jdata['data']
        elif not jdata['data']:
            raise KeyError('No data returned')
    except KeyError:
        print("API Call went wrong, no usable data returned")
        sys.exit(1)


# Run it
allcrypto = cryptodata(assets)

msg = "{0:8}{1:<8}\t{2}\t{3}\n".format("Coin", "Qty", "Price", "Totals (USD)")

for coin, coinitems in allcrypto.items():
    price = coinitems['quote']['USD']['price']
    assetval = price*assets[coin]
    msg += "{0:8}{1:<8}\t{2:.2f}:\t{3:.2f}\n".format(coin, assets[coin], price, assetval)

sendmail(msg)

En zo kan je vrij eenvoudig data ophalen en verwerken. Dit script doet namelijk:

  • alle informatie ophalen, op basis van de quote (dus pricing) op basis van de gewenste coins (assets)
    • code: allcrypto = cryptodata(assets)
  • een bericht starten
  • per coin uit de json data de prijs halen en de totale waarde berekenen
    • code: for coin, coinitems in allcrypto.items()
  • het bericht aanvullen
  • en uiteindelijk het bericht versturen
    • code: sendmail(msg)

En uiteindelijk krijg je een mooi overzicht in de email. Dit script draait nu elke dag op 12.00u en stuurt een email naar mijn postvak.

Coin    Qty     Price       Totals (USD)
BTC     2       3651.62:    7303.24
ETH     1       117.69 :    117.69

Uiteraard is dit script ook via Github te vinden. Zie het aan de linkerzijde de Link.

ps.: de coins genoemd hier, zijn als voorbeeld en niet mijn jammerlijke hoeveelheid alt-coins.