Allgemein

check_mk Update durchführen

Veröffentlicht am

Das Update von check_mk verhält sich ein wenig anders als man es gewohnt ist. Es muss zuerst dass jeweilige Paket runtergeladen und installiert werden. Achtet dabei auf die Version, die ihr haben wollt und die Edition, die ihr benötigt. Nehmt aus der unteren Tabelle das Editionskürzel. CRE ist dabei die kostenlose Variante. Auf der Seite werden die unterstützten Distributionen aufgelistet. Ladet eure richtige Version herunter.

https://checkmk.de/download_version.php?&version=1.6.0&edition=cee

Die Dateiendung .cee steht für Checkmk Enterprise Edition. Neben dieser gibt es noch

.cre Checkmk Raw Edition
.demo Demo Version der Checkmk Enterprise Edition
.cme Checkmk Managed Services Edition

 

wgett https://checkmk.de/support/1.6.0/check-mk-enterprise-1.6.0_0.xenial_amd64.deb

Überprüfen wir die aktuelle check_mk Version auf dem Server, sehen wir noch die anderen Versionen die zur Verfügung stehen. Die Besonderheit an check_mk ist, dass wir alle Instanzen(Sites) mit einer unterschiedlichen Version laufen lassen könnten.

omd versions

Ausgabe:
1.2.8p18.cee
1.4.0b4.cee
1.4.0p5.cee (default)

Listen wir die Sites auf, so sehen wir, dass wir 2 zur Verfügung haben.

omd sites

Ausgabe:
SITE            VERSION          COMMENTS
Testsite2       1.4.0p5.cee      default version
checkmk         1.4.0p5.cee      default version

Da jede Instanz einen gleichnamigen User in Linux erzeugt, habe ich mir die passwd ebenfalls angeschaut, ob der relevante User immer noch vorhanden ist. Dieser Schritt ist allerdings optional und wird nicht benötigt.

grep omd /etc/passwd

checkmk:x:999:1001:OMD site checkmk:/omd/sites/checkmk:/bin/bash
Testsite2:x:997:1006:OMD site Testsite2:/omd/sites/Testsite2:/bin/bash

Zur Installation benutzen wir dpkg mit dem Parameter -i.

sudo dpkg -i check-mk-raw-1.6.0_0.bionic_amd64.deb

Überprüfen wir nun ein weiteres mal die check_mk Versionen, sehen wir die gerade installierte.

sudo omd versions
1.2.8p18.cee
1.4.0b4.cee
1.4.0p5.cee
1.6.0.cee (default)

Die Instanzen werden nicht automatisch auf die neuste Version gebracht. Damit dies geschieht, müssen wir uns vorerst als den Site User anmelden.

sudo su <Instanzuser>

Geben wir

omd version

ein, so sehen wir, dass sich für diese Instanz die Version nicht geändert hat, was wir nun nachholen.

sudo su checkmk
OMD[checkmk]:~$ omd version
OMD - Open Monitoring Distribution Version 1.4.0p5.cee

Achtet bitte darauf, dass ihr immer noch als der Instanz User angemeldet seid!

Zuerst müssen wir die Instanz stoppen.

omd stop

Removing Crontab...OK
Stopping apache...killing 1393....OK
Stopping nagios....OK
Stopping npcd...OK
Stopping rrdcached...waiting for termination...OK
Stopping mkeventd...killing 1286...OK
Stopping 1 remaining site processes...OK

 

Das Update

Nach dem die Instanz gestoppt ist, können wir mit dem Update beginnen.

omd update

Es sollte ein Abfragefenster mit einer Warnung auftauchen, in der ihr explizit das Update genehmigen müsst.

 

Nachdem bestätigt wurde, wird das Update für die Instanz installiert.

So sah bei mir die Ausgabe aus:

* Updated        .profile
 * Installed link var/dokuwiki/lib/plugins/cli.php
 * Installed dir  local/share/check_mk/web/htdocs/themes
 * Installed dir  etc/stunnel
 * Merged         etc/mk-livestatus/xinetd.conf
 * Updated        etc/nagvis/nagvis.ini.php
 * Updated        etc/dokuwiki/dokuwiki.php
 * Updated        etc/dokuwiki/mime.conf
 * Updated        etc/dokuwiki/local.php
 * Installed link etc/rc.d/85-stunnel
 * Installed file etc/logrotate.d/stunnel
 * Updated        etc/check_mk/apache.conf
 * Updated        etc/init-hooks.d/README
 * Updated        etc/apache/apache.conf
 * Installed file etc/apache/conf.d/security.conf
 * Updated        etc/apache/conf.d/omd.conf
 * Installed file etc/apache/conf.d/01_wsgi.conf
 * Installed file etc/init.d/stunnel
 * Installed file etc/stunnel/server.conf
 * Vanished       etc/icinga/ssi/extinfo-header.ssi
 * Vanished       etc/icinga/ssi/status-header.ssi
 * Vanished       etc/icinga/ssi/README
 * Vanished       etc/icinga/icinga.d/omd.cfg
 * Vanished       etc/icinga/icinga.d/timing.cfg
 * Vanished       etc/icinga/icinga.d/mk-livestatus.cfg
 * Vanished       etc/icinga/icinga.d/flapping.cfg
 * Vanished       etc/icinga/icinga.d/obsess.cfg
 * Vanished       etc/icinga/icinga.d/misc.cfg
 * Vanished       etc/icinga/icinga.d/retention.cfg
 * Vanished       etc/icinga/icinga.d/logging.cfg
 * Vanished       etc/icinga/icinga.d/freshness.cfg
 * Vanished       etc/icinga/icinga.d/dependency.cfg
 * Vanished       etc/icinga/icinga.d/eventhandler.cfg
 * Vanished       etc/icinga/icinga.d/tuning.cfg
 * Vanished       etc/icinga/idomod.cfg-sample
 * Vanished       etc/icinga/apache.conf
 * Vanished       etc/icinga/cgiauth.cfg
 * Vanished       etc/icinga/resource.cfg
 * Vanished       etc/icinga/icinga.cfg
 * Vanished       etc/icinga/config.inc.php
 * Vanished       etc/icinga/cgi.cfg
 * Vanished       etc/icinga/icinga.d
 * Vanished       etc/icinga/conf.d
 * Vanished       etc/icinga/ssi
 * Vanished       etc/init.d/icinga
 * Vanished       etc/apache/conf.d/01_python.conf
 * Vanished       etc/rc.d/80-icinga
 * Vanished       etc/icinga
 * Vanished       local/share/icinga/htdocs
 * Vanished       local/share/icinga
 * Vanished       local/lib/icinga
 * Vanished       var/icinga
