Merge lp:~soren/surveilr/make-it-slightly-useful into lp:surveilr
- make-it-slightly-useful
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Soren Hansen |
Approved revision: | 4 |
Merged at revision: | 4 |
Proposed branch: | lp:~soren/surveilr/make-it-slightly-useful |
Merge into: | lp:surveilr |
Diff against target: |
367 lines (+334/-0) 6 files modified
setup.cfg (+2/-0) surveilr/__init__.py (+19/-0) surveilr/api/server.py (+134/-0) surveilr/models.py (+54/-0) surveilr/tests/test_api_server.py (+90/-0) surveilr/utils.py (+35/-0) |
To merge this branch: | bzr merge lp:~soren/surveilr/make-it-slightly-useful |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Soren Hansen | Pending | ||
Review via email:
|
Commit message
Add basic data model and API server
Description of the change
Add basic data model and API server
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Linux2Go Jenkins (linux2go-jenkins) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Soren Hansen (soren) wrote : | # |
Installed python-
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Linux2Go Jenkins (linux2go-jenkins) wrote : | # |
The attempt to merge lp:~soren/surveilr/make-it-slightly-useful into lp:surveilr failed. Below is the output from the failed tests.
running nosetests
running egg_info
creating surveilr.egg-info
writing requirements to surveilr.
writing surveilr.
writing top-level names to surveilr.
writing dependency_links to surveilr.
writing manifest file 'surveilr.
reading manifest file 'surveilr.
writing manifest file 'surveilr.
running build_ext
ok
E.E
=======
ERROR: Failure: ImportError (No module named routes)
-------
Traceback (most recent call last):
File "/usr/lib/
addr.filename, addr.module)
File "/usr/lib/
return self.importFrom
File "/usr/lib/
mod = load_module(
File "/tmp/tmpXx8lxx
from routes import Mapper
ImportError: No module named routes
=======
ERROR: Failure: ImportError (No module named webob)
-------
Traceback (most recent call last):
File "/usr/lib/
addr.filename, addr.module)
File "/usr/lib/
return self.importFrom
File "/usr/lib/
mod = load_module(
File "/tmp/tmpXx8lxx
from webob import Request
ImportError: No module named webob
-------
Ran 3 tests in 0.811s
FAILED (errors=2)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Soren Hansen (soren) wrote : | # |
...and python-
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Linux2Go Jenkins (linux2go-jenkins) wrote : | # |
The attempt to merge lp:~soren/surveilr/make-it-slightly-useful into lp:surveilr failed. Below is the output from the failed tests.
running nosetests
running egg_info
creating surveilr.egg-info
writing requirements to surveilr.
writing surveilr.
writing top-level names to surveilr.
writing dependency_links to surveilr.
writing manifest file 'surveilr.
reading manifest file 'surveilr.
writing manifest file 'surveilr.
running build_ext
ok
.EE..
=======
ERROR: test_create_
-------
Traceback (most recent call last):
File "/tmp/tmpt9Dlnm
resp = application(req)
File "/usr/lib/
return self.func(req, *args, **kw)
File "/tmp/tmpt9Dlnm
return getattr(controller, method)(req, **kwargs)
File "/tmp/tmpt9Dlnm
service.save()
File "/var/lib/
self._riak_obj = bucket.
File "/usr/lib/
raise TypeError('Unicode data values are not supported.')
TypeError: Unicode data values are not supported.
=======
ERROR: Create, retrieve, delete, attempt to retrieve again
-------
Traceback (most recent call last):
File "/tmp/tmpt9Dlnm
resp = application(req)
File "/usr/lib/
return self.func(req, *args, **kw)
File "/tmp/tmpt9Dlnm
return getattr(controller, method)(req, **kwargs)
File "/tmp/tmpt9Dlnm
service.save()
File "/var/lib/
self._riak_obj = bucket.
File "/usr/lib/
raise TypeError('Unicode data values are not supported.')
TypeError: Unicode data values are not supported.
-------
Ran 5 tests in 1.128s
FAILED (errors=2)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Linux2Go Jenkins (linux2go-jenkins) wrote : | # |
The attempt to merge lp:~soren/surveilr/make-it-slightly-useful into lp:surveilr failed. Below is the output from the failed tests.
running nosetests
running egg_info
creating surveilr.egg-info
writing requirements to surveilr.
writing surveilr.
writing top-level names to surveilr.
writing dependency_links to surveilr.
writing manifest file 'surveilr.
reading manifest file 'surveilr.
writing manifest file 'surveilr.
running build_ext
ok
.EE..
=======
ERROR: test_create_
-------
Traceback (most recent call last):
File "/tmp/tmpfUlS7f
resp = application(req)
File "/usr/lib/
return self.func(req, *args, **kw)
File "/tmp/tmpfUlS7f
return getattr(controller, method)(req, **kwargs)
File "/tmp/tmpfUlS7f
service.save()
File "/var/lib/
self._riak_obj = bucket.
File "/usr/lib/
raise TypeError('Unicode data values are not supported.')
TypeError: Unicode data values are not supported.
=======
ERROR: Create, retrieve, delete, attempt to retrieve again
-------
Traceback (most recent call last):
File "/tmp/tmpfUlS7f
resp = application(req)
File "/usr/lib/
return self.func(req, *args, **kw)
File "/tmp/tmpfUlS7f
return getattr(controller, method)(req, **kwargs)
File "/tmp/tmpfUlS7f
service.save()
File "/var/lib/
self._riak_obj = bucket.
File "/usr/lib/
raise TypeError('Unicode data values are not supported.')
TypeError: Unicode data values are not supported.
-------
Ran 5 tests in 0.382s
FAILED (errors=2)
Preview Diff
1 | === added file 'setup.cfg' | |||
2 | --- setup.cfg 1970-01-01 00:00:00 +0000 | |||
3 | +++ setup.cfg 2011-11-19 22:08:22 +0000 | |||
4 | @@ -0,0 +1,2 @@ | |||
5 | 1 | [nosetests] | ||
6 | 2 | with-doctest=1 | ||
7 | 0 | 3 | ||
8 | === added directory 'surveilr' | |||
9 | === added file 'surveilr/__init__.py' | |||
10 | --- surveilr/__init__.py 1970-01-01 00:00:00 +0000 | |||
11 | +++ surveilr/__init__.py 2011-11-19 22:08:22 +0000 | |||
12 | @@ -0,0 +1,19 @@ | |||
13 | 1 | """ | ||
14 | 2 | Surveilr - Log aggregation, analysis and visualisation | ||
15 | 3 | |||
16 | 4 | Copyright (C) 2011 Linux2Go | ||
17 | 5 | |||
18 | 6 | This program is free software: you can redistribute it and/or | ||
19 | 7 | modify it under the terms of the GNU Affero General Public License | ||
20 | 8 | as published by the Free Software Foundation, either version 3 of | ||
21 | 9 | the License, or (at your option) any later version. | ||
22 | 10 | |||
23 | 11 | This program is distributed in the hope that it will be useful, | ||
24 | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
25 | 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
26 | 14 | GNU Affero General Public License for more details. | ||
27 | 15 | |||
28 | 16 | You should have received a copy of the GNU Affero General Public | ||
29 | 17 | License along with this program. If not, see | ||
30 | 18 | <http://www.gnu.org/licenses/>. | ||
31 | 19 | """ | ||
32 | 0 | 20 | ||
33 | === added directory 'surveilr/api' | |||
34 | === added file 'surveilr/api/__init__.py' | |||
35 | === added file 'surveilr/api/server.py' | |||
36 | --- surveilr/api/server.py 1970-01-01 00:00:00 +0000 | |||
37 | +++ surveilr/api/server.py 2011-11-19 22:08:22 +0000 | |||
38 | @@ -0,0 +1,134 @@ | |||
39 | 1 | #!/usr/bin/python | ||
40 | 2 | """ | ||
41 | 3 | Surveilr - Log aggregation, analysis and visualisation | ||
42 | 4 | |||
43 | 5 | Copyright (C) 2011 Linux2Go | ||
44 | 6 | |||
45 | 7 | This program is free software: you can redistribute it and/or | ||
46 | 8 | modify it under the terms of the GNU Affero General Public License | ||
47 | 9 | as published by the Free Software Foundation, either version 3 of | ||
48 | 10 | the License, or (at your option) any later version. | ||
49 | 11 | |||
50 | 12 | This program is distributed in the hope that it will be useful, | ||
51 | 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
52 | 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
53 | 15 | GNU Affero General Public License for more details. | ||
54 | 16 | |||
55 | 17 | You should have received a copy of the GNU Affero General Public | ||
56 | 18 | License along with this program. If not, see | ||
57 | 19 | <http://www.gnu.org/licenses/>. | ||
58 | 20 | |||
59 | 21 | Log collection server implementation | ||
60 | 22 | """ | ||
61 | 23 | |||
62 | 24 | import eventlet | ||
63 | 25 | import eventlet.wsgi | ||
64 | 26 | import json | ||
65 | 27 | import time | ||
66 | 28 | |||
67 | 29 | import riakalchemy | ||
68 | 30 | from routes import Mapper | ||
69 | 31 | from routes.util import URLGenerator | ||
70 | 32 | |||
71 | 33 | from webob import exc | ||
72 | 34 | from webob import Response | ||
73 | 35 | from webob.dec import wsgify | ||
74 | 36 | from webob.exc import HTTPNotFound | ||
75 | 37 | |||
76 | 38 | from surveilr import models | ||
77 | 39 | from surveilr import utils | ||
78 | 40 | |||
79 | 41 | class ServiceController(object): | ||
80 | 42 | """Routes style controller for actions related to services""" | ||
81 | 43 | |||
82 | 44 | def create(self, req): | ||
83 | 45 | """Called for POST requests to /services | ||
84 | 46 | |||
85 | 47 | Creates the service, returns a JSON object with the ID assigned | ||
86 | 48 | to the service""" | ||
87 | 49 | data = json.loads(req.body) | ||
88 | 50 | service = models.Service(**data) | ||
89 | 51 | service.save() | ||
90 | 52 | response = {'id':service.key} | ||
91 | 53 | return Response(json.dumps(response)) | ||
92 | 54 | |||
93 | 55 | def show(self, req, id): | ||
94 | 56 | """Called for GET requests to /services/{id} | ||
95 | 57 | |||
96 | 58 | Returns information for the given service""" | ||
97 | 59 | try: | ||
98 | 60 | service = models.Service.get(id) | ||
99 | 61 | return Response({'id': service.key}) | ||
100 | 62 | except riakalchemy.NoSuchObjectError: | ||
101 | 63 | return HTTPNotFound() | ||
102 | 64 | |||
103 | 65 | def delete(self, req, id): | ||
104 | 66 | """Called for DELETE requests to /services/{id} | ||
105 | 67 | |||
106 | 68 | Delete the given service""" | ||
107 | 69 | models.Service.get(id).delete() | ||
108 | 70 | return Response('') | ||
109 | 71 | |||
110 | 72 | |||
111 | 73 | class MetricController(object): | ||
112 | 74 | """Routes style controller for actions related to log entries""" | ||
113 | 75 | def create(self, req, service_name): | ||
114 | 76 | """Called for POST requests to /services/{id}/metrics | ||
115 | 77 | |||
116 | 78 | Logs a measurement against the service identified by {id}. | ||
117 | 79 | Returns an empty response""" | ||
118 | 80 | data = json.loads(req.body) | ||
119 | 81 | service = models.Service.get(service_name) | ||
120 | 82 | data['service'] = [service] | ||
121 | 83 | data['timestamp'] = utils.truncate(time.time(), 60) | ||
122 | 84 | models.LogEntry(**data).save() | ||
123 | 85 | return Response('') | ||
124 | 86 | |||
125 | 87 | def index(self, req, service_name): | ||
126 | 88 | """Called for GET requests to /services/{id}/metrics | ||
127 | 89 | |||
128 | 90 | Returns a list of metrics logged against the service identified | ||
129 | 91 | by {id}.""" | ||
130 | 92 | service = models.Service.get(service_name) | ||
131 | 93 | retval = [] | ||
132 | 94 | for x in models.LogEntry.get(service=service).all(): | ||
133 | 95 | retval += [{'metrics': x.metrics, 'timestamp': x.timestamp}] | ||
134 | 96 | return Response(json.dumps(retval)) | ||
135 | 97 | |||
136 | 98 | class SurveilrApplication(object): | ||
137 | 99 | """The core Surveilr Monitoring WSGI application""" | ||
138 | 100 | controllers = {} | ||
139 | 101 | |||
140 | 102 | map = Mapper() | ||
141 | 103 | map.resource("metric", "metrics", controller='MetricController', | ||
142 | 104 | path_prefix='/services/{service_name}') | ||
143 | 105 | map.resource("service", "services", controller='ServiceController') | ||
144 | 106 | |||
145 | 107 | @wsgify | ||
146 | 108 | def __call__(self, req): | ||
147 | 109 | """Where it all happens | ||
148 | 110 | |||
149 | 111 | Using the Mapper object, it finds the relevant controller | ||
150 | 112 | based on the URL and delegates the call to that.""" | ||
151 | 113 | results = self.map.routematch(environ=req.environ) | ||
152 | 114 | if not results: | ||
153 | 115 | return exc.HTTPNotFound() | ||
154 | 116 | match, route = results | ||
155 | 117 | link = URLGenerator(self.map, req.environ) | ||
156 | 118 | req.urlvars = ((), match) | ||
157 | 119 | kwargs = match.copy() | ||
158 | 120 | controller = globals()[kwargs.pop('controller')]() | ||
159 | 121 | method = kwargs.pop('action') | ||
160 | 122 | req.link = link | ||
161 | 123 | req.route_name = route.name | ||
162 | 124 | |||
163 | 125 | return getattr(controller, method)(req, **kwargs) | ||
164 | 126 | |||
165 | 127 | application = SurveilrApplication() | ||
166 | 128 | |||
167 | 129 | def main(): | ||
168 | 130 | socket = eventlet.listen(('', 9877)) | ||
169 | 131 | eventlet.wsgi.server(socket, application) | ||
170 | 132 | |||
171 | 133 | if __name__ == '__main__': # pragma: nocover | ||
172 | 134 | main() | ||
173 | 0 | 135 | ||
174 | === added file 'surveilr/models.py' | |||
175 | --- surveilr/models.py 1970-01-01 00:00:00 +0000 | |||
176 | +++ surveilr/models.py 2011-11-19 22:08:22 +0000 | |||
177 | @@ -0,0 +1,54 @@ | |||
178 | 1 | """ | ||
179 | 2 | Surveilr - Log aggregation, analysis and visualisation | ||
180 | 3 | |||
181 | 4 | Copyright (C) 2011 Linux2Go | ||
182 | 5 | |||
183 | 6 | This program is free software: you can redistribute it and/or | ||
184 | 7 | modify it under the terms of the GNU Affero General Public License | ||
185 | 8 | as published by the Free Software Foundation, either version 3 of | ||
186 | 9 | the License, or (at your option) any later version. | ||
187 | 10 | |||
188 | 11 | This program is distributed in the hope that it will be useful, | ||
189 | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
190 | 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
191 | 14 | GNU Affero General Public License for more details. | ||
192 | 15 | |||
193 | 16 | You should have received a copy of the GNU Affero General Public | ||
194 | 17 | License along with this program. If not, see | ||
195 | 18 | <http://www.gnu.org/licenses/>. | ||
196 | 19 | |||
197 | 20 | Model definitions | ||
198 | 21 | """ | ||
199 | 22 | from riakalchemy import RiakObject | ||
200 | 23 | from riakalchemy.types import Integer, String, Dict, RelatedObjects | ||
201 | 24 | |||
202 | 25 | class Service(RiakObject): | ||
203 | 26 | """A service that is referenced by many LogEntry's | ||
204 | 27 | |||
205 | 28 | If the same logical service gets information from multiple sources, | ||
206 | 29 | it needs separate Service objects. However, if several metrics are | ||
207 | 30 | provided for a single service in each log entry, only one Service | ||
208 | 31 | is needed""" | ||
209 | 32 | bucket_name = 'services' | ||
210 | 33 | searchable = True | ||
211 | 34 | |||
212 | 35 | name = String() | ||
213 | 36 | most_recent_log_entry = RelatedObjects() | ||
214 | 37 | |||
215 | 38 | class LogEntry(RiakObject): | ||
216 | 39 | """A log entry holding one or more metrics | ||
217 | 40 | |||
218 | 41 | A log entry "belongs" to a single Service. Each LogEntry | ||
219 | 42 | can hold multiple metrics, but all LogEntry's must | ||
220 | 43 | have the same set of metrics.""" | ||
221 | 44 | bucket_name = 'log_entries' | ||
222 | 45 | |||
223 | 46 | timestamp = Integer() | ||
224 | 47 | metrics = Dict() | ||
225 | 48 | service = RelatedObjects(backref=True) | ||
226 | 49 | |||
227 | 50 | def post_save(self): | ||
228 | 51 | super(LogEntry, self).post_save() | ||
229 | 52 | service = self.service[0] | ||
230 | 53 | service.most_recent_log_entry = [self] | ||
231 | 54 | service.save() | ||
232 | 0 | 55 | ||
233 | === added directory 'surveilr/tests' | |||
234 | === added file 'surveilr/tests/test_api_server.py' | |||
235 | --- surveilr/tests/test_api_server.py 1970-01-01 00:00:00 +0000 | |||
236 | +++ surveilr/tests/test_api_server.py 2011-11-19 22:08:22 +0000 | |||
237 | @@ -0,0 +1,90 @@ | |||
238 | 1 | """ | ||
239 | 2 | Surveilr - Log aggregation, analysis and visualisation | ||
240 | 3 | |||
241 | 4 | Copyright (C) 2011 Linux2Go | ||
242 | 5 | |||
243 | 6 | This program is free software: you can redistribute it and/or | ||
244 | 7 | modify it under the terms of the GNU Affero General Public License | ||
245 | 8 | as published by the Free Software Foundation, either version 3 of | ||
246 | 9 | the License, or (at your option) any later version. | ||
247 | 10 | |||
248 | 11 | This program is distributed in the hope that it will be useful, | ||
249 | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
250 | 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
251 | 14 | GNU Affero General Public License for more details. | ||
252 | 15 | |||
253 | 16 | You should have received a copy of the GNU Affero General Public | ||
254 | 17 | License along with this program. If not, see | ||
255 | 18 | <http://www.gnu.org/licenses/>. | ||
256 | 19 | |||
257 | 20 | Tests for API server | ||
258 | 21 | """ | ||
259 | 22 | |||
260 | 23 | import json | ||
261 | 24 | import mock | ||
262 | 25 | import unittest | ||
263 | 26 | from webob import Request | ||
264 | 27 | |||
265 | 28 | from surveilr.api import server | ||
266 | 29 | from surveilr.api.server import application | ||
267 | 30 | from surveilr.api.server import main as server_main | ||
268 | 31 | |||
269 | 32 | class APIServerTests(unittest.TestCase): | ||
270 | 33 | def setUp(self): | ||
271 | 34 | import riakalchemy | ||
272 | 35 | riakalchemy.connect() | ||
273 | 36 | |||
274 | 37 | def test_create_retrieve_service(self): | ||
275 | 38 | """Create, retrieve, delete, attempt to retrieve again""" | ||
276 | 39 | req = Request.blank('/services', | ||
277 | 40 | method='POST', | ||
278 | 41 | POST=json.dumps({'name': 'this_or_the_other'})) | ||
279 | 42 | resp = application(req) | ||
280 | 43 | self.assertEquals(resp.status_int, 200) | ||
281 | 44 | |||
282 | 45 | service_id = json.loads(resp.body)['id'] | ||
283 | 46 | |||
284 | 47 | req = Request.blank('/services/%s' % service_id) | ||
285 | 48 | resp = application(req) | ||
286 | 49 | self.assertEquals(resp.status_int, 200) | ||
287 | 50 | |||
288 | 51 | req = Request.blank('/services/%s' % service_id, method='DELETE') | ||
289 | 52 | resp = application(req) | ||
290 | 53 | self.assertEquals(resp.status_int, 200) | ||
291 | 54 | |||
292 | 55 | req = Request.blank('/services/%s' % service_id) | ||
293 | 56 | resp = application(req) | ||
294 | 57 | self.assertEquals(resp.status_int, 404) | ||
295 | 58 | |||
296 | 59 | def test_create_retrieve_metric(self): | ||
297 | 60 | req = Request.blank('/services', method='POST', POST='{"name": "this_or_the_other"}') | ||
298 | 61 | resp = application(req) | ||
299 | 62 | self.assertEquals(resp.status_int, 200) | ||
300 | 63 | |||
301 | 64 | service_id = json.loads(resp.body)['id'] | ||
302 | 65 | req = Request.blank('/services/%s/metrics' % service_id, | ||
303 | 66 | method='POST', | ||
304 | 67 | POST=json.dumps({ | ||
305 | 68 | 'timestamp': 13217362355575, | ||
306 | 69 | 'metrics': {'duration': 85000, | ||
307 | 70 | 'response_size': 12435}})) | ||
308 | 71 | resp = application(req) | ||
309 | 72 | self.assertEquals(resp.status_int, 200) | ||
310 | 73 | |||
311 | 74 | req = Request.blank('/services/%s/metrics' % service_id) | ||
312 | 75 | resp = application(req) | ||
313 | 76 | |||
314 | 77 | def test_invalid_url(self): | ||
315 | 78 | req = Request.blank('/stuff') | ||
316 | 79 | resp = application(req) | ||
317 | 80 | self.assertEquals(resp.status_int, 404) | ||
318 | 81 | |||
319 | 82 | def test_main(self): | ||
320 | 83 | with mock.patch('surveilr.api.server.eventlet', | ||
321 | 84 | spec=['listen', 'wsgi']) as eventlet: | ||
322 | 85 | socket_sentinel = mock.sentinel.return_value | ||
323 | 86 | eventlet.listen.return_value = socket_sentinel | ||
324 | 87 | server.main() | ||
325 | 88 | |||
326 | 89 | eventlet.listen.assert_called_with(('', 9877)) | ||
327 | 90 | eventlet.wsgi.server.assert_called_with(socket_sentinel, application) | ||
328 | 0 | 91 | ||
329 | === added file 'surveilr/utils.py' | |||
330 | --- surveilr/utils.py 1970-01-01 00:00:00 +0000 | |||
331 | +++ surveilr/utils.py 2011-11-19 22:08:22 +0000 | |||
332 | @@ -0,0 +1,35 @@ | |||
333 | 1 | """ | ||
334 | 2 | Surveilr - Log aggregation, analysis and visualisation | ||
335 | 3 | |||
336 | 4 | Copyright (C) 2011 Linux2Go | ||
337 | 5 | |||
338 | 6 | This program is free software: you can redistribute it and/or | ||
339 | 7 | modify it under the terms of the GNU Affero General Public License | ||
340 | 8 | as published by the Free Software Foundation, either version 3 of | ||
341 | 9 | the License, or (at your option) any later version. | ||
342 | 10 | |||
343 | 11 | This program is distributed in the hope that it will be useful, | ||
344 | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
345 | 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
346 | 14 | GNU Affero General Public License for more details. | ||
347 | 15 | |||
348 | 16 | You should have received a copy of the GNU Affero General Public | ||
349 | 17 | License along with this program. If not, see | ||
350 | 18 | <http://www.gnu.org/licenses/>. | ||
351 | 19 | |||
352 | 20 | Utility functions | ||
353 | 21 | """ | ||
354 | 22 | |||
355 | 23 | def truncate(number, rounding_factor): | ||
356 | 24 | """Truncate to nearest arbitrary multiple | ||
357 | 25 | |||
358 | 26 | >>> truncate(1000, 100) | ||
359 | 27 | 1000 | ||
360 | 28 | >>> truncate(1099, 100) | ||
361 | 29 | 1000 | ||
362 | 30 | >>> truncate(1099, 50) | ||
363 | 31 | 1050 | ||
364 | 32 | >>> truncate(1099, 50) | ||
365 | 33 | 1050 | ||
366 | 34 | """ | ||
367 | 35 | return (int(number) / int(rounding_factor)) * int(rounding_factor) |
The attempt to merge lp:~soren/surveilr/make-it-slightly-useful into lp:surveilr failed. Below is the output from the failed tests.
running nosetests egg-info/ requires. txt egg-info/ PKG-INFO egg-info/ top_level. txt egg-info/ dependency_ links.txt egg-info/ SOURCES. txt' egg-info/ SOURCES. txt' egg-info/ SOURCES. txt'
running egg_info
creating surveilr.egg-info
writing requirements to surveilr.
writing surveilr.
writing top-level names to surveilr.
writing dependency_links to surveilr.
writing manifest file 'surveilr.
reading manifest file 'surveilr.
writing manifest file 'surveilr.
running build_ext
ok
E.E ======= ======= ======= ======= ======= ======= ======= ======= ======= ------- ------- ------- ------- ------- ------- ------- ------- ------- pymodules/ python2. 7/nose/ loader. py", line 390, in loadTestsFromName pymodules/ python2. 7/nose/ importer. py", line 39, in importFromPath Dir(dir_ path, fqname) pymodules/ python2. 7/nose/ importer. py", line 86, in importFromDir part_fqname, fh, filename, desc) /surveilr/ api/server. py", line 24, in <module>
=======
ERROR: Failure: ImportError (No module named eventlet)
-------
Traceback (most recent call last):
File "/usr/lib/
addr.filename, addr.module)
File "/usr/lib/
return self.importFrom
File "/usr/lib/
mod = load_module(
File "/tmp/tmpBkJp3S
import eventlet
ImportError: No module named eventlet
======= ======= ======= ======= ======= ======= ======= ======= ======= ======= ------- ------- ------- ------- ------- ------- ------- ------- ------- pymodules/ python2. 7/nose/ loader. py", line 390, in loadTestsFromName pymodules/ python2. 7/nose/ importer. py", line 39, in importFromPath Dir(dir_ path, fqname) pymodules/ python2. 7/nose/ importer. py", line 86, in importFromDir part_fqname, fh, filename, desc) /surveilr/ tests/test_ api_server. py", line 24, in <module>
ERROR: Failure: ImportError (No module named mock)
-------
Traceback (most recent call last):
File "/usr/lib/
addr.filename, addr.module)
File "/usr/lib/
return self.importFrom
File "/usr/lib/
mod = load_module(
File "/tmp/tmpBkJp3S
import mock
ImportError: No module named mock
------- ------- ------- ------- ------- ------- ------- ------- ------- -------
Ran 3 tests in 0.418s
FAILED (errors=2)