b554c856cc9bdb45629493bc45220f24afbb988a
David Blume first commit

David Blume authored 8 years ago

1) #!/usr/bin/env python
2) # index.py for a rudimentary key/value server by David Blume
3) import os
4) import sys
5) import time
6) import datetime
7) import cgi, cgitb
8) import filelock
9) import tempfile
David Blume Improve the documentation a...

David Blume authored 8 years ago

10) import yaml  # You may need to "pip install pyyaml"
David Blume first commit

David Blume authored 8 years ago

11) import texttime
12) 
13) cgitb.enable(display=0, logdir="/tmp")
14) store_name = 'store.txt'
15) 
16) 
17) def read_file(full_pathname):
David Blume Improve the documentation a...

David Blume authored 8 years ago

18)     """ The file must be a YAML file containing a dict. """
David Blume first commit

David Blume authored 8 years ago

19)     store = dict()
20)     if os.path.isfile(full_pathname):
21)         try:
22)             with filelock.FileLock(full_pathname):
23)                 with open(full_pathname, 'r') as f:
24)                     store = yaml.load(f)
25)         except filelock.FileLockException as e:
26)             raise
27)     return store
28) 
29) 
30) def write_file(full_pathname, data):
David Blume Improve the documentation a...

David Blume authored 8 years ago

31)     """ See http://david.dlma.com/blog/dads-project-in-the-garage """
David Blume first commit

David Blume authored 8 years ago

32)     try:
33)         with filelock.FileLock(full_pathname):
34)             with tempfile.NamedTemporaryFile(mode='w',
35)                     dir=os.path.dirname(full_pathname),
36)                     delete=False) as f:
37)                 yaml.dump(data, f)
38)                 # exiting this "with" flushes, but does not sync
39)                 f.flush()
40)                 os.fdatasync(f.fileno())  # Faster, Unix only
41)                 tempname = f.name
42)             os.rename(tempname, full_pathname)  # Atomic on Unix, not Windows
43)     except filelock.FileLockException as e:
44)         raise
45) 
46) 
47) def map_ip_to_location(ip):
48)     if ip == '67.188.28.83':
49)         return 'home'.ljust(13)
50)     elif ip in ('50.224.7.233', '50.224.7.248'):
51)         return 'work'.ljust(13)
52)     return ip
53) 
54) 
55) if __name__ == '__main__':
56)     localdir = os.path.abspath(os.path.dirname(sys.argv[0]))
57)     args = cgi.FieldStorage()
58)     key = None
59)     auth = None
60)     get_key = False
61)     auth_in_get = False
62)     doing_post = False
63) 
64)     # Find the parameters that are GET parameters
65)     # Could also check os.environ['REQUEST_METHOD'] == 'POST',
66)     # but that doesn't say which way individual params were sent.
67)     if os.environ['QUERY_STRING']:
68)         qs = os.environ['QUERY_STRING'].split('&')
69)         for pair in qs:
70)             k, v = pair.split('=')
71)             if k == 'k':
72)                 get_key = True
73)             if k == 'auth':
74)                 auth_in_get = True
75) 
76)     # Could be either GET or POST. Need to check QUERY_STRING above.
77)     if "k" in args:
78)         key = args["k"].value
79) 
80)     if os.environ['REQUEST_METHOD'] == 'POST':
81)         doing_post = True
82)         if not auth_in_get and "auth" in args:
83)             auth = args["auth"].value
84) 
85)     store = read_file(os.path.join(localdir, store_name))
86)     if get_key:
87)         keys = key.split(',')
David Blume Use list comprehension.

David Blume authored 8 years ago

88)         matches = sorted([(store[x]['time'], store[x]['value']) for x in keys if x in store], reverse=True)
89)         if matches:
David Blume first commit

David Blume authored 8 years ago

90)             print "Content-type: text/plain; charset=utf-8\n"
David Blume Use list comprehension.

David Blume authored 8 years ago

91)             sys.stdout.write(matches[0][1])
David Blume first commit

David Blume authored 8 years ago

92)         else:
93)             print 'Status: 404 Not Found\n'
94)     else:
95)         wrote_response = False
96)         with open('auth.txt', 'r') as f:
97)             authorization = f.read().strip()
98)         if doing_post and auth == authorization:
99)             for new_key in args:
David Blume Use list comprehension.

David Blume authored 8 years ago

100)                 if new_key not in ('auth', ) and \
David Blume first commit

David Blume authored 8 years ago

101)                     len(new_key) < 80 and len(args[new_key].value) < 140:
102)                     if new_key in store and 'created' in store[new_key]:
103)                         created_time = store[new_key]['created']
104)                     else:
105)                         created_time = int(time.time())
106)                     store[new_key] = {'value': args[new_key].value,
107)                                       'time': int(time.time()),
108)                                       'created': created_time,
109)                                       'origin': os.environ['REMOTE_ADDR']}
110)                     write_file(os.path.join(localdir, store_name), store)
111)                     wrote_response = True
112)                     print "Content-type: text/plain; charset=utf-8\n"
113)                     print "OK"
114)                     break
115)         elif not os.environ['QUERY_STRING'] and not doing_post:
116)             # just the home page, then
117)             print "Content-type: text/plain; charset=UTF-8\n"
118)             wrote_response = True
119)             print "key          \tvalue         \tcreated \tupdated \torigin      \tNotes"
120)             print "------------ \t------------- \t----------\t----------\t-------------\t---------------------"
121)             devices = { '1G': '(Au)',
122)                         '5S': '(Br)',
123)                         'YW': '(FW)',
124)                         'YY': '(Da)',
125)                         '2N': '(Li)',
126)                         '1R': '(Ty)',
127)                         'YU': '(Lf)',
128)                         'YP': '(C4)',
129)                       }
130)             for key in sorted(store, key=lambda k: store[k]['time'], reverse=True):
131)                 if key[:2] in devices:
132)                     device = devices[key[:2]] + ' '
133)                 else:
134)                     device = ''
135)                 d = store[key]
136)                 td = texttime.stringify(datetime.timedelta(seconds=time.time()-d['time']))
137)                 if 'created' in d:
138)                     created = time.strftime('%Y-%m-%d', time.localtime(d['created']))
139)                 else:
140)                     created = "          "
141)                 if 'origin' in d:
142)                     origin = map_ip_to_location(d['origin'])
143)                 else:
144)                     origin = "               "
145)                 print '\t'.join((key.ljust(12), d['value'].ljust(13), created, str(d['time']), origin, "%supdated %s ago" % (device, td)))
146)             print
147)             print 'Tip:'
148)             print 'sync_ip () { export ENV_VAR=$(curl -s "%s?k=2FN50L002036,2FN44N010911"); }' % os.environ['SCRIPT_URI']
149)             print 'curl --data "`hostname -s`=`hostname -i`&auth=$AUTH" "%s"' % os.environ['SCRIPT_URI']
David Blume Use list comprehension.

David Blume authored 8 years ago

150)             print 'Source code at http://git.dlma.com/kvs.git/'