snir g
snir g

Reputation: 21

Deploy Flask on Apache using mod_wsgi

I'm trying to deploy a Flask web app I built to be deployed on an Apache server. I'm using Raspbian (Jessie) OS on a Raspberry Pi 3. The app runs perfect on the flask built-in dev web server, but I have been unable to deploy it on the Apache sever.

This is what I did:

sudo apt-get update
sudo apt-get -y install python3 ipython3 python3-flask
sudo apt-get -y install apache2
sudo apt-get -y install libapache2-mod-wsgi-py3

The conf file is: /etc/apach2/sites-available/arduinoweb.conf:

<VirtualHost *>
 ServerName 10.0.0.20

 WSGIDaemonProcess arduinoweb user=pi group=pi threads=5
 WSGIScriptAlias / /var/www/ArduinoWeb/arduinoweb.wsgi

<Directory /var/www/ArduinoWeb/ArduinoWeb>
 WSGIProcessGroup arduinoweb
 WSGIApplicationGroup %{GLOBAL}
 WSGIScriptReloading On
 Require all granted
</Directory>

Alias /static /var/www/ArduinoWeb/ArduinoWeb/static
<Directory /var/www/ArduinoWeb/ArduinoWeb/static/>
Require all granted
</Directory>

Alias /temp /var/www/ArduinoWeb/ArduinoWeb/temp
<Directory /var/www/ArduinoWeb/ArduinoWeb/temp/>
Require all granted
</Directory>

Alias /templates /var/www/ArduinoWeb/ArduinoWeb/templates
<Directory /var/www/ArduinoWeb/ArduinoWeb/templates/>
Require all granted
</Directory>

ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined


</VirtualHost>
~

The WSGI script file in /var/www/Arduinoweb/arduinoweb.wsgi:

import sys

if sys.version_info[0]<3:       # require python3
    raise Exception("Python3 required! Current (wrong) version: '%s'" % sys.version_info)

sys.path.insert(0, '/var/www/Arduinoweb/Arduinoweb')

from app import app as application

The error log of Apache:

[Wed Sep 21 21:46:22.669633 2016] [wsgi:error] [pid 17681:tid 1972667440] [client 10.0.0.3:64819] mod_wsgi (pid=17681): Target WSGI script '/var/www/ArduinoWeb/arduinoweb.wsgi' cannot be loaded as Python module.
[Wed Sep 21 21:46:22.669971 2016] [wsgi:error] [pid 17681:tid 1972667440] [client 10.0.0.3:64819] mod_wsgi (pid=17681): Exception occurred processing WSGI script '/var/www/ArduinoWeb/arduinoweb.wsgi'.
[Wed Sep 21 21:46:22.670196 2016] [wsgi:error] [pid 17681:tid 1972667440] [client 10.0.0.3:64819] Traceback (most recent call last):
[Wed Sep 21 21:46:22.671185 2016] [wsgi:error] [pid 17681:tid 1972667440] [client 10.0.0.3:64819]   File "/var/www/ArduinoWeb/arduinoweb.wsgi", line 8, in <module>
[Wed Sep 21 21:46:22.671238 2016] [wsgi:error] [pid 17681:tid 1972667440] [client 10.0.0.3:64819]     from app import app as application
[Wed Sep 21 21:46:22.671406 2016] [wsgi:error] [pid 17681:tid 1972667440] [client 10.0.0.3:64819] ImportError: No module named 'app'

I don't understand why it cant find the app.

This is the python file /var/www/Arduinoweb/Arduinoweb/app.py :

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask import request, redirect, url_for, render_template, jsonify
from socket import *
from time import time
from threading import Timer
from datetime import datetime
import fileinput

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:Qazwsx@localhost/arduinoweb'
app.debug = True
db = SQLAlchemy(app)

class User(db.Model):

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)

    def __init__(self, username, email):
        self.username = username
        self.email = email
    def __repr__(self):
        return '<user %r>' % self.username


class Temp(db.Model):
        __tablename__ = "Temp"
        id = db.Column("id", db.Integer, primary_key=True)
        Temp = db.Column("Temp", db.Integer)
        Date = db.Column("Date", db.Date)
        Time = db.Column("Time", db.Time)
        DateTime = db.Column("DateTime", db.String)

        def __init__(self, Temp, Date=None, Time=None, DateTime=None):
                self.Temp = Temp
                if Date is None:
                        Date = str(datetime.now()).split('.')[0]
                self.Date = Date
                if Time is None:
                        Time = str(datetime.now()).split('.')[0]
                self.Time = Time
                if DateTime is None:
                        DateTime = repr(datetime.now().replace(second=0, microsecond=0)).split('datetime.datetime',1)[1]
                self.DateTime = DateTime