Executing update-pre-hooks script "cmk.update-pre-hooks"...OK
Output: Initializing application...
Loading GUI plugins...
Updating Checkmk configuration...
 + Rewriting WATO tags...
 + Rewriting WATO hosts and folders...
 + Rewriting WATO rulesets...
 + Rewriting autochecks...
Done

Finished update.

Das Update wäre nun vollzogen und wir können die Instanz wieder starten:

omd start

Creating temporary filesystem /omd/sites/test/tmp...OK
Starting mkeventd...OK
Starting rrdcached...OK
Starting npcd...OK
Starting nagios...OK
Starting apache...OK
Initializing Crontab...OK

 

Nach Abschluss sollte der Status und die Version ein weiteres Mal überprüft werden.

OMD[test]:~$ omd status
mkeventd:       running
rrdcached:      running
npcd:           running
nagios:         running
apache:         running
crontab:        running
-----------------------
Overall state:  running


OMD[test]:~$ omd version
OMD - Open Monitoring Distribution Version 1.6.0.cre

 

Ihr solltet unbedingt die Release Notes auf etwaige Inkompatibilitäten überprüfen, sowie Änderungen, die anfallen.

Allgemein

multiple Datenbanken mit Django

Veröffentlicht am

Django hat durch sein MVC(Model-View-Controller) von Beginn an eine Datenbank mit am Board. Mein Projekt basiert auf mysql, jedoch hatte ich weitere Datenbanken, die ich über Django managen wollte, die allerdings nicht in der Default DB von Django sind. Daher müssen diese Django bekannt gemacht und Models für diese Datenbanken erstellt werden.

Die Bekanntmachung findet in der settings.py statt. Hier trägt man die weiteren DB Verbindungen ein. Dabei könnte es wie folgt aussehen. In dem DATABASE Dictionary können neben dem Default weitere Werte eingetragen werden. Die ENGINE bestimmt dabei, ob mysql, sqlite oder die anderen unterstützen DBs eingetragen werden. Im NAME steht der Name der Datenbank. User und Password sind selbsterklärend, genauso wie der HOST und der PORT. Ich habe insgesamt 2 weitere Datenbanken hinzugefügt.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {
                'read_default_file': '~/Projekte/controlpanel/controlpanel_root/controlpanel/my.conf',
        },
    },
    'Weather': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'Weather',
        'USER': 'user',
        'PASSWORD': 'password',
        'HOST': '127.0.0.1',
        'PORT': '3306'
    },
     'Metabase': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'Metabase',
        'USER': 'user',
        'PASSWORD': 'password',
        'HOST': '127.0.0.1',
        'PORT': '3306'

    }
}

Damit mit den neuen Datenbanken gearbeitet werden kann, müssen Models erzeugt werden. Django bietet dazu ein eigenständiges Werkzeug an, welches über die Tabellen geht und die Atribute niederschreibt. Führt ihr folgendes Befehl aus,

python3 manage.py inspectdb --database Weather

sollte im Terminal der Aufbau vom Model stehen, wie Django es aufbauen würde. Bei mir sieht es so aus:

 

# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
#   * Rearrange models' order
#   * Make sure each model has one field with primary_key=True
#   * Make sure each ForeignKey has `on_delete` set to the desired behavior.
#   * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models


class CurWeather(models.Model):
    id = models.AutoField(db_column='ID', primary_key=True)  # Field name made lowercase.
    c_lon = models.DecimalField(db_column='C_LON', max_digits=5, decimal_places=2)  # Field name made lowercase.
    c_lat = models.DecimalField(db_column='C_LAT', max_digits=5, decimal_places=2)  # Field name made lowercase.
    w_main = models.CharField(db_column='W_MAIN', max_length=50)  # Field name made lowercase.
    w_description = models.CharField(db_column='W_DESCRIPTION', max_length=100)  # Field name made lowercase.
    w_icon = models.CharField(db_column='W_ICON', max_length=50)  # Field name made lowercase.
    m_temp = models.DecimalField(db_column='M_TEMP', max_digits=4, decimal_places=2)  # Field name made lowercase.
    m_pressure = models.DecimalField(db_column='M_PRESSURE', max_digits=7, decimal_places=2)  # Field name made lowercase.
    m_humidity = models.DecimalField(db_column='M_HUMIDITY', max_digits=7, decimal_places=2)  # Field name made lowercase.
    m_temp_min = models.DecimalField(db_column='M_TEMP_MIN', max_digits=7, decimal_places=2)  # Field name made lowercase.
    w_speed = models.DecimalField(db_column='W_SPEED', max_digits=4, decimal_places=2)  # Field name made lowercase.
    w_deg = models.DecimalField(db_column='W_DEG', max_digits=5, decimal_places=2)  # Field name made lowercase.
    rain_3h = models.DecimalField(db_column='RAIN_3H', max_digits=5, decimal_places=2, blank=True, null=True)  # Field name made lowercase.
    cloudiness = models.DecimalField(db_column='CLOUDINESS', max_digits=5, decimal_places=2)  # Field name made lowercase.
    dt_timestamp = models.DateTimeField(db_column='DT_TIMESTAMP')  # Field name made lowercase.
    sys_country = models.CharField(db_column='SYS_COUNTRY', max_length=4)  # Field name made lowercase.
    sys_sunrise = models.DateTimeField(db_column='SYS_SUNRISE')  # Field name made lowercase.
    sys_sunset = models.DateTimeField(db_column='SYS_SUNSET')  # Field name made lowercase.
    timezone = models.IntegerField(db_column='TIMEZONE')  # Field name made lowercase.
    city = models.ForeignKey('WeaCity', models.DO_NOTHING, db_column='CITY_ID')  # Field name made lowercase.
    created_at = models.DateTimeField(db_column='CREATED_AT')  # Field name made lowercase.

    class Meta:
        managed = False
        db_table = 'CUR_WEATHER'


