-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathserver.py
168 lines (133 loc) · 5.66 KB
/
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import argparse
import logging
import os
import sys
from urlparse import urlparse
from ConfigParser import ConfigParser
from wsgiref.simple_server import make_server
from werkzeug.wrappers import AuthorizationMixin, BaseRequest, Response
from local import local, local_manager
import pyslet.odata2.metadata as edmx
from pyslet.odata2.server import ReadOnlyServer
from influxdbmeta import generate_metadata
from influxdbds import InfluxDBEntityContainer
cache_app = None #: our Server instance
#logging.basicConfig()
logHandler = logging.StreamHandler(sys.stdout)
logFormatter = logging.Formatter(fmt='%(levelname)s:%(name)s:%(message)s')
#logHandler.formatter = logFormatter
logger = logging.getLogger("odata-influxdb")
logger.addHandler(logHandler)
logger.setLevel(logging.DEBUG)
class Request(BaseRequest, AuthorizationMixin):
pass
class HTTPAuthPassThrough(object):
def __init__(self, app):
self.wrapped = app
self.realm = 'influxdb'
def __call__(self, environ, start_response):
local.request = req = Request(environ)
if req.authorization is None:
resp = Response('Unauthorized. Please supply authorization.',
status=401,
headers={
('WWW-Authenticate', 'Basic Realm="{}"'.format(self.realm)),
}
)
return resp(environ, start_response)
return self.wrapped(environ, start_response)
class FileExistsError(IOError):
def __init__(self, path):
self.__path = path
def __str__(self):
return 'file already exists: {}'.format(self.__path)
def load_metadata(config):
"""Regenerate and load the metadata file and connects the InfluxDBEntityContainer."""
metadata_filename = config.get('metadata', 'metadata_file')
dsn = config.get('influxdb', 'dsn')
if config.getboolean('metadata', 'autogenerate'):
logger.info("Generating OData metadata xml file from InfluxDB metadata")
metadata = generate_metadata(dsn)
with open(metadata_filename, 'wb') as f:
f.write(metadata)
doc = edmx.Document()
with open(metadata_filename, 'rb') as f:
doc.ReadFromStream(f)
container = doc.root.DataServices['InfluxDBSchema.InfluxDB']
try:
topmax = config.getint('influxdb', 'max_items_per_query')
except:
topmax = 50
InfluxDBEntityContainer(container=container, dsn=dsn, topmax=topmax)
return doc
def configure_app(c, doc):
service_root = c.get('server', 'service_advertise_root')
logger.info("Advertising service at %s" % service_root)
app = ReadOnlyServer(serviceRoot=service_root)
app.SetModel(doc)
return app
def start_server(c, doc):
app = configure_app(c, doc)
if c.getboolean('influxdb', 'authentication_required'):
app = HTTPAuthPassThrough(app)
app = local_manager.make_middleware(app)
from werkzeug.serving import run_simple
listen_interface = c.get('server', 'server_listen_interface')
listen_port = int(c.get('server', 'server_listen_port'))
logger.info("Starting HTTP server on: interface: %s, port: %i..." % (listen_interface, listen_port))
run_simple(listen_interface, listen_port, application=app)
def get_sample_config():
config = ConfigParser(allow_no_value=True)
config.add_section('server')
config.set('server', 'service_advertise_root', 'http://localhost:8080')
config.set('server', 'server_listen_interface', '127.0.0.1')
config.set('server', 'server_listen_port', '8080')
config.add_section('metadata')
config.set('metadata', '; set autogenerate to "no" for quicker startup of the server if you know your influxdb structure has not changed')
config.set('metadata', 'autogenerate', 'yes')
config.set('metadata', '; metadata_file specifies the location of the metadata file to generate')
config.set('metadata', 'metadata_file', 'test_metadata.xml')
config.add_section('influxdb')
config.set('influxdb', '; supported schemes include https+influxdb:// and udp+influxdb://')
config.set('influxdb', '; user:pass in this dsn is used for generating metadata')
config.set('influxdb', 'dsn', 'influxdb://user:pass@localhost:8086')
config.set('influxdb', 'max_items_per_query', '50')
config.set('influxdb', '; authentication_required will pass through http basic auth username')
config.set('influxdb', '; and password to influxdb')
config.set('influxdb', 'authentication_required', 'no')
return config
def make_sample_config():
config = get_sample_config()
sample_name = 'sample.conf'
if os.path.exists(sample_name):
raise FileExistsError(sample_name)
with open(sample_name, 'w') as cf:
config.write(cf)
print('generated sample conf at: {}'.format(os.path.join(os.getcwd(), sample_name)))
def get_config(config):
with open(config, 'r') as fp:
c = get_sample_config()
c.readfp(fp)
return c
def main():
"""read config and start odata api server"""
# parse arguments
p = argparse.ArgumentParser()
p.add_argument('-c', '--config',
help='specify a conf file (default=production.conf)',
default='production.conf')
p.add_argument('-m', '--makeSampleConfig',
help='generates sample.conf in your current directory (does not start server)',
action="store_true")
args = p.parse_args()
if args.makeSampleConfig:
make_sample_config()
sys.exit()
# parse config file
c = get_config(args.config)
# generate and load metadata
doc = load_metadata(c)
# start server
start_server(c, doc)
if __name__ == '__main__':
main()