Merge lp:~soren/surveilr/make-it-slightly-useful into lp:surveilr

Proposed by Soren Hansen
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
Reviewer Review Type Date Requested Status
Soren Hansen Pending
Review via email: mp+82797@code.launchpad.net

Commit message

Add basic data model and API server

Description of the change

Add basic data model and API server

To post a comment you must log in.
Revision history for this message
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.egg-info/requires.txt
writing surveilr.egg-info/PKG-INFO
writing top-level names to surveilr.egg-info/top_level.txt
writing dependency_links to surveilr.egg-info/dependency_links.txt
writing manifest file 'surveilr.egg-info/SOURCES.txt'
reading manifest file 'surveilr.egg-info/SOURCES.txt'
writing manifest file 'surveilr.egg-info/SOURCES.txt'
running build_ext
ok

E.E
======================================================================
ERROR: Failure: ImportError (No module named eventlet)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.7/nose/loader.py", line 390, in loadTestsFromName
    addr.filename, addr.module)
  File "/usr/lib/pymodules/python2.7/nose/importer.py", line 39, in importFromPath
    return self.importFromDir(dir_path, fqname)
  File "/usr/lib/pymodules/python2.7/nose/importer.py", line 86, in importFromDir
    mod = load_module(part_fqname, fh, filename, desc)
  File "/tmp/tmpBkJp3S/surveilr/api/server.py", line 24, in <module>
    import eventlet
ImportError: No module named eventlet

======================================================================
ERROR: Failure: ImportError (No module named mock)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.7/nose/loader.py", line 390, in loadTestsFromName
    addr.filename, addr.module)
  File "/usr/lib/pymodules/python2.7/nose/importer.py", line 39, in importFromPath
    return self.importFromDir(dir_path, fqname)
  File "/usr/lib/pymodules/python2.7/nose/importer.py", line 86, in importFromDir
    mod = load_module(part_fqname, fh, filename, desc)
  File "/tmp/tmpBkJp3S/surveilr/tests/test_api_server.py", line 24, in <module>
    import mock
ImportError: No module named mock

----------------------------------------------------------------------
Ran 3 tests in 0.418s

FAILED (errors=2)

Revision history for this message
Soren Hansen (soren) wrote :

Installed python-{mock,eventlet} on the Jenkis box.

Revision history for this message
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.egg-info/requires.txt
writing surveilr.egg-info/PKG-INFO
writing top-level names to surveilr.egg-info/top_level.txt
writing dependency_links to surveilr.egg-info/dependency_links.txt
writing manifest file 'surveilr.egg-info/SOURCES.txt'
reading manifest file 'surveilr.egg-info/SOURCES.txt'
writing manifest file 'surveilr.egg-info/SOURCES.txt'
running build_ext
ok

E.E
======================================================================
ERROR: Failure: ImportError (No module named routes)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.7/nose/loader.py", line 390, in loadTestsFromName
    addr.filename, addr.module)
  File "/usr/lib/pymodules/python2.7/nose/importer.py", line 39, in importFromPath
    return self.importFromDir(dir_path, fqname)
  File "/usr/lib/pymodules/python2.7/nose/importer.py", line 86, in importFromDir
    mod = load_module(part_fqname, fh, filename, desc)
  File "/tmp/tmpXx8lxx/surveilr/api/server.py", line 30, in <module>
    from routes import Mapper
ImportError: No module named routes

======================================================================
ERROR: Failure: ImportError (No module named webob)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.7/nose/loader.py", line 390, in loadTestsFromName
    addr.filename, addr.module)
  File "/usr/lib/pymodules/python2.7/nose/importer.py", line 39, in importFromPath
    return self.importFromDir(dir_path, fqname)
  File "/usr/lib/pymodules/python2.7/nose/importer.py", line 86, in importFromDir
    mod = load_module(part_fqname, fh, filename, desc)
  File "/tmp/tmpXx8lxx/surveilr/tests/test_api_server.py", line 26, in <module>
    from webob import Request