class WeaCity(models.Model):
    id = models.IntegerField(db_column='ID', primary_key=True)  # Field name made lowercase.
    city_name = models.CharField(db_column='CITY_NAME', max_length=100)  # Field name made lowercase.

    class Meta:
        managed = False
        db_table = 'WEA_CITY'


class WeaOpenweather(models.Model):
    id = models.AutoField(db_column='ID', primary_key=True)  # Field name made lowercase.
    query = models.CharField(db_column='QUERY', max_length=500)  # Field name made lowercase.
    city_id = models.IntegerField(db_column='CITY_ID')  # Field name made lowercase.

    class Meta:
        managed = False
        db_table = 'WEA_OPENWEATHER'

 

Diese Ausgabe sollte nun in eine Datei geleitet werden und schon existiert für die DB das Model.

python3 manage.py inspectdb --database Weather > pages/Weather.py

Allerdings nimmt Django nicht die komplette Arbeit ab. Wir müssen noch überprüfen, dass jedes Model mindestens ein Feld hat, wo das PrimaryKey Attribute True ist. Wenn Django Zeilen hinzufügen, entfernen und ändern darf, muss das managed Attribute noch auf True umgestellt werden.

 

Allgemein

openweather

Veröffentlicht am

Ich war auf der Suche nach einen kostenlosen Onlineservice um Wetterdaten via API abzugreifen. Ziel ist es, ein lokales Chart über das Wetter zu bekommen, welches über Jahre auswertbar ist. Mit den erworbenen Wetterdaten möchte ich vor allem die Windgeschwindigkeiten Vorort im Auge behalten. Fündig geworden bin ich bei Openweather. Um den Service und somit die API abzufragen, benötigt ihr ein Key. Dieser muss bei jeder Abfrage angegeben werden. Den Key erhält man bei der Registrierung im Portal. Ihr könnt dort ebenfalls zur kostenpflichtigen Version wechseln. Diese bietet gegenüber der Kostenfreien mehr Funktionen an. Für mich reicht die kostenlose Variante. Die Wetterdaten werden jede Stunde über die API abgefragt und bei mir lokal in eine Datenbank gespeichert.

Damit der http Request korrekt verläuft, muss unter anderem der vorher erlangte Key in jeder Abfrage verwendet werden.

http://api.openweathermap.org/data/2.5/weather?q=berlin,de&APPID=<key>

Dieser Request gibt ein JSON Packet als Response zurück, der wie folgt aussieht:

{"coord":{"lon":13.39,"lat":52.52},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"base":"stations","main":{"temp":279,"feels_like":274.77,"temp_min":277.59,"temp_max":279.82,"pressure":1032,"humidity":75},"visibility":10000,"wind":{"speed":3.6,"deg":300},"clouds":{"all":75},"dt":1577818777,"sys":{"type":1,"id":1262,"country":"DE","sunrise":1577776641,"sunset":1577804447},"timezone":3600,"id":2950159,"name":"Berlin","cod":200}

Die Parameter sind sehr gut auf der Openweather Seite beschrieben. Um mehr darüber zu erfahren, folgt diesen Link:
https://openweathermap.org/weather-data

Nur das JSON Packet alleine reicht natürlich nicht. Es muss ein komplettes Grundgerüst, bestehend aus Datenbank und Code der die Abfrage triggert, aufgebaut werden.

Datenbank Schema

Das Datenbank Schema, welches zurzeit existiert, ist relativ simple aufgebaut. Es besteht aus 3 Tabellen, eine für die Wetterdaten und die anderen beiden dienen zum Erstellen des Requests.

use Weather;

SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS CUR_WEATHER;
DROP TABLE IF EXISTS WEA_OPENWEATHER;
DROP TABLE IF EXISTS WEA_CITY;

SET FOREIGN_KEY_CHECKS = 1;

CREATE TABLE IF NOT EXISTS WEA_CITY(
  ID int(10) NOT NULL,
  CITY_NAME varchar(100) NOT NULL,
  PRIMARY KEY(ID)
);

CREATE TABLE IF NOT EXISTS CUR_WEATHER(
  ID int(14) NOT NULL auto_increment,
  C_LON decimal(5,2) NOT NULL,
  C_LAT decimal(5,2) NOT NULL,
  W_MAIN varchar(50) NOT NULL,
  W_DESCRIPTION varchar(100) NOT NULL,
  W_ICON varchar(50) NOT NULL,
  M_TEMP decimal(4,2) NOT NULL,
  M_PRESSURE decimal(7,2) NOT NULL,
  M_HUMIDITY decimal(7,2) NOT NULL,
  M_TEMP_MIN decimal(7,2) NOT NULL,
  W_SPEED decimal(4,2) NOT NULL,
  W_DEG decimal(5,2) NOT NULL,
  RAIN_3H decimal(5,2),
  CLOUDINESS decimal(5,2) NOT NULL,
  DT_TIMESTAMP datetime NOT NULL,
  SYS_COUNTRY varchar(4) NOT NULL,
  SYS_SUNRISE datetime NOT NULL,
  SYS_SUNSET datetime NOT NULL,
  TIMEZONE int(15) NOT NULL,
  CITY_ID int(10) NOT NULL,
  CREATED_AT datetime NOT NULL,
  PRIMARY KEY(ID),
  FOREIGN KEY(CITY_ID) REFERENCES WEA_CITY(ID)
);

CREATE TABLE IF NOT EXISTS WEA_OPENWEATHER(
  ID int(5) NOT NULL auto_increment,
  QUERY varchar(500) NOT NULL,
  CITY_ID int(10) NOT NULL,
  PRIMARY KEY(ID)
);

Shell Code

Der Shell Code ist dafür verantwortlich, dass das Python Script angestoßen wird und somit die Wetterdaten in die Datenbank landen.
Dabei wird der Code als Cronjob ausgeführt und jede Stunde getriggert. Die Ausgabe wird dabei in die Weather.log gepiped.

#!/bin/bash

echo "`date +%d.%m.%y\ %T` ===weather cronjob execution==== " >> /var/log/Weather.log
python3 ~/Projekte/Weather/WEATHER.py >> /var/log/Weather.log

exit

Python Code

Die benötigten Pakete für Python habe ich über pip3 installiert:

