Basic CGI Example Help in Python - python

I'm working through Programming Python and am trying to work through the basic CGI example. I so far have the following files saved in the same directory (titled: cgi101.html and webserver.py respectively):
<html>
<title>Interactive Page</title>
<body>
<form method=POST action="cgi-bin/cgi101.py">
<p><b>Enter your name:</b>
<p><input type=text name=user>
<p><input type=submit>
</form>
</body>
import os, sys
from http.server import HTTPServer, CGIHTTPRequestHandler
webdir = '.'
port = 8080
os.chdir(webdir)
srvraddr = ("", port)
srvrobj = HTTPServer(srvraddr, CGIHTTPRequestHandler)
srvrobj.serve_forever()
Then in a sub directory titled cgi-bin I have the following script called cgi101.py saved:
#!/usr/bin/python
import cgi
form = cgi.FieldStorage()
print('Content-type: text/html\n')
print('<title>Reply Page</title>')
if not 'user' in form:
print('<h1>Who are you?</h1>')
else:
print('<h1>Hello <i>%s</i>!</h1>' % cgi.escape(form['user'].value))
I then follow these steps:
Run webserver.py from the directory where webserver.py and cgi101.html are located
Navigate to http://localhost:8080/cgi101.html this opens up as expected
When I enter a name and submit, initially I get an error saying the cgi script is not executable. If I go to cgi101.py and then run chmod 744 to make it executable, when I repeat the above steps the following URL renders: http://localhost:8080/cgi-bin/cgi101.py but no content renders whatsoever and the command line where I ran the server gives a FileNotFound error.
What am I doing wrong?
Any help appreciated.
Richard

Related

Using an HTML button to run an python script on a Raspberry

I'm trying to make the following. I want to make a remote control for powering on/off my NAS. See: https://www.youtube.com/watch?v=bDtPBzR6FF0 I didn't want to control it by knobs, but by a HTML button. I want to use a Raspberry Pi to host a website with the buttons.
I tried https://raspberrypi.stackexchange.com/questions/66908/how-to-call-and-run-python-file-on-raspberry-pi-using-web-based-button.
#!/usr/bin/python
# coding: utf-8
import cherrypy
import subprocess
text = """
<html><body>
<form method='get' action='do_it'>
<input type='submit' value='Submit' />
</form></body>
{}
</html>
"""
class PiButton(object):
#cherrypy.expose
def index(self):
return text.format("")
#cherrypy.expose
def do_it(self, *vargs, **kwargs):
#command = "ls /"
command = "python my_other_python.script.py"
result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8').replace('\n', '<br>')
result2 = "command: {!r}<br>result:<br>{}".format(command, result)
return text.format(result2)
if __name__ == "__main__":
cherrypy.engine.autoreload.unsubscribe()
cherrypy.config.update({'server.socket_host': "0.0.0.0", 'server.socket_port': 8181})
cherrypy.quickstart(PiButton(), '/', {'':{}})
It gave an error:
Traceback (most recent call last):
File "button.py", line 17, in
class PiButton(object):
File "button.py", line 34, in PiButton
cherrypy.quickstart(PiButton(), '/', {'':{}})
NameError: name 'PiButton' is not defined
I'm quit new to Python?Raspberry and I don't quite understand what goes wrong. Anyone can tell me what the right solution can be?

Executing python subprocess while extending the test class