ImportError: No module named webob

----------------------------------------------------------------------
Ran 3 tests in 0.811s

FAILED (errors=2)

Revision history for this message
Soren Hansen (soren) wrote :

...and python-{routes,webob}.

Revision history for this message
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.egg-info/requires.txt
writing surveilr.egg-info/PKG-INFO
writing top-level names to surveilr.egg-info/top_level.txt
writing dependency_links to surveilr.egg-info/dependency_links.txt
writing manifest file 'surveilr.egg-info/SOURCES.txt'
reading manifest file 'surveilr.egg-info/SOURCES.txt'
writing manifest file 'surveilr.egg-info/SOURCES.txt'
running build_ext
ok

.EE..
======================================================================
ERROR: test_create_retrieve_metric (test_api_server.APIServerTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/tmpt9Dlnm/surveilr/tests/test_api_server.py", line 61, in test_create_retrieve_metric
    resp = application(req)
  File "/usr/lib/python2.7/dist-packages/webob/dec.py", line 161, in __call__
    return self.func(req, *args, **kw)
  File "/tmp/tmpt9Dlnm/surveilr/api/server.py", line 125, in __call__
    return getattr(controller, method)(req, **kwargs)
  File "/tmp/tmpt9Dlnm/surveilr/api/server.py", line 51, in create
    service.save()
  File "/var/lib/jenkins/riakalchemy/riakalchemy/__init__.py", line 182, in save
    self._riak_obj = bucket.new(self.key, data=data_dict)
  File "/usr/lib/python2.7/dist-packages/riak/bucket.py", line 218, in new
    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/surveilr/tests/test_api_server.py", line 42, in test_create_retrieve_service
    resp = application(req)
  File "/usr/lib/python2.7/dist-packages/webob/dec.py", line 161, in __call__
    return self.func(req, *args, **kw)
  File "/tmp/tmpt9Dlnm/surveilr/api/server.py", line 125, in __call__
    return getattr(controller, method)(req, **kwargs)
  File "/tmp/tmpt9Dlnm/surveilr/api/server.py", line 51, in create
    service.save()
  File "/var/lib/jenkins/riakalchemy/riakalchemy/__init__.py", line 182, in save
    self._riak_obj = bucket.new(self.key, data=data_dict)
  File "/usr/lib/python2.7/dist-packages/riak/bucket.py", line 218, in new
    raise TypeError('Unicode data values are not supported.')
TypeError: Unicode data values are not supported.

----------------------------------------------------------------------
Ran 5 tests in 1.128s

FAILED (errors=2)

Revision history for this message
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.egg-info/requires.txt
writing surveilr.egg-info/PKG-INFO
writing top-level names to surveilr.egg-info/top_level.txt
writing dependency_links to surveilr.egg-info/dependency_links.txt
writing manifest file 'surveilr.egg-info/SOURCES.txt'
reading manifest file 'surveilr.egg-info/SOURCES.txt'
writing manifest file 'surveilr.egg-info/SOURCES.txt'
running build_ext
ok

.EE..
======================================================================
ERROR: test_create_retrieve_metric (test_api_server.APIServerTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/tmpfUlS7f/surveilr/tests/test_api_server.py", line 61, in test_create_retrieve_metric
    resp = application(req)
  File "/usr/lib/python2.7/dist-packages/webob/dec.py", line 161, in __call__
    return self.func(req, *args, **kw)
  File "/tmp/tmpfUlS7f/surveilr/api/server.py", line 125, in __call__
    return getattr(controller, method)(req, **kwargs)
  File "/tmp/tmpfUlS7f/surveilr/api/server.py", line 51, in create
    service.save()
  File "/var/lib/jenkins/riakalchemy/riakalchemy/model.py", line 182, in save
    self._riak_obj = bucket.new(self.key, data=data_dict)
  File "/usr/lib/python2.7/dist-packages/riak/bucket.py", line 218, in new
    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/surveilr/tests/test_api_server.py", line 42, in test_create_retrieve_service
    resp = application(req)
  File "/usr/lib/python2.7/dist-packages/webob/dec.py", line 161, in __call__
    return self.func(req, *args, **kw)
  File "/tmp/tmpfUlS7f/surveilr/api/server.py", line 125, in __call__
    return getattr(controller, method)(req, **kwargs)
  File "/tmp/tmpfUlS7f/surveilr/api/server.py", line 51, in create
    service.save()
  File "/var/lib/jenkins/riakalchemy/riakalchemy/model.py", line 182, in save
    self._riak_obj = bucket.new(self.key, data=data_dict)
  File "/usr/lib/python2.7/dist-packages/riak/bucket.py", line 218, in new
    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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'setup.cfg'
--- setup.cfg 1970-01-01 00:00:00 +0000
+++ setup.cfg 2011-11-19 22:08:22 +0000
@@ -0,0 +1,2 @@
1[nosetests]
2with-doctest=1
03
=== added directory 'surveilr'
=== added file 'surveilr/__init__.py'
--- surveilr/__init__.py 1970-01-01 00:00:00 +0000
+++ surveilr/__init__.py 2011-11-19 22:08:22 +0000
@@ -0,0 +1,19 @@
1"""
2 Surveilr - Log aggregation, analysis and visualisation
3
4 Copyright (C) 2011 Linux2Go
5
6 This program is free software: you can redistribute it and/or
7 modify it under the terms of the GNU Affero General Public License
8 as published by the Free Software Foundation, either version 3 of
9 the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public
17 License along with this program. If not, see
18 <http://www.gnu.org/licenses/>.
19"""
020
=== added directory 'surveilr/api'
=== added file 'surveilr/api/__init__.py'
=== added file 'surveilr/api/server.py'
--- surveilr/api/server.py 1970-01-01 00:00:00 +0000
+++ surveilr/api/server.py 2011-11-19 22:08:22 +0000
@@ -0,0 +1,134 @@
1#!/usr/bin/python
2"""
3 Surveilr - Log aggregation, analysis and visualisation
4
5 Copyright (C) 2011 Linux2Go
6
7 This program is free software: you can redistribute it and/or
8 modify it under the terms of the GNU Affero General Public License
9 as published by the Free Software Foundation, either version 3 of
10 the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public
18 License along with this program. If not, see
19 <http://www.gnu.org/licenses/>.
20
21 Log collection server implementation
22"""
23
24import eventlet
25import eventlet.wsgi
26import json
27import time
28
29import riakalchemy
30from routes import Mapper
31from routes.util import URLGenerator
32
33from webob import exc
34from webob import Response
35from webob.dec import wsgify
36from webob.exc import HTTPNotFound
37
38from surveilr import models
39from surveilr import utils
40
41class ServiceController(object):
42 """Routes style controller for actions related to services"""
43
44 def create(self, req):
45 """Called for POST requests to /services
46
47 Creates the service, returns a JSON object with the ID assigned
48 to the service"""
49 data = json.loads(req.body)
50 service = models.Service(**data)
51 service.save()
52 response = {'id':service.key}
53 return Response(json.dumps(response))
54
55 def show(self, req, id):
56 """Called for GET requests to /services/{id}
57
58 Returns information for the given service"""
59 try:
60 service = models.Service.get(id)
61 return Response({'id': service.key})
62 except riakalchemy.NoSuchObjectError:
63 return HTTPNotFound()
64
65 def delete(self, req, id):
66 """Called for DELETE requests to /services/{id}
67
68 Delete the given service"""
69 models.Service.get(id).delete()
70 return Response('')
71
72
73class MetricController(object):
74 """Routes style controller for actions related to log entries"""
75 def create(self, req, service_name):
76 """Called for POST requests to /services/{id}/metrics
77
78 Logs a measurement against the service identified by {id}.
79 Returns an empty response"""
80 data = json.loads(req.body)
81 service = models.Service.get(service_name)
82 data['service'] = [service]
83 data['timestamp'] = utils.truncate(time.time(), 60)
84 models.LogEntry(**data).save()
85 return Response('')
86
87 def index(self, req, service_name):
88 """Called for GET requests to /services/{id}/metrics
89
90 Returns a list of metrics logged against the service identified
91 by {id}."""
92 service = models.Service.get(service_name)
93 retval = []
94 for x in models.LogEntry.get(service=service).all():
95 retval += [{'metrics': x.metrics, 'timestamp': x.timestamp}]
96 return Response(json.dumps(retval))
97
98class SurveilrApplication(object):
99 """The core Surveilr Monitoring WSGI application"""
100 controllers = {}
101
102 map = Mapper()
103 map.resource("metric", "metrics", controller='MetricController',
104 path_prefix='/services/{service_name}')
105 map.resource("service", "services", controller='ServiceController')
106
107 @wsgify
108 def __call__(self, req):
109 """Where it all happens
110
111 Using the Mapper object, it finds the relevant controller
112 based on the URL and delegates the call to that."""
113 results = self.map.routematch(environ=req.environ)
114 if not results:
115 return exc.HTTPNotFound()
116 match, route = results
117 link = URLGenerator(self.map, req.environ)
118 req.urlvars = ((), match)
119 kwargs = match.copy()
120 controller = globals()[kwargs.pop('controller')]()
121 method = kwargs.pop('action')
122 req.link = link
123 req.route_name = route.name
124
125 return getattr(controller, method)(req, **kwargs)
126
127application = SurveilrApplication()
128
129def main():
130 socket = eventlet.listen(('', 9877))
131 eventlet.wsgi.server(socket, application)
132
133if __name__ == '__main__': # pragma: nocover
134 main()
0135
=== added file 'surveilr/models.py'
--- surveilr/models.py 1970-01-01 00:00:00 +0000
+++ surveilr/models.py 2011-11-19 22:08:22 +0000
@@ -0,0 +1,54 @@
1"""
2 Surveilr - Log aggregation, analysis and visualisation
3
4 Copyright (C) 2011 Linux2Go
5
6 This program is free software: you can redistribute it and/or
7 modify it under the terms of the GNU Affero General Public License
8 as published by the Free Software Foundation, either version 3 of
9 the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public
17 License along with this program. If not, see
18 <http://www.gnu.org/licenses/>.
19
20 Model definitions
21"""
22from riakalchemy import RiakObject
23from riakalchemy.types import Integer, String, Dict, RelatedObjects
24
25class Service(RiakObject):
26 """A service that is referenced by many LogEntry's
27
28 If the same logical service gets information from multiple sources,
29 it needs separate Service objects. However, if several metrics are
30 provided for a single service in each log entry, only one Service
31 is needed"""
32 bucket_name = 'services'
33 searchable = True
34
35 name = String()
36 most_recent_log_entry = RelatedObjects()
37
38class LogEntry(RiakObject):
39 """A log entry holding one or more metrics
40
41 A log entry "belongs" to a single Service. Each LogEntry
42 can hold multiple metrics, but all LogEntry's must
43 have the same set of metrics."""
44 bucket_name = 'log_entries'
45
46 timestamp = Integer()
47 metrics = Dict()
48 service = RelatedObjects(backref=True)
49
50 def post_save(self):
51 super(LogEntry, self).post_save()
52 service = self.service[0]
53 service.most_recent_log_entry = [self]
54 service.save()
055
=== added directory 'surveilr/tests'
=== added file 'surveilr/tests/test_api_server.py'
--- surveilr/tests/test_api_server.py 1970-01-01 00:00:00 +0000
+++ surveilr/tests/test_api_server.py 2011-11-19 22:08:22 +0000
@@ -0,0 +1,90 @@
1"""
2 Surveilr - Log aggregation, analysis and visualisation
3
4 Copyright (C) 2011 Linux2Go
5
6 This program is free software: you can redistribute it and/or
7 modify it under the terms of the GNU Affero General Public License
8 as published by the Free Software Foundation, either version 3 of
9 the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public
17 License along with this program. If not, see
18 <http://www.gnu.org/licenses/>.
19
20 Tests for API server
21"""
22
23import json
24import mock
25import unittest
26from webob import Request
27
28from surveilr.api import server
29from surveilr.api.server import application
30from surveilr.api.server import main as server_main
31
32class APIServerTests(unittest.TestCase):
33 def setUp(self):
34 import riakalchemy
35 riakalchemy.connect()
36
37 def test_create_retrieve_service(self):
38 """Create, retrieve, delete, attempt to retrieve again"""
39 req = Request.blank('/services',
40 method='POST',
41 POST=json.dumps({'name': 'this_or_the_other'}))
42 resp = application(req)
43 self.assertEquals(resp.status_int, 200)
44
45 service_id = json.loads(resp.body)['id']
46
47 req = Request.blank('/services/%s' % service_id)
48 resp = application(req)
49 self.assertEquals(resp.status_int, 200)
50
51 req = Request.blank('/services/%s' % service_id, method='DELETE')
52 resp = application(req)
53 self.assertEquals(resp.status_int, 200)
54
55 req = Request.blank('/services/%s' % service_id)
56 resp = application(req)
57 self.assertEquals(resp.status_int, 404)
58
59 def test_create_retrieve_metric(self):
60 req = Request.blank('/services', method='POST', POST='{"name": "this_or_the_other"}')
61 resp = application(req)
62 self.assertEquals(resp.status_int, 200)
63
64 service_id = json.loads(resp.body)['id']
65 req = Request.blank('/services/%s/metrics' % service_id,
66 method='POST',
67 POST=json.dumps({
68 'timestamp': 13217362355575,
69 'metrics': {'duration': 85000,
70 'response_size': 12435}}))
71 resp = application(req)
72 self.assertEquals(resp.status_int, 200)
73
74 req = Request.blank('/services/%s/metrics' % service_id)
75 resp = application(req)
76
77 def test_invalid_url(self):
78 req = Request.blank('/stuff')
79 resp = application(req)
80 self.assertEquals(resp.status_int, 404)
81
82 def test_main(self):
83 with mock.patch('surveilr.api.server.eventlet',
84 spec=['listen', 'wsgi']) as eventlet:
85 socket_sentinel = mock.sentinel.return_value
86 eventlet.listen.return_value = socket_sentinel
87 server.main()
88
89 eventlet.listen.assert_called_with(('', 9877))
90 eventlet.wsgi.server.assert_called_with(socket_sentinel, application)
091
=== added file 'surveilr/utils.py'
--- surveilr/utils.py 1970-01-01 00:00:00 +0000
+++ surveilr/utils.py 2011-11-19 22:08:22 +0000
@@ -0,0 +1,35 @@
1"""
2 Surveilr - Log aggregation, analysis and visualisation
3
4 Copyright (C) 2011 Linux2Go
5
6 This program is free software: you can redistribute it and/or
7 modify it under the terms of the GNU Affero General Public License
8 as published by the Free Software Foundation, either version 3 of
9 the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public
17 License along with this program. If not, see
18 <http://www.gnu.org/licenses/>.
19
20 Utility functions
21"""
22
23def truncate(number, rounding_factor):
24 """Truncate to nearest arbitrary multiple
25
26 >>> truncate(1000, 100)
27 1000
28 >>> truncate(1099, 100)
29 1000
30 >>> truncate(1099, 50)
31 1050
32 >>> truncate(1099, 50)
33 1050
34 """
35 return (int(number) / int(rounding_factor)) * int(rounding_factor)

Subscribers

People subscribed via source and target branches

to all changes: