In de vorige post Mod_wsgi in Apache hebben we de module in Apache 2.4 op Centos7 beschikbaar gemaakt. Het gebruik daarentegen is de volgende stap.
Laten we eerst beginnen met een simpele API in de default document root ‘/var/www/html/’ waar we een directory maken api_test. Daarin maken we een virtuele omgeving via python3.8 en zetten wat structuur klaar:
In het ‘wsgi’ bestand wat Apache gaat lezen instrueren we wat er gestart moet en een aantal path variabelen. De verwijzing naar de juiste package directory is vanwege de ‘virtual environment’. De andere is om de import van de API te laten werken. Uiteindelijk importeren wij dus uit de api de app als application
import sys
sys.path.insert(0, '/var/www/html/api_test/venv/lib/python3.8/site-packages')
sys.path.insert(1, '/var/www/html/api_test')
from api import app as application
Binnen Apache, ‘virtual hosts’ of niet, kan je een aantal parameters opnemen die de mod_wsgi aansturen en het bovengenoemde bestand uitvoeren. Dit moet ingevoerd worden binnen de VirtualHost tags of daar buiten:
WSGIDaemonProcess api user=apache group=apache threads=5 home=/var/www/html/api_test
WSGIScriptAlias /api/v1 /var/www/html/api_test/api.wsgi
<Directory /var/www/html/api_test>
WSGIProcessGroup api
<IfModule mod_authz_core.c>
Require all granted
</IfModule>
</Directory>
Na het herstarten van de ‘httpd’ daemon zou de api beschikbaar moeten zijn. Laten we het testen van een externe server:
Om via een webserver Python scripts te kunnen gebruiken en specifiek Django of Flask voor bijvoorbeeld API deployments zal je de server iets bij moeten werken.
Onderstaand voorbeeld is hoe je dus Mod_wsgi activeert in een Centos7 server voorzien van Apache 2.4. De keuze op Python versie is sowieso 3.x en in dit voorbeeld 3.8.7.
Belangrijk is om root rechten te hebben. We beginnen met 2 environment variabelen te definiëren:
Hierna zorgen we dat de package apxs beschikbaar is, die via httpd-devel te vinden is:
yum install -y httpd-devel
Dan is het zaak Python te installeren. Vanwege redenen is Centos7 nog steeds standaard uitgerust met versie 2.7 en dat willen we verder niet gebruiken. Het is dus belangrijk dat de installatie naast de bestaande komt te draaien. Het hele Centos ecosysteem is afhankelijk van de huidige versie. Dat word met ‘make altinstall‘ bepaald.
Nog veel belangrijker is de ‘–enable-shared‘ optie waarme Python op de juiste manier compiled word. Vergeet deze niet!
cd /tmp
wget https://www.python.org/ftp/python/3.8.7/Python-3.8.7.tgz
tar -xf Python-3.8.7.tgz -C /usr/local/src
cd /usr/local/src/Python-3.8.7
./configure --enable-shared --enable-optimizations
make altinstall
Als het goed is kan je daarna Python3.8 starten:
[root@mightyserver ~]# python3.8
Python 3.8.7 (default, Jan 2 2021, 14:19:51)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Voor we de module mod_wsgi introduceren in Apache moet er nog een kleine update gedaan worden:
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.
Ooit, een hele tijd terug heb ik een APC rack pdu (ap7921) in huis gehaald om 2 dingen te doen. Het meten van het totale verbruik van de mediahoek én het in/uitschakelen van lichten of andere zaken. Dat kan prima met deze pdu, want er zit netwerk op en dus ook via SNMP aan te sturen. Via een php webpagina (die ik met mijn mobile device kan openen) kan ik al heel lang de belangrijkste outlets schakelen en ik heb ook al een tijd een cronjob op een server draaien die de lichten schakelen. Ja, ook de kerstboom hoewel het stekkerblok op die outlet nu onder de bank ligt voor algemeen gebruik. De subwoofer was voor het gemak, tijdens een film licht uit, sub aan en geniet.
De pagina stuurt in feite ook via een ‘snmpset’ commando een 1 of 2 naar de juiste poort. Daarbij is de kleur groen ‘aan’ en rood is ‘uit’ puur voor de opmaak.
Het script in cronjob is een paar keer herschreven en kwijt geraakt door een crash van de OrangePi thuis en dus weer opnieuw gemaakt. Voor nu is het een ‘fancy’ versie want het kan uiteraard ook gewoon met praktisch 8 of zo regels. Maar dat is het mooie, je wil leren en je script verder ontwikkelen, verharden. Een geval van “good practice” dus.
#!/bin/bash#
# Switching on or off light at home
# bartjan@pc-mania.nl - 08 Mar 2018
snmp_exec="/usr/bin/snmpset"
function check_reqs {
if [ ! -f $snmp_exec ]; then
echo "Error: snmpset executable not found.."
echo ""
exit 1
fi
}
function show_help {
echo "Error: incorrect syntax.."
echo "Usage: ./lights.sh [on|off]"
echo ""
}
function switch {
# 1st arg is outlet nr (integer)
# 2nd arg options: on / off
if [ $2 == "on" ]; then
${snmp_exec} -v2c -cpublic 192.168.1.251 1.3.6.1.4.1.318.1.1.4.4.2.1.3.${1} i 1 1>/dev/null 2>&1
elif [ $2 == "off" ]; then
${snmp_exec} -v2c -cpublic 192.168.1.251 1.3.6.1.4.1.318.1.1.4.4.2.1.3.${1} i 2 1>/dev/null 2>&1
fi
}
# Start the program
check_reqs
if [ ! $1 ]; then
show_help
exit 1
fi
if [ $1 == "on" ]; then
echo "Iluminate"
switch 1 on
switch 8 on
elif [ $1 == "off" ]; then
echo "Deluminate"
switch 1 off
switch 8 off
else
show_help
exit 1
fi
De output op de cli is vanzelf sprekend. De cronjob zelf voert de output naar dev/null.
Nu kwam het zo naar boven dat in de herfst de tijden van de job in cron verschuiven. Elke keer dit aanpassen is suf en dat kan slimmer. Ik ben al een ruime tijd veel meer met Python bezig en dat bevalt erg goed. Ik heb een script geschreven die tijden ophaalt via een API call bij een publieke dienst aan de hand van de fysieke locatie. Hiermee kan ik de crontab van de user ‘root’ op de OrangePi weer aanpassen. Deze gebruikt deze dus de handige module ‘python-crontab‘.
#!/usr/bin/python
#
# @author bartjan@pc-mania.nl
# Oct-6,2018
import requests
import json
import smtplib
import sys
import time
from crontab import CronTab
link = 'https://api.sunrise-sunset.org/json?lat=51.840449&lng=-4.972762?&formatted=0'
localtime = time.localtime()
def time_of(input):
# Get the JSON output from the API
try:
data = requests.get(link).text
except Exception:
sendmail("Failed to connect to API")
sys.exit()
data = json.loads(data)
# Fetch the wanted time
try:
time = str(data['results'][input]).split('T')[1].split('+')[0]
except Exception:
sendmail("No usable data is returned, keeping old time")
sys.exit()
return time
def reset_crontab(minute,hour):
cron = CronTab(user='root')
# Check and remove the old job
try:
for job in cron.find_command('lights.sh on'):
job.delete()
except Exception:
sendmail("Old cron job not found, stopping update")
sys.exit()
# Set the new job at the given time
job = cron.new(command='/bin/bash /root/scripts/lights.sh on >/dev/null 2>&1')
job.minute.on(minute)
job.hour.on(hour)
# Save job
try:
cron.write()
except Exception:
sendmail("Writing new cron failed")
sys.exit()
def sendmail(msg):
msg = "Subject: sunset tool warning" + "\n\n" + msg
server = smtplib.SMTP('smtp.yourisp.org',25)
server.sendmail('you@domain.net','johndoe@somedomain.com',msg)
###
# Get new time and split in hour/minute
time = time_of('nautical_twilight_end')
hour = time.split(':')[0]
if not localtime.tm_isdst:
# Correct daylight savings
hour = hour - 1
minute = time.split(':')[1]
# Update (so reset) the cronjob
reset_crontab(minute,hour)
Dit script draait sowieso ook in de crontab van dezelfde user ‘root’. Dit alleen 1x per dag om 06.00, om te job voor diezelfde dag goed te zetten. De gewenste tijd is aan te passen door de parameter “time” te veranderen. De API call geeft namelijk al deze velden terug (formatted):
Dus, nu past de cronjob zichzelf aan en zorgt dat de 2 lichten rondom het tv-meubel aangaan als het begint te schemeren en ik hoef niet meer in te loggen via SSH en crontab -e te doen en de tijd per x-dagen aan te passen.
De ‘nautical_twilight’ is een andere benadering van wanneer het donker word. Lees anders even ook deze pagina voor meer informatie.