I am trying to figure out the best way to run a python subprocess on a remote vm while the subprocess is wrapped in a test framework
#!/usr/bin/python3
import unittest, subprocess
from utils.framework.baseTest import BaseTest
class BriansTest(BaseTest):
def test_briansTest(self):
subprocess.call(['ls -la', '-1'], shell=True)
if __name__ == '__main__':
unittest.main()
But when we run this it send the entire script over to the vm and executes, but obviously the VM does not know anything about BaseTest as this is on the testRunner (rundeck)
We want a solution that can just send commands to a VM without having to send an entire script and returns the desired output so we can assert it wrapped in a test class.
By design, inline-scripts and "external" scripts always imply the script copy on remote nodes, maybe a "dirty" workaround is to use a specific directory with your classes and use it on your node definition to copy the script that calls that class as copy directory (in that way the script which copies from Rundeck server to remote server contains only the call).
The resources.xml definition (with file-copy-destination-dir attribute as a "library location" which uses to copy the file):
<?xml version="1.0" encoding="UTF-8"?>
<project>
<node name="node00" description="Node 00" tags="user" hostname="192.168.33.20" osArch="amd64" osFamily="unix" osName="Linux" osVersion="3.10.0-1062.4.1.el7.x86_64" username="vagrant" ssh-key-storage-path="keys/rundeck" file-copy-destination-dir="/mylib/"/>
</project>
In that directory, I leave the main code (classes in your case), module.py, you can securitize that directory to avoid reads from other accounts.
def greeting(name):
print("Hello, " + name)
So, It means that the code that executes that module is the only copied from Rundeck instance:
import module
module.greeting("Brian")
And here the full job definition (I've used inline-script):
<joblist>
<job>
<defaultTab>nodes</defaultTab>
<description></description>
<dispatch>
<excludePrecedence>true</excludePrecedence>
<keepgoing>false</keepgoing>
<rankOrder>ascending</rankOrder>
<successOnEmptyNodeFilter>false</successOnEmptyNodeFilter>
<threadcount>1</threadcount>
</dispatch>
<executionEnabled>true</executionEnabled>
<id>4edefa30-8fb7-4b74-b8d6-a86f68625e12</id>
<loglevel>INFO</loglevel>
<name>HelloWorld</name>
<nodeFilterEditable>false</nodeFilterEditable>
<nodefilters>
<filter>name: node00</filter>
</nodefilters>
<nodesSelectedByDefault>true</nodesSelectedByDefault>
<plugins />
<scheduleEnabled>true</scheduleEnabled>
<sequence keepgoing='false' strategy='node-first'>
<command>
<fileExtension>.py</fileExtension>
<script><![CDATA[import module
module.greeting("Brian")]]></script>
<scriptargs />
<scriptinterpreter>/usr/bin/python2</scriptinterpreter>
</command>
</sequence>
<uuid>4edefa30-8fb7-4b74-b8d6-a86f68625e12</uuid>
</job>
</joblist>
And here the result.

Executing Modular Python Script on Apache Webserver

I am trying to run a python script (adsb-exchange.py) on an Apache httpd webserver running on an EC2 instance. The script runs perfect from the AWS CLI, but when I try and run from a simple html page off the server I get:
Internal Server Error 500 - The server encountered an internal error or misconfiguration and was unable to complete your request.
Note, running a simple 'hello.py' script from the html page works fine.
Everything is added under the /var/www/html directory and the httpd.conf file is updated to reflect the DocumentRoot path.
Index.html file:
<h1>Welcome!</h1>
<p>Click the button below to start running the scipt...</p>
<form action="/adsb-exchange.py" method="post">
<button>Start</button>
</form>
adsb-exchange.py file:
#!/usr/bin/python
#!/usr/bin/env python
#import Python modules
import cgi
import sys
cgi.enable()
print "Content-type: text/html\n\n"
print
#application imports
import modules.get_config_vals as cfgvals
import modules.adsb_pull as ap
import modules.json2csv as j2c
#get payload params from adsb_config file
payload = cfgvals.get_payload()
#get the JSON pull frequency and runtime from config file
adsb_freq = cfgvals.get_pullfreq()
#send to adsb_pull module, create a JSON object file, and get the filename returned
f = ap.pull_data(payload, adsb_freq)
#convert JSON object file to CSV, output CSV file to directory
j2c.j2csv(f)
raw_input("Press Enter to exit")
The adsb-exchange.py file makes calls to various modules under the 'modules' folder via the /var/www/html/modules path, and each module includes the lines:
print "Content-type: text/html\n\n"
print
GET and POST methods do not appear to impact the results. Output files are dumped to the same directory as adsb-exchange.py is in, for now.
Again, the script runs when executed directly from the ACL, but not when it is called from the index.html page.
Thanks!

subprocess.Popen fails in nginx

I am developing a simple website using Flask + gunicorn + nginx on a Raspberry Pi with Rasbian Jessie.
I am stuck at launching a process with this Python code:
def which(program):
def is_exe(fpath):
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
fpath, fname = os.path.split(program)
if fpath:
if is_exe(program):
return program
else:
for path in os.environ["PATH"].split(os.pathsep):
path = path.strip('"')
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
return None
mplayer_path = which("mplayer")
try:
player = subprocess.Popen([mplayer_path, mp3], stdin=subprocess.PIPE)
except:
return render_template('no_mp3s.html', mp3_message=sys.exc_info())
"mp3" is the path to an mp3 file while "mplayer_path" is the absolute path to mplayer, as returned by the which function described in this answer.
The code works in development when I launch flask directly. In production, when I access the website through nginx, I get the following error message through the no_mp3s.html template:
type 'exceptions.AttributeError'
AttributeError("'NoneType' object has no attribute 'rfind'",)
traceback object at 0x7612ab98
I suspect a permission issue with nginx, but being very new with Linux I am a bit lost!
Edit:
I should add that nowhere in my code (which fits in a single file) I call rfind(). Also, I am sure that the error is caught in this specific try/except because it is the only one that outputs to no_mp3s.html.
Edit:
Following blubberdiblub comments I found out that it is the which function that does not work when the app is run in nginx. Hard coding the path to mplayer seems to work!

Accessing OS environment variables from Jinja2 template

Is it possible to access a OS environment variable directly from a Jinja2 template?
Following #Renier's pointer about custom filters in the comments, I figured out a possible solution.
Define a custom filter:
def env_override(value, key):
return os.getenv(key, value)
Install the filter in the environment:
env.filters['env_override'] = env_override
Use the filter as follows:
"test" : {{ "default" | env_override('CUSTOM') }}
Where the appropriate environment variable can be set as:
export CUSTOM=some_value
If the environment variable is set the output will be:
"test" : some_value
Otherwise:
"test" : default
I believe you can access environment variables like so:
{{ env['XMPP_DOMAIN'] or "localhost" }}
This is from an example in a config file that I saw recently for a Docker deployment of ejabberd.
hosts:
- "{{ env['XMPP_DOMAIN'] or "localhost" }}"
NOTE: You can see the rest of the example in the run file from the Github repo.
As I understand things the heavy lifting is done by this bit of code:
readonly PYTHON_JINJA2="import os;
import sys;
import jinja2;
sys.stdout.write(
jinja2.Template
(sys.stdin.read()
).render(env=os.environ))
"""
And this code is what is used to generate a template file:
cat ${CONFIGTEMPLATE} \
| python -c "${PYTHON_JINJA2}" \
> ${CONFIGFILE}
References
rroemhild/docker-ejabberd docker image via github
The answer here works beautifully but you can still get rid of the useless use of cat and compress it to a single statement:
python -c 'import os
import sys
import jinja2
sys.stdout.write(
jinja2.Template(sys.stdin.read()
).render(env=os.environ))' <$CONFIGTEMPLATE >$CONFIGFILE
P.S.: Stack Overflow does not allow formatted code in comments. Therefore I had to post this as a separate answer instead of commenting on https://stackoverflow.com/a/27984610/1070890.
In bash let's setup our example
export MYENVVAR=foo
$ nano example.py
from jinja2 import Template
import os
template = Template("Hello {{ env['MYENVVAR'] or 'DefaultVal' }}")
r = template.render(env=os.environ, name='somethingelse')
print(r)
Run template
$ python3 example.py
https://jinja.palletsprojects.com/en/2.11.x/intro/
Here is a list of variables that you can access from your template.
I was trying to access some app.config variables and I managed to do it by calling config:
{% if current_user.id == config['ADMIN_ID'] %}
######## SOME HTML ########
{% endif %}
Flask-Login adds the current_user variable to your templates
My simplest solution for FastAPI
templates.env.globals.update(getenv=os.getenv)
In Jinja2
{{ getenv("FOO") }}

Categories

Resources