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