apt install python3-pip
pip3 install requests
pip3 install mysql-connector-python
pip3 install termcolor
pip3 install python-dateutil

Der Python Code versucht zuerst die benötigten Module zu laden und springt in eine Exception, sobald eins davon nicht vorhanden ist. Verwendet wird hier ebenfalls die von mir erstellte Datenbank Klasse PDB, welche ich in diesem Artikel beschrieben habe. Nach dem die Module geladen sind, folgt die Abfrage gegen die Datenbank, um die erforderliche Query für die Openweather API zu erhalten.  In die Variable response wird das JSON Objekt geschrieben, welches später mittels json.loads in WeatherVar geschrieben wird. Danach wird mi DB.QueryInsert ein neuer Eintrag in der Datenbank erzeugt und die Verbindung wieder geschlossen.

try:
        import json
        import requests
        import PDB
        from termcolor import colored
        from datetime import datetime
except:
        print("Python could not load all required modules")
        sys.exit(1)


DB=OpenweatherQuery=response=WeatherVar = None

if DB is None:
        DB = PDB.databaseconnection('localhost','user','password','Weather')

DB.Connect()

if OpenweatherQuery is None:
        OpenweatherQuery = DB.QuerySelect("select QUERY from WEA_OPENWEATHER where CITY_ID = 6552907", None,False)

try:
        if response is None:
                response = requests.get(OpenweatherQuery[0][0])

except requests.exceptions.RequestException as e:
        print(e)
        sys.exit(1)

print(colored("[I] + " + OpenweatherQuery[0][0],"green"))
print(colored("[I] + " + response.text,"green"))

if WeatherVar is None:
        WeatherVar = json.loads(response.text)

if not DB.QueryInsert("insert CUR_WEATHER(%s) VALUES(%s)",{"C_LON":WeatherVar["coord"]["lon"],"C_LAT":WeatherVar["coord"]["lat"],"W_MAIN":WeatherVar["weather"][0]["main"],"W_DESCRIPTION":WeatherVar["weather"][0]["description"],"W_ICON":WeatherVar["weather"][0]["icon"],"M_TEMP":float(WeatherVar["main"]["temp"]) - 273.15,"M_PRESSURE":WeatherVar["main"]["pressure"],"M_HUMIDITY":WeatherVar["main"]["humidity"],"M_TEMP_MIN":float(WeatherVar["main"]["temp_min"]) -273.15,"W_SPEED":float(WeatherVar["wind"]["speed"]) * 3.6,"W_DEG":WeatherVar["wind"]["deg"],"RAIN_3H":"0","CLOUDINESS":WeatherVar["clouds"]["all"],"DT_TIMESTAMP":datetime.utcfromtimestamp(WeatherVar["dt"]).strftime('%Y-%m-%d %H:%M:%S'),"SYS_COUNTRY":WeatherVar["sys"]["country"],"SYS_SUNRISE":datetime.utcfromtimestamp(WeatherVar["sys"]["sunrise"]).strftime('%Y-%m-%d %H:%M:%S'),"SYS_SUNSET":datetime.utcfromtimestamp(WeatherVar["sys"]["sunset"]).strftime('%Y-%m-%d %H:%M:%S'),"TIMEZONE":WeatherVar["timezone"],"CITY_ID":WeatherVar["id"],"CREATED_AT":str(datetime.now())}, True):
        print("Insert query went wrong for some reason, See Error")

DB.Disconnect()

 

Allgemein

Django mit Mysql Datenbank

Veröffentlicht am

Django

Django benötigt für das Model Prinzip eine eigene Datenbank. Standardmäßig hat es die SQLite Datenbank mit am Board , jedoch kann die Datenbank auch auf eine besser skalierbare Datenbank wie PostgreSQL, MySQL oder Oracle laufen.
Ich habe mich gegen die SQLite Version und für die MySQL Datenbank entschieden, daher gehe ich in diesem Beitrag auf die MySQL Integrierung ein. SQLite sollte unter keinen Umständen in einer produktiv Umgebung genutzt werden!

Um Mysql verwenden zu können, benötigen wir ein DB API Treiber wie mysqlclient. Mysqlclient ist ebenfalls die bevorzugte Wahl von Django. Die dazugehörige Dokumentation von Django kann unter dem Tutorial Part2 gefunden werden. Hier wird explizit auf die Integration der Datenbank eingegangen, wie eine anderen Datenbank Version verwendet werden kann und wie Models erzeugt und behandelt werden.

Den Link zum Tutorial findet ihr hier: https://docs.djangoproject.com/en/2.2/intro/tutorial02/

Um die DB API einrichten zu können, müssen einige Abhängigkeiten installiert werden:

sudo apt-get install python3-dev default-libmysqlclient-dev

gefolgt von dem eigentlichen mysqlclient Paket, welches mit pip installiert wird

pip3 install mysqlclient

Nach der Installation kann die Datenbank, mit den MySQL Utilities, eingerichtet werden.

CREATE DATABASE <dbname> CHARACTER SET utf8;

Auch der Datenbank User sollte nur Berechtigungen auf die für Django bereitgestellte Datenbank haben. Dafür bedienen wir uns bei dem grant Befehl von mysql.

GRANT ALL PRIVILEGES ON <database_name>.* TO '<username>'@'localhost';

Die Django Settings müssen für die MySQL Benutzung angepasst und bearbeitet werden. Dafür müssen wir die Settings.py vom Django-Projekt öffnen und wie unten angegeben, abändern.

# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {
            'read_default_file': '/path/to/my.cnf',
        },
    }
}

Die my.cnf Datei beinhaltet die Zugangsdaten für die Datenbank,  um eine Verbindung mit der Datenbank herzustellen.

# my.cnf
[client]	
database = NAME
user = USER
password = PASSWORD
default-character-set = utf8

Für jede installierte App in Settings.py, auch wenn es die Standardapps von Django sind, wird eine eigene Tabelle erstellt. Nachdem die API und die Einstellungen angepasst worden sind, kann die eigentliche Django Migration gestartet werden.

sudo python3 manage.py migrate

Die migrate Funktion schaut in der Settings.py nach allen installierten Apps und erstellt eine Tabelle in der Datenbank. Dieser Befehl sollte ohne Unterbrechung durchlaufen.

MySQL ist nach erfolgreicher Migrierung die Hauptdatenbank.

Allgemein

Shell Backup Script erzeugen

Veröffentlicht am

Backups sind wohl eines der wichtigsten Dinge auf einem Server und so darf es natürlich auch nicht auf meinem Server fehlen. Statt einer Software Solution habe ich mir meine mit Hilfe von Shell selbst geschrieben. Das Script ist relativ schmal gehalten, erledigt aber alles mit bravour. Es hat eine Vorhalte Zeit von 5 Tagen welches in der Variable mindir abgespeichert ist. Es werden also insgesamt die letzten 5 Backups beibehalten, die älteren Backups werden automatisch gelöscht.  Die For Schleife beinhaltet alle Ordner die gesichert werden sollen.

Das Interessante ist der find Aufruf, um die ältesten Backups zu finden und diese aufzulisten

`find ${dir} -maxdepth 1 -type d -printf '%T+ %p\n' | sort -n | head -n ${t} | awk '{print $2}'`

Dieser Befehl geht in das Verzeichnis, welches in der Variable „dir“ angegeben worden ist. Mit maxdepth ist die Tiefe angegeben. Der Befehl bleibt also nur im obersten Ordner. Mit type suchen wir nur nach Ordnern. Anhand printf wird das Format der Ausgabe festgelegt. Diese Informationen werden nach sort gepiped, wo die Informationen noch sortiert werden, währen head dann die Ordner heraussucht, die gelöscht werden dürfen. Das awk sucht dann nur noch den Ordnerpfad aus dem String heraus.

Ich habe im ganzen Code noch jede Stelle einzeln kommentiert, damit es besser verstanden werden kann.

#!/bin/sh

#where to save the backup
dir="/root/backup/"
#min number of backups to keep
mindir=5

d=`date +%Y%m%d`
#get the current number of the backup existing
curnumber=`find /root/backup -maxdepth 1 -type d | wc -l`

#check if root backup dir exists
if [ ! -d "${dir}" ]
then
        #if not create
        mkdir ${dir}
        echo "backupfolder does not exists - created" >> /var/log/backup.log
fi

#check if backupfolder exists
if [ ! -d "${dir}${d}" ]
then
        #if not create
        mkdir ${dir}${d}/
        echo "create folder ${d}" >> /var/log/backup.log
        #add folders to be backed up here. Loop iterates over the folders then to back them up
        for f in "/root/Projekte/" "/root/client-configs/" "/root/openvpn-ca/"
        do
                #copy defined folders into the backup folder
                cp -p -R -P ${f} ${dir}${d}
                echo "cp -p -R -P ${f} ${dir}${d}" >> /var/log/backup.log
        done
fi

#to not overblowing the server capacity the backup system only keeps the last ${mindir} backups.
# checks if the current number is greater than or equal to ${mindir}
if [ $(($curnumber - 1 )) -ge ${mindir} ]
then
        #get the numbers of how many backups can be deleted
        t=$((${curnumber} - ${mindir} -1 ))
        #find the last ${t} backups
        lastbackup=`find ${dir} -maxdepth 1 -type d -printf '%T+ %p\n' | sort -n | head -n ${t} | awk '{print $2}'`
        echo "backups to delete: ${lastbackup}" >> /var/log/backup.log
        #delete those backups
        rm -r -f ${lastbackup}
fi

exit 0

Dieses Script kann dann z.B. als Cronjob angelegt werden, damit regelmäßig Backups gemacht werden. Bei mir läuft das Script z.B. jeden Tag um 0:55, dies ist jedem allerdings selbst überlassen.

Die Ausführung, bzw. die Ausgaben des Scripts werden dann unter /var/log/backup.log abgespeichert. Somit kann im Fehlerfall überprüft werden, was denn zu dem Fehler geführt hat. Diese Log Datei befindet sich in einer sogenannten Rotation, damit die Log nicht aufgebläht wird.

 

 

 

Allgemein

Python Datenbank Klasse Update

Veröffentlicht am

In meiner Datenbank Klasse habe ich lange Zeit die Update Funktion missen lassen. Hier gelangt ihr zum alten Artikel. Einfach aus dem Grund weil ich sie vorher noch nicht benötigt habe. Das habe ich hiermit nun nachgezogen(Eigentlich existiert sie schon wesentlich länger, aber ich habe jetzt erst den Artikel verfasst 😉 ). Das Update kann separat aufgerufen werden, macht aber auch Sinn in Zusammenhang mit einem Insert. Falls dort z.B. ein Primärschlüssel schon existiert, kann dieser geupdated werden.

Für ein internes Projekt habe ich folgende Befehlszeilen dafür benutzt

if not DB.QueryInsert("insert into AUX_PROJECTS(%s) VALUES(%s)",{"ID":PList[i][0],PList[0][1]:PList[i][1],"ZINS":str(PList[i][2]).replace(",","."),PList[0][3]:PList[i][3],"ANLAGE":PList[i][4],"STATUS":PList[i][5],"SCORE":PList[i][6],PList[0][7]:PList[i][7],PList[0][8]:PList[i][8],"UNIT":PList[i][9],"UPLOADED_AT":PList[i][10]}, True):
                DB.QueryUpdate("update AUX_PROJECTS SET %s where %s",{PList[0][1]:PList[i][1],"ZINS":str(PList[i][2]).replace(",","."),"ANLAGE":PList[i][4]},"ID", PList[i][0])

Das Insert erwahrtet wie gewohnt die insert Query gefolgt von einem Tupel mit den Namen und den Werten. Als 3.ten Parameter kann noch das Update auf True oder False gesetzt werden, dies triggert allerdings zurzeit nur eine Ausgabe. Wenn dieses Insert nicht funktioniert, bzw. ein False zurückgibt, versucht er ein Update. Der Aufbau ist ähnlich wie beim Insert. Zuerst die Update Query, gefolgt von dem Tupel.

Eine Update könnte dann wie folgt in der Shell angezeigt werden:

Hier der komplette neue Code.

import mysql.connector
from mysql.connector import errorcode
from termcolor import colored

