initial commit
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
#
|
||||
# This files allows to delegate authentication for every URL within a domain
|
||||
# to a web2py app within the same domain
|
||||
# If you are logged in the app, you have access to the URL
|
||||
# even if the URL is not a web2py URL
|
||||
#
|
||||
# in /etc/apache2/sites-available/default
|
||||
#
|
||||
# <VirtualHost *:80>
|
||||
# WSGIDaemonProcess web2py user=www-data group=www-data
|
||||
# WSGIProcessGroup web2py
|
||||
# WSGIScriptAlias / /home/www-data/web2py/wsgihandler.py
|
||||
#
|
||||
# AliasMatch ^myapp/whatever/myfile /path/to/myfile
|
||||
# <Directory /path/to/>
|
||||
# WSGIAccessScript /path/to/web2py/scripts/access.wsgi
|
||||
# </Directory>
|
||||
# </VirtualHost>
|
||||
#
|
||||
# in yourapp/controllers/default.py
|
||||
#
|
||||
# def check_access():
|
||||
# request_uri = request.vars.request_uri
|
||||
# return 'true' if auth.is_logged_in() else 'false'
|
||||
#
|
||||
# start web2py as deamon
|
||||
#
|
||||
# nohup python web2py.py -a '' -p 8002
|
||||
#
|
||||
# now try visit:
|
||||
#
|
||||
# http://domain/myapp/whatever/myfile
|
||||
#
|
||||
# and you will have access ONLY if you are logged into myapp
|
||||
#
|
||||
|
||||
URL_CHECK_ACCESS = 'http://127.0.0.1:8002/%(app)s/default/check_access'
|
||||
|
||||
def allow_access(environ,host):
|
||||
import os
|
||||
import urllib
|
||||
import urllib2
|
||||
import datetime
|
||||
header = '%s @ %s ' % (datetime.datetime.now(),host) + '='*20
|
||||
pprint = '\n'.join('%s:%s' % item for item in environ.items())
|
||||
filename = os.path.join(os.path.dirname(__file__),'access.wsgi.log')
|
||||
f = open(filename,'a')
|
||||
try:
|
||||
f.write('\n'+header+'\n'+pprint+'\n')
|
||||
finally:
|
||||
f.close()
|
||||
app = environ['REQUEST_URI'].split('/')[1]
|
||||
keys = [key for key in environ if key.startswith('HTTP_')]
|
||||
headers = {}
|
||||
for key in environ:
|
||||
if key.startswith('HTTP_'):
|
||||
headers[key[5:]] = environ[key] # this passes the cookies through!
|
||||
try:
|
||||
data = urllib.urlencode({'request_uri':environ['REQUEST_URI']})
|
||||
request = urllib2.Request(URL_CHECK_ACCESS % dict(app=app),data,headers)
|
||||
response = urllib2.urlopen(request).read().strip().lower()
|
||||
if response.startswith('true'): return True
|
||||
except: pass
|
||||
return False
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
autoroutes writes routes for you based on a simpler routing
|
||||
configuration file called routes.conf. Example:
|
||||
|
||||
----- BEGIN routes.conf-------
|
||||
127.0.0.1 /examples/default
|
||||
domain1.com /app1/default
|
||||
domain2.com /app2/default
|
||||
domain3.com /app3/default
|
||||
----- END ----------
|
||||
|
||||
It maps a domain (the left-hand side) to an app (one app per domain),
|
||||
and shortens the URLs for the app by removing the listed path prefix. That means:
|
||||
|
||||
http://domain1.com/index is mapped to /app1/default/index
|
||||
http://domain2.com/index is mapped to /app2/default/index
|
||||
|
||||
It preserves admin, appadmin, static files, favicon.ico and robots.txt:
|
||||
|
||||
http://domain1.com/favicon.ico /welcome/static/favicon.ico
|
||||
http://domain1.com/robots.txt /welcome/static/robots.txt
|
||||
http://domain1.com/admin/... /admin/...
|
||||
http://domain1.com/appadmin/... /app1/appadmin/...
|
||||
http://domain1.com/static/... /app1/static/...
|
||||
|
||||
and vice-versa.
|
||||
|
||||
To use, cp scripts/autoroutes.py routes.py
|
||||
|
||||
and either edit the config string below, or set config = "" and edit routes.conf
|
||||
'''
|
||||
|
||||
config = '''
|
||||
127.0.0.1 /examples/default
|
||||
domain1.com /app1/default
|
||||
domain2.com /app2/default
|
||||
domain3.com /app3/defcon3
|
||||
'''
|
||||
if not config.strip():
|
||||
try:
|
||||
config_file = open('routes.conf','r')
|
||||
try:
|
||||
config = config_file.read()
|
||||
finally:
|
||||
config_file.close()
|
||||
except:
|
||||
config=''
|
||||
|
||||
def auto_in(apps):
|
||||
routes = [
|
||||
('/robots.txt','/welcome/static/robots.txt'),
|
||||
('/favicon.ico','/welcome/static/favicon.ico'),
|
||||
('/admin$anything','/admin$anything'),
|
||||
]
|
||||
for domain,path in [x.strip().split() for x in apps.split('\n') if x.strip() and not x.strip().startswith('#')]:
|
||||
if not path.startswith('/'): path = '/'+path
|
||||
if path.endswith('/'): path = path[:-1]
|
||||
app = path.split('/')[1]
|
||||
routes += [
|
||||
('.*:https?://(.*\.)?%s:$method /' % domain,'%s' % path),
|
||||
('.*:https?://(.*\.)?%s:$method /static/$anything' % domain,'/%s/static/$anything' % app),
|
||||
('.*:https?://(.*\.)?%s:$method /appadmin/$anything' % domain,'/%s/appadmin/$anything' % app),
|
||||
('.*:https?://(.*\.)?%s:$method /$anything' % domain,'%s/$anything' % path),
|
||||
]
|
||||
return routes
|
||||
|
||||
def auto_out(apps):
|
||||
routes = []
|
||||
for domain,path in [x.strip().split() for x in apps.split('\n') if x.strip() and not x.strip().startswith('#')]:
|
||||
if not path.startswith('/'): path = '/'+path
|
||||
if path.endswith('/'): path = path[:-1]
|
||||
app = path.split('/')[1]
|
||||
routes += [
|
||||
('/%s/static/$anything' % app,'/static/$anything'),
|
||||
('/%s/appadmin/$anything' % app, '/appadmin/$anything'),
|
||||
('%s/$anything' % path, '/$anything'),
|
||||
]
|
||||
return routes
|
||||
|
||||
routes_in = auto_in(config)
|
||||
routes_out = auto_out(config)
|
||||
|
||||
def __routes_doctest():
|
||||
'''
|
||||
Dummy function for doctesting autoroutes.py.
|
||||
|
||||
Use filter_url() to test incoming or outgoing routes;
|
||||
filter_err() for error redirection.
|
||||
|
||||
filter_url() accepts overrides for method and remote host:
|
||||
filter_url(url, method='get', remote='0.0.0.0', out=False)
|
||||
|
||||
filter_err() accepts overrides for application and ticket:
|
||||
filter_err(status, application='app', ticket='tkt')
|
||||
|
||||
>>> filter_url('http://domain1.com/favicon.ico')
|
||||
'http://domain1.com/welcome/static/favicon.ico'
|
||||
>>> filter_url('https://domain2.com/robots.txt')
|
||||
'https://domain2.com/welcome/static/robots.txt'
|
||||
>>> filter_url('http://domain3.com/fcn')
|
||||
'http://domain3.com/app3/defcon3/fcn'
|
||||
>>> filter_url('http://127.0.0.1/fcn')
|
||||
'http://127.0.0.1/examples/default/fcn'
|
||||
>>> filter_url('HTTP://DOMAIN.COM/app/ctr/fcn')
|
||||
'http://domain.com/app/ctr/fcn'
|
||||
>>> filter_url('http://domain.com/app/ctr/fcn?query')
|
||||
'http://domain.com/app/ctr/fcn?query'
|
||||
>>> filter_url('http://otherdomain.com/fcn')
|
||||
'http://otherdomain.com/fcn'
|
||||
>>> regex_filter_out('/app/ctr/fcn')
|
||||
'/app/ctr/fcn'
|
||||
>>> regex_filter_out('/app1/ctr/fcn')
|
||||
'/app1/ctr/fcn'
|
||||
>>> filter_url('https://otherdomain.com/app1/default/fcn', out=True)
|
||||
'/fcn'
|
||||
>>> filter_url('http://otherdomain.com/app2/ctr/fcn', out=True)
|
||||
'/app2/ctr/fcn'
|
||||
>>> filter_url('http://domain1.com/app1/default/fcn?query', out=True)
|
||||
'/fcn?query'
|
||||
>>> filter_url('http://domain2.com/app3/defcon3/fcn#anchor', out=True)
|
||||
'/fcn#anchor'
|
||||
'''
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
import gluon.main
|
||||
except ImportError:
|
||||
import sys, os
|
||||
os.chdir(os.path.dirname(os.path.dirname(__file__)))
|
||||
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
|
||||
import gluon.main
|
||||
from gluon.rewrite import regex_select, load, filter_url, regex_filter_out
|
||||
regex_select() # use base routing parameters
|
||||
load(routes=__file__) # load this file
|
||||
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import re
|
||||
|
||||
filename = sys.argv[1]
|
||||
|
||||
datafile = open(filename, 'r')
|
||||
try:
|
||||
data = '\n'+datafile.read()
|
||||
finally:
|
||||
datafile.close()
|
||||
SPACE = '\n ' if '-n' in sys.argv[1:] else ' '
|
||||
|
||||
data = re.compile('(?<!\:)//(?P<a>.*)').sub('/* \g<a> */',data)
|
||||
data = re.compile('[ ]+').sub(' ', data)
|
||||
data = re.compile('\s*{\s*').sub(' {'+SPACE, data)
|
||||
data = re.compile('\s*;\s*').sub(';'+SPACE, data)
|
||||
data = re.compile(',\s*').sub(', ', data)
|
||||
data = re.compile('\s*\*/\s*').sub('*/'+SPACE, data)
|
||||
data = re.compile('\s*}\s*').sub(SPACE+'}\n', data)
|
||||
data = re.compile('\n\s*\n').sub('\n', data)
|
||||
data = re.compile(';\s+/\*').sub('; /*',data)
|
||||
data = re.compile('\*/\s+/\*').sub(' ',data)
|
||||
data = re.compile('[ ]+\n').sub('\n', data)
|
||||
data = re.compile('\n\s*/[\*]+(?P<a>.*?)[\*]+/',re.DOTALL).sub(
|
||||
'\n/*\g<a>*/\n',data)
|
||||
data = re.compile('[ \t]+(?P<a>\S.+?){').sub('\g<a>{',data)
|
||||
data = data.replace('}','}\n')
|
||||
|
||||
print data
|
||||
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
import sys
|
||||
import re
|
||||
|
||||
def cleancss(text):
|
||||
text=re.compile('\s+').sub(' ', text)
|
||||
text=re.compile('\s*(?P<a>,|:)\s*').sub('\g<a> ', text)
|
||||
text=re.compile('\s*;\s*').sub(';\n ', text)
|
||||
text=re.compile('\s*\{\s*').sub(' {\n ', text)
|
||||
text=re.compile('\s*\}\s*').sub('\n}\n\n', text)
|
||||
return text
|
||||
|
||||
def cleanhtml(text):
|
||||
text=text.lower()
|
||||
r=re.compile('\<script.+?/script\>', re.DOTALL)
|
||||
scripts=r.findall(text)
|
||||
text=r.sub('<script />', text)
|
||||
r=re.compile('\<style.+?/style\>', re.DOTALL)
|
||||
styles=r.findall(text)
|
||||
text=r.sub('<style />', text)
|
||||
text=re.compile(
|
||||
'<(?P<tag>(input|meta|link|hr|br|img|param))(?P<any>[^\>]*)\s*(?<!/)>')\
|
||||
.sub('<\g<tag>\g<any> />', text)
|
||||
text=text.replace('\n', ' ')
|
||||
text=text.replace('>', '>\n')
|
||||
text=text.replace('<', '\n<')
|
||||
text=re.compile('\s*\n\s*').sub('\n', text)
|
||||
lines=text.split('\n')
|
||||
(indent, newlines)=(0, [])
|
||||
for line in lines:
|
||||
if line[:2]=='</': indent=indent-1
|
||||
newlines.append(indent*' '+line)
|
||||
if not line[:2]=='</' and line[-1:]=='>' and \
|
||||
not line[-2:] in ['/>', '->']: indent=indent+1
|
||||
text='\n'.join(newlines)
|
||||
text=re.compile('\<div(?P<a>( .+)?)\>\s+\</div\>').sub('<div\g<a>></div>',text)
|
||||
text=re.compile('\<a(?P<a>( .+)?)\>\s+(?P<b>[\w\s\(\)\/]+?)\s+\</a\>').sub('<a\g<a>>\g<b></a>',text)
|
||||
text=re.compile('\<b(?P<a>( .+)?)\>\s+(?P<b>[\w\s\(\)\/]+?)\s+\</b\>').sub('<b\g<a>>\g<b></b>',text)
|
||||
text=re.compile('\<i(?P<a>( .+)?)\>\s+(?P<b>[\w\s\(\)\/]+?)\s+\</i\>').sub('<i\g<a>>\g<b></i>',text)
|
||||
text=re.compile('\<span(?P<a>( .+)?)\>\s+(?P<b>[\w\s\(\)\/]+?)\s+\</span\>').sub('<span\g<a>>\g<b></span>',text)
|
||||
text=re.compile('\s+\<br(?P<a>.*?)\/\>').sub('<br\g<a>/>',text)
|
||||
text=re.compile('\>(?P<a>\s+)(?P<b>[\.\,\:\;])').sub('>\g<b>\g<a>',text)
|
||||
text=re.compile('\n\s*\n').sub('\n',text)
|
||||
for script in scripts:
|
||||
text=text.replace('<script />', script, 1)
|
||||
for style in styles:
|
||||
text=text.replace('<style />', cleancss(style), 1)
|
||||
return text
|
||||
|
||||
def read_file(filename):
|
||||
f = open(filename, 'r')
|
||||
try:
|
||||
return f.read()
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
file=sys.argv[1]
|
||||
if file[-4:]=='.css':
|
||||
print cleancss(read_file(file))
|
||||
if file[-5:]=='.html':
|
||||
print cleanhtml(read_file(file))
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import re
|
||||
|
||||
def cleanjs(text):
|
||||
text = re.sub('\s*}\s*','\n}\n',text)
|
||||
text = re.sub('\s*{\s*',' {\n',text)
|
||||
text = re.sub('\s*;\s*',';\n',text)
|
||||
text = re.sub('\s*,\s*',', ',text)
|
||||
text = re.sub('\s*(?P<a>[\+\-\*/\=]+)\s*',' \g<a> ',text)
|
||||
lines = text.split('\n')
|
||||
text=''
|
||||
indent=0
|
||||
for line in lines:
|
||||
rline=line.strip()
|
||||
if rline:
|
||||
pass
|
||||
return text
|
||||
|
||||
Executable
+132
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import cStringIO
|
||||
import re
|
||||
import sys
|
||||
import tarfile
|
||||
import urllib
|
||||
import xml.parsers.expat as expat
|
||||
|
||||
"""
|
||||
Update script for contenttype.py module.
|
||||
|
||||
Usage: python contentupdate.py /path/to/contenttype.py
|
||||
|
||||
If no path is specified, script will look for contenttype.py in current
|
||||
working directory.
|
||||
|
||||
Internet connection is required to perform the update.
|
||||
"""
|
||||
|
||||
OVERRIDE = [
|
||||
('.pdb', 'chemical/x-pdb'),
|
||||
('.xyz', 'chemical/x-pdb')
|
||||
]
|
||||
|
||||
|
||||
class MIMEParser(dict):
|
||||
|
||||
def __start_element_handler(self, name, attrs):
|
||||
if name == 'mime-type':
|
||||
if self.type:
|
||||
for extension in self.extensions:
|
||||
self[extension] = self.type
|
||||
self.type = attrs['type'].lower()
|
||||
self.extensions = []
|
||||
elif name == 'glob':
|
||||
pattern = attrs['pattern']
|
||||
if pattern.startswith('*.'):
|
||||
self.extensions.append(pattern[1:].lower())
|
||||
|
||||
def __init__(self, fileobj):
|
||||
dict.__init__(self)
|
||||
self.type = ''
|
||||
self.extensions = ''
|
||||
parser = expat.ParserCreate()
|
||||
parser.StartElementHandler = self.__start_element_handler
|
||||
parser.ParseFile(fileobj)
|
||||
for extension, contenttype in OVERRIDE:
|
||||
self[extension] = contenttype
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
path = sys.argv[1]
|
||||
except:
|
||||
path = 'contenttype.py'
|
||||
vregex = re.compile('database version (?P<version>.+?)\.?\n')
|
||||
sys.stdout.write('Checking contenttype.py database version:')
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
pathfile = open(path)
|
||||
try:
|
||||
current = pathfile.read()
|
||||
finally:
|
||||
pathfile.close()
|
||||
cversion = re.search(vregex, current).group('version')
|
||||
sys.stdout.write('\t[OK] version %s\n' % cversion)
|
||||
except Exception, e:
|
||||
sys.stdout.write('\t[ERROR] %s\n' % e)
|
||||
exit()
|
||||
sys.stdout.write('Checking freedesktop.org database version:')
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
search = re.search('(?P<url>http://freedesktop.org/.+?/shared-mime-info-(?P<version>.+?)\.tar\.(?P<type>[gb]z2?))',
|
||||
urllib.urlopen('http://www.freedesktop.org/wiki/Software/shared-mime-info').read())
|
||||
url = search.group('url')
|
||||
assert url != None
|
||||
nversion = search.group('version')
|
||||
assert nversion != None
|
||||
ftype = search.group('type')
|
||||
assert ftype != None
|
||||
sys.stdout.write('\t[OK] version %s\n' % nversion)
|
||||
except:
|
||||
sys.stdout.write('\t[ERROR] unknown version\n')
|
||||
exit()
|
||||
if cversion == nversion:
|
||||
sys.stdout.write('\nContenttype.py database is up to date\n')
|
||||
exit()
|
||||
try:
|
||||
raw_input('\nContenttype.py database updates are available from:\n%s (approx. 0.5MB)\nPress enter to continue or CTRL-C to quit now\nWARNING: this will replace contenttype.py file content IN PLACE' % url)
|
||||
except:
|
||||
exit()
|
||||
sys.stdout.write('\nDownloading new database:')
|
||||
sys.stdout.flush()
|
||||
fregex = re.compile('^.*/freedesktop\.org\.xml$')
|
||||
try:
|
||||
io = cStringIO.StringIO()
|
||||
io.write(urllib.urlopen(url).read())
|
||||
sys.stdout.write('\t[OK] done\n')
|
||||
except Exception, e:
|
||||
sys.stdout.write('\t[ERROR] %s\n' % e)
|
||||
exit()
|
||||
sys.stdout.write('Installing new database:')
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
tar = tarfile.TarFile.open(fileobj=io, mode='r:%s' % ftype)
|
||||
try:
|
||||
for content in tar.getnames():
|
||||
if fregex.match(content):
|
||||
xml = tar.extractfile(content)
|
||||
break
|
||||
finally:
|
||||
tar.close()
|
||||
data = MIMEParser(xml)
|
||||
io = cStringIO.StringIO()
|
||||
io.write('CONTENT_TYPE = {\n')
|
||||
for key in sorted(data):
|
||||
io.write(' \'%s\': \'%s\',\n' % (key, data[key]))
|
||||
io.write(' }')
|
||||
io.seek(0)
|
||||
contenttype = open('contenttype.py', 'w')
|
||||
try:
|
||||
contenttype.write(re.sub(vregex, 'database version %s.\n' % nversion, re.sub('CONTENT_TYPE = \{(.|\n)+?\}', io.getvalue(), current)))
|
||||
finally:
|
||||
contenttype.close()
|
||||
if not current.closed:
|
||||
current.close()
|
||||
sys.stdout.write('\t\t\t[OK] done\n')
|
||||
except Exception, e:
|
||||
sys.stdout.write('\t\t\t[ERROR] %s\n' % e)
|
||||
|
||||
+682
@@ -0,0 +1,682 @@
|
||||
|
||||
import os,sys
|
||||
from collections import deque
|
||||
import string
|
||||
import argparse
|
||||
import cStringIO,operator
|
||||
import cPickle as pickle
|
||||
from collections import deque
|
||||
import math
|
||||
import re
|
||||
import cmd
|
||||
import readline
|
||||
try:
|
||||
from gluon import DAL
|
||||
except ImportError as err:
|
||||
print('gluon path not found')
|
||||
|
||||
class refTable(object):
|
||||
def __init__(self):
|
||||
self.columns = None
|
||||
self.rows = None
|
||||
|
||||
def getcolHeader(self,colHeader):
|
||||
return "{0}".format(' | '.join([string.join(string.strip('**{0}**'.format(item)),
|
||||
'') for item in colHeader]))
|
||||
|
||||
|
||||
def wrapTable(self,rows, hasHeader=False, headerChar='-', delim=' | ', justify='left',
|
||||
separateRows=False, prefix='', postfix='', wrapfunc=lambda x:x):
|
||||
|
||||
def rowWrapper(row):
|
||||
|
||||
'''---
|
||||
newRows is returned like
|
||||
[['w'], ['x'], ['y'], ['z']]
|
||||
---'''
|
||||
newRows = [wrapfunc(item).split('\n') for item in row]
|
||||
self.rows = newRows
|
||||
'''---
|
||||
rowList gives like newRows but
|
||||
formatted like [[w, x, y, z]]
|
||||
---'''
|
||||
rowList = [[substr or '' for substr in item] for item in map(None,*newRows)]
|
||||
return rowList
|
||||
|
||||
logicalRows = [rowWrapper(row) for row in rows]
|
||||
|
||||
columns = map(None,*reduce(operator.add,logicalRows))
|
||||
self.columns = columns
|
||||
|
||||
maxWidths = [max(\
|
||||
[len(str\
|
||||
(item)) for \
|
||||
item in column]\
|
||||
) for column \
|
||||
in columns]
|
||||
|
||||
rowSeparator = headerChar * (len(prefix) + len(postfix) + sum(maxWidths) + \
|
||||
len(delim)*(len(maxWidths)-1))
|
||||
|
||||
justify = {'center'\
|
||||
:str\
|
||||
.center,
|
||||
'right'\
|
||||
:str\
|
||||
.rjust,
|
||||
'left'\
|
||||
:str.\
|
||||
ljust\
|
||||
}[justify\
|
||||
.lower(\
|
||||
)]
|
||||
|
||||
output=cStringIO.StringIO()
|
||||
|
||||
if separateRows:
|
||||
print >> output, rowSeparator
|
||||
|
||||
for physicalRows in logicalRows:
|
||||
for row in physicalRows:
|
||||
print >> output,\
|
||||
prefix + delim.join([\
|
||||
justify(str(item),width) for (\
|
||||
item,width) in zip(row,maxWidths)]\
|
||||
) + postfix
|
||||
|
||||
if separateRows or hasHeader:
|
||||
print >> output, rowSeparator; hasHeader=False
|
||||
return output.getvalue()
|
||||
|
||||
def wrap_onspace(self,text,width):
|
||||
return reduce(lambda line, word, width=width: '{0}{1}{2}'\
|
||||
.format(line\
|
||||
,' \n'[(len(\
|
||||
line[line.rfind('\n'\
|
||||
) + 1:]) + len(\
|
||||
word.split('\n',1)[0]) >=\
|
||||
width)],word),text.split(' '))
|
||||
|
||||
def wrap_onspace_strict(self,text,width):
|
||||
wordRegex = re.compile(r'\S{'+str(width)+r',}')
|
||||
return self.wrap_onspace(\
|
||||
wordRegex.sub(\
|
||||
lambda m: self.\
|
||||
wrap_always(\
|
||||
m.group(),width),text\
|
||||
),width)
|
||||
|
||||
def wrap_always(self,text,width):
|
||||
return '\n'.join(\
|
||||
[ text[width*i:width*(i+1\
|
||||
)] for i in xrange(\
|
||||
int(math.ceil(1.*len(\
|
||||
text)/width))) ])
|
||||
|
||||
class tableHelper():
|
||||
def __init__(self):
|
||||
self.oTable = refTable()
|
||||
|
||||
def getAsRows(self,data):
|
||||
return [row.strip().split(',') for row in data.splitlines()]
|
||||
|
||||
def getTable_noWrap(self,data,header=None):
|
||||
rows = self.getAsRows(data)
|
||||
if header is not None:hRows = [header]+rows
|
||||
else:hRows = rows
|
||||
table = self.oTable.wrapTable(hRows, hasHeader=True)
|
||||
return table
|
||||
|
||||
def getTable_Wrap(self,data,wrapStyle,header=None,width=65):
|
||||
wrapper = None
|
||||
if len(wrapStyle) > 1:
|
||||
rows = self.getAsRows(data)
|
||||
if header is not None:hRows = [header]+rows
|
||||
else:hRows = rows
|
||||
|
||||
for wrapper in (self.oTable.wrap_always,
|
||||
self.oTable.wrap_onspace,
|
||||
self.oTable.wrap_onspace_strict):
|
||||
return self.oTable.wrapTable(hRows\
|
||||
,hasHeader=True\
|
||||
,separateRows=True\
|
||||
,prefix='| '\
|
||||
,postfix=' |'\
|
||||
,wrapfunc\
|
||||
=lambda x:\
|
||||
wrapper(x,width))
|
||||
else:
|
||||
return self.getTable_noWrap(data,header)
|
||||
|
||||
def getAsErrorTable(self,err):
|
||||
return self.getTable_Wrap(err,None)
|
||||
|
||||
|
||||
class console:
|
||||
def __init__(self,prompt,banner=None):
|
||||
self.prompt=prompt
|
||||
self.banner=banner
|
||||
self.commands={}
|
||||
self.commandSort=[]
|
||||
self.db=None
|
||||
|
||||
for i in dir(self):
|
||||
if "cmd_"==i[:4]:
|
||||
cmd=i.split("cmd_")[1].lower()
|
||||
self.commands[cmd]=getattr(self,i)
|
||||
try:self.commandSort.append((int(self\
|
||||
.commands[cmd].__doc__.split(\
|
||||
"|")[0]),cmd))
|
||||
except:pass
|
||||
|
||||
self.commandSort.sort()
|
||||
self.commandSort=[i[1] for i in self.commandSort]
|
||||
|
||||
self.var_DEBUG=False
|
||||
self.var_tableStyle=''
|
||||
|
||||
self.configvars={}
|
||||
for i in dir(self):
|
||||
if "var_"==i[:4]:
|
||||
var=i.split("var_")[1]
|
||||
self.configvars[var]=i
|
||||
|
||||
def setBanner(self,banner):
|
||||
self.banner=banner
|
||||
|
||||
def execCmd(self,db):
|
||||
self.db=db
|
||||
print self.banner
|
||||
while True:
|
||||
try:
|
||||
command=raw_input(self.prompt)
|
||||
try:
|
||||
self.execCommand(command)
|
||||
except:
|
||||
self.execute(command)
|
||||
except KeyboardInterrupt:break
|
||||
except EOFError:break
|
||||
except Exception,a:self.printError (a)
|
||||
print ("\r\n\r\nBye!...")
|
||||
sys.exit(0)
|
||||
|
||||
def printError(self,err):
|
||||
sys.stderr.write("Error: {0}\r\n".format(str(err),))
|
||||
if self.var_DEBUG:pass
|
||||
|
||||
def execute(self,cmd):
|
||||
try:
|
||||
if not '-table ' in cmd:
|
||||
exec '{0}'.format(cmd)
|
||||
else:
|
||||
file=None
|
||||
table=None
|
||||
|
||||
fields=[]
|
||||
items=string.split(cmd,' ')
|
||||
invalidParams=[]
|
||||
table=self.getTable(items[1])
|
||||
allowedParams=['fields','file']
|
||||
for i in items:
|
||||
if '=' in i and not string.split(i,'=')[0] in allowedParams:
|
||||
try:
|
||||
invalidParams.append(i)
|
||||
except Exception, err:
|
||||
raise Exception, 'invalid parameter\n{0}'.format(i)
|
||||
else:
|
||||
if 'file=' in i:
|
||||
file=os.path.abspath(string.strip(string.split(i,'=')[1]))
|
||||
if 'fields=' in i:
|
||||
for field in string.split(string.split(i,'=')[1],','):
|
||||
if field in self.db[table].fields:
|
||||
fields.append(string.strip(field))
|
||||
|
||||
if len(invalidParams)>0:
|
||||
print('the following parameter(s) is not valid\n{0}'.format(\
|
||||
string.join(invalidParams,',')))
|
||||
else:
|
||||
try:
|
||||
self.cmd_table(table,file,fields)
|
||||
except Exception, err:
|
||||
print('could not generate table for table {0}\n{1}'\
|
||||
.format(table,err))
|
||||
except Exception, err:
|
||||
print('sorry, can not do that!\n{0}'.format(err))
|
||||
|
||||
def getTable(self,tbl):
|
||||
for mTbl in db.tables:
|
||||
if tbl in mTbl:
|
||||
if mTbl.startswith(tbl):
|
||||
return mTbl
|
||||
|
||||
def execCommand(self,cmd):
|
||||
words=cmd.split(" ")
|
||||
words=[i for i in words if i]
|
||||
if not words:return
|
||||
cmd,parameters=words[0].lower(),words[1:]
|
||||
|
||||
if not cmd in self.commands:
|
||||
raise Exception("Command {0} not found. Try 'help'\r\n".format(cmd))
|
||||
|
||||
self.commands[cmd](*parameters)
|
||||
|
||||
'''---
|
||||
DEFAULT COMMANDS (begins with cmd_)
|
||||
---'''
|
||||
def cmd_clear(self,numlines=100):
|
||||
"""-5|clear|clear the screen"""
|
||||
if os.name == "posix":
|
||||
'''---
|
||||
Unix/Linux/MacOS/BSD/etc
|
||||
---'''
|
||||
os.system('clear')
|
||||
elif os.name in ("nt", "dos", "ce"):
|
||||
'''---
|
||||
Windows
|
||||
---'''
|
||||
os.system('CLS')
|
||||
else:
|
||||
'''---
|
||||
Fallback for other operating systems.
|
||||
---'''
|
||||
print '\n'*numlines
|
||||
|
||||
def cmd_table(self,tbl,file=None,fields=[]):
|
||||
"""-4|-table [TABLENAME] optional[file=None] [fields=None]|\
|
||||
the default tableStyle is no_wrap - use the 'set x y' command to change the style\n\
|
||||
style choices:
|
||||
\twrap_always
|
||||
\twrap_onspace
|
||||
\twrap_onspace_strict
|
||||
\tno_wrap (value '')\n
|
||||
\t the 2nd optional param is a path to a file where the table will be written
|
||||
\t the 3rd optional param is a list of fields you want displayed\n"""
|
||||
table=None
|
||||
for mTbl in db.tables:
|
||||
if tbl in mTbl:
|
||||
if mTbl.startswith(tbl):
|
||||
table=mTbl
|
||||
break
|
||||
oTable=tableHelper()
|
||||
'''---
|
||||
tablestyle:
|
||||
wrap_always
|
||||
wrap_onspace
|
||||
wrap_onspace_strict
|
||||
or set set to "" for no wrapping
|
||||
---'''
|
||||
tableStyle=self.var_tableStyle
|
||||
filedNotFound=[]
|
||||
table_fields=None
|
||||
if len(fields)==0:
|
||||
table_fields=self.db[table].fields
|
||||
else:
|
||||
table_fields=fields
|
||||
|
||||
for field in fields:
|
||||
if not field in self.db[table].fields:
|
||||
filedNotFound.append(field)
|
||||
if len(filedNotFound)==0:
|
||||
rows=self.db(self.db[table].id>0).select()
|
||||
rows_data=[]
|
||||
for row in rows:
|
||||
rowdata=[]
|
||||
for f in table_fields:
|
||||
rowdata.append('{0}'.format(row[f]))
|
||||
rows_data.append(string.join(rowdata,','))
|
||||
data=string.join(rows_data,'\n')
|
||||
dataTable=oTable.getTable_Wrap(data,tableStyle,table_fields)
|
||||
print('TABLE {0}\n{1}'.format(table,dataTable))
|
||||
if file!=None:
|
||||
try:
|
||||
tail,head=os.path.split(file)
|
||||
try:
|
||||
os.makedirs(tail)
|
||||
except:'do nothing, folders exist'
|
||||
oFile=open(file,'w')
|
||||
oFile.write('TABLE: {0}\n{1}'.format(table,dataTable))
|
||||
oFile.close()
|
||||
print('{0} has been created and populated with all available data from table {1}\n'.format(file,table))
|
||||
except Exception, err:
|
||||
print("EXCEPTION: could not create table {0}\n{1}".format(table,err))
|
||||
else:
|
||||
print('the following fields are not valid [{0}]'.format(string.join(filedNotFound,',')))
|
||||
|
||||
def cmd_help(self,*args):
|
||||
'''-3|help|Show's help'''
|
||||
alldata=[]
|
||||
lengths=[]
|
||||
|
||||
for i in self.commandSort:alldata.append(\
|
||||
self.commands[i].__doc__.split("|")[1:])
|
||||
|
||||
for i in alldata:
|
||||
if len(i) > len(lengths):
|
||||
for j in range(len(i)\
|
||||
-len(lengths)):
|
||||
lengths.append(0)
|
||||
|
||||
j=0
|
||||
while j<len(i):
|
||||
if len(i[j])>lengths[j]:
|
||||
lengths[j]=len(i[j])
|
||||
j+=1
|
||||
|
||||
print ("-"*(lengths[0]+lengths[1]+4))
|
||||
for i in alldata:
|
||||
print (("%-"+str(lengths[0])+"s - %-"+str(lengths[1])+"s") % (i[0],i[1]))
|
||||
if len(i)>2:
|
||||
for j in i[2:]:print (("%"+str(lengths[0]+9)+"s* %s") % (" ",j))
|
||||
print
|
||||
|
||||
def cmd_vars(self,*args):
|
||||
'''-2|vars|Show variables'''
|
||||
print ("variables\r\n"+"-"*79)
|
||||
for i,j in self.configvars.items():
|
||||
value=self.parfmt(repr(getattr(self,j)),52)
|
||||
print ("| %20s | %52s |" % (i,value[0]))
|
||||
for k in value[1:]:print ("| %20s | %52s |" % ("",k))
|
||||
if len(value)>1:print("| %20s | %52s |" % ("",""))
|
||||
print ("-"*79)
|
||||
|
||||
def parfmt(self,txt,width):
|
||||
res=[]
|
||||
pos=0
|
||||
while True:
|
||||
a=txt[pos:pos+width]
|
||||
if not a:break
|
||||
res.append(a)
|
||||
pos+=width
|
||||
return res
|
||||
|
||||
def cmd_set(self,*args):
|
||||
'''-1|set [variable_name] [value]|Set configuration variable value|Values are an expressions (100 | string.lower('ABC') | etc.'''
|
||||
value=" ".join(args[1:])
|
||||
if args[0] not in self.configvars:
|
||||
setattr(self,"var_{0}".format(args[0]),eval(value))
|
||||
setattr(self,"var_{0}".format(args[0]),eval(value))
|
||||
|
||||
def cmd_clearscreen(self,numlines=50):
|
||||
'''---Clear the console.
|
||||
---'''
|
||||
if os.name == "posix":
|
||||
'''---
|
||||
Unix/Linux/MacOS/BSD/etc
|
||||
---'''
|
||||
os.system('clear')
|
||||
elif os.name in ("nt", "dos", "ce"):
|
||||
'''---
|
||||
Windows
|
||||
---'''
|
||||
os.system('CLS')
|
||||
else:
|
||||
'''---
|
||||
Fallback for other operating systems.
|
||||
---'''
|
||||
print '\n'*numlines
|
||||
|
||||
class dalShell(console):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def shell(self,db):
|
||||
console.__init__(self,prompt=">>> ",banner='dal interactive shell')
|
||||
self.execCmd(db)
|
||||
|
||||
class setCopyDB():
|
||||
def __init__(self):
|
||||
'''---
|
||||
non source or target specific vars
|
||||
---'''
|
||||
self.strModel=None
|
||||
self.dalPath=None
|
||||
self.db=None
|
||||
'''---
|
||||
source vars
|
||||
---'''
|
||||
self.sourceModel=None
|
||||
self.sourceFolder=None
|
||||
self.sourceConnectionString=None
|
||||
self.sourcedbType=None
|
||||
self.sourcedbName=None
|
||||
'''---
|
||||
target vars
|
||||
---'''
|
||||
self.targetdbType=None
|
||||
self.targetdbName=None
|
||||
self.targetModel=None
|
||||
self.targetFolder=None
|
||||
self.targetConnectionString=None
|
||||
self.truncate=False
|
||||
|
||||
def _getDal(self):
|
||||
mDal=None
|
||||
if self.dalPath is not None:
|
||||
global DAL
|
||||
sys.path.append(self.dalPath)
|
||||
mDal=__import__('dal',globals={},locals={},fromlist=['DAL'],level=0)
|
||||
DAL=mDal.DAL
|
||||
return mDal
|
||||
|
||||
def instDB(self,storageFolder,storageConnectionString,autoImport):
|
||||
self.db=DAL(storageConnectionString,folder=os.path.abspath(storageFolder),auto_import=autoImport)
|
||||
return self.db
|
||||
|
||||
def delete_DB_tables(self,storageFolder,storageType):
|
||||
print 'delete_DB_tablesn\n\t{0}\n\t{1}'.format(storageFolder,storageType)
|
||||
dataFiles=[storageType,"sql.log"]
|
||||
try:
|
||||
for f in os.listdir(storageFolder):
|
||||
if ".table" in f:
|
||||
fTable="{0}/{1}".format(storageFolder,f)
|
||||
os.remove(fTable)
|
||||
print('deleted {0}'.format(fTable))
|
||||
for dFile in dataFiles:
|
||||
os.remove("{0}/{1}".format(storageFolder,dFile))
|
||||
print('deleted {0}'.format("{0}/{1}".format(storageFolder,dFile)))
|
||||
except Exception, errObj:
|
||||
print(str(errObj))
|
||||
|
||||
def truncatetables(self,tables=[]):
|
||||
if len(tables)!=0:
|
||||
try:
|
||||
print 'table value: {0}'.format(tables)
|
||||
for tbl in self.db.tables:
|
||||
for mTbl in tables:
|
||||
if mTbl.startswith(tbl):
|
||||
self.db[mTbl].truncate()
|
||||
except Exception, err:
|
||||
print('EXCEPTION: {0}'.format(err))
|
||||
else:
|
||||
try:
|
||||
for tbl in self.db.tables:
|
||||
self.db[tbl].truncate()
|
||||
except Exception, err:
|
||||
print('EXCEPTION: {0}'.format(err))
|
||||
|
||||
def copyDB(self):
|
||||
other_db=DAL("{0}://{1}".format(self.targetdbType,self.targetdbName),folder=self.targetFolder)
|
||||
|
||||
print 'creating tables...'
|
||||
|
||||
for table in self.db:
|
||||
other_db.define_table(table._tablename,*[field for field in table])
|
||||
'''
|
||||
should there be an option to truncAte target DB?
|
||||
if yes, then change args to allow for choice
|
||||
and set self.trancate to the art value
|
||||
|
||||
if self.truncate==True:
|
||||
other_db[table._tablename].truncate()
|
||||
'''
|
||||
|
||||
print 'exporting data...'
|
||||
self.db.export_to_csv_file(open('tmp.sql','wb'))
|
||||
|
||||
print 'importing data...'
|
||||
other_db.import_from_csv_file(open('tmp.sql','rb'))
|
||||
other_db.commit()
|
||||
print 'done!'
|
||||
print 'Attention: do not run this program again or you end up with duplicate records'
|
||||
|
||||
def createfolderPath(self,folder):
|
||||
try:
|
||||
if folder!=None:os.makedirs(folder)
|
||||
except Exception, err:
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
oCopy=setCopyDB()
|
||||
db=None
|
||||
targetDB=None
|
||||
dbfolder=None
|
||||
clean=False
|
||||
model=None
|
||||
truncate=False
|
||||
|
||||
parser=argparse.ArgumentParser(description='\
|
||||
samplecmd line:\n\
|
||||
-f ./blueLite/db_storage -i -y sqlite://storage.sqlite -Y sqlite://storage2.sqlite -d ./blueLite/pyUtils/sql/blueSQL -t True',
|
||||
epilog = '')
|
||||
reqGroup=parser.add_argument_group('Required arguments')
|
||||
reqGroup.add_argument('-f','--sourceFolder'\
|
||||
,required=True\
|
||||
,help="path to the 'source' folder of the 'source' DB")
|
||||
reqGroup.add_argument('-F','--targetFolder'\
|
||||
,required=False\
|
||||
,help="path to the 'target' folder of the 'target' DB")
|
||||
reqGroup.add_argument('-y','--sourceConnectionString'\
|
||||
,required=True\
|
||||
,help="source db connection string ()\n\
|
||||
------------------------------------------------\n\
|
||||
\
|
||||
sqlite://storage.db\n\
|
||||
mysql://username:password@localhost/test\n\
|
||||
postgres://username:password@localhost/test\n\
|
||||
mssql://username:password@localhost/test\n\
|
||||
firebird://username:password@localhost/test\n\
|
||||
oracle://username/password@test\n\
|
||||
db2://username:password@test\n\
|
||||
ingres://username:password@localhost/test\n\
|
||||
informix://username:password@test\n\
|
||||
\
|
||||
------------------------------------------------")
|
||||
reqGroup.add_argument('-Y','--targetConnectionString'\
|
||||
,required=True\
|
||||
,help="target db type (sqlite,mySql,etc.)")
|
||||
autoImpGroup=parser.add_argument_group('optional args (auto_import)')
|
||||
autoImpGroup.add_argument('-a','--autoimport'\
|
||||
,required=False\
|
||||
,help='set to True to bypass loading of the model')
|
||||
|
||||
"""
|
||||
|
||||
*** removing -m/-M options for now --> i need a
|
||||
better regex to match db.define('bla')...with optional db.commit()
|
||||
|
||||
modelGroup=parser.add_argument_group('optional args (create model)')
|
||||
modelGroup.add_argument('-m','--sourcemodel'\
|
||||
,required=False\
|
||||
,help='to create a model from an existing model, point to the source model')
|
||||
modelGroup.add_argument('-M','--targetmodel'\
|
||||
,required=False\
|
||||
,help='to create a model from an existing model, point to the target model')
|
||||
|
||||
"""
|
||||
|
||||
|
||||
miscGroup=parser.add_argument_group('optional args/tasks')
|
||||
miscGroup.add_argument('-i','--interactive'\
|
||||
,required=False\
|
||||
,action='store_true'\
|
||||
,help='run in interactive mode')
|
||||
miscGroup.add_argument('-d','--dal'\
|
||||
,required=False\
|
||||
,help='path to dal.py')
|
||||
miscGroup.add_argument('-t','--truncate'\
|
||||
,choices=['True','False']\
|
||||
,help='delete the records but *not* the table of the SOURCE DB')
|
||||
miscGroup.add_argument('-b','--tables'\
|
||||
,required=False\
|
||||
,type=list\
|
||||
,help='optional list (comma delimited) of SOURCE tables to truncate, defaults to all')
|
||||
miscGroup.add_argument('-c','--clean'\
|
||||
,required=False\
|
||||
,help='delete the DB,tables and the log file, WARNING: this is unrecoverable')
|
||||
|
||||
args=parser.parse_args()
|
||||
db=None
|
||||
mDal=None
|
||||
|
||||
try:
|
||||
oCopy.sourceFolder=args.sourceFolder
|
||||
oCopy.targetFolder=args.sourceFolder
|
||||
sourceItems=string.split(args.sourceConnectionString,'://')
|
||||
oCopy.sourcedbType=sourceItems[0]
|
||||
oCopy.sourcedbName=sourceItems[1]
|
||||
targetItems=string.split(args.targetConnectionString,'://')
|
||||
oCopy.targetdbType=sourceItems[0]
|
||||
oCopy.targetdbName=sourceItems[1]
|
||||
except Exception, err:
|
||||
print('EXCEPTION: {0}'.format(err))
|
||||
|
||||
if args.dal:
|
||||
try:
|
||||
autoImport=True
|
||||
if args.autoimport:autoImport=args.autoimport
|
||||
#sif not DAL in globals:
|
||||
#if not sys.path.__contains__():
|
||||
oCopy.dalPath=args.dal
|
||||
mDal=oCopy._getDal()
|
||||
db=oCopy.instDB(args.sourceFolder,args.sourceConnectionString,autoImport)
|
||||
except Exception, err:
|
||||
print('EXCEPTION: could not set DAL\n{0}'.format(err))
|
||||
if args.truncate:
|
||||
try:
|
||||
if args.truncate:
|
||||
if args.tables:tables=string.split(string.strip(args.tables),',')
|
||||
else:oCopy.truncatetables([])
|
||||
except Exception, err:
|
||||
print('EXCEPTION: could not truncate tables\n{0}'.format(err))
|
||||
try:
|
||||
if args.clean:oCopy.delete_DB_tables(oCopy.targetFolder,oCopy.targetType)
|
||||
except Exception, err:
|
||||
print('EXCEPTION: could not clean db\n{0}'.format(err))
|
||||
|
||||
|
||||
"""
|
||||
*** goes with -m/-M options... removed for now
|
||||
|
||||
if args.sourcemodel:
|
||||
try:
|
||||
oCopy.sourceModel=args.sourcemodel
|
||||
oCopy.targetModel=args.sourcemodel
|
||||
oCopy.createModel()
|
||||
except Exception, err:
|
||||
print('EXCEPTION: could not create model\n\
|
||||
source model: {0}\n\
|
||||
target model: {1}\n\
|
||||
{2}'.format(args.sourcemodel,args.targetmodel,err))
|
||||
"""
|
||||
|
||||
if args.sourceFolder:
|
||||
try:
|
||||
oCopy.sourceFolder=os.path.abspath(args.sourceFolder)
|
||||
oCopy.createfolderPath(oCopy.sourceFolder)
|
||||
except Exception, err:
|
||||
print('EXCEPTION: could not create folder path\n{0}'.format(err))
|
||||
else:oCopy.dbStorageFolder=os.path.abspath(os.getcwd())
|
||||
if args.targetFolder:
|
||||
try:
|
||||
oCopy.targetFolder=os.path.abspath(args.targetFolder)
|
||||
oCopy.createfolderPath(oCopy.targetFolder)
|
||||
except Exception, err:
|
||||
print('EXCEPTION: could not create folder path\n{0}'.format(err))
|
||||
if not args.interactive:
|
||||
try:
|
||||
oCopy.copyDB()
|
||||
except Exception, err:
|
||||
print('EXCEPTION: could not make a copy of the database\n{0}'.format(err))
|
||||
else:
|
||||
s=dalShell()
|
||||
s.shell(db)
|
||||
@@ -0,0 +1,26 @@
|
||||
import sys, glob, os, shutil
|
||||
name=sys.argv[1]
|
||||
app=sys.argv[2]
|
||||
dest=sys.argv[3]
|
||||
a=glob.glob('applications/%(app)s/*/plugin_%(name)s.*' % dict(app=app,name=name))
|
||||
b=glob.glob('applications/%(app)s/*/plugin_%(name)s/*' % dict(app=app,name=name))
|
||||
|
||||
for f in a:
|
||||
print 'cp %s ...' % f,
|
||||
shutil.copyfile(f,os.path.join('applications',dest,*f.split('/')[2:]))
|
||||
print 'done'
|
||||
|
||||
for f in b:
|
||||
print 'cp %s ...' % f,
|
||||
path = f.split('/')
|
||||
for i in range(3,len(path)):
|
||||
try: os.mkdir(os.path.join('applications',dest,*path[2:i]))
|
||||
except: pass
|
||||
path = os.path.join('applications',dest,*f.split('/')[2:])
|
||||
if os.path.isdir(f):
|
||||
if not os.path.exists(path):
|
||||
shutil.copytree(f,path)
|
||||
else:
|
||||
shutil.copyfile(f,path)
|
||||
print 'done'
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from datetime import datetime
|
||||
from time import mktime
|
||||
from time import sleep
|
||||
from time import time
|
||||
|
||||
DB_URI = 'sqlite://sessions.sqlite'
|
||||
EXPIRATION_MINUTES = 60
|
||||
SLEEP_MINUTES = 5
|
||||
|
||||
while 1: # Infinite loop
|
||||
now = time() # get current Unix timestamp
|
||||
|
||||
for row in db().select(db.web2py_session_welcome.ALL):
|
||||
t = row.modified_datetime
|
||||
# Convert to a Unix timestamp
|
||||
t = mktime(t.timetuple())+1e-6*t.microsecond
|
||||
if now - t > EXPIRATION_MINUTES * 60:
|
||||
del db.web2py_session_welcome[row.id]
|
||||
|
||||
db.commit() # Write changes to database
|
||||
|
||||
sleep(SLEEP_MINUTES * 60)
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
@author: Pierre Thibault (pierre.thibault1 -at- gmail.com)
|
||||
@license: MIT
|
||||
@since: 2011-06-17
|
||||
|
||||
Usage: dict_diff [OPTION]... dict1 dict2
|
||||
Show the differences for two dictionaries.
|
||||
|
||||
-h, --help Display this help message.
|
||||
|
||||
dict1 and dict2 are two web2py dictionary files to compare. These are the files
|
||||
located in the "languages" directory of a web2py app. The tools show the
|
||||
differences between the two files.
|
||||
'''
|
||||
|
||||
__docformat__ = "epytext en"
|
||||
|
||||
import getopt
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
def main(argv):
|
||||
"""Parse the arguments and start the main process."""
|
||||
try:
|
||||
opts, args = getopt.getopt(argv, "h", ["help"])
|
||||
except getopt.GetoptError:
|
||||
exit_with_parsing_error()
|
||||
for opt, arg in opts:
|
||||
arg = arg # To avoid a warning from Pydev
|
||||
if opt in ("-h", "--help"):
|
||||
usage()
|
||||
sys.exit()
|
||||
if len(args) == 2:
|
||||
params = list(get_dicts(*args))
|
||||
params.extend(get_dict_names(*args))
|
||||
compare_dicts(*params)
|
||||
else:
|
||||
exit_with_parsing_error()
|
||||
|
||||
def exit_with_parsing_error():
|
||||
"""Report invalid arguments and usage."""
|
||||
print("Invalid argument(s).")
|
||||
usage()
|
||||
sys.exit(2)
|
||||
|
||||
def usage():
|
||||
"""Display the documentation"""
|
||||
print(__doc__)
|
||||
|
||||
def get_dicts(dict_path1, dict_path2):
|
||||
"""
|
||||
Parse the dictionaries.
|
||||
@param dict_path1: The path to the first dictionary.
|
||||
@param dict_path2: The path to the second dictionary.
|
||||
@return: The two dictionaries as a sequence.
|
||||
"""
|
||||
|
||||
return eval(open(dict_path1).read()), eval(open(dict_path2).read())
|
||||
|
||||
def get_dict_names(dict1_path, dict2_path):
|
||||
"""
|
||||
Get the name of the dictionaries for the end user. Use the base name of the
|
||||
files. If the two base names are identical, returns "dict1" and "dict2."
|
||||
@param dict1_path: The path to the first dictionary.
|
||||
@param dict2_path: The path to the second dictionary.
|
||||
@return: The two dictionary names as a sequence.
|
||||
"""
|
||||
|
||||
dict1_name = os.path.basename(dict1_path)
|
||||
dict2_name = os.path.basename(dict2_path)
|
||||
if dict1_name == dict2_name:
|
||||
dict1_name = "dict1"
|
||||
dict2_name = "dict2"
|
||||
return dict1_name, dict2_name
|
||||
|
||||
def compare_dicts(dict1, dict2, dict1_name, dict2_name):
|
||||
"""
|
||||
Compare the two dictionaries. Print out the result.
|
||||
@param dict1: The first dictionary.
|
||||
@param dict1: The second dictionary.
|
||||
@param dict1_name: The name of the first dictionary.
|
||||
@param dict2_name: The name of the second dictionary.
|
||||
"""
|
||||
|
||||
dict1_keyset = set(dict1.keys())
|
||||
dict2_keyset = set(dict2.keys())
|
||||
print_key_diff(dict1_keyset - dict2_keyset, dict1_name, dict2_name)
|
||||
print_key_diff(dict2_keyset - dict1_keyset, dict2_name, dict1_name)
|
||||
print "Value differences:"
|
||||
has_value_differences = False
|
||||
for key in dict1_keyset & dict2_keyset:
|
||||
if dict1[key] != dict2[key]:
|
||||
print " %s:" % (key,)
|
||||
print " %s: %s" % (dict1_name, dict1[key],)
|
||||
print " %s: %s" % (dict2_name, dict2[key],)
|
||||
print
|
||||
has_value_differences = True
|
||||
if not has_value_differences:
|
||||
print " None"
|
||||
|
||||
def print_key_diff(key_diff, dict1_name, dict2_name):
|
||||
"""
|
||||
Prints the keys in the first dictionary and are in the second dictionary.
|
||||
@param key_diff: Keys in dictionary 1 not in dictionary 2.
|
||||
@param dict1_name: Name used for the first dictionary.
|
||||
@param dict2_name: Name used for the second dictionary.
|
||||
"""
|
||||
|
||||
print "Keys in %s not in %s:" % (dict1_name, dict2_name)
|
||||
if len(key_diff):
|
||||
for key in key_diff:
|
||||
print " %s" % (key,)
|
||||
else:
|
||||
print " None"
|
||||
print
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:]) # Start the process (without the application name)
|
||||
@@ -0,0 +1,107 @@
|
||||
'''
|
||||
Create the web2py code needed to access your mysql legacy db.
|
||||
|
||||
To make this work all the legacy tables you want to access need to have an "id" field.
|
||||
|
||||
This plugin needs:
|
||||
mysql
|
||||
mysqldump
|
||||
installed and globally available.
|
||||
|
||||
Under Windows you will probably need to add the mysql executable directory to the PATH variable,
|
||||
you will also need to modify mysql to mysql.exe and mysqldump to mysqldump.exe below.
|
||||
Just guessing here :)
|
||||
|
||||
Access your tables with:
|
||||
legacy_db(legacy_db.mytable.id>0).select()
|
||||
|
||||
If the script crashes this is might be due to that fact that the data_type_map dictionary below is incomplete.
|
||||
Please complete it, improve it and continue.
|
||||
|
||||
Created by Falko Krause, minor modifications by Massimo Di Pierro and Ron McOuat
|
||||
'''
|
||||
import subprocess
|
||||
import re
|
||||
import sys
|
||||
data_type_map = dict(
|
||||
varchar = 'string',
|
||||
int = 'integer',
|
||||
integer = 'integer',
|
||||
tinyint = 'integer',
|
||||
smallint = 'integer',
|
||||
mediumint = 'integer',
|
||||
bigint = 'integer',
|
||||
float = 'double',
|
||||
double = 'double',
|
||||
char = 'string',
|
||||
decimal = 'integer',
|
||||
date = 'date',
|
||||
#year = 'date',
|
||||
time = 'time',
|
||||
timestamp = 'datetime',
|
||||
datetime = 'datetime',
|
||||
binary = 'blob',
|
||||
blob = 'blob',
|
||||
tinyblob = 'blob',
|
||||
mediumblob = 'blob',
|
||||
longblob = 'blob',
|
||||
text = 'text',
|
||||
tinytext = 'text',
|
||||
mediumtext = 'text',
|
||||
longtext = 'text',
|
||||
)
|
||||
|
||||
def mysql(database_name, username, password):
|
||||
p = subprocess.Popen(['mysql',
|
||||
'--user=%s' % username,
|
||||
'--password=%s'% password,
|
||||
'--execute=show tables;',
|
||||
database_name],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
sql_showtables, stderr = p.communicate()
|
||||
tables = [re.sub('\|\s+([^\|*])\s+.*', '\1', x) for x in sql_showtables.split()[1:]]
|
||||
connection_string = "legacy_db = DAL('mysql://%s:%s@localhost/%s')"%(username, password, database_name)
|
||||
legacy_db_table_web2py_code = []
|
||||
for table_name in tables:
|
||||
#get the sql create statement
|
||||
p = subprocess.Popen(['mysqldump',
|
||||
'--user=%s' % username,
|
||||
'--password=%s' % password,
|
||||
'--skip-add-drop-table',
|
||||
'--no-data', database_name,
|
||||
table_name], stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
|
||||
sql_create_stmnt,stderr = p.communicate()
|
||||
if 'CREATE' in sql_create_stmnt:#check if the table exists
|
||||
#remove garbage lines from sql statement
|
||||
sql_lines = sql_create_stmnt.split('\n')
|
||||
sql_lines = [x for x in sql_lines if not(x.startswith('--') or x.startswith('/*') or x =='')]
|
||||
#generate the web2py code from the create statement
|
||||
web2py_table_code = ''
|
||||
table_name = re.search('CREATE TABLE .(\S+). \(', sql_lines[0]).group(1)
|
||||
fields = []
|
||||
for line in sql_lines[1:-1]:
|
||||
if re.search('KEY', line) or re.search('PRIMARY', line) or re.search(' ID', line) or line.startswith(')'):
|
||||
continue
|
||||
hit = re.search('(\S+)\s+(\S+)(,| )( .*)?', line)
|
||||
if hit!=None:
|
||||
name, d_type = hit.group(1), hit.group(2)
|
||||
d_type = re.sub(r'(\w+)\(.*',r'\1',d_type)
|
||||
name = re.sub('`','',name)
|
||||
web2py_table_code += "\n Field('%s','%s'),"%(name,data_type_map[d_type])
|
||||
web2py_table_code = "legacy_db.define_table('%s',%s\n migrate=False)"%(table_name,web2py_table_code)
|
||||
legacy_db_table_web2py_code.append(web2py_table_code)
|
||||
#----------------------------------------
|
||||
#write the legacy db to file
|
||||
legacy_db_web2py_code = connection_string+"\n\n"
|
||||
legacy_db_web2py_code += "\n\n#--------\n".join(legacy_db_table_web2py_code)
|
||||
return legacy_db_web2py_code
|
||||
|
||||
regex = re.compile('(.*?):(.*?)@(.*)')
|
||||
if len(sys.argv)<2 or not regex.match(sys.argv[1]):
|
||||
print 'USAGE:\n\n extract_mysql_models.py username:password@data_basename\n\n'
|
||||
else:
|
||||
m = regex.match(sys.argv[1])
|
||||
print mysql(m.group(3),m.group(1),m.group(2))
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Create web2py model (python code) to represent PostgreSQL tables.
|
||||
|
||||
Features:
|
||||
|
||||
* Uses ANSI Standard INFORMATION_SCHEMA (might work with other RDBMS)
|
||||
* Detects legacy "keyed" tables (not having an "id" PK)
|
||||
* Connects directly to running databases, no need to do a SQL dump
|
||||
* Handles notnull, unique and referential constraints
|
||||
* Detects most common datatypes and default values
|
||||
* Support PostgreSQL columns comments (ie. for documentation)
|
||||
|
||||
Requeriments:
|
||||
|
||||
* Needs PostgreSQL pyscopg2 python connector (same as web2py)
|
||||
* If used against other RDBMS, import and use proper connector (remove pg_ code)
|
||||
|
||||
|
||||
Created by Mariano Reingart, based on a script to "generate schemas from dbs"
|
||||
(mysql) by Alexandre Andrade
|
||||
|
||||
"""
|
||||
|
||||
_author__ = "Mariano Reingart <reingart@gmail.com>"
|
||||
|
||||
HELP = """
|
||||
USAGE: extract_pgsql_models db host port user passwd
|
||||
|
||||
Call with PostgreSQL database connection parameters,
|
||||
web2py model will be printed on standard output.
|
||||
|
||||
EXAMPLE: python extract_pgsql_models.py mydb localhost 5432 reingart saraza
|
||||
"""
|
||||
|
||||
# Config options
|
||||
DEBUG = False # print debug messages to STDERR
|
||||
SCHEMA = 'public' # change if not using default PostgreSQL schema
|
||||
|
||||
# Constant for Field keyword parameter order (and filter):
|
||||
KWARGS = ('type', 'length', 'default', 'required', 'ondelete',
|
||||
'notnull', 'unique', 'label', 'comment')
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def query(conn, sql,*args):
|
||||
"Execute a SQL query and return rows as a list of dicts"
|
||||
cur = conn.cursor()
|
||||
ret = []
|
||||
try:
|
||||
if DEBUG: print >> sys.stderr, "QUERY: ", sql % args
|
||||
cur.execute(sql, args)
|
||||
for row in cur:
|
||||
dic = {}
|
||||
for i, value in enumerate(row):
|
||||
field = cur.description[i][0]
|
||||
dic[field] = value
|
||||
if DEBUG: print >> sys.stderr, "RET: ", dic
|
||||
ret.append(dic)
|
||||
return ret
|
||||
finally:
|
||||
cur.close()
|
||||
|
||||
|
||||
def get_tables(conn, schema=SCHEMA):
|
||||
"List table names in a given schema"
|
||||
rows = query(conn, """SELECT table_name FROM information_schema.tables
|
||||
WHERE table_schema = %s
|
||||
ORDER BY table_name""", schema)
|
||||
return [row['table_name'] for row in rows]
|
||||
|
||||
|
||||
def get_fields(conn, table):
|
||||
"Retrieve field list for a given table"
|
||||
if DEBUG: print >> sys.stderr, "Processing TABLE", table
|
||||
rows = query(conn, """
|
||||
SELECT column_name, data_type,
|
||||
is_nullable,
|
||||
character_maximum_length,
|
||||
numeric_precision, numeric_precision_radix, numeric_scale,
|
||||
column_default
|
||||
FROM information_schema.columns
|
||||
WHERE table_name=%s
|
||||
ORDER BY ordinal_position""", table)
|
||||
return rows
|
||||
|
||||
|
||||
def define_field(conn, table, field, pks):
|
||||
"Determine field type, default value, references, etc."
|
||||
f={}
|
||||
ref = references(conn, table, field['column_name'])
|
||||
if ref:
|
||||
f.update(ref)
|
||||
elif field['column_default'] and \
|
||||
field['column_default'].startswith("nextval") and \
|
||||
field['column_name'] in pks:
|
||||
# postgresql sequence (SERIAL) and primary key!
|
||||
f['type'] = "'id'"
|
||||
elif field['data_type'].startswith('character'):
|
||||
f['type'] = "'string'"
|
||||
if field['character_maximum_length']:
|
||||
f['length'] = field['character_maximum_length']
|
||||
elif field['data_type'] in ('text', ):
|
||||
f['type'] = "'text'"
|
||||
elif field['data_type'] in ('boolean', 'bit'):
|
||||
f['type'] = "'boolean'"
|
||||
elif field['data_type'] in ('integer', 'smallint', 'bigint'):
|
||||
f['type'] = "'integer'"
|
||||
elif field['data_type'] in ('double precision', 'real' ):
|
||||
f['type'] = "'double'"
|
||||
elif field['data_type'] in ('timestamp', 'timestamp without time zone'):
|
||||
f['type'] = "'datetime'"
|
||||
elif field['data_type'] in ('date', ):
|
||||
f['type'] = "'date'"
|
||||
elif field['data_type'] in ('time', 'time without time zone'):
|
||||
f['type'] = "'time'"
|
||||
elif field['data_type'] in ('numeric', 'currency'):
|
||||
f['type'] = "'decimal'"
|
||||
f['precision'] = field['numeric_precision']
|
||||
f['scale'] = field['numeric_scale'] or 0
|
||||
elif field['data_type'] in ('bytea', ):
|
||||
f['type'] = "'blob'"
|
||||
elif field['data_type'] in ('point', 'lseg', 'polygon', 'unknown', 'USER-DEFINED'):
|
||||
f['type'] = "" # unsupported?
|
||||
else:
|
||||
raise RuntimeError("Data Type not supported: %s " % str(field))
|
||||
|
||||
try:
|
||||
if field['column_default']:
|
||||
if field['column_default']=="now()":
|
||||
d = "request.now"
|
||||
elif field['column_default']=="true":
|
||||
d = "True"
|
||||
elif field['column_default']=="false":
|
||||
d = "False"
|
||||
else:
|
||||
d = repr(eval(field['column_default']))
|
||||
f['default'] = str(d)
|
||||
except (ValueError, SyntaxError):
|
||||
pass
|
||||
except Exception, e:
|
||||
raise RuntimeError("Default unsupported '%s'" % field['column_default'])
|
||||
|
||||
if not field['is_nullable']:
|
||||
f['notnull'] = "True"
|
||||
|
||||
comment = get_comment(conn, table, field)
|
||||
if comment is not None:
|
||||
f['comment'] = repr(comment)
|
||||
return f
|
||||
|
||||
|
||||
def is_unique(conn, table, field):
|
||||
"Find unique columns (incomplete support)"
|
||||
rows = query(conn, """
|
||||
SELECT information_schema.constraint_column_usage.column_name
|
||||
FROM information_schema.table_constraints
|
||||
NATURAL JOIN information_schema.constraint_column_usage
|
||||
WHERE information_schema.table_constraints.table_name=%s
|
||||
AND information_schema.constraint_column_usage.column_name=%s
|
||||
AND information_schema.table_constraints.constraint_type='UNIQUE'
|
||||
;""", table, field['column_name'])
|
||||
return rows and True or False
|
||||
|
||||
|
||||
def get_comment(conn, table, field):
|
||||
"Find the column comment (postgres specific)"
|
||||
rows = query(conn, """
|
||||
SELECT d.description AS comment
|
||||
FROM pg_class c
|
||||
JOIN pg_description d ON c.oid=d.objoid
|
||||
JOIN pg_attribute a ON c.oid = a.attrelid
|
||||
WHERE c.relname=%s AND a.attname=%s
|
||||
AND a.attnum = d.objsubid
|
||||
;""", table, field['column_name'])
|
||||
return rows and rows[0]['comment'] or None
|
||||
|
||||
|
||||
def primarykeys(conn, table):
|
||||
"Find primary keys"
|
||||
rows = query(conn, """
|
||||
SELECT information_schema.constraint_column_usage.column_name
|
||||
FROM information_schema.table_constraints
|
||||
NATURAL JOIN information_schema.constraint_column_usage
|
||||
WHERE information_schema.table_constraints.table_name=%s
|
||||
AND information_schema.table_constraints.constraint_type='PRIMARY KEY'
|
||||
;""", table)
|
||||
return [row['column_name'] for row in rows]
|
||||
|
||||
|
||||
def references(conn, table, field):
|
||||
"Find a FK (fails if multiple)"
|
||||
rows1 = query(conn, """
|
||||
SELECT table_name, column_name, constraint_name,
|
||||
update_rule, delete_rule, ordinal_position
|
||||
FROM information_schema.key_column_usage
|
||||
NATURAL JOIN information_schema.referential_constraints
|
||||
NATURAL JOIN information_schema.table_constraints
|
||||
WHERE information_schema.key_column_usage.table_name=%s
|
||||
AND information_schema.key_column_usage.column_name=%s
|
||||
AND information_schema.table_constraints.constraint_type='FOREIGN KEY'
|
||||
;""", table, field)
|
||||
if len(rows1)==1:
|
||||
rows2 = query(conn, """
|
||||
SELECT table_name, column_name, *
|
||||
FROM information_schema.constraint_column_usage
|
||||
WHERE constraint_name=%s
|
||||
""", rows1[0]['constraint_name'])
|
||||
row = None
|
||||
if len(rows2)>1:
|
||||
row = rows2[int(rows1[0]['ordinal_position'])-1]
|
||||
keyed = True
|
||||
if len(rows2)==1:
|
||||
row = rows2[0]
|
||||
keyed = False
|
||||
if row:
|
||||
if keyed: # THIS IS BAD, DON'T MIX "id" and primarykey!!!
|
||||
ref = {'type': "'reference %s.%s'" % (row['table_name'],
|
||||
row['column_name'])}
|
||||
else:
|
||||
ref = {'type': "'reference %s'" % (row['table_name'],)}
|
||||
if rows1[0]['delete_rule']!="NO ACTION":
|
||||
ref['ondelete'] = repr(rows1[0]['delete_rule'])
|
||||
return ref
|
||||
elif rows2:
|
||||
raise RuntimeError("Unsupported foreign key reference: %s" %
|
||||
str(rows2))
|
||||
|
||||
elif rows1:
|
||||
raise RuntimeError("Unsupported referential constraint: %s" %
|
||||
str(rows1))
|
||||
|
||||
|
||||
def define_table(conn, table):
|
||||
"Output single table definition"
|
||||
fields = get_fields(conn, table)
|
||||
pks = primarykeys(conn, table)
|
||||
print "db.define_table('%s'," % (table, )
|
||||
for field in fields:
|
||||
fname = field['column_name']
|
||||
fdef = define_field(conn, table, field, pks)
|
||||
if fname not in pks and is_unique(conn, table, field):
|
||||
fdef['unique'] = "True"
|
||||
if fdef['type']=="'id'" and fname in pks:
|
||||
pks.pop(pks.index(fname))
|
||||
print " Field('%s', %s)," % (fname,
|
||||
', '.join(["%s=%s" % (k, fdef[k]) for k in KWARGS
|
||||
if k in fdef and fdef[k]]))
|
||||
if pks:
|
||||
print " primarykey=[%s]," % ", ".join(["'%s'" % pk for pk in pks])
|
||||
print " migrate=migrate)"
|
||||
print
|
||||
|
||||
|
||||
def define_db(conn, db, host, port, user, passwd):
|
||||
"Output database definition (model)"
|
||||
dal = 'db = DAL("postgres://%s:%s@%s:%s/%s", pool_size=10)'
|
||||
print dal % (user, passwd, host, port, db)
|
||||
print
|
||||
print "migrate = False"
|
||||
print
|
||||
for table in get_tables(conn):
|
||||
define_table(conn, table)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 6:
|
||||
print HELP
|
||||
else:
|
||||
# Parse arguments from command line:
|
||||
db, host, port, user, passwd = sys.argv[1:6]
|
||||
|
||||
# Make the database connection (change driver if required)
|
||||
import psycopg2
|
||||
cnn = psycopg2.connect(database=db, host=host, port=port,
|
||||
user=user, password=passwd,
|
||||
)
|
||||
# Start model code generation:
|
||||
define_db(cnn, db, host, port, user, passwd)
|
||||
|
||||
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
import sys, glob
|
||||
|
||||
def read_fileb(filename, mode='rb'):
|
||||
f = open(filename, mode)
|
||||
try:
|
||||
return f.read()
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def write_fileb(filename, value, mode='wb'):
|
||||
f = open(filename, mode)
|
||||
try:
|
||||
f.write(value)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
for filename in glob.glob(sys.argv[1]):
|
||||
data1 = read_fileb(filename)
|
||||
write_fileb(filename + '.bak2', data1)
|
||||
data2lines = read_fileb(filename).split('\n')
|
||||
data2 = '\n'.join([line.rstrip() for line in data2lines])+'\n'
|
||||
write_fileb(filename, data2)
|
||||
print filename, len(data1)-len(data2)
|
||||
|
||||
Executable
+99
@@ -0,0 +1,99 @@
|
||||
import glob
|
||||
import os
|
||||
import zipfile
|
||||
import sys
|
||||
import re
|
||||
from BeautifulSoup import BeautifulSoup as BS
|
||||
|
||||
def head(styles):
|
||||
title = '<title>{{=response.title or request.application}}</title>'
|
||||
items = '\n'.join(["{{response.files.append(URL(request.application,'static','%s'))}}" % (style) for style in styles])
|
||||
loc="""<style>
|
||||
div.flash {
|
||||
position: absolute;
|
||||
float: right;
|
||||
padding: 10px;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
opacity: 0.75;
|
||||
margin: 10px 10px 10px 10px;
|
||||
text-align: center;
|
||||
clear: both;
|
||||
color: #fff;
|
||||
font-size: 11pt;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
background: black;
|
||||
border: 2px solid #fff;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
z-index: 2;
|
||||
}
|
||||
div.error {
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
background-color: red;
|
||||
color: white;
|
||||
padding: 3px;
|
||||
border: 1px solid #666;
|
||||
}
|
||||
</style>"""
|
||||
return "\n%s\n%s\n{{include 'web2py_ajax.html'}}\n%s" % (title,items,loc)
|
||||
|
||||
def content():
|
||||
return """<div class="flash">{{=response.flash or ''}}</div>{{include}}"""
|
||||
|
||||
def process(folder):
|
||||
indexfile = open(os.path.join(folder,'index.html'),'rb')
|
||||
try:
|
||||
soup = BS(indexfile.read())
|
||||
finally:
|
||||
indexfile.close()
|
||||
styles = [x['href'] for x in soup.findAll('link')]
|
||||
soup.find('head').contents=BS(head(styles))
|
||||
try:
|
||||
soup.find('h1').contents=BS('{{=response.title or request.application}}')
|
||||
soup.find('h2').contents=BS("{{=response.subtitle or '=response.subtitle'}}")
|
||||
except:
|
||||
pass
|
||||
for match in (soup.find('div',id='menu'),
|
||||
soup.find('div',{'class':'menu'}),
|
||||
soup.find('div',id='nav'),
|
||||
soup.find('div',{'class':'nav'})):
|
||||
if match:
|
||||
match.contents=BS('{{=MENU(response.menu)}}')
|
||||
break
|
||||
done=False
|
||||
for match in (soup.find('div',id='content'),
|
||||
soup.find('div',{'class':'content'}),
|
||||
soup.find('div',id='main'),
|
||||
soup.find('div',{'class':'main'})):
|
||||
if match:
|
||||
match.contents=BS(content())
|
||||
done=True
|
||||
break
|
||||
if done:
|
||||
page = soup.prettify()
|
||||
page = re.compile("\s*\{\{=response\.flash or ''\}\}\s*",re.MULTILINE)\
|
||||
.sub("{{=response.flash or ''}}",page)
|
||||
print page
|
||||
else:
|
||||
raise Exception, "Unable to convert"
|
||||
|
||||
if __name__=='__main__':
|
||||
if len(sys.argv)<2:
|
||||
print """USAGE:
|
||||
1) start a new web2py application
|
||||
2) Download a sample free layout from the web into the static/ folder of
|
||||
your web2py application (make sure a sample index.html is there)
|
||||
3) run this script with
|
||||
|
||||
python layout_make.py /path/to/web2py/applications/app/static/
|
||||
> /path/to/web2py/applications/app/views/layout.html
|
||||
"""
|
||||
elif not os.path.exists(sys.argv[1]):
|
||||
print 'Folder %s does not exist' % sys.argv[1]
|
||||
else:
|
||||
process(sys.argv[1])
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
USAGE = """
|
||||
from web2py main folder
|
||||
python scripts/make_min_web2py.py /path/to/minweb2py
|
||||
|
||||
it will mkdir minweb2py and build a minimal web2py installation
|
||||
- no admin, no examples, one line welcome
|
||||
- no scripts
|
||||
- drops same rarely used contrib modules
|
||||
- more modules could be dropped but minimal difference
|
||||
"""
|
||||
|
||||
# files to include from top level folder (default.py will be rebuilt)
|
||||
REQUIRED = """
|
||||
VERSION
|
||||
web2py.py
|
||||
fcgihandler.py
|
||||
gaehandler.py
|
||||
wsgihandler.py
|
||||
anyserver.py
|
||||
applications/__init__.py
|
||||
applications/welcome/controllers/default.py
|
||||
"""
|
||||
|
||||
# files and folders to exclude from gluon folder (comment with # if needed)
|
||||
IGNORED = """
|
||||
gluon/contrib/comet_messaging.py
|
||||
gluon/contrib/feedparser.py
|
||||
gluon/contrib/generics.py
|
||||
gluon/contrib/gql.py
|
||||
gluon/contrib/populate.py
|
||||
gluon/contrib/sms_utils.py
|
||||
gluon/contrib/spreadsheet.py
|
||||
gluon/tests/
|
||||
gluon/contrib/markdown/
|
||||
gluon/contrib/pyfpdf/
|
||||
gluon/contrib/pymysql/
|
||||
gluon/contrib/pyrtf/
|
||||
gluon/contrib/pysimplesoap/
|
||||
"""
|
||||
|
||||
import sys, os, shutil, glob
|
||||
|
||||
def main():
|
||||
if len(sys.argv)<2:
|
||||
print USAGE
|
||||
|
||||
# make target folder
|
||||
target = sys.argv[1]
|
||||
os.mkdir(target)
|
||||
|
||||
# make a list of all files to include
|
||||
files = [x.strip() for x in REQUIRED.split('\n') \
|
||||
if x and not x[0]=='#']
|
||||
ignore = [x.strip() for x in IGNORED.split('\n') \
|
||||
if x and not x[0]=='#']
|
||||
def accept(filename):
|
||||
for p in ignore:
|
||||
if filename.startswith(p):
|
||||
return False
|
||||
return True
|
||||
pattern = 'gluon/*.py'
|
||||
while True:
|
||||
newfiles = [x for x in glob.glob(pattern) if accept(x)]
|
||||
if not newfiles: break
|
||||
files += newfiles
|
||||
pattern = pattern[:-3]+'/*.py'
|
||||
# copy all files, make missing folder, build default.py
|
||||
files.sort()
|
||||
for f in files:
|
||||
dirs = f.split(os.path.sep)
|
||||
for i in range(1,len(dirs)):
|
||||
try: os.mkdir(target+'/'+os.path.join(*dirs[:i]))
|
||||
except OSError: pass
|
||||
if f=='applications/welcome/controllers/default.py':
|
||||
open(target+'/'+f,'w').write('def index(): return "hello"\n')
|
||||
else:
|
||||
shutil.copyfile(f,target+'/'+f)
|
||||
|
||||
if __name__=='__main__': main()
|
||||
@@ -0,0 +1,115 @@
|
||||
user nginx nginx;
|
||||
worker_processes 1;
|
||||
|
||||
error_log /var/log/nginx/error_log info;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
use epoll;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main
|
||||
'$remote_addr - $remote_user [$time_local] '
|
||||
'"$request" $status $bytes_sent '
|
||||
'"$http_referer" "$http_user_agent" '
|
||||
'"$gzip_ratio"';
|
||||
|
||||
client_header_timeout 10m;
|
||||
client_body_timeout 10m;
|
||||
send_timeout 10m;
|
||||
|
||||
connection_pool_size 256;
|
||||
client_header_buffer_size 1k;
|
||||
large_client_header_buffers 4 2k;
|
||||
request_pool_size 4k;
|
||||
|
||||
gzip on;
|
||||
gzip_min_length 1100;
|
||||
gzip_buffers 4 8k;
|
||||
gzip_types text/plain;
|
||||
|
||||
output_buffers 1 32k;
|
||||
postpone_output 1460;
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
|
||||
keepalive_timeout 75 20;
|
||||
|
||||
ignore_invalid_headers on;
|
||||
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
|
||||
index index.html;
|
||||
|
||||
server {
|
||||
listen 127.0.0.1;
|
||||
server_name localhost;
|
||||
|
||||
access_log /var/log/nginx/localhost.access_log main;
|
||||
error_log /var/log/nginx/localhost.error_log info;
|
||||
|
||||
root /var/www/localhost/htdocs;
|
||||
}
|
||||
|
||||
# SSL example
|
||||
server {
|
||||
listen 127.0.0.1:443;
|
||||
server_name localhost;
|
||||
|
||||
ssl on;
|
||||
ssl_certificate /etc/ssl/nginx/nginx-server.pem;
|
||||
ssl_client_certificate /etc/ssl/nginx/cacert.pem;
|
||||
ssl_certificate_key /etc/ssl/nginx/nginx.key;
|
||||
ssl_verify_client optional;
|
||||
|
||||
access_log /var/log/nginx/localhost.ssl_access_log main;
|
||||
error_log /var/log/nginx/localhost.ssl_error_log info;
|
||||
|
||||
root /var/www/localhost/htdocs;
|
||||
|
||||
set $web2pyroot /home/Desktop/source/michelecomitini-facebookaccess;
|
||||
|
||||
|
||||
location /pki/ {
|
||||
root /var/www/localhost/html;
|
||||
}
|
||||
|
||||
|
||||
location ^/(.*)/static/(.*) {
|
||||
alias $web2pyroot/applications/$1/static/$2;
|
||||
}
|
||||
|
||||
location / {
|
||||
include /etc/nginx/scgi_params;
|
||||
scgi_pass 127.0.0.1:4000;
|
||||
|
||||
#Module ngx_http_ssl_module supports the following built-in variables:
|
||||
|
||||
#$ssl_cipher returns the cipher suite being used for the currently established SSL/TLS connection
|
||||
#$ssl_client_serial returns the serial number of the client certificate for the currently established SSL/TLS connection — if applicable, i.e., if client authentication is activated in the connection
|
||||
#$ssl_client_s_dn returns the subject Distinguished Name (DN) of the client certificate for the currently established SSL/TLS connection — if applicable, i.e., if client authentication is activated in the connection
|
||||
#$ssl_client_i_dn returns the issuer DN of the client certificate for the currently established SSL/TLS connection — if applicable, i.e., if client authentication is activated in the connection
|
||||
#$ssl_protocol returns the protocol of the currently established SSL/TLS connection — depending on the configuration and client available options it's one of SSLv2, SSLv3 or TLSv1
|
||||
#$ssl_session_id the Session ID of the established secure connection — requires Nginx version greater or equal to 0.8.20
|
||||
#$ssl_client_cert
|
||||
#$ssl_client_raw_cert
|
||||
#$ssl_client_verify takes the value "SUCCESS" when the client certificate is successfully verified
|
||||
scgi_param SSL_PROTOCOL $ssl_protocol;
|
||||
scgi_param HTTPS on;
|
||||
scgi_param SSL_CIPHER $ssl_cipher;
|
||||
scgi_param SSL_CLIENT_SERIAL $ssl_client_serial;
|
||||
scgi_param SSL_CLIENT_S_DN $ssl_client_s_dn;
|
||||
scgi_param SSL_CLIENT_I_DN $ssl_client_i_dn;
|
||||
scgi_param SSL_SESSION_ID $ssl_session_id;
|
||||
scgi_param SSL_CLIENT_CERT $ssl_client_cert;
|
||||
scgi_param SSL_CLIENT_RAW_CERT $ssl_client_raw_cert;
|
||||
scgi_param SSL_CLIENT_VERIFY $ssl_client_verify;
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+222
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sessions2trash.py
|
||||
|
||||
Run this script in a web2py environment shell e.g. python web2py.py -S app
|
||||
If models are loaded (-M option) auth.settings.expiration is assumed
|
||||
for sessions without an expiration. If models are not loaded, sessions older
|
||||
than 60 minutes are removed. Use the --expiration option to override these
|
||||
values.
|
||||
|
||||
Typical usage:
|
||||
|
||||
# Delete expired sessions every 5 minutes
|
||||
nohup python web2py.py -S app -M -R scripts/sessions2trash.py &
|
||||
|
||||
# Delete sessions older than 60 minutes regardless of expiration,
|
||||
# with verbose output, then exit.
|
||||
python web2py.py -S app -M -R scripts/sessions2trash.py -A -o -x 3600 -f -v
|
||||
|
||||
# Delete all sessions regardless of expiry and exit.
|
||||
python web2py.py -S app -M -R scripts/sessions2trash.py -A -o -x 0
|
||||
"""
|
||||
|
||||
from gluon.storage import Storage
|
||||
from optparse import OptionParser
|
||||
import cPickle
|
||||
import datetime
|
||||
import os
|
||||
import stat
|
||||
import time
|
||||
|
||||
EXPIRATION_MINUTES = 60
|
||||
SLEEP_MINUTES = 5
|
||||
VERSION = 0.3
|
||||
|
||||
|
||||
class SessionSet(object):
|
||||
"""Class representing a set of sessions"""
|
||||
|
||||
def __init__(self, expiration, force, verbose):
|
||||
self.expiration = expiration
|
||||
self.force = force
|
||||
self.verbose = verbose
|
||||
|
||||
def get(self):
|
||||
"""Get session files/records."""
|
||||
raise NotImplementedError
|
||||
|
||||
def trash(self):
|
||||
"""Trash expired sessions."""
|
||||
now = datetime.datetime.now()
|
||||
for item in self.get():
|
||||
status = 'OK'
|
||||
last_visit = item.last_visit_default()
|
||||
|
||||
try:
|
||||
session = item.get()
|
||||
if session.auth:
|
||||
if session.auth.expiration and not self.force:
|
||||
self.expiration = session.auth.expiration
|
||||
if session.auth.last_visit:
|
||||
last_visit = session.auth.last_visit
|
||||
except:
|
||||
pass
|
||||
|
||||
age = 0
|
||||
if last_visit:
|
||||
age = total_seconds(now - last_visit)
|
||||
|
||||
if age > self.expiration or not self.expiration:
|
||||
item.delete()
|
||||
status = 'trashed'
|
||||
|
||||
if self.verbose > 1:
|
||||
print 'key: %s' % str(item)
|
||||
print 'expiration: %s seconds' % self.expiration
|
||||
print 'last visit: %s' % str(last_visit)
|
||||
print 'age: %s seconds' % age
|
||||
print 'status: %s' % status
|
||||
print ''
|
||||
elif self.verbose > 0:
|
||||
print('%s %s' % (str(item), status))
|
||||
|
||||
|
||||
class SessionSetDb(SessionSet):
|
||||
"""Class representing a set of sessions stored in database"""
|
||||
|
||||
def __init__(self, expiration, force, verbose):
|
||||
SessionSet.__init__(self, expiration, force, verbose)
|
||||
|
||||
def get(self):
|
||||
"""Return list of SessionDb instances for existing sessions."""
|
||||
sessions = []
|
||||
tablename = 'web2py_session'
|
||||
if request.application:
|
||||
tablename = 'web2py_session_' + request.application
|
||||
if tablename in db:
|
||||
for row in db(db[tablename].id > 0).select():
|
||||
sessions.append(SessionDb(row))
|
||||
return sessions
|
||||
|
||||
|
||||
class SessionSetFiles(SessionSet):
|
||||
"""Class representing a set of sessions stored in flat files"""
|
||||
|
||||
def __init__(self, expiration, force, verbose):
|
||||
SessionSet.__init__(self, expiration, force, verbose)
|
||||
|
||||
def get(self):
|
||||
"""Return list of SessionFile instances for existing sessions."""
|
||||
path = os.path.join(request.folder, 'sessions')
|
||||
return [SessionFile(os.path.join(path, x)) for x in os.listdir(path)]
|
||||
|
||||
|
||||
class SessionDb(object):
|
||||
"""Class representing a single session stored in database"""
|
||||
|
||||
def __init__(self, row):
|
||||
self.row = row
|
||||
|
||||
def delete(self):
|
||||
self.row.delete_record()
|
||||
db.commit()
|
||||
|
||||
def get(self):
|
||||
session = Storage()
|
||||
session.update(cPickle.loads(self.row.session_data))
|
||||
return session
|
||||
|
||||
def last_visit_default(self):
|
||||
return self.row.modified_datetime
|
||||
|
||||
def __str__(self):
|
||||
return self.row.unique_key
|
||||
|
||||
|
||||
class SessionFile(object):
|
||||
"""Class representing a single session stored as a flat file"""
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
|
||||
def delete(self):
|
||||
os.unlink(self.filename)
|
||||
|
||||
def get(self):
|
||||
session = Storage()
|
||||
with open(self.filename, 'rb+') as f:
|
||||
session.update(cPickle.load(f))
|
||||
return session
|
||||
|
||||
def last_visit_default(self):
|
||||
return datetime.datetime.fromtimestamp(
|
||||
os.stat(self.filename)[stat.ST_MTIME])
|
||||
|
||||
def __str__(self):
|
||||
return self.filename
|
||||
|
||||
|
||||
def total_seconds(delta):
|
||||
"""
|
||||
Adapted from Python 2.7's timedelta.total_seconds() method.
|
||||
|
||||
Args:
|
||||
delta: datetime.timedelta instance.
|
||||
"""
|
||||
return (delta.microseconds + (delta.seconds + (delta.days * 24 * 3600)) * \
|
||||
10 ** 6) / 10 ** 6
|
||||
|
||||
|
||||
def main():
|
||||
"""Main processing."""
|
||||
|
||||
usage = '%prog [options]' + '\nVersion: %s' % VERSION
|
||||
parser = OptionParser(usage=usage)
|
||||
|
||||
parser.add_option('-f', '--force',
|
||||
action='store_true', dest='force', default=False,
|
||||
help=('Ignore session expiration. '
|
||||
'Force expiry based on -x option or auth.settings.expiration.')
|
||||
)
|
||||
parser.add_option('-o', '--once',
|
||||
action='store_true', dest='once', default=False,
|
||||
help='Delete sessions, then exit.',
|
||||
)
|
||||
parser.add_option('-s', '--sleep',
|
||||
dest='sleep', default=SLEEP_MINUTES * 60, type="int",
|
||||
help='Number of seconds to sleep between executions. Default 300.',
|
||||
)
|
||||
parser.add_option('-v', '--verbose',
|
||||
default=0, action='count',
|
||||
help="print verbose output, a second -v increases verbosity")
|
||||
parser.add_option('-x', '--expiration',
|
||||
dest='expiration', default=None, type="int",
|
||||
help='Expiration value for sessions without expiration (in seconds)',
|
||||
)
|
||||
|
||||
(options, unused_args) = parser.parse_args()
|
||||
|
||||
expiration = options.expiration
|
||||
if expiration is None:
|
||||
try:
|
||||
expiration = auth.settings.expiration
|
||||
except:
|
||||
expiration = EXPIRATION_MINUTES * 60
|
||||
|
||||
set_db = SessionSetDb(expiration, options.force, options.verbose)
|
||||
set_files = SessionSetFiles(expiration, options.force, options.verbose)
|
||||
while True:
|
||||
set_db.trash()
|
||||
set_files.trash()
|
||||
|
||||
if options.once:
|
||||
break
|
||||
else:
|
||||
if options.verbose:
|
||||
print 'Sleeping %s seconds' % (options.sleep)
|
||||
time.sleep(options.sleep)
|
||||
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,12 @@
|
||||
# install virtualenv
|
||||
easy_install virtualenv
|
||||
python virtualenv.py w2env
|
||||
# install missing modules
|
||||
w2env/bin/easy_install -U pysqlite hashlib
|
||||
# donwload web2py and unpack
|
||||
wget http://web2py.com/examples/static/web2py_src.zip
|
||||
unzip web2py_src.zip
|
||||
cd web2py
|
||||
# start web2py using command-line script
|
||||
w2env/bin/python web2py.py -i 0.0.0.0 -p 8123 -a 'adminpasswd'
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
#
|
||||
# Author: Christopher Steel
|
||||
# Organization: Voice of Access
|
||||
# Date: 2010-11-24
|
||||
# License: Same as Web2py, MIT / GNU
|
||||
# Email: Christopher DOT Steel AT Voice of Access DOT org
|
||||
#
|
||||
# This script will :
|
||||
# download and install virtualenv
|
||||
# start a virtual environment
|
||||
# move into the virtual environment
|
||||
# download and install latest stable version of web2py
|
||||
# start web2py in the virtual environment
|
||||
#
|
||||
# To disactivate the virtual environment, shut down web2py
|
||||
# and type 'disactivate' at the command line.
|
||||
#
|
||||
# Testing:
|
||||
# OS X
|
||||
# should work on POSIX systems
|
||||
#
|
||||
# Usage:
|
||||
# create a directory to hold your virtual environments, for example
|
||||
# /home/user_name/virtual_environments
|
||||
# place this script in the directory and make it executable
|
||||
# chmod +x web2py-install-virtualenv.sh
|
||||
customize the variables below to meet your needs
|
||||
# execute from terminal
|
||||
# ./web2py-install-virtualenv.sh
|
||||
# relax...
|
||||
|
||||
################ VARIABLES
|
||||
# Change to reflect version changes etc.
|
||||
#
|
||||
# name for your virtual environment
|
||||
ENV=VIRTUAL_ENV
|
||||
# version to install
|
||||
APP_NAME=virtualenv
|
||||
VER=1.5.1
|
||||
DIR=${APP_NAME}-${VER}
|
||||
EXT=tar.gz
|
||||
ARCHIVE=${APP_NAME}-${VER}.${EXT}
|
||||
# md5 sum, see end of url from pypi
|
||||
MD5_SUM=3daa1f449d5d2ee03099484cecb1c2b7
|
||||
################
|
||||
#
|
||||
echo 'downloading' ${ARCHIVE}
|
||||
echo '================================'
|
||||
echo `wget http://pypi.python.org/packages/source/v/virtualenv/${ARCHIVE}`
|
||||
md5 ${ARCHIVE}
|
||||
echo 'MD5 ('${ARCHIVE}') =' ${MD5_SUM}
|
||||
echo 'unarchive' ${ARCHIVE}
|
||||
echo '================================='
|
||||
tar xvfz ${ARCHIVE}
|
||||
|
||||
echo ' comparing md5 sums'
|
||||
echo '================================='
|
||||
md5 ${ARCHIVE}
|
||||
echo 'MD5 ('${ARCHIVE}') =' ${MD5_SUM}
|
||||
|
||||
#echo 'installing compatibility modules'
|
||||
#echo '================================'
|
||||
#virtualenv/bin/easy_install -U pysqlite hashlib
|
||||
|
||||
#echo 'Installing distribute'
|
||||
#echo '====================='
|
||||
#echo 'Creating Environment'
|
||||
#echo '====================='
|
||||
#echo `python ./${DIRAPP_NAME}-${VER}/virtualenv.py --distribute ${ENV}`
|
||||
|
||||
echo 'Start virtual environment'
|
||||
echo '========================='
|
||||
virtualenv --no-site-packages ${ENV}
|
||||
|
||||
RUN_THIS='source ${ENV}/bin/activate'
|
||||
`echo source ${ENV}/bin/activate`
|
||||
|
||||
echo 'Moving into virtual environment directory'
|
||||
echo '========================================='
|
||||
cd ${ENV}
|
||||
|
||||
echo 'downloading web2py'
|
||||
echo '=================='
|
||||
wget http://web2py.com/examples/static/web2py_src.zip
|
||||
unzip web2py_src.zip
|
||||
cd web2py
|
||||
|
||||
echo 'to deactivate your virtual environment'
|
||||
echo 'shutdown web2py and then type "deactivate"'
|
||||
echo '=========================================='
|
||||
read -p "Press any key to start web2py…"
|
||||
|
||||
|
||||
echo 'starting web2py'
|
||||
echo '==============='
|
||||
../bin/python2.5 web2py.py
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
echo "This script will:
|
||||
1) Install modules needed to run web2py on Fedora and CentOS/RHEL
|
||||
2) Install Python 2.6 to /opt and recompile wsgi if not provided
|
||||
2) Install web2py in /opt/web-apps/
|
||||
3) Configure SELinux and iptables
|
||||
5) Create a self signed ssl certificate
|
||||
6) Setup web2py with mod_wsgi
|
||||
7) Create virtualhost entries so that web2py responds for '/'
|
||||
8) Restart Apache.
|
||||
|
||||
You should probably read this script before running it.
|
||||
|
||||
Although SELinux permissions changes have been made,
|
||||
further SELinux changes will be required for your personal
|
||||
apps. (There may also be additional changes required for the
|
||||
bundled apps.) As a last resort, SELinux can be disabled.
|
||||
|
||||
A simple iptables configuration has been applied. You may
|
||||
want to review it to verify that it meets your needs.
|
||||
|
||||
Finally, if you require a proxy to access the Internet, please
|
||||
set up your machine to do so before running this script.
|
||||
|
||||
(author: berubejd)
|
||||
|
||||
Press ENTER to continue...[ctrl+C to abort]"
|
||||
|
||||
read CONFIRM
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
###
|
||||
### Phase 0 - This may get messy. Lets work from a temporary directory
|
||||
###
|
||||
|
||||
current_dir=`pwd`
|
||||
|
||||
if [ -d /tmp/setup-web2py/ ]; then
|
||||
mv /tmp/setup-web2py/ /tmp/setup-web2py.old/
|
||||
fi
|
||||
|
||||
mkdir -p /tmp/setup-web2py
|
||||
cd /tmp/setup-web2py
|
||||
|
||||
###
|
||||
### Phase 1 - Requirements installation
|
||||
###
|
||||
|
||||
echo
|
||||
echo " - Installing packages"
|
||||
echo
|
||||
|
||||
# Verify packages are up to date
|
||||
yum update
|
||||
|
||||
# Install required packages
|
||||
yum install httpd mod_ssl mod_wsgi wget python
|
||||
|
||||
# Verify we have at least Python 2.5
|
||||
typeset -i version_major
|
||||
typeset -i version_minor
|
||||
|
||||
version=`rpm --qf %{Version} -q python`
|
||||
version_major=`echo ${version} | awk '{split($0, parts, "."); print parts[1]}'`
|
||||
version_minor=`echo ${version} | awk '{split($0, parts, "."); print parts[2]}'`
|
||||
|
||||
if [ ! ${version_major} -ge 2 -o ! ${version_minor} -ge 5 ]; then
|
||||
# Setup 2.6 in /opt - based upon
|
||||
# http://markkoberlein.com/getting-python-26-with-django-11-together-on
|
||||
|
||||
# Check for earlier Python 2.6 install
|
||||
if [ -e /opt/python2.6 ]; then
|
||||
# Is Python already installed?
|
||||
RETV=`/opt/python2.6/bin/python -V > /dev/null 2>&1; echo $?`
|
||||
if [ ${RETV} -eq 0 ]; then
|
||||
python_installed='True'
|
||||
else
|
||||
mv /opt/python2.6 /opt/python2.6-old
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install Python 2.6 if it doesn't exist already
|
||||
if [ ! "${python_installed}" == "True" ]; then
|
||||
# Install requirements for the Python build
|
||||
yum install sqlite-devel zlib-devel
|
||||
|
||||
mkdir -p /opt/python2.6
|
||||
|
||||
# Download and install
|
||||
wget http://www.python.org/ftp/python/2.6.4/Python-2.6.4.tgz
|
||||
tar -xzf Python-2.6.4.tgz
|
||||
cd Python-2.6.4
|
||||
./configure --prefix=/opt/python2.6 --with-threads --enable-shared --with-zlib=/usr/include
|
||||
make && make install
|
||||
|
||||
cd /tmp/setup-web2py
|
||||
fi
|
||||
|
||||
# Create links for Python 2.6
|
||||
# even if it was previously installed just to be sure
|
||||
ln -s /opt/python2.6/lib/libpython2.6.so /usr/lib
|
||||
ln -s /opt/python2.6/lib/libpython2.6.so.1.0 /usr/lib
|
||||
ln -s /opt/python2.6/bin/python /usr/local/bin/python
|
||||
ln -s /opt/python2.6/bin/python /usr/bin/python2.6
|
||||
ln -s /opt/python2.6/lib/python2.6.so /opt/python2.6/lib/python2.6/config/
|
||||
|
||||
# Update linker for new libraries
|
||||
/sbin/ldconfig
|
||||
|
||||
# Rebuild wsgi to take advantage of Python 2.6
|
||||
yum install httpd-devel
|
||||
|
||||
cd /tmp/setup-web2py
|
||||
|
||||
wget http://modwsgi.googlecode.com/files/mod_wsgi-3.3.tar.gz
|
||||
tar -xzf mod_wsgi-3.3.tar.gz
|
||||
cd mod_wsgi-3.3
|
||||
./configure --with-python=/usr/local/bin/python
|
||||
make && make install
|
||||
|
||||
echo "LoadModule wsgi_module modules/mod_wsgi.so" > /etc/httpd/conf.d/wsgi.conf
|
||||
|
||||
cd /tmp/setup-web2py
|
||||
fi
|
||||
|
||||
### MySQL install untested!
|
||||
# Install mysql packages (optional)
|
||||
#yum install mysql mysql-server
|
||||
|
||||
# Enable mysql to start at boot (optional)
|
||||
#chkconfig --levels 235 mysqld on
|
||||
#service mysqld start
|
||||
|
||||
# Configure mysql security settings (not really optional if mysql installed)
|
||||
#/usr/bin/mysql_secure_installation
|
||||
|
||||
###
|
||||
### Phase 2 - Install web2py
|
||||
###
|
||||
|
||||
echo
|
||||
echo " - Downloading, installing, and starting web2py"
|
||||
echo
|
||||
|
||||
# Create web-apps directory, if required
|
||||
if [ ! -d "/opt/web-apps" ]; then
|
||||
mkdir -p /opt/web-apps
|
||||
|
||||
chmod 755 /opt
|
||||
chmod 755 /opt/web-apps
|
||||
fi
|
||||
|
||||
cd /opt/web-apps
|
||||
|
||||
# Download web2py
|
||||
if [ -e web2py_src.zip* ]; then
|
||||
rm web2py_src.zip*
|
||||
fi
|
||||
|
||||
wget http://web2py.com/examples/static/web2py_src.zip
|
||||
unzip web2py_src.zip
|
||||
chown -R apache:apache web2py
|
||||
|
||||
###
|
||||
### Phase 3 - Setup SELinux context
|
||||
###
|
||||
|
||||
# Set context for Python libraries if Python 2.6 installed
|
||||
if [ -d /opt/python2.6 ]; then
|
||||
cd /opt/python2.6
|
||||
chcon -R -t lib_t lib/
|
||||
fi
|
||||
|
||||
# Allow http_tmp_exec required for wsgi
|
||||
RETV=`setsebool -P httpd_tmp_exec on > /dev/null 2>&1; echo $?`
|
||||
if [ ! ${RETV} -eq 0 ]; then
|
||||
# CentOS doesn't support httpd_tmp_exec
|
||||
cd /tmp/setup-web2py
|
||||
|
||||
# Create the SELinux policy
|
||||
cat > httpd.te <<EOF
|
||||
EOF
|
||||
|
||||
module httpd 1.0;
|
||||
|
||||
require {
|
||||
type httpd_t;
|
||||
class process execmem;
|
||||
}
|
||||
|
||||
#============= httpd_t ==============
|
||||
allow httpd_t self:process execmem;
|
||||
EOF
|
||||
|
||||
checkmodule -M -m -o httpd.mod httpd.te
|
||||
semodule_package -o httpd.pp -m httpd.mod
|
||||
semodule -i httpd.pp
|
||||
|
||||
fi
|
||||
|
||||
# Setup the overall web2py SELinux context
|
||||
cd /opt
|
||||
chcon -R -t httpd_user_content_t web-apps/
|
||||
|
||||
cd /opt/web-apps/web2py/applications
|
||||
|
||||
# Setup the proper context on the writable application directories
|
||||
for app in `ls`
|
||||
do
|
||||
for dir in databases cache errors sessions private uploads
|
||||
do
|
||||
mkdir ${app}/${dir}
|
||||
chown apache:apache ${app}/${dir}
|
||||
chcon -R -t tmp_t ${app}/${dir}
|
||||
done
|
||||
done
|
||||
|
||||
|
||||
###
|
||||
### Phase 4 - Configure iptables
|
||||
###
|
||||
|
||||
cd /tmp/setup-web2py
|
||||
|
||||
# Create rules file - based upon
|
||||
# http://articles.slicehost.com/assets/2007/9/4/iptables.txt
|
||||
cat > iptables.rules <<EOF
|
||||
EOF
|
||||
*filter
|
||||
|
||||
# Allows all loopback (lo0) traffic
|
||||
# drop all traffic to 127/8 that doesn't use lo0
|
||||
-A INPUT -i lo -j ACCEPT
|
||||
-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
|
||||
|
||||
# Accepts all established inbound connections
|
||||
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
|
||||
# Allows all outbound traffic
|
||||
-A OUTPUT -j ACCEPT
|
||||
|
||||
# Allows SSH, HTTP, and HTTPS
|
||||
# Consider changing the SSH port and/or using rate limiting
|
||||
# see http://blog.andrew.net.au/2005/02/16#ipt_recent_and_ssh_attacks
|
||||
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT
|
||||
-A INPUT -p tcp --dport 80 -j ACCEPT
|
||||
-A INPUT -p tcp --dport 443 -j ACCEPT
|
||||
|
||||
# Allow ping
|
||||
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
|
||||
|
||||
# log iptables denied calls
|
||||
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
|
||||
|
||||
# Reject all other inbound - default deny unless explicitly allowed policy
|
||||
-A INPUT -j REJECT
|
||||
-A FORWARD -j REJECT
|
||||
|
||||
COMMIT
|
||||
EOF
|
||||
|
||||
/sbin/iptables -F
|
||||
cat iptables.rules | /sbin/iptables-restore
|
||||
/sbin/service iptables save
|
||||
|
||||
###
|
||||
### Phase 5 - Setup SSL
|
||||
###
|
||||
|
||||
echo
|
||||
echo " - Creating a self signed certificate"
|
||||
echo
|
||||
|
||||
# Verify ssl directory exists
|
||||
if [ ! -d "/etc/httpd/ssl" ]; then
|
||||
mkdir -p /etc/httpd/ssl
|
||||
fi
|
||||
|
||||
# Generate and protect certificate
|
||||
openssl genrsa 1024 > /etc/httpd/ssl/self_signed.key
|
||||
openssl req -new -x509 -nodes -sha1 -days 365 -key /etc/httpd/ssl/self_signed.key > /etc/httpd/ssl/self_signed.cert
|
||||
openssl x509 -noout -fingerprint -text < /etc/httpd/ssl/self_signed.cert > /etc/httpd/ssl/self_signed.info
|
||||
|
||||
chmod 400 /etc/httpd/ssl/self_signed.*
|
||||
|
||||
###
|
||||
### Phase 6 - Configure Apache
|
||||
###
|
||||
|
||||
echo
|
||||
echo " - Configure Apache to use mod_wsgi"
|
||||
echo
|
||||
|
||||
# Create config
|
||||
if [ -e /etc/httpd/conf.d/welcome.conf ]; then
|
||||
mv /etc/httpd/conf.d/welcome.conf /etc/httpd/conf.d/welcome.conf.disabled
|
||||
fi
|
||||
|
||||
cat > /etc/httpd/conf.d/default.conf <<EOF
|
||||
EOF
|
||||
|
||||
NameVirtualHost *:80
|
||||
NameVirtualHost *:443
|
||||
|
||||
<VirtualHost *:80>
|
||||
WSGIDaemonProcess web2py user=apache group=apache
|
||||
WSGIProcessGroup web2py
|
||||
WSGIScriptAlias / /opt/web-apps/web2py/wsgihandler.py
|
||||
|
||||
<Directory /opt/web-apps/web2py>
|
||||
AllowOverride None
|
||||
Order Allow,Deny
|
||||
Deny from all
|
||||
<Files wsgihandler.py>
|
||||
Allow from all
|
||||
</Files>
|
||||
</Directory>
|
||||
|
||||
AliasMatch ^/([^/]+)/static/(.*) /opt/web-apps/web2py/applications/\$1/static/\$2
|
||||
|
||||
<Directory /opt/web-apps/web2py/applications/*/static>
|
||||
Options -Indexes
|
||||
Order Allow,Deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
<Location /admin>
|
||||
Deny from all
|
||||
</Location>
|
||||
|
||||
<LocationMatch ^/([^/]+)/appadmin>
|
||||
Deny from all
|
||||
</LocationMatch>
|
||||
|
||||
CustomLog /var/log/httpd/access_log common
|
||||
ErrorLog /var/log/httpd/error_log
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
SSLEngine on
|
||||
SSLCertificateFile /etc/httpd/ssl/self_signed.cert
|
||||
SSLCertificateKeyFile /etc/httpd/ssl/self_signed.key
|
||||
|
||||
WSGIProcessGroup web2py
|
||||
|
||||
WSGIScriptAlias / /opt/web-apps/web2py/wsgihandler.py
|
||||
|
||||
<Directory /opt/web-apps/web2py>
|
||||
AllowOverride None
|
||||
Order Allow,Deny
|
||||
Deny from all
|
||||
<Files wsgihandler.py>
|
||||
Allow from all
|
||||
</Files>
|
||||
</Directory>
|
||||
|
||||
AliasMatch ^/([^/]+)/static/(.*) /opt/web-apps/web2py/applications/\$1/static/\$2
|
||||
|
||||
<Directory /opt/web-apps/web2py/applications/*/static>
|
||||
Options -Indexes
|
||||
ExpiresActive On
|
||||
ExpiresDefault "access plus 1 hour"
|
||||
Order Allow,Deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
CustomLog /var/log/httpd/access_log common
|
||||
ErrorLog /var/log/httpd/error_log
|
||||
</VirtualHost>
|
||||
|
||||
EOF
|
||||
|
||||
# Fix wsgi socket locations
|
||||
echo "WSGISocketPrefix run/wsgi" >> /etc/httpd/conf.d/wsgi.conf
|
||||
|
||||
# Restart Apache to pick up changes
|
||||
service httpd restart
|
||||
|
||||
###
|
||||
### Phase 7 - Setup web2py admin password
|
||||
###
|
||||
|
||||
echo
|
||||
echo " - Setup web2py admin password"
|
||||
echo
|
||||
|
||||
cd /opt/web-apps/web2py
|
||||
sudo -u apache python -c "from gluon.main import save_password; save_password(raw_input('admin password: '),443)"
|
||||
|
||||
###
|
||||
### Phase 8 - Verify that required services start at boot
|
||||
###
|
||||
|
||||
/sbin/chkconfig iptables on
|
||||
/sbin/chkconfig httpd on
|
||||
|
||||
###
|
||||
### Phase 999 - Done!
|
||||
###
|
||||
|
||||
# Change back to original directory
|
||||
cd ${current_directory}
|
||||
|
||||
echo " - Complete!"
|
||||
echo
|
||||
|
||||
EOF
|
||||
EOF
|
||||
EOF
|
||||
@@ -0,0 +1,81 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo 'setup-web2py-nginx-uwsgi-ubuntu.sh'
|
||||
echo 'Requires Ubuntu 10.04 (LTS) and installs Nginx + uWSGI + Web2py'
|
||||
|
||||
# Get Web2py Admin Password
|
||||
echo -e "Web2py Admin Password: \c "
|
||||
read PW
|
||||
|
||||
# Upgrade and install needed software
|
||||
apt-get update
|
||||
apt-get -y upgrade
|
||||
apt-get install python-software-properties
|
||||
add-apt-repository ppa:nginx/stable
|
||||
add-apt-repository ppa:uwsgi/release
|
||||
apt-get update
|
||||
apt-get -y install nginx-full
|
||||
apt-get -y install uwsgi-python
|
||||
|
||||
# Create configuration file /etc/nginx/sites-available/web2py
|
||||
echo 'server {
|
||||
listen 80;
|
||||
server_name $hostname;
|
||||
location ~* /(\w+)/static/ {
|
||||
root /home/www-data/web2py/applications/;
|
||||
}
|
||||
location / {
|
||||
uwsgi_pass 127.0.0.1:9001;
|
||||
include uwsgi_params;
|
||||
uwsgi_param UWSGI_SCHEME $scheme;
|
||||
uwsgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443;
|
||||
server_name $hostname;
|
||||
ssl on;
|
||||
ssl_certificate /etc/nginx/ssl/web2py.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/web2py.key;
|
||||
location / {
|
||||
uwsgi_pass 127.0.0.1:9001;
|
||||
include uwsgi_params;
|
||||
uwsgi_param UWSGI_SCHEME $scheme;
|
||||
uwsgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||
}
|
||||
|
||||
}' >/etc/nginx/sites-available/web2py
|
||||
|
||||
ln -s /etc/nginx/sites-available/web2py /etc/nginx/sites-enabled/web2py
|
||||
rm /etc/nginx/sites-enabled/default
|
||||
rm /etc/nginx/sites-available/default
|
||||
mkdir /etc/nginx/ssl
|
||||
cd /etc/nginx/ssl
|
||||
openssl genrsa -out web2py.key 1024
|
||||
openssl req -batch -new -key web2py.key -out web2py.csr
|
||||
openssl x509 -req -days 1780 -in web2py.csr -signkey web2py.key -out web2py.crt
|
||||
|
||||
# Create configuration file /etc/uwsgi-python/apps-available/web2py.xml
|
||||
echo '<uwsgi>
|
||||
<socket>127.0.0.1:9001</socket>
|
||||
<pythonpath>/home/www-data/web2py/</pythonpath>
|
||||
<app mountpoint="/">
|
||||
<script>wsgihandler</script>
|
||||
</app>
|
||||
</uwsgi>' >/etc/uwsgi-python/apps-available/web2py.xml
|
||||
ln -s /etc/uwsgi-python/apps-available/web2py.xml /etc/uwsgi-python/apps-enabled/web2py.xml
|
||||
|
||||
# Install Web2py
|
||||
apt-get -y install unzip
|
||||
cd /home
|
||||
mkdir www-data
|
||||
cd www-data
|
||||
wget http://web2py.com/examples/static/web2py_src.zip
|
||||
unzip web2py_src.zip
|
||||
rm web2py_src.zip
|
||||
chown -R www-data:www-data web2py
|
||||
cd /home/www-data/web2py
|
||||
sudo -u www-data python -c "from gluon.main import save_password; save_password('$PW',443)"
|
||||
/etc/init.d/uwsgi-python restart
|
||||
/etc/init.d/nginx restart
|
||||
Executable
+169
@@ -0,0 +1,169 @@
|
||||
echo "This script will:
|
||||
1) install all modules need to run web2py on Ubuntu/Debian
|
||||
2) install web2py in /home/www-data/
|
||||
3) create a self signed sll certificate
|
||||
4) setup web2py with mod_wsgi
|
||||
5) overwrite /etc/apache2/sites-available/default
|
||||
6) restart apache.
|
||||
|
||||
You may want to read this cript before running it.
|
||||
|
||||
Press a key to continue...[ctrl+C to abort]"
|
||||
|
||||
read CONFIRM
|
||||
|
||||
#!/bin/bash
|
||||
# optional
|
||||
# dpkg-reconfigure console-setup
|
||||
# dpkg-reconfigure timezoneconf
|
||||
# nano /etc/hostname
|
||||
# nano /etc/network/interfaces
|
||||
# nano /etc/resolv.conf
|
||||
# reboot now
|
||||
# ifconfig eth0
|
||||
|
||||
echo "installing useful packages"
|
||||
echo "=========================="
|
||||
apt-get update
|
||||
apt-get -y install ssh
|
||||
apt-get -y install zip unzip
|
||||
apt-get -y install tar
|
||||
apt-get -y install openssh-server
|
||||
apt-get -y install build-essential
|
||||
apt-get -y install python2.5
|
||||
apt-get -y install ipython
|
||||
apt-get -y install python-dev
|
||||
apt-get -y install postgresql
|
||||
apt-get -y install apache2
|
||||
apt-get -y install libapache2-mod-wsgi
|
||||
apt-get -y install python2.5-psycopg2
|
||||
apt-get -y install postfix
|
||||
apt-get -y install wget
|
||||
apt-get -y install python-matplotlib
|
||||
apt-get -y install python-reportlab
|
||||
apt-get -y install mercurial
|
||||
/etc/init.d/postgresql restart
|
||||
|
||||
# optional, uncomment for emacs
|
||||
# apt-get -y install emacs
|
||||
|
||||
# optional, uncomment for backups using samba
|
||||
# apt-get -y install samba
|
||||
# apt-get -y install smbfs
|
||||
|
||||
echo "downloading, installing and starting web2py"
|
||||
echo "==========================================="
|
||||
cd /home
|
||||
mkdir www-data
|
||||
cd www-data
|
||||
rm web2py_src.zip*
|
||||
wget http://web2py.com/examples/static/web2py_src.zip
|
||||
unzip web2py_src.zip
|
||||
chown -R www-data:www-data web2py
|
||||
|
||||
echo "setting up apache modules"
|
||||
echo "========================="
|
||||
a2enmod ssl
|
||||
a2enmod proxy
|
||||
a2enmod proxy_http
|
||||
a2enmod headers
|
||||
a2enmod expires
|
||||
mkdir /etc/apache2/ssl
|
||||
|
||||
echo "creating a self signed certificate"
|
||||
echo "=================================="
|
||||
openssl genrsa 1024 > /etc/apache2/ssl/self_signed.key
|
||||
chmod 400 /etc/apache2/ssl/self_signed.key
|
||||
openssl req -new -x509 -nodes -sha1 -days 365 -key /etc/apache2/ssl/self_signed.key > /etc/apache2/ssl/self_signed.cert
|
||||
openssl x509 -noout -fingerprint -text < /etc/apache2/ssl/self_signed.cert > /etc/apache2/ssl/self_signed.info
|
||||
|
||||
echo "rewriting your apache config file to use mod_wsgi"
|
||||
echo "================================================="
|
||||
echo '
|
||||
NameVirtualHost *:80
|
||||
NameVirtualHost *:443
|
||||
|
||||
<VirtualHost *:80>
|
||||
WSGIDaemonProcess web2py user=www-data group=www-data
|
||||
WSGIProcessGroup web2py
|
||||
WSGIScriptAlias / /home/www-data/web2py/wsgihandler.py
|
||||
|
||||
<Directory /home/www-data/web2py>
|
||||
AllowOverride None
|
||||
Order Allow,Deny
|
||||
Deny from all
|
||||
<Files wsgihandler.py>
|
||||
Allow from all
|
||||
</Files>
|
||||
</Directory>
|
||||
|
||||
AliasMatch ^/([^/]+)/static/(.*) \
|
||||
/home/www-data/web2py/applications/$1/static/$2
|
||||
<Directory /home/www-data/web2py/applications/*/static/>
|
||||
Options -Indexes
|
||||
Order Allow,Deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
<Location /admin>
|
||||
Deny from all
|
||||
</Location>
|
||||
|
||||
<LocationMatch ^/([^/]+)/appadmin>
|
||||
Deny from all
|
||||
</LocationMatch>
|
||||
|
||||
CustomLog /var/log/apache2/access.log common
|
||||
ErrorLog /var/log/apache2/error.log
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
SSLEngine on
|
||||
SSLCertificateFile /etc/apache2/ssl/self_signed.cert
|
||||
SSLCertificateKeyFile /etc/apache2/ssl/self_signed.key
|
||||
|
||||
WSGIProcessGroup web2py
|
||||
|
||||
WSGIScriptAlias / /home/www-data/web2py/wsgihandler.py
|
||||
|
||||
<Directory /home/www-data/web2py>
|
||||
AllowOverride None
|
||||
Order Allow,Deny
|
||||
Deny from all
|
||||
<Files wsgihandler.py>
|
||||
Allow from all
|
||||
</Files>
|
||||
</Directory>
|
||||
|
||||
AliasMatch ^/([^/]+)/static/(.*) \
|
||||
/home/www-data/web2py/applications/$1/static/$2
|
||||
|
||||
<Directory /home/www-data/web2py/applications/*/static/>
|
||||
Options -Indexes
|
||||
ExpiresActive On
|
||||
ExpiresDefault "access plus 1 hour"
|
||||
Order Allow,Deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
CustomLog /var/log/apache2/access.log common
|
||||
ErrorLog /var/log/apache2/error.log
|
||||
</VirtualHost>
|
||||
' > /etc/apache2/sites-available/default
|
||||
|
||||
# echo "setting up PAM"
|
||||
# echo "================"
|
||||
# sudo apt-get install pwauth
|
||||
# sudo ln -s /etc/apache2/mods-available/authnz_external.load /etc/apache2/mods-enabled
|
||||
# ln -s /etc/pam.d/apache2 /etc/pam.d/httpd
|
||||
# usermod -a -G shadow www-data
|
||||
|
||||
echo "restarting apage"
|
||||
echo "================"
|
||||
|
||||
/etc/init.d/apache2 restart
|
||||
cd /home/www-data/web2py
|
||||
sudo -u www-data python -c "from gluon.widget import console; console();"
|
||||
sudo -u www-data python -c "from gluon.main import save_password; save_password(raw_input('admin password: '),443)"
|
||||
echo "done!"
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Usage:
|
||||
Install cx_Freeze: http://cx-freeze.sourceforge.net/
|
||||
Copy script to the web2py directory
|
||||
c:\Python27\python standalone_exe_cxfreeze.py build_exe
|
||||
"""
|
||||
from cx_Freeze import setup, Executable
|
||||
from gluon.import_all import base_modules, contributed_modules
|
||||
from gluon.fileutils import readlines_file
|
||||
from glob import glob
|
||||
import fnmatch
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import re
|
||||
|
||||
#read web2py version from VERSION file
|
||||
web2py_version_line = readlines_file('VERSION')[0]
|
||||
#use regular expression to get just the version number
|
||||
v_re = re.compile('[0-9]+\.[0-9]+\.[0-9]+')
|
||||
web2py_version = v_re.search(web2py_version_line).group(0)
|
||||
|
||||
base = None
|
||||
|
||||
if sys.platform == 'win32':
|
||||
base = "Win32GUI"
|
||||
|
||||
base_modules.remove('macpath')
|
||||
buildOptions = dict(
|
||||
compressed = True,
|
||||
excludes = ["macpath","PyQt4"],
|
||||
includes = base_modules,
|
||||
include_files=[
|
||||
'applications',
|
||||
'ABOUT',
|
||||
'LICENSE',
|
||||
'VERSION',
|
||||
'logging.example.conf',
|
||||
'options_std.py',
|
||||
'app.example.yaml',
|
||||
'queue.example.yaml',
|
||||
],
|
||||
# append any extra module by extending the list below -
|
||||
# "contributed_modules+["lxml"]"
|
||||
packages = contributed_modules,
|
||||
)
|
||||
|
||||
setup(
|
||||
name = "Web2py",
|
||||
version=web2py_version,
|
||||
author="Massimo DiPierro",
|
||||
description="web2py web framework",
|
||||
license = "LGPL v3",
|
||||
options = dict(build_exe = buildOptions),
|
||||
executables = [Executable("web2py.py",
|
||||
base=base,
|
||||
compress = True,
|
||||
icon = "web2py.ico",
|
||||
targetName="web2py.exe",
|
||||
copyDependentFiles = True)],
|
||||
)
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# TODO: Comment this code
|
||||
|
||||
import sys
|
||||
import shutil
|
||||
import os
|
||||
|
||||
from gluon.languages import findT
|
||||
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
file = sys.argv[1]
|
||||
apps = sys.argv[2:]
|
||||
|
||||
d = {}
|
||||
for app in apps:
|
||||
path = 'applications/%s/' % app
|
||||
findT(path, file)
|
||||
langfile = open(os.path.join(path, 'languages', '%s.py' % file))
|
||||
try:
|
||||
data = eval(langfile.read())
|
||||
finally:
|
||||
langfile.close()
|
||||
d.update(data)
|
||||
|
||||
path = 'applications/%s/' % apps[-1]
|
||||
file1 = os.path.join(path, 'languages', '%s.py' % file)
|
||||
|
||||
f = open(file1, 'w')
|
||||
try:
|
||||
f.write('{\n')
|
||||
keys = d.keys()
|
||||
keys.sort()
|
||||
for key in keys:
|
||||
f.write('%s:%s,\n' % (repr(key), repr(str(d[key]))))
|
||||
f.write('}\n')
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
oapps = reversed(apps[:-1])
|
||||
for app in oapps:
|
||||
path2 = 'applications/%s/' % app
|
||||
file2 = os.path.join(path2, 'languages', '%s.py' % file)
|
||||
shutil.copyfile(file1, file2)
|
||||
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import stat
|
||||
import datetime
|
||||
|
||||
from gluon.utils import md5_hash
|
||||
from gluon.restricted import RestrictedError, TicketStorage
|
||||
from gluon import DAL
|
||||
|
||||
SLEEP_MINUTES = 5
|
||||
|
||||
errors_path = os.path.join(request.folder, 'errors')
|
||||
try:
|
||||
db_string = open(os.path.join(request.folder, 'private', 'ticket_storage.txt')).read().replace('\r','').replace('\n','').strip()
|
||||
except:
|
||||
db_string = 'sqlite://storage.db'
|
||||
|
||||
db_path = os.path.join(request.folder, 'databases')
|
||||
|
||||
tk_db = DAL(db_string, folder=db_path, auto_import=True)
|
||||
ts = TicketStorage(db=tk_db)
|
||||
tk_table = ts._get_table(db=tk_db, tablename=ts.tablename, app=request.application)
|
||||
|
||||
|
||||
hashes = {}
|
||||
|
||||
while 1:
|
||||
if request.tickets_db:
|
||||
print "You're storing tickets yet in database"
|
||||
sys.exit(1)
|
||||
|
||||
for file in os.listdir(errors_path):
|
||||
filename = os.path.join(errors_path, file)
|
||||
|
||||
modified_time = os.stat(filename)[stat.ST_MTIME]
|
||||
modified_time = datetime.datetime.fromtimestamp(modified_time)
|
||||
ticket_id = file
|
||||
ticket_data = open(filename).read()
|
||||
tk_table.insert(ticket_id=ticket_id,
|
||||
ticket_data=ticket_data,
|
||||
created_datetime=modified_time
|
||||
)
|
||||
tk_db.commit()
|
||||
os.unlink(filename)
|
||||
|
||||
time.sleep(SLEEP_MINUTES * 60)
|
||||
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import stat
|
||||
import datetime
|
||||
|
||||
from gluon.utils import md5_hash
|
||||
from gluon.restricted import RestrictedError
|
||||
from gluon.tools import Mail
|
||||
|
||||
|
||||
path = os.path.join(request.folder, 'errors')
|
||||
hashes = {}
|
||||
mail = Mail()
|
||||
|
||||
### CONFIGURE HERE
|
||||
SLEEP_MINUTES = 5
|
||||
ALLOW_DUPLICATES = True
|
||||
mail.settings.server = 'localhost:25'
|
||||
mail.settings.sender = 'you@localhost'
|
||||
administrator_email = 'you@localhost'
|
||||
### END CONFIGURATION
|
||||
|
||||
while 1:
|
||||
for file in os.listdir(path):
|
||||
filename = os.path.join(path, file)
|
||||
|
||||
if not ALLOW_DUPLICATES:
|
||||
fileobj = open(filename, 'r')
|
||||
try:
|
||||
file_data = fileobj.read()
|
||||
finally:
|
||||
fileobj.close()
|
||||
key = md5_hash(file_data)
|
||||
|
||||
if key in hashes:
|
||||
continue
|
||||
|
||||
hashes[key] = 1
|
||||
|
||||
error = RestrictedError()
|
||||
error.load(request, request.application, filename)
|
||||
|
||||
mail.send(to=administrator_email, subject='new web2py ticket', message=error.traceback)
|
||||
|
||||
os.unlink(filename)
|
||||
time.sleep(SLEEP_MINUTES * 60)
|
||||
|
||||
Executable
+59
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# update-web2py.sh
|
||||
# 2009-12-16
|
||||
#
|
||||
# install in web2py/.. or web2py/ or web2py/scripts as update-web2py.sh
|
||||
# make executable: chmod +x web2py.sh
|
||||
#
|
||||
# save a snapshot of current web2py/ as web2py/../web2py-version.zip
|
||||
# download the current stable version of web2py
|
||||
# unzip downloaded version over web2py/
|
||||
#
|
||||
TARGET=web2py
|
||||
|
||||
if [ ! -d $TARGET ]; then
|
||||
# in case we're in web2py/
|
||||
if [ -f ../$TARGET/VERSION ]; then
|
||||
cd ..
|
||||
# in case we're in web2py/scripts
|
||||
elif [ -f ../../$TARGET/VERSION ]; then
|
||||
cd ../..
|
||||
fi
|
||||
fi
|
||||
read a VERSION c < $TARGET/VERSION
|
||||
SAVE=$TARGET-$VERSION
|
||||
URL=http://www.web2py.com/examples/static/web2py_src.zip
|
||||
|
||||
ZIP=`basename $URL`
|
||||
SAVED=""
|
||||
|
||||
# Save a zip archive of the current version,
|
||||
# but don't overwrite a previous save of the same version.
|
||||
#
|
||||
if [ -f $SAVE.zip ]; then
|
||||
echo "Remove or rename $SAVE.zip first" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -d $TARGET ]; then
|
||||
echo -n ">>Save old version: " >&2
|
||||
cat $TARGET/VERSION >&2
|
||||
zip -q -r $SAVE.zip $TARGET
|
||||
SAVED=$SAVE.zip
|
||||
fi
|
||||
#
|
||||
# Download the new version.
|
||||
#
|
||||
echo ">>Download latest web2py release:" >&2
|
||||
curl -O $URL
|
||||
#
|
||||
# Unzip into web2py/
|
||||
#
|
||||
unzip -q -o $ZIP
|
||||
rm $ZIP
|
||||
echo -n ">>New version: " >&2
|
||||
cat $TARGET/VERSION >&2
|
||||
if [ "$SAVED" != "" ]; then
|
||||
echo ">>Old version saved as $SAVED"
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
crontab -e
|
||||
* 3 * * * root path/to/this/file
|
||||
"""
|
||||
|
||||
USER = 'www-data'
|
||||
TMPFILENAME = 'web2py_src_update.zip'
|
||||
|
||||
import sys
|
||||
import os
|
||||
import urllib
|
||||
import zipfile
|
||||
|
||||
if len(sys.argv)>1 and sys.argv[1] == 'nightly':
|
||||
version = 'http://web2py.com/examples/static/nightly/web2py_src.zip'
|
||||
else:
|
||||
version = 'http://web2py.com/examples/static/web2py_src.zip'
|
||||
|
||||
realpath = os.path.realpath(__file__)
|
||||
path = os.path.dirname(os.path.dirname(os.path.dirname(realpath)))
|
||||
os.chdir(path)
|
||||
try:
|
||||
old_version = open('web2py/VERSION','r').read().strip()
|
||||
except IOError:
|
||||
old_version = ''
|
||||
open(TMPFILENAME,'wb').write(urllib.urlopen(version).read())
|
||||
new_version = zipfile.ZipFile(TMPFILENAME).read('web2py/VERSION').strip()
|
||||
if new_version>old_version:
|
||||
os.system('sudo -u %s unzip -o %s' % (USER,TMPFILENAME))
|
||||
os.system('apachectl restart | apache2ctl restart')
|
||||
@@ -0,0 +1,11 @@
|
||||
chown -R nobody:nobody *.py
|
||||
chown -R nobody:nobody gluon
|
||||
chown -R nobody:nobody scripts
|
||||
chown -R nobody:nobody applications/*/modules/
|
||||
chown -R nobody:nobody applications/*/models/
|
||||
chown -R nobody:nobody applications/*/controllers/
|
||||
chown -R nobody:nobody applications/*/views/
|
||||
chown -R nobody:nobody applications/*/static/
|
||||
chown -R nobody:nobody applications/*/cron/
|
||||
|
||||
echo "unlock with chown -R www-data:www-data ./"
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
# the script should be run
|
||||
# from WEB2PY root directory
|
||||
|
||||
prog=`basename $0`
|
||||
|
||||
cd `pwd`
|
||||
chmod +x $prog
|
||||
|
||||
function web2py_start {
|
||||
nohup ./$prog -a "<recycle>" 2>/dev/null &
|
||||
pid=`pgrep $prog | tail -1`
|
||||
if [ $pid -ne $$ ]
|
||||
then
|
||||
echo "WEB2PY has been started."
|
||||
fi
|
||||
}
|
||||
function web2py_stop {
|
||||
kill -15 `pgrep $prog | grep -v $$` 2>/dev/null
|
||||
pid=`pgrep $prog | head -1`
|
||||
if [ $pid -ne $$ ]
|
||||
then
|
||||
echo "WEB2PY has been stopped."
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
web2py_start
|
||||
;;
|
||||
stop)
|
||||
web2py_stop
|
||||
;;
|
||||
restart)
|
||||
web2py_stop
|
||||
web2py_start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $prog [start|stop|restart]"
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# /etc/rc.d/init.d/web2pyd
|
||||
#
|
||||
# Starts the Web2py Daemon on Fedora (Red Hat Linux)
|
||||
#
|
||||
# To execute automatically at startup
|
||||
#
|
||||
# sudo chkconfig --add web2pyd
|
||||
#
|
||||
# chkconfig: 2345 90 10
|
||||
# description: Web2py Daemon
|
||||
# processname: web2pyd
|
||||
# pidfile: /var/lock/subsys/web2pyd
|
||||
|
||||
source /etc/rc.d/init.d/functions
|
||||
|
||||
RETVAL=0
|
||||
NAME=web2pyd
|
||||
DESC="Web2py Daemon"
|
||||
DAEMON_DIR="/usr/lib/web2py"
|
||||
ADMINPASS="admin"
|
||||
#ADMINPASS="\<recycle\>"
|
||||
PIDFILE=/var/run/$NAME.pid
|
||||
PORT=8001
|
||||
PYTHON=python
|
||||
|
||||
cd $DAEMON_DIR
|
||||
|
||||
start() {
|
||||
echo -n $"Starting $DESC ($NAME): "
|
||||
daemon --check $NAME $PYTHON $DAEMON_DIR/web2py.py -Q --nogui -a $ADMINPASS -d $PIDFILE -p $PORT &
|
||||
RETVAL=$?
|
||||
if [ $RETVAL -eq 0 ]; then
|
||||
touch /var/lock/subsys/$NAME
|
||||
fi
|
||||
echo
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
stop() {
|
||||
echo -n $"Shutting down $DESC ($NAME): "
|
||||
killproc -p "$PIDFILE" -d 3 "$NAME"
|
||||
echo
|
||||
if [ $RETVAL -eq 0 ]; then
|
||||
rm -f /var/lock/subsys/$NAME
|
||||
rm -f $PIDFILE
|
||||
fi
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
restart() {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
status() {
|
||||
if [ -r "$PIDFILE" ]; then
|
||||
pid=`cat $PIDFILE`
|
||||
fi
|
||||
if [ $pid ]; then
|
||||
echo "$NAME (pid $pid) is running..."
|
||||
else
|
||||
echo "$NAME is stopped"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start) start;;
|
||||
stop) stop;;
|
||||
status) status;;
|
||||
restart) restart;;
|
||||
condrestart) [ -e /var/lock/subsys/$NAME ] && restart
|
||||
RETVAL=$?
|
||||
;;
|
||||
*) echo $"Usage: $0 {start|stop|restart|condrestart|status}"
|
||||
RETVAL=1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit $RETVAL
|
||||
|
||||
Executable
+223
@@ -0,0 +1,223 @@
|
||||
#! /bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# startup script for Ubuntu and Debian Linux servers
|
||||
#
|
||||
# To use this file
|
||||
# cp ubuntu.sh /etc/init.d/web2py
|
||||
#
|
||||
# To automatitcally start at reboot
|
||||
# sudo update-rc.d web2py defaults
|
||||
#
|
||||
# Provides: web2py
|
||||
# Required-Start: $local_fs $remote_fs
|
||||
# Required-Stop: $local_fs $remote_fs
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: S 0 1 6
|
||||
# Short-Description: web2py initscript
|
||||
# Description: This file starts up the web2py server.
|
||||
### END INIT INFO
|
||||
|
||||
# Author: Mark Moore <mark.moore@fonjax.com>
|
||||
|
||||
PATH=/usr/sbin:/usr/bin:/sbin:/bin
|
||||
DESC="Web Framework"
|
||||
NAME=web2py
|
||||
PIDDIR=/var/run/$NAME
|
||||
PIDFILE=$PIDDIR/$NAME.pid
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
DAEMON=/usr/bin/python
|
||||
DAEMON_DIR=/usr/lib/$NAME
|
||||
DAEMON_ARGS="web2py.py --password=<recycle> --pid_filename=$PIDFILE"
|
||||
DAEMON_USER=web2py
|
||||
|
||||
# Exit if the package is not installed
|
||||
[ -x "$DAEMON" ] || exit 0
|
||||
|
||||
# Read configuration variable file if it is present
|
||||
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
|
||||
|
||||
# Load the VERBOSE setting and other rcS variables
|
||||
[ -f /etc/default/rcS ] && . /etc/default/rcS
|
||||
|
||||
# Define LSB log_* functions.
|
||||
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
#
|
||||
# Function that starts the daemon/service
|
||||
#
|
||||
do_start()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been started
|
||||
# 1 if daemon was already running
|
||||
# 2 if daemon could not be started
|
||||
|
||||
# The PIDDIR should normally be created during installation. This
|
||||
# fixes things just in case.
|
||||
[ -d $PIDDIR ] || mkdir -p $PIDDIR
|
||||
[ -n "$DAEMON_USER" ] && chown --recursive $DAEMON_USER $PIDDIR
|
||||
|
||||
# Check to see if the daemon is already running.
|
||||
start-stop-daemon --stop --test --quiet --pidfile $PIDFILE \
|
||||
&& return 1
|
||||
|
||||
start-stop-daemon --start --quiet --pidfile $PIDFILE \
|
||||
${DAEMON_USER:+--chuid $DAEMON_USER} --chdir $DAEMON_DIR \
|
||||
--background --exec $DAEMON -- $DAEMON_ARGS \
|
||||
|| return 2
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service
|
||||
#
|
||||
do_stop()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been stopped
|
||||
# 1 if daemon was already stopped
|
||||
# 2 if daemon could not be stopped
|
||||
# other if a failure occurred
|
||||
|
||||
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
|
||||
RETVAL=$?
|
||||
# Many daemons don't delete their pidfiles when they exit.
|
||||
rm -f $PIDFILE
|
||||
return "$RETVAL"
|
||||
}
|
||||
|
||||
#
|
||||
# Function that restarts the daemon/service
|
||||
#
|
||||
do_restart()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon was (re-)started
|
||||
# 1 if daemon was not strated or re-started
|
||||
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1)
|
||||
do_start
|
||||
case "$?" in
|
||||
0) RETVAL=0 ;;
|
||||
1) RETVAL=1 ;; # Old process is still running
|
||||
*) RETVAL=1 ;; # Failed to start
|
||||
esac
|
||||
;;
|
||||
*) RETVAL=1 ;; # Failed to stop
|
||||
esac
|
||||
|
||||
return "$RETVAL"
|
||||
}
|
||||
|
||||
#
|
||||
# Function that sends a SIGHUP to the daemon/service
|
||||
#
|
||||
do_reload() {
|
||||
#
|
||||
# If the daemon can reload its configuration without
|
||||
# restarting (for example, when it is sent a SIGHUP),
|
||||
# then implement that here.
|
||||
#
|
||||
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE
|
||||
return 0
|
||||
}
|
||||
|
||||
#
|
||||
# Function that queries the status of the daemon/service
|
||||
#
|
||||
do_status()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon is responding and OK
|
||||
# 1 if daemon is not responding, but PIDFILE exists
|
||||
# 2 if daemon is not responding, but LOCKFILE exists
|
||||
# 3 if deamon is not running
|
||||
# 4 if daemon status is unknown
|
||||
|
||||
# Check to see if the daemon is already running.
|
||||
start-stop-daemon --stop --test --quiet --pidfile $PIDFILE \
|
||||
&& return 0
|
||||
[ -f $PIDFILE ] && return 1
|
||||
return 3
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
||||
do_start
|
||||
RETVAL=$?
|
||||
[ "$VERBOSE" != no ] &&
|
||||
case "$RETVAL" in
|
||||
0|1) log_end_msg 0 ;;
|
||||
*) log_end_msg 1 ;;
|
||||
esac
|
||||
exit "$RETVAL"
|
||||
;;
|
||||
stop)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
do_stop
|
||||
RETVAL=$?
|
||||
[ "$VERBOSE" != no ] &&
|
||||
case "$RETVAL" in
|
||||
0|1) log_end_msg 0 ;;
|
||||
*) log_end_msg 1 ;;
|
||||
esac
|
||||
exit "$RETVAL"
|
||||
;;
|
||||
#reload|force-reload)
|
||||
#
|
||||
# If do_reload() is not implemented then leave this commented out
|
||||
# and leave 'force-reload' as an alias for 'restart'.
|
||||
#
|
||||
#[ "$VERBOSE" != no ] && log_daemon_msg "Reloading $DESC" "$NAME"
|
||||
#do_reload
|
||||
#RETVAL=$?
|
||||
#[ "$VERBOSE" != no ] && log_end_msg $?
|
||||
#exit "$RETVAL"
|
||||
#;;
|
||||
restart|force-reload)
|
||||
#
|
||||
# If the "reload" option is implemented then remove the
|
||||
# 'force-reload' alias
|
||||
#
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
do_restart
|
||||
RETVAL=$?
|
||||
[ "$VERBOSE" != no ] && log_end_msg "$RETVAL"
|
||||
exit "$RETVAL"
|
||||
;;
|
||||
status)
|
||||
do_status
|
||||
RETVAL=$?
|
||||
[ "$VERBOSE" != no ] &&
|
||||
case "$RETVAL" in
|
||||
0) log_success_msg "$NAME is running" ;;
|
||||
*) log_failure_msg "$NAME is not running" ;;
|
||||
esac
|
||||
exit "$RETVAL"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload|status}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
:
|
||||
|
||||
# This was based off /etc/init.d/skeleton from the Ubuntu 8.04 Hardy release.
|
||||
# (md5sum: da0162012b6a916bdbd4e2580282af78). If we notice that changes, we
|
||||
# should re-examine things.
|
||||
|
||||
# The configuration at the very top seems to be documented as part of the
|
||||
# Linux Standard Base (LSB) Specification. See section 20.6 Facility Names
|
||||
# in particular. This is also where I got the spec for the status parm.
|
||||
|
||||
# References:
|
||||
# http://refspecs.linux-foundation.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic.pdf
|
||||
# Debian Policy SysV init: http://www.debian.org/doc/debian-policy/ch-opersys.html#s-sysvinit
|
||||
# Examine files in /usr/share/doc/sysv-rc/
|
||||
|
||||
Reference in New Issue
Block a user