class EC(db.Model):
        __tablename__ = "EC"
        id = db.Column("id", db.Integer, primary_key=True)
        EC = db.Column("EC", db.Float)
        Date = db.Column("Date", db.Date)
        Time = db.Column("Time", db.Time)
        DateTime = db.Column("DateTime", db.String)

        def __init__(self, EC, Date=None, Time=None, DateTime=None):
                self.EC = EC
                if Date is None:
                        Date = str(datetime.now()).split('.')[0]
                self.Date = Date
                if Time is None:
                        Time = str(datetime.now()).split('.')[0]
                self.Time = Time
                if DateTime is None:
                        DateTime = repr(datetime.now().replace(second=0, microsecond=0)).split('datetime.datetime',1)[1]
                self.DateTime = DateTime

class PH(db.Model):
        __tablename__ = "PH"
        id = db.Column("id", db.Integer, primary_key=True)
        PH = db.Column("PH", db.Float)
        Date = db.Column("Date", db.Date)
        Time = db.Column("Time", db.Time)
        DateTime = db.Column("DateTime", db.String)

        def __init__(self, PH, Date=None, Time=None, DateTime=None):
                self.PH = PH
                if Date is None:
                        Date = str(datetime.now()).split('.')[0]
                self.Date = Date
                if Time is None:
                        Time = str(datetime.now()).split('.')[0]
                self.Time = Time
                if DateTime is None:
                        DateTime = repr(datetime.now().replace(second=0, microsecond=0)).split('datetime.datetime',1)[1]
                self.DateTime = DateTime

class Humidity(db.Model):
        __tablename__ = "Humidity"
        id = db.Column("id", db.Integer, primary_key=True)
        Humidity = db.Column("Humidity", db.Integer)
        Date = db.Column("Date", db.Date)
        Time = db.Column("Time", db.Time)
        DateTime = db.Column("DateTime", db.String)

        def __init__(self, Humidity, Date=None, Time=None, DateTime=None):
                self.Humidity = Humidity
                if Date is None:
                        Date = str(datetime.now()).split('.')[0]
                self.Date = Date
                if Time is None:
                        Time = str(datetime.now()).split('.')[0]
                self.Time = Time
                if DateTime is None:
                        DateTime = repr(datetime.now().replace(second=0, microsecond=0)).split('datetime.datetime',1)[1]
                self.DateTime = DateTime

class HumidityRoots(db.Model):
        __tablename__ = "HumidityRoots"
        id = db.Column("id", db.Integer, primary_key=True)
        HumidityRoots = db.Column("HumidityRoots", db.Integer)
        Date = db.Column("Date", db.Date)
        Time = db.Column("Time", db.Time)
        DateTime = db.Column("DateTime", db.String)

        def __init__(self, HumidityRoots, Date=None, Time=None, DateTime=None):
                self.HumidityRoots = HumidityRoots
                if Date is None:
                        Date = str(datetime.now()).split('.')[0]
                self.Date = Date
                if Time is None:
                        Time = str(datetime.now()).split('.')[0]
                self.Time = Time
                if DateTime is None:
                        DateTime = repr(datetime.now().replace(second=0, microsecond=0)).split('datetime.datetime',1)[1]
                self.DateTime = DateTime