class databaseconnection:
    host=username=password=database=cnx=cursor=None

    def __init__(self,host,username,password,database):
        self.host = host
        self.username = username
        self.password = password
        self.database = database

    def Connect(self):
        config = {
            'user': self.username,
            'password': self.password,
            'host': self.host,
            'database': self.database
            }
        try:
            self.cnx = mysql.connector.connect(**config)
            self.cursor = self.cnx.cursor()
            print(colored("[I] + DB Connection established","green"))
        except mysql.connector.Error as err:
            if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
                print(colored("[C] + Wrong Username/Password","red"))
            elif err.errno == errorcode.ER_BAD_DB_ERROR:
                print(colored("[C] + Database Error","red"))
            else:
                print(colored("[C] + " + err,"red"))

    def QuerySelect(self,sqlstatement, params, header):
        if (params is not None):
            try:
                self.cursor.execute(sqlstatement, (params,))
                rtnvalue = self.cursor.fetchall()
                print(colored("[I] + executed select with params","green"))
                if header:
                    rtnvalue.insert(0,self.cursor.column_names)
                return rtnvalue
            except:
                print(colored("[C] + something went wrong while executing select with params:","red"))
                return False
        else:
            try:
                self.cursor.execute(sqlstatement)
                rtnvalue = self.cursor.fetchall()
                print(colored("[I] + executed select without params: " + sqlstatement,"green"))
                if header:
                    rtnvalue.insert(0,self.cursor.column_names)
                return rtnvalue
            except:
                print(colored("[C] + something went wrong while executing select without params","red"))
                return None

    def QueryInsert(self, sqlstatement, params, update):
        if (params is not None):
            columns = ', '.join(params.keys())
            values = params.values()
            sql = sqlstatement % (columns, ', '.join(repr(e) for e in values))
            print(colored("[I] + " + sql,"yellow"))
            try:
                self.cursor.execute(sql)
            except mysql.connector.Error as er:
                if er.errno == errorcode.ER_DUP_ENTRY:
                    if(update == True):
                        print(colored("[W] + transaction rolled back: Duplication found - Try update " ,"yellow"))
                    else:
                        print(colored("[W] + transaction rolled back: Duplication found " ,"yellow"))
                    ###########try to make an update##########
                    return False
                else:
                    print(colored("[C] + a criticial error occured " + er.msg,"red"))
                    return False
        else:
            print(colored("[C] + insert execution failed due missing parameters","red"))
            return False

        try:
            self.cnx.commit()
            print(colored("[I] + transaction committed, value added","green"))
            return True
        except mysql.connector.Error as er:
            self.cnx.rollback()
            print(colored("[C] + transaction rolled back: " + er.msg,"red"))
            return False

    # performs an DB update
    # @var sqlstatement (update AUX_PROJECTS SET %s where %s)
    # @var params Data to being updated
    # @var Conditionheader Primary key
    # @var ConditionValue PK agains value
    def QueryUpdate(self, sqlstatement, params, Conditionheader,ConditionValue):
        if (params is not None):
            updatestring = self.CreateUpdateString(params)
            sql = sqlstatement % (updatestring,Conditionheader + " = " + ConditionValue)
            print(colored("[I] + " + sql,"yellow"))
            try:
                self.cursor.execute(sql)
            except mysql.connector.Error as er:
                self.cnx.rollback()
                print(colored("[C] + transaction rolled back: " + er.msg,"red"))
                return False
        else:
            print(colored("[C] + update execution failed due missing parameters","red"))
            return False

        try:
            self.cnx.commit()
            print(colored("[I] + transaction committed, value updated","green"))
            return True
        except mysql.connector.Error as er:
            self.cnx.rollback()
            print(colored("[C] + transaction rolled back: " + er.msg,"red"))
            return False

    def CreateUpdateString(self,params):
        updatestring = ''
        for key,value in params.items():
                updatestring = updatestring + key + '=' + repr(str(value))
                if(key == list(params.keys())[-1]):
                        continue

                updatestring = updatestring + ','

        return str(updatestring)

    def Disconnect(self):
        self.cnx.close()
        print(colored("[I] + Connection closed","green"))

 

 

 

 

 

 

 

Allgemein

matplotlib über putty ssh

Veröffentlicht am

Ich habe mich vor ein paar Wochen in ein edx Kurse für Data Scientist eingeschrieben und diesen erfolgreich mit einem Zertifikat bestanden. Die zu verwendete Shell hat damals DataCamp bereitgestellt. IPython kam hierbei zum Einsatz. In dem Kurs habe ich erstes Grundwissen über numpy, matplotlib und Pandas erworben. Dieses Wissen wollte ich nun an meinem vServer anwenden. Dafür habe ich zuerst matplotlib für Python3 installiert.

apt install python3-matplotlib

Zum Testen habe ich das Script von unten(Testbeispiel) benutzt. Führt man das Script direkt aus, sollte folgende Fehlermeldung kommen.

/usr/lib/python3/dist-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.
  warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')
/usr/lib/python3/dist-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.
  warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')
Traceback (most recent call last):
  File "test.py", line 6, in <module>
    plt.plot(x, x, label='linear')
  File "/usr/lib/python3/dist-packages/matplotlib/pyplot.py", line 3147, in plot
    ax = gca()
  File "/usr/lib/python3/dist-packages/matplotlib/pyplot.py", line 928, in gca
    return gcf().gca(**kwargs)
  File "/usr/lib/python3/dist-packages/matplotlib/pyplot.py", line 578, in gcf
    return figure()
  File "/usr/lib/python3/dist-packages/matplotlib/pyplot.py", line 527, in figure
    **kwargs)
  File "/usr/lib/python3/dist-packages/matplotlib/backends/backend_tkagg.py", line 84, in new_figure_manager
    return new_figure_manager_given_figure(num, figure)
  File "/usr/lib/python3/dist-packages/matplotlib/backends/backend_tkagg.py", line 92, in new_figure_manager_given_figure
    window = Tk.Tk()
  File "/usr/lib/python3.5/tkinter/__init__.py", line 1871, in __init__
    self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
_tkinter.TclError: no display name and no $DISPLAY environment variable

Der Fehler besagt, dass kein „Display“ gefunden wird, in dem der Graph angezeigt werden kann.
Wir rufen das Script ja schließlich über die Shell auf. Es fehlt dementsprechend ein xServer, der den Output zu einem Client tunnelt. Dazu sind ein paar Anpassungen auf dem Server als auch am Client notwendig.

Server-Seite

Auf dem Server muss unter anderem das xauth Paket installiert werden.

