blob: 62f0804b43cf82939dbb4fc43ff634a0e024fcde [file] [log] [blame]
Sapan Bhatia2ac88642016-01-15 10:43:19 -05001#!/usr/bin/env python
2import web
3import ConfigParser
4import io
5import json
6from ceilometerclient import client
7import logging
8import urllib
9import urllib2
10from wsgilog import WsgiLog
11
12web.config.debug=False
13
14logfile = "ceilometer_proxy_server.log"
15level=logging.INFO
16logger=logging.getLogger('ceilometer_proxy_server')
17logger.setLevel(level)
18handler=logging.handlers.RotatingFileHandler(logfile,maxBytes=1000000, backupCount=1)
19logger.addHandler(handler)
20
21class FileLog(WsgiLog):
22 def __init__(self, application):
23 WsgiLog.__init__(
24 self,
25 application,
26 logformat = '%(message)s',
27 tofile = True,
28 toprint = True,
29 prnlevel = level,
30 file = logfile,
31 backups =1
32 )
33 def __call__(self, environ, start_response):
34 def hstart_response(status, response_headers, *args):
35 out = start_response(status, response_headers, *args)
36 try:
37 logline=environ["SERVER_PROTOCOL"]+" "+environ["REQUEST_METHOD"]+" "+environ["REQUEST_URI"]+" - "+status
38 except err:
39 logline="Could not log <%s> due to err <%s>" % (str(environ), err)
40 logger.info(logline)
41
42 return out
43
44 return super(FileLog, self).__call__(environ, hstart_response)
45
46#TODOs:
47#-See if we can avoid using python-ceilometerclient and instead use the REST calls directly with AuthToken
48#
49urls = (
50 r'^/v2/meters$', 'meter_list',
51 r'^/v2/meters/(?P<meter_name>[A-Za-z0-9_:.\-]+)/statistics$', 'statistics_list',
52 r'^/v2/samples$', 'sample_list',
53 r'^/v2/resources$', 'resource_list',
54 r'^/v2/subscribe$', 'pubsub_handler',
55)
56
57app = web.application(urls, globals())
58
59config = None
60ceilometer_client = None
61
62
63def parse_ceilometer_proxy_config():
64 global config
65 config = ConfigParser.RawConfigParser(allow_no_value=True)
66 config.read('ceilometer_proxy_config')
67
68def ceilometerclient():
69 global config, ceilometer_client
70 if ceilometer_client:
71 return ceilometer_client
72
73 if not config:
74 parse_ceilometer_proxy_config()
75
76 keystone = {}
77 keystone['os_username']=config.get('default','admin_user')
78 keystone['os_password']=config.get('default','admin_password')
79 keystone['os_auth_url']=config.get('default','auth_url')
80 keystone['os_tenant_name']=config.get('default','admin_tenant')
81 ceilometer_client = client.get_client(2,**keystone)
82 logger.info('ceilometer get_client is successful')
83 return ceilometer_client
84
85def make_query(user_id=None, tenant_id=None, resource_id=None,
86 user_ids=None, tenant_ids=None, resource_ids=None):
87 """Returns query built from given parameters.
88
89 This query can be then used for querying resources, meters and
90 statistics.
91
92 :Parameters:
93 - `user_id`: user_id, has a priority over list of ids
94 - `tenant_id`: tenant_id, has a priority over list of ids
95 - `resource_id`: resource_id, has a priority over list of ids
96 - `user_ids`: list of user_ids
97 - `tenant_ids`: list of tenant_ids
98 - `resource_ids`: list of resource_ids
99 """
100 user_ids = user_ids or []
101 tenant_ids = tenant_ids or []
102 resource_ids = resource_ids or []
103
104 query = []
105 if user_id:
106 user_ids = [user_id]
107 for u_id in user_ids:
108 query.append({"field": "user_id", "op": "eq", "value": u_id})
109
110 if tenant_id:
111 tenant_ids = [tenant_id]
112 for t_id in tenant_ids:
113 query.append({"field": "project_id", "op": "eq", "value": t_id})
114
115 if resource_id:
116 resource_ids = [resource_id]
117 for r_id in resource_ids:
118 query.append({"field": "resource_id", "op": "eq", "value": r_id})
119
120 return query
121
122def filter_query_params(query_params):
123 new_query=[]
124 i=0
125 user_specified_tenants=[]
126 for field in query_params['q.field']:
127 if (field != 'project_id') and (field != 'project'):
128 query = {}
129 query['field']=field
130 if query_params['q.op'][i] != '':
131 query['op']=query_params['q.op'][i]
132 query['value']=query_params['q.value'][i]
133 new_query.append(query)
134 else:
135 user_specified_tenants.append(query_params['q.value'][i])
136 i=i+1
137 return new_query,user_specified_tenants
138
139class meter_list:
140 def GET(self):
141 global config
142 keyword_args = {
143 "q.field": [],
144 "q.op": [],
145 "q.type": [],
146 "q.value": [],
147 }
148 query_params = web.input(**keyword_args)
149 new_query, user_specified_tenants = filter_query_params(query_params)
150
151 client = ceilometerclient()
152 meters=[]
153 for (k,v) in config.items('allowed_tenants'):
154 if user_specified_tenants and (k not in user_specified_tenants):
155 continue
156 final_query=[]
157 final_query.extend(new_query)
158 query = make_query(tenant_id=k)
159 final_query.extend(query)
160 logger.debug('final query=%s',final_query)
161 results = client.meters.list(q=final_query)
162 meters.extend(results)
163 return json.dumps([ob._info for ob in meters])
164
165class statistics_list:
166 def GET(self, meter_name):
167 global config
168 keyword_args = {
169 "q.field": [],
170 "q.op": [],
171 "q.type": [],
172 "q.value": [],
173 "period": None
174 }
175 query_params = web.input(**keyword_args)
176 new_query, user_specified_tenants = filter_query_params(query_params)
177
178 client = ceilometerclient()
179 period = query_params.period
180 statistics = []
181 for (k,v) in config.items('allowed_tenants'):
182 if user_specified_tenants and (k not in user_specified_tenants):
183 continue
184 final_query=[]
185 final_query.extend(new_query)
186 query = make_query(tenant_id=k)
187 final_query.extend(query)
188 logger.debug('final query=%s',final_query)
189 results = client.statistics.list(meter_name=meter_name, q=final_query, period=period)
190 statistics.extend(results)
191 return json.dumps([ob._info for ob in statistics])
192
193class sample_list:
194 def GET(self):
195 global config
196 keyword_args = {
197 "q.field": [],
198 "q.op": [],
199 "q.type": [],
200 "q.value": [],
201 "limit": None,
202 }
203 query_params = web.input(**keyword_args)
204 new_query, user_specified_tenants = filter_query_params(query_params)
205
206 client = ceilometerclient()
207 limit=query_params.limit
208 samples=[]
209 for (k,v) in config.items('allowed_tenants'):
210 if user_specified_tenants and (k not in user_specified_tenants):
211 continue
212 final_query=[]
213 final_query.extend(new_query)
214 query = make_query(tenant_id=k)
215 final_query.extend(query)
216 logger.debug('final query=%s',final_query)
217 results = client.new_samples.list(q=final_query,limit=limit)
218 samples.extend(results)
219 return json.dumps([ob._info for ob in samples])
220
221class resource_list:
222 def GET(self):
223 global config
224 keyword_args = {
225 "q.field": [],
226 "q.op": [],
227 "q.type": [],
228 "q.value": [],
229 "limit": None,
230 "links": None,
231 }
232 query_params = web.input(**keyword_args)
233 new_query, user_specified_tenants = filter_query_params(query_params)
234
235 client = ceilometerclient()
236 limit=query_params.limit
237 links=query_params.links
238 resources=[]
239 for (k,v) in config.items('allowed_tenants'):
240 if user_specified_tenants and (k not in user_specified_tenants):
241 continue
242 final_query=[]
243 final_query.extend(new_query)
244 query = make_query(tenant_id=k)
245 final_query.extend(query)
246 logger.debug('final query=%s',final_query)
247 results = client.resources.list(q=final_query, limit=limit, links=links)
248 resources.extend(results)
249 return json.dumps([ob._info for ob in resources])
250
251class pubsub_handler:
252 def POST(self):
253 global config
254 parse_ceilometer_proxy_config()
255 data_str = unicode(web.data(),'iso-8859-1')
256 post_data = json.loads(data_str)
257 final_query=[]
258 for (k,v) in config.items('allowed_tenants'):
259 query = make_query(tenant_id=k)
260 final_query.extend(query)
261 if not final_query:
262 raise Exception("Not allowed to subscribe to any meters")
263 post_data["query"] = final_query
264 #TODO: The PUB/SUB url needs to be read from config
265 put_request = urllib2.Request("http://10.11.10.1:4455/subscribe", json.dumps(post_data))
266 put_request.get_method = lambda: 'SUB'
267 put_request.add_header('Content-Type', 'application/json')
268 response = urllib2.urlopen(put_request)
269 response_text = response.read()
270 return json.dumps(response_text)
271
272 def DELETE(self):
273 data_str = web.data()
274 #TODO: The PUB/SUB url needs to be read from config
275 put_request = urllib2.Request("http://10.11.10.1:4455/unsubscribe", data_str)
276 put_request.get_method = lambda: 'UNSUB'
277 put_request.add_header('Content-Type', 'application/json')
278 response = urllib2.urlopen(put_request)
279 response_text = response.read()
280 return json.dumps(response_text)
281
282if __name__ == "__main__":
283 app.run(FileLog)