Skip to content

Commit 6cd50c7

Browse files
author
Buanzo
committed
initial import
0 parents  commit 6cd50c7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2072
-0
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*~
2+
*pyc
3+
DEADJOE
4+
tests/*
5+
.#*
6+
requirements.txt.old

README.md

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
Web Monitoring
2+
--------------
3+
4+
Webmon executes plugins against assets (URLs, originally).
5+
6+
The API will contain groups of calls, divided in asset management,
7+
statistics gathering and execution.
8+
9+
Asset Management involves URL management, adding, modifying and removing
10+
items (i.e "https://somesite.net, http://some.other.server/somespecificURI,
11+
etc").
12+
13+
Statistics Gathering will provide statistical information about said assets.
14+
15+
Execution will provide the necessary means to push jobs to the work queue.
16+
17+
Storage is implemented in Redis
18+
19+
20+
Work Queue
21+
----------
22+
23+
The Jobs API (A Ronald Class) will send jobs to a Messaging Queue (zeromq).
24+
This Queue will be read by the Dispatcher, which is a standalone module.
25+
26+
27+
Statistics
28+
----------
29+
30+
The statistics API will provide information about assets. Information will
31+
come from the different plugins that the Executor enables. All plugins will
32+
be executed for each Asset.
33+
34+
35+
Executor Module (standalone)
36+
----------------------------
37+
38+
The Executor Module will be a Task Worker, using the standard zeromq
39+
Streamer: http://learning-0mq-with-pyzmq.readthedocs.io/en/latest/pyzmq/devices/streamer.html
40+
41+
Dispatcher Module (standalone)
42+
------------------------------
43+
44+
The Dispatcher Module will act as a broker. The Jobs API class will use zeromq
45+
to communicate with the Dispatcher, indicating Job IDs.
46+
47+
Protocols
48+
---------
49+
50+
Each executor will have access to all plugins, directly, hence no need to do
51+
unsafe things like transporting code. The executor is NOT a generic 'worker'
52+
node. The transport between Jobs API <-> Dispatcher <-> Executor is zeromq.
53+
54+
The transport between Executor and Storage is Redis.
55+
56+
57+
TO DO LIST
58+
----------
59+
60+
In consideration to https://redis.io/topics/persistence, snapshots will be
61+
triggered after each important API call, and whenever the Dispatcher gets a
62+
"SNAPSHOT NOW" command over its zeromq control channel.
63+
64+
The AOF (Append-Only-File) is a persistence method for
65+
Redis Key-Value noSQL storage system). It is comparable to a Changelog.
66+
67+
The RDB (Redis Database) is the snapshot-style persistence method. This is
68+
what we will be using.
69+
70+
https://www.fullstackpython.com/blog/install-redis-use-python-3-ubuntu-1604.html
71+
72+
https://redis.io/commands/save
73+
74+
https://redis.io/commands/bgsave
75+
76+
PLUGINS
77+
-------
78+
79+
* HtmlStats: uses numpy to create standard deviation statistical
80+
calculations to detect excessive changes between different parameters such
81+
as html tags, pagesize in bytes, etc.
82+
83+
There are other plugins included.

api/.dockerignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*~
2+
*pyc
3+
DEADJOE
4+
tests/*
5+
.#*
6+
requirements.txt.old

api/Dockerfile

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FROM python:3.5
2+
ADD . /code
3+
WORKDIR /code
4+
RUN pip install -r requirements.txt
5+
RUN chmod +x /code/ronald.py
6+
RUN ls -l /code
7+
CMD ["/code/ronald.py"]

api/requirements.txt

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
redis==2.10.6
2+
Flask_RESTful==0.3.6
3+
Flask==0.12.2
4+
pyzmq==17.0.0b3

api/ronald.py

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
# Author: Arturo Busleiman <[email protected]>
4+
5+
6+
from flask import Flask, request
7+
from flask_restful import Api, Resource
8+
9+
"""This is a comment.
10+
Yes. Very nice.
11+
"""
12+
13+
from ronald.api_assets import API_Assets
14+
from ronald.api_stats import API_Stats
15+
from ronald.api_jobs import API_Jobs
16+
from ronald.api_workers import API_Workers
17+
from ronald.api_notifications import API_Notifications
18+
from ronald.api_charting import API_Charting
19+
20+
#
21+
# INITIALIZATION
22+
#
23+
ronald_app = Flask(__name__)
24+
api = Api(ronald_app)
25+
26+
#
27+
# API Routing
28+
#
29+
30+
api.add_resource(API_Assets,
31+
'/assets',
32+
'/assets/',
33+
'/assets/<assetId>',
34+
)
35+
36+
api.add_resource(API_Stats,
37+
'/stats',
38+
'/stats/',
39+
'/stats/<assetId>',
40+
)
41+
42+
api.add_resource(API_Jobs,
43+
'/jobs',
44+
'/jobs/',
45+
'/jobs/<jobId>',
46+
)
47+
48+
api.add_resource(API_Workers,
49+
'/workers',
50+
'/workers/',
51+
)
52+
53+
api.add_resource(API_Notifications,
54+
'/notifications',
55+
'/notifications/',
56+
'/notifications/<count>',
57+
'/notifications/<count>/<assetId>',
58+
)
59+
60+
api.add_resource(API_Charting,
61+
'/charting',
62+
'/charting/',
63+
'/charting/<count>',
64+
'/charting/<count>/<assetId>',
65+
)
66+
67+
if __name__ == '__main__':
68+
ronald_app.run(host='0.0.0.0', debug=True)
69+
# Remember to cleanup here [dbClose(), etc, etc]

api/ronald/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
# Author: Arturo Busleiman <[email protected]>

api/ronald/api_assets.py

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
# Author: Arturo Busleiman <[email protected]>
4+
5+
import redis
6+
from ronald.errors import *
7+
from ronald.storage import Storage, Asset
8+
from flask_restful import Resource, request, abort as restAbort
9+
from pprint import pprint
10+
11+
12+
class API_Assets(Resource):
13+
def __init__(self):
14+
"""
15+
Class Initialization.
16+
Assets API uses the Storage class, which is a redis abstraction
17+
"""
18+
pass
19+
20+
def get(self, assetId=None):
21+
"""
22+
GET method handler for API_Assets
23+
"""
24+
if assetId is None:
25+
assets = Storage.list_assets()
26+
return(assets)
27+
else:
28+
return(Asset(assetId=assetId).get())
29+
30+
def delete(self, assetId=None):
31+
"""
32+
DELETE method handler for API_Assets
33+
"""
34+
if assetId is None:
35+
abort(400, message=ERR_MSG_ASSETS_DELETE_NO_ASSETID)
36+
return(Asset(assetId=assetId).delete())
37+
38+
def put(self, assetId=None):
39+
"""
40+
PUT method handler for API_Assets
41+
"""
42+
if assetId is not None:
43+
restAbort(400, message=ERR_MSG_ASSETS_PUT_NO_ARGS_REQ)
44+
# get parameters
45+
js = request.get_json(force=True)
46+
# validate
47+
if 'assetUrl' not in js:
48+
restAbort(400, message=ERR_MSG_ASSETS_PUT_INVALID_REQUEST)
49+
# TODO: check if assetUrl is indeed a valid Url
50+
# attempt to create new asset
51+
newAsset = Asset(assetUrl=js['assetUrl']).create()
52+
# Asset() always returns a dictionary. send to client.
53+
return(newAsset)
54+
55+
56+
if __name__ == '__main__':
57+
print("")

api/ronald/api_charting.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
# Author: Arturo Busleiman <[email protected]>
4+
5+
from flask_restful import Resource, request
6+
from ronald.storage import Storage
7+
8+
9+
class API_Charting(Resource):
10+
def __init__(self):
11+
"""
12+
Class Initialization.
13+
"""
14+
pass
15+
16+
def get(self, count=None, assetId=False):
17+
if count == 'all':
18+
self.count = -1 # Standard, use -1 to specify 'all'
19+
elif count == 'latest':
20+
self.count = 10 # TODO: define from config
21+
else:
22+
try:
23+
self.count = int(count)
24+
except Exception:
25+
return({'status': 'ERROR',
26+
'detail': 'Invalid count parameter'})
27+
if self.count < 1:
28+
return({'status': 'ERROR',
29+
'detail': 'Count parameter must be > 0'})
30+
31+
print("self.count={} assetId={}".format(self.count, assetId))
32+
if assetId is False:
33+
pass
34+
# return(Storage.get_chartdata(library='default',count=self.count))
35+
36+
if Storage.get_asset(assetId=assetId) is None:
37+
return({'status': 'ERROR',
38+
'detail': 'Invalid or inexistent asset'})
39+
40+
# return(Storage.get_chartdata(library='default',count=self.count,
41+
# assetId=))
42+
43+
44+
if __name__ == '__main__':
45+
print("")

api/ronald/api_jobs.py

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
# Author: Arturo Busleiman <[email protected]>
4+
5+
import zmq
6+
import time
7+
from ronald.storage import Storage
8+
from ronald.messaging import Messaging
9+
from flask_restful import Resource, request
10+
from pprint import pprint
11+
12+
""" The API_Jobs class implements job management.
13+
get() _might_ list upcoming jobs. (redis?)
14+
delete() cancels a job
15+
put() inserts a job
16+
post(), if implemented, will allow to modify
17+
an existing job's parameters
18+
"""
19+
20+
21+
class API_Jobs(Resource):
22+
def __init__(self):
23+
"""
24+
Class Initialization.
25+
"""
26+
pass
27+
28+
def get(self, jobId=None):
29+
"""
30+
GET method handler for API_Jobs.
31+
Takes care of getting a list of assets that need
32+
to be run at the current period, and sending that
33+
list to the Dispatcher, via ZMQ (implemented in Messaging class)
34+
"""
35+
workSpec = Storage.list_assets_without_current_stats()
36+
# pprint(workSpec)
37+
ret = Messaging.queue_jobs(workSpec=workSpec)
38+
if workSpec['assets'] is None:
39+
return({'status': 'ERROR',
40+
'detail': 'No available workers. Notify admin.'})
41+
return({'status': 'OK',
42+
'detail': 'Jobs have been pushed to workers',
43+
'dispatched_work_specification': workSpec})
44+
45+
def post(self, jobId=None):
46+
"""
47+
POST method handler for API_Jobs
48+
"""
49+
pass
50+
51+
def delete(self, jobId=None):
52+
"""
53+
DELETE method handler for API_Jobs
54+
"""
55+
pass
56+
57+
def put(self, jobId=None):
58+
"""
59+
PUT method handler for API_Jobs
60+
"""
61+
pass
62+
63+
64+
if __name__ == '__main__':
65+
print("")

0 commit comments

Comments
 (0)