apt install xauth

Neben dem Paket ist auch eine Änderung an der sshd_config notwendig, damit ssh den eigentlichen Output auch weiterleitet. Dazu fügt die 2 Parameter hinzu oder ändert sie ab.

vi /etc/ssh/sshd_config

X11Forwarding yes
X11DisplayOffset 10

 

Client-Seite

Auf der Client Seite sollte ein xServer installiert sein. Sehr beliebt ist hier Xming. Xming ist hier zu bekommen.

Die Installation ist schnell und einfach. Nach ein paar Einstellungen die zu treffen sind, ist der xServer auch schnell installiert.

 

 

 

 

 

Auf der Putty Shell im Client muss der X11 noch aktiviert werden. Dazu tickt den „Enable X11 forwarding“ an.

 

TestBeispiel

Hier das Testbeispiel, welches ich benutzt habe, um den xServer zu testen. Das Scipt habe ich dabei von hier: https://matplotlib.org/3.1.1/tutorials/introductory/usage.html#sphx-glr-tutorials-introductory-usage-py

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2, 100)

plt.plot(x, x, label='linear')
plt.plot(x, x**2, label='quadratic')
plt.plot(x, x**3, label='cubic')

plt.xlabel('x label')
plt.ylabel('y label')

plt.title("Simple Plot")

plt.legend()

plt.show()

 

Ist der xServer auf dem Client installiert und gestartet, sollte der Graphic Output nun lokal auf dem Rechner zu sehen sein, auf dem der Command ausgeführt wurde.

 

Allgemein

IPs mittels iptables automatisch blockieren lassen

Veröffentlicht am

Eigentlich wollte ich meinen vServer noch sicherer machen, indem ich von Seiten wie myip.ms eine vorgefertigte Blackliste herunterlade. Die gezogenen IPs sollten mittels iptables blockiert werden. Ich hatte auch schon soweit alles fertig geschrieben, ein Python3 Script, ein Shell Script und die SQL Scripte zum Anlegen der Datenbank.
Allerdings habe ich nach ein „paar“ Einträgen folgende Nachricht von iptables bekommen: iptables: Memory allocation problem.

Nach ein bisschen Internet Recherche fand ich heraus, dass mich mein Hoster auf eine bestimmte Anzahl an iptable Einträgen begrenzt.

Die maximal Möglichen Einträge findet ihr mit diesem Befehl.

egrep "failcnt|numiptent" /proc/user_beancounters

numiptent steht dabei für die NETFILTER Einträge. In diesem Beitrag steht, dass diese dem Schwellenwert von 200 – 300 Einträgen ebenfalls aufgrund eventueller Performanzprobleme nicht überschreiten sollte. Da ich von myip.ms über 500 Einträge übertragen habe, war ich auch schon weit über das Limit. Ebenfalls habe ich noch diesen Artikel gefunden: https://www.danami.com/clients/knowledgebase/92/How-can-I-fix-the-error-The-VPS-iptables-rule-limit-numiptent-is-too-low.html

Naja, ganz abschreiben wollte ich es noch nicht und vielleicht fällt mir auch noch ein automatischer Prozess ein, den ich integrieren kann. Ich möchte jedoch schon den bis dato geschriebenen Code veröffentlichen.

Da ist zum einen der Shell Code, der die beiden SQL Dateien CREATE_FIREWALL.sql und FIREWALL_SCHEMA.sql aufruft.

#!/bin/bash

if [ -f "CREATE_FIREWALL.sql" ] && [ -f "FIREWALL_SCHEMA.sql" ]
then
                mysql -uuser -ppassword < "CREATE_FIREWALL.sql"
                mysql -uuser -ppassword < "FIREWALL_SCHEMA.sql"
else
        echo "[C] CREATE_FIREWALL.sql OR FIREWALL SCHEMA.sql missing"
fi

in der CREATE_FIREWALL steht die Einrichtung der Datenbank,

CREATE Database IF NOT EXISTS Firewall;
GRANT ALL PRIVILEGES ON Firewall.* TO 'user'@'localhost' IDENTIFIED BY 'password';

während in der FIREWALL_SCHEMA das Schema der Tabelle, der gerade erzeugten DB, beinhaltet.

use Firewall;

SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS FW_IPS;

SET FOREIGN_KEY_CHECKS = 1;

CREATE TABLE IF NOT EXISTS FW_IPS(
IP varchar(16) NOT NULL,
ORIGIN varchar(100),
SOURCE varchar(100),
STATUS boolean not null default 0,
NEW boolean not null default 0,
PRIMARY KEY(IP)
);

Kurz eine kleine Beschreibung der einzelnen Spalten:

IP zu blockierende IP
ORIGIN Herkunftsland der IP
SOURCE Resource wo diese IP steht, in diesem Fall myip.ms
STATUS Soll dieser Block aktiv sein?(True/False)
NEW Ist diese IP neu dazu gekommen?(True/False)
Python Code