@app.route('/Sensors')
def sensors_function():
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto("GETSENSORS".encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return rec_data

@app.route('/OutputsState')
def outputs_state_function():
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto("GETOUTPUTSSTATE".encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return rec_data

@app.route('/WebModeState')
def web_mode_state_function():
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto("GETWEBMODE".encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return rec_data

@app.route('/PLCState')
def plcstatefunction():
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto("GETPLCSTATE".encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return rec_data

@app.route('/IrrigateOnOff')
def irrigate_on_off_function():
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto("IRRIGATEOnOff".encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return rec_data

@app.route('/SprinklersOnOff')
def sprinklers_on_off_function():
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto("SprinklersOnOff".encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return rec_data

@app.route('/SetDateTime' , methods=['POST'])
def set_date_time_function():
        completeAnswer =  "%s:%s:%s:%s:%s:%s:%s:%s" % ("SETDATETIME", request.form.get('dOw'), request.form.get('SetDate'), request.form.get('SetMonth'), request.form.get('SetYear'), request.form.get('SetHour'), request.form.get('SetMinute'), request.form.get('SetSeconds'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/SetIrrigation' , methods=['POST'])
def set_irrigation_function():
        completeAnswer =  "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s" % ("SETIRRIGATION",request.form.get('SetIrrigationMode'), request.form.get('SetHumidityRangeMin'), request.form.get('SetHumidityRangeMax'), request.form.get('SetHour1'), request.form.get('SetHour1OnTime'), request.form.get('SetHour1OffTime'), request.form.get('SetHour2'), request.form.get('SetHour2OnTime'), request.form.get('SetHour2OffTime'), request.form.get('SetHour3'), request.form.get('SetHour3OnTime'), request.form.get('SetHour3OffTime'), request.form.get('SetHour4'), request.form.get('SetHour4OnTime'), request.form.get('SetHour4OffTime'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/SetEC' , methods=['POST'])
def set_EC_function():
        completeAnswer =  "%s:%s:%s:%s:%s" % ("SETEC", request.form.get('SetECRangeMin'), request.form.get('SetECRangeMax'), request.form.get('SetDoseEC'), request.form.get('SetECDelay'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/SetPH' , methods=['POST'])
def set_PH_function():
        completeAnswer =  "%s:%s:%s:%s:%s:%s" % ("SETPH", request.form.get('SetPHRangeMin'), request.form.get('SetPHRangeMax'), request.form.get('SetDosePHUp'), request.form.get('SetDosePHDown'), request.form.get('SetPHDelay'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/SetWaterTemp' , methods=['POST'])
def set_water_temp_function():
        completeAnswer =  "%s:%s:%s" % ("SETWATERTEMP", request.form.get('SetWaterTempRangeMin'), request.form.get('SetWaterTempRangeMax'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/SetSprinklers' , methods=['POST'])
def set_sprinklers_function():
        completeAnswer =  "%s:%s:%s:%s:%s" % ("SETSPRINKLERS", request.form.get('SetSprinklersBeginEndHoursBegin'), request.form.get('SetSprinklersBeginEndHoursEnd'), request.form.get('SetSprinklersOnTime'), request.form.get('SetSprinklersOffTime'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/SetAlerts' , methods=['POST'])
def set_alerts_function():
        completeAnswer =  "%s:%s:%s:%s:%s:%s" % ("SETALERTS", request.form.get('SetIrrigationThresholdAlert'), request.form.get('ECAlertOffset'), request.form.get('PHAlertOffset'), request.form.get('ResetCounterState'), request.form.get('AlertsState'))
        address= ( '192.168.0.196', 5000) #define server IP and port
        client_socket =socket(AF_INET, SOCK_DGRAM) #Set up the Socket
        client_socket.settimeout(1) #Only wait 1 second for a response
        client_socket.sendto(completeAnswer.encode(), address) #Send the data request
        rec_data, addr = client_socket.recvfrom(2048) #Read response from arduino
        return "ok"

@app.route('/')
def index():
        return render_template('index.html')

@app.route('/Charts')
def charts():
        return render_template('charts.html')

@app.route('/livechart')
def live_chart():
        return render_template('livechart.html')

@app.route('/TempQuery' , methods=['POST'])
def temp_query():
        answerDate = request.form.get('date')
        answerSensor = request.form.get('sensor')
        datafile = 'temp/TempByDateDbFile.txt'
        if answerSensor == 'Temp':
                DbTemp = Temp.query.filter_by(Date = answerDate).all()
                ## create the file from db
                file = open(datafile, 'w')
                for item in DbTemp:
                        file.write('{x: new Date' + str(item.DateTime) + ' , y: ' + str(item.Temp) + '},' + '\n')
                file.close()
        elif  answerSensor == 'EC':
                DbTemp = EC.query.filter_by(Date = answerDate).all()
                ## create the file from db
                file = open(datafile, 'w')
                for item in DbTemp:
                        file.write('{x: new Date' + str(item.DateTime) + ' , y: ' + str(item.EC) + '},' + '\n')
                file.close()
        elif  answerSensor == 'PH':
                DbTemp = PH.query.filter_by(Date = answerDate).all()
                ## create the file from db
                file = open(datafile, 'w')
                for item in DbTemp:
                        file.write('{x: new Date' + str(item.DateTime) + ' , y: ' + str(item.PH) + '},' + '\n')
                file.close()
        elif  answerSensor == 'Humidity':
                DbTemp = Humidity.query.filter_by(Date = answerDate).all()
                ## create the file from db
                file = open(datafile, 'w')
                for item in DbTemp:
                        file.write('{x: new Date' + str(item.DateTime) + ' , y: ' + str(item.Humidity) + '},' + '\n')
                file.close()
        elif  answerSensor == 'HumidityRoots':
                DbTemp = HumidityRoots.query.filter_by(Date = answerDate).all()
                ## create the file from db
                file = open(datafile, 'w')
                for item in DbTemp:
                        file.write('{x: new Date' + str(item.DateTime) + ' , y: ' + str(item.HumidityRoots) + '},' + '\n')
                file.close()
        ##replace "-" in ","
        f = open(datafile,'r')
        filedata = f.read()
        f.close()
        newdata = filedata.replace("-",", ")
        f = open(datafile,'w')
        f.write(newdata)
        f.close()
        return 'OK'

@app.route('/RenderTempChart' , methods=['POST' , 'GET'])
def render_temp_chart():
        datafile = 'temp/TempByDateDbFile.txt'
        with open(datafile, 'r') as myfile:
                file = myfile.read()
        return render_template('DbTemp.html', file = file)

@app.route('/Control' , methods=['POST' , 'GET'])
def control():
        return render_template('control.html')
"""
def update_data(interval):  # store in DB all sensors real time data
        Timer(interval, update_data, [interval]).start()
        SensorsAnswer = sensors_function().split()
        addTemp = Temp(int(SensorsAnswer[2]))
        addEC = EC(float(SensorsAnswer[0]))
        addPH = PH(float(SensorsAnswer[1]))
        addHumidity = Humidity(int(SensorsAnswer[3]))
        addHumidityRoots = HumidityRoots(int(SensorsAnswer[5]))
        db.session.add(addTemp)
        db.session.add(addEC)
        db.session.add(addPH)
        db.session.add(addHumidity)
        db.session.add(addHumidityRoots)
        db.session.commit()

update_data(300) # Store data in DB every x seconds
"""
if __name__ == "__main__":
        app.run()

It's not working even with a simple code like this (same error):

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello world!"

if __name__ == "__main__":
    app.run()

I'm using python 3.4.2. I'm not using it in a virtual environment.

The folder structure:

app.py is at /var/www/Arduinoweb/Arduinoweb/app.py arduinoweb.wsgi is at /var/www/Arduinoweb/arduinoweb.wsgi

I do enable the VirtualHost arduinoweb.conf and restart apach2 service.

Upvotes: 1

Views: 407

Answers (2)

snir g
snir g

Reputation: 21

thanks for the help .. eventually it was seems to be just a typing mistake in the wsgi script path: "/var/www/ArduinoWeb/ArduinoWeb" instead "/var/www/Arduinoweb/Arduinoweb" .

now its seems to work fine . thanks!!

Upvotes: 1

Graham Dumpleton
Graham Dumpleton

Reputation: 58523

For a start, this:

<Directory /var/www/ArduinoWeb/ArduinoWeb>
 WSGIProcessGroup arduinoweb
 WSGIApplicationGroup %{GLOBAL}
 WSGIScriptReloading On
 Require all granted
</Directory>

should be:

<Directory /var/www/ArduinoWeb>
 WSGIProcessGroup arduinoweb
 WSGIApplicationGroup %{GLOBAL}
 Require all granted
</Directory>

Technically this being wrong would cause two problems.

The first is that Apache shouldn't have even let you use that WSGI script as it wouldn't have permission to. Looks though like somewhere else in your Apache configuration is giving broad access to your file system when it shouldn't really.

I have also dropped the reloading option as that default to on anyway and isn't needed.

The second is that the WSGI script wouldn't be run in the context of the daemon process group. This would mean code would load in embedded mode instead and would run as Apache user. If the permissions on your files were such that only the pi user could read them, then it wouldn't be able to access the app module.

It is also generally wrong to have SeverName be an IP address. If it is working then it is only doing so because this is the first VirtualHost in the Apache configuration and so Apache is defaulting to using it when it can't properly do name base virtual host matching.

Anyway, see if that helps, otherwise provide output of running:

ls -las /var/www/ArduinoWeb /var/www/ArduinoWeb/ArduinoWeb

so can check what ownership/permissions of directories/files are.

Upvotes: 1

Related Questions