Kommen wir hier zu dem Python Code, der eine Verbindung zu myip.ms aufbaut. Der Aufbau wird mit dem urllib.request Module vollzogen. Als Response bekomme ich eine Byte String, den ich mit .data.read().decode() umkonvertiere. In der Schleife benutze ich regex und entferne bloße Kommentare(gekennzeichnet mit #) oder IPv6 Einträge. Somit bleiben nur die von mir erforderlichen IPv4 Einträge. Diese verbleibenden Einträge kippe ich dann in die Firewall Datenbank. Das PDB ist ein von mir geschriebenes Modul. Die QueryInsert Methode benötigt die SQL-Abfrage, die Werte als Tupel und als letztes ein FLAG, das bestimmt ob ein Update versucht wird, wenn der Private Schlüssel schon existiert. Jenes ist jedoch nicht der Fall, daher habe ich ihn auf False gesetzt. Der NEW FLAG wird bei den Werten als 1 übergeben, da es sich hier immer um eine neue IP handeln wird, wenn die IP in die DB geschrieben wird.

try:
        import urllib.request as req
        import re
        import PDB
except:
        print("could not load modules")
        exit(1)
try:
        #try to open URL
        data = req.urlopen("https://myip.ms/files/blacklist/csf/latest_blacklist.txt")
except urllib.error.URLError as e: print(e.reason)

#decode received data from URL
ips=data.read().decode()

#Open DB Connection
DB= PDB.databaseconnection('localhost','user','password','Firewall')
DB.Connect()

#for each line in ip collected from URL
for line in ips.splitlines():
        #reject lines with a # and a total length of < 15
        if  not re.findall("\A#", line) and len(line) < 15:
                #also check if not empty
                if not line == '':
                        #DB Insert without update process
                        DB.QueryInsert("insert into FW_IPS(%s) VALUES(%s)",{"IP":line,"SOURCE":"myip.ms","STATUS":0,"NEW":1},False)
#Close DB
DB.Disconnect()
Shell Code

In dem Shell Script lege ich zuerst die BLACKLIST Chain an und platziere die bei der INPUT Chain an erster Stelle. Danach geht die Loop über die Einträge in der DB, die das FLAG NEW noch auf 1 stehen haben. Sobald es die IP erfolgreich mit iptables registriert hat, kümmern sich 2 weitere SQL Befehle darum, die FLAGS STATUS und NEW auf jeweils 1 bzw. 0 zu setzen.

 

iptables -N BLACKLIST 
iptables -I INPUT 1 -j BLACKLIST 
for ip in `mysql -s -N -e "select IP from Firewall.FW_IPS where NEW = 1"` 
do 
  iptables -A BLACKLIST -s $ip -j DROP 
  if [ $? -eq 0 ] 
  then 
    mysql -s -N -e "update Firewall.FW_IPS set STATUS = 1 where IP = '${ip}'" 
    mysql -s -N -e "update Firewall.FW_IPS set NEW = 0 where IP = '${ip}'" 
  fi 
done

Der komplette Prozess hat wie gesagt funktioniert, jedoch durch den Lock mehr oder weniger unbrauchbar. Zusätzlich verwende ich nämlich noch fail2ban, der auch Einträge in die iptables tätigt. Alles in allem muss ich mir etwas neues überlegen, damit ich die IPs von bestimmten Herkunftsländern blockieren kann.

 

 

Allgemein

SSH Email bei Client Verbindung

Veröffentlicht am

Mein vServer hat zurzeit den SSH Port offen. Dieser befindet sich zwar nicht mehr standardmäßig auf dem Port 22, um sogenannte Bots größtenteils vom Server zu halten, allerdings wollte ich bei jedem Connect informiert werden, damit ich im Falle einer Kompromittierung direkt reagieren kann.

Der Port kann in der /etc/ssh/sshd_config angepasst werden.

Die eigentliche Mail Benachrichtigung, in der es in diesem Beitrag auch geht, muss in pam.d eingerichtet werden. Eine, wie ich finde gute Seite, die pam.d beschreibt, ist

https://web.archive.org/web/20180303034326/http://www.tuxradar.com/content/how-pam-works

Kommen wir weiter zu den Änderungen der Datei.

Öffnen wir die ssh Datei mit einem beliebigen Editor, in meinem Fall ist es der vim Editor.

vi /etc/pam.d/sshd

Dort muss folgende Zeile eingetragen werden.

session optional pam_exec.so seteuid /etc/ssh/login-notify.sh

Am Anfang sollte der Flag „optional“ benutzt werden, damit bei falscher Konfiguration eine Verbindung trotzdem noch möglich ist. Als letztes Argument wird das Script übergeben.

Das Script zum Versenden der Mail sieht schließlich wie folgt aus.

#!/bin/bash

if [ "$PAM_TYPE" != "close_session" ]; then

body="<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>
    <html>
    <head><title>SSH Mail</title>
    </head>
    <body>
    <table>
    <tr>
    <th>Tag</th>
    <th>Value</th>
    </tr>
    <tr>
    <td>service</td>
    <td>SSH</td>
    </tr>
    <tr>
    <td>direction</td>
    <td>connected</td>
    </tr>
    <tr>
    <td>Timestamp</td>
    <td>`date '+%T %F'`
    </tr>
    </table>
    <p></p>
    <p>`env`</p>
    </body>
    </html>"

echo $body | /usr/bin/mail -a "From: ssh@server.com" -a "MIME-Version: 1.0" -a "Content-Type: text/html" -s "SSH Client Connection established" mail@dns.de >> /dev/null
fi
exit 0

Verbindung sich nun jemand per SSH auf dem Server, wird direkt eine Mail generiert und versendet.

Der Mail Body sieht dann genau so aus, wobei RHOST die Remote Addresse enthält.

Allgemein

Metabase Startup Script

Veröffentlicht am

Damit Metabase automatisch starten, habe ich dieses Script geschrieben. Dafür überprüft es, ob Metabase, bzw. eine Java Instanz schon auf Port 3000 lauscht. Falls dies der Fall ist, gehe ich davon aus, dass Metabase schon läuft und schreibe es in die Variable alreadystarted. In die eventuallystarted schreibe ich, falls schon ein Java Dämon existiert. Dies könnte unter Umständen bedeuten, dass Metabase im Boot Mode ist und gerade gestartet wurde. In diesem Fall bricht das Script ab und man muss es manuell ausführen. Metabase ist definitiv nicht am Laufen, wenn beide Variablen nicht gesetzt sind. Dementsprechend sind die export Variablen für den Metabase Mysql Startup. Die STDOUT von Metabase wird in eine Log Datei weitergeleitet.

alreadystarted=`netstat -tapen | grep LISTEN | grep 3000 | awk '{print $9}'`
eventuallystarted=`ps -Al | grep java | awk '{print $4"/"$14}'`


echo "JAVA,METABASE:${eventuallystarted},${alreadystarted}"


if [ ! -z "$eventuallystarted" ]
then
if [ ! -z "$alreadystarted" ]
then
echo "[I] A Metabase Instance is already running!"
exit 0
fi
echo "[I] It may be that a Metabase Instance is already running. Please check and run manually!"
exit 1
fi


if [ -z "$alreadystarted" ]
then
export MB_DB_TYPE=mysql
export MB_DB_DBNAME=db
export MB_DB_PORT=3306
export MB_DB_USER=user
export MB_DB_PASS=password
export MB_DB_HOST=localhost
java -jar metabase.jar >> /var/log/Metabase/metabase`date +%V`.log &
else
echo "[I] A Metabase Instance is already running!"
exit 0
fi