Revision 3e99e2d1
b/flowspec/models.py | ||
---|---|---|
11 | 11 |
from flowspec.tasks import * |
12 | 12 |
from time import sleep |
13 | 13 |
|
14 |
from flowspy.utils import beanstalkc |
|
15 |
|
|
16 |
|
|
14 | 17 |
FORMAT = '%(asctime)s %(levelname)s: %(message)s' |
15 | 18 |
logging.basicConfig(format=FORMAT) |
16 | 19 |
logger = logging.getLogger(__name__) |
... | ... | |
119 | 122 |
# logger.info("Got save job id: %s" %response) |
120 | 123 |
|
121 | 124 |
def commit_add(self, *args, **kwargs): |
125 |
send_message("Adding route %s. Please wait..." %self.name) |
|
122 | 126 |
response = add.delay(self) |
123 | 127 |
logger.info("Got save job id: %s" %response) |
124 |
# |
|
128 |
|
|
129 |
def deactivate(self): |
|
130 |
self.is_online = False |
|
131 |
self.is_active = False |
|
132 |
self.save() |
|
125 | 133 |
# def delete(self, *args, **kwargs): |
126 | 134 |
# response = delete.delay(self) |
127 | 135 |
# logger.info("Got delete job id: %s" %response) |
128 | 136 |
|
137 |
def commit_edit(self, *args, **kwargs): |
|
138 |
send_message("Editing route %s. Please wait..." %self.name) |
|
139 |
response = edit.delay(self) |
|
140 |
logger.info("Got edit job id: %s" %response) |
|
129 | 141 |
|
142 |
def commit_delete(self, *args, **kwargs): |
|
143 |
send_message("Removing route %s. Please wait..." %self.name) |
|
144 |
response = delete.delay(self) |
|
145 |
logger.info("Got edit job id: %s" %response) |
|
146 |
# |
|
147 |
# def delete(self, *args, **kwargs): |
|
148 |
# response = delete.delay(self) |
|
149 |
# logger.info("Got delete job id: %s" %response) |
|
130 | 150 |
def is_synced(self): |
131 | 151 |
|
132 | 152 |
found = False |
... | ... | |
232 | 252 |
def get_match(self): |
233 | 253 |
ret = '' |
234 | 254 |
if self.destination: |
235 |
ret = ret = '%s Destination Address:<strong>%s</strong><br/>' %(ret, self.destination)
|
|
255 |
ret = '%s Destination Address:<strong>%s</strong><br/>' %(ret, self.destination) |
|
236 | 256 |
if self.fragmenttype: |
237 |
ret = ret = "%s Fragment Type:<strong>%s</strong><br/>" %(ret, self.fragmenttype)
|
|
257 |
ret = "%s Fragment Type:<strong>%s</strong><br/>" %(ret, self.fragmenttype) |
|
238 | 258 |
if self.icmpcode: |
239 |
ret = ret = "%s ICMP code:<strong>%s</strong><br/>" %(ret, self.icmpcode)
|
|
259 |
ret = "%s ICMP code:<strong>%s</strong><br/>" %(ret, self.icmpcode) |
|
240 | 260 |
if self.icmptype: |
241 |
ret = ret = "%s ICMP Type:<strong>%s</strong><br/>" %(ret, self.icmptype)
|
|
261 |
ret = "%s ICMP Type:<strong>%s</strong><br/>" %(ret, self.icmptype) |
|
242 | 262 |
if self.packetlength: |
243 |
ret = ret = "%s Packet Length:<strong>%s</strong><br/>" %(ret, self.packetlength)
|
|
263 |
ret = "%s Packet Length:<strong>%s</strong><br/>" %(ret, self.packetlength) |
|
244 | 264 |
if self.protocol: |
245 |
ret = ret = "%s Protocol:<strong>%s</strong><br/>" %(ret, self.protocol)
|
|
265 |
ret = "%s Protocol:<strong>%s</strong><br/>" %(ret, self.protocol) |
|
246 | 266 |
if self.source: |
247 |
ret = ret = "%s Source Address:<strong>%s</strong><br/>" %(ret, self.source)
|
|
267 |
ret = "%s Source Address:<strong>%s</strong><br/>" %(ret, self.source) |
|
248 | 268 |
if self.tcpflag: |
249 |
ret = ret = "%s TCP flag:<strong>%s</strong><br/>" %(ret, self.tcpflag)
|
|
269 |
ret = "%s TCP flag:<strong>%s</strong><br/>" %(ret, self.tcpflag) |
|
250 | 270 |
if self.port: |
251 | 271 |
for port in self.port.all(): |
252 |
ret = "%s Port:<strong>%s</strong><br/>" %(ret, port)
|
|
272 |
ret = ret + "Port:<strong>%s</strong><br/>" %(port)
|
|
253 | 273 |
if self.destinationport: |
254 | 274 |
for port in self.destinationport.all(): |
255 |
ret = "%s Port:<strong>%s</strong><br/>" %(ret, port)
|
|
275 |
ret = ret + "Destination Port:<strong>%s</strong><br/>" %(port)
|
|
256 | 276 |
if self.sourceport: |
257 | 277 |
for port in self.sourceport.all(): |
258 |
ret = "%s Port:<strong>%s</strong><br/>" %(ret, port)
|
|
278 |
ret = ret +"Source Port:<strong>%s</strong><br/>" %(port)
|
|
259 | 279 |
if self.dscp: |
260 | 280 |
for dscp in self.dscp.all(): |
261 |
ret = "%s Port:<strong>%s</strong><br/>" %(ret, dscp) |
|
281 |
ret = ret + "%s Port:<strong>%s</strong><br/>" %(ret, dscp)
|
|
262 | 282 |
return ret.rstrip('<br/>') |
263 | 283 |
|
264 | 284 |
get_match.short_description = 'Match statement' |
265 | 285 |
get_match.allow_tags = True |
266 | 286 |
|
287 |
def send_message(msg): |
|
288 |
b = beanstalkc.Connection() |
|
289 |
b.use(settings.POLLS_TUBE) |
|
290 |
b.put(str(msg)) |
|
291 |
b.close() |
b/flowspec/tasks.py | ||
---|---|---|
1 | 1 |
from utils import proxy as PR |
2 | 2 |
from celery.task import task |
3 |
from celery.task.sets import subtask |
|
4 |
import logging |
|
5 |
from celery.task.http import * |
|
6 |
from flowspy.utils import beanstalkc |
|
7 |
from django.conf import settings |
|
8 |
|
|
9 |
FORMAT = '%(asctime)s %(levelname)s: %(message)s' |
|
10 |
logging.basicConfig(format=FORMAT) |
|
11 |
logger = logging.getLogger(__name__) |
|
12 |
logger.setLevel(logging.DEBUG) |
|
3 | 13 |
|
4 | 14 |
@task |
5 |
def add(route): |
|
15 |
def add(route, callback=None):
|
|
6 | 16 |
applier = PR.Applier(route_object=route) |
7 | 17 |
commit, response = applier.apply() |
8 | 18 |
if commit: |
... | ... | |
14 | 24 |
route.is_online = is_online |
15 | 25 |
route.is_active = is_active |
16 | 26 |
route.response = response |
27 |
subtask(announce).delay("Route add: %s - Result: %s" %(route.name, response)) |
|
28 |
route.save() |
|
29 |
|
|
30 |
@task |
|
31 |
def edit(route, callback=None): |
|
32 |
applier = PR.Applier(route_object=route) |
|
33 |
commit, response = applier.apply(operation="replace") |
|
34 |
if commit: |
|
35 |
is_online = True |
|
36 |
else: |
|
37 |
is_online = False |
|
38 |
route.is_active = True |
|
39 |
route.is_online = is_online |
|
40 |
route.response = response |
|
17 | 41 |
route.save() |
42 |
subtask(announce).delay("Route edit: %s - Result: %s" %(route.name, response)) |
|
43 |
|
|
44 |
|
|
18 | 45 |
|
19 | 46 |
@task |
20 |
def multi(x,y): |
|
21 |
return x*y |
|
22 |
# |
|
23 |
#@task |
|
47 |
def delete(route, callback=None): |
|
48 |
applier = PR.Applier(route_object=route) |
|
49 |
commit, response = applier.apply(operation="delete") |
|
50 |
if commit: |
|
51 |
is_online = False |
|
52 |
is_active = False |
|
53 |
else: |
|
54 |
is_online = route.is_online |
|
55 |
is_active = route.is_active |
|
56 |
route.is_online = is_online |
|
57 |
route.is_active = is_active |
|
58 |
route.response = response |
|
59 |
route.save() |
|
60 |
subtask(announce).delay("Route delete: %s - Result %s" %(route.name, response)) |
|
61 |
|
|
62 |
|
|
63 |
|
|
64 |
@task |
|
65 |
def announce(messg): |
|
66 |
messg = str(messg) |
|
67 |
b = beanstalkc.Connection() |
|
68 |
b.use(settings.POLLS_TUBE) |
|
69 |
b.put(messg) |
|
70 |
b.close() |
|
71 |
|
|
72 |
|
|
24 | 73 |
#def delete(route): |
25 | 74 |
# |
26 | 75 |
# applier = PR.Applier(route_object=route) |
b/flowspec/views.py | ||
---|---|---|
15 | 15 |
from django.core.urlresolvers import reverse |
16 | 16 |
from django.contrib import messages |
17 | 17 |
|
18 |
from django.forms.models import model_to_dict |
|
19 |
|
|
18 | 20 |
from flowspy.flowspec.forms import * |
19 | 21 |
from flowspy.flowspec.models import * |
20 | 22 |
|
23 |
from copy import deepcopy |
|
24 |
|
|
21 | 25 |
def days_offset(): return datetime.now() + timedelta(days = settings.EXPIRATION_DAYS_OFFSET) |
22 | 26 |
|
27 |
@login_required |
|
23 | 28 |
def user_routes(request): |
24 | 29 |
if request.user.is_anonymous(): |
25 | 30 |
return HttpResponseRedirect(reverse('login')) |
... | ... | |
27 | 32 |
return render_to_response('user_routes.html', {'routes': user_routes}, |
28 | 33 |
context_instance=RequestContext(request)) |
29 | 34 |
|
30 |
|
|
35 |
@login_required |
|
31 | 36 |
def add_route(request): |
32 | 37 |
if request.method == "GET": |
33 | 38 |
form = RouteForm() |
... | ... | |
47 | 52 |
else: |
48 | 53 |
return render_to_response('apply.html', {'form': form}, |
49 | 54 |
context_instance=RequestContext(request)) |
55 |
@login_required |
|
56 |
def edit_route(request, route_slug): |
|
57 |
route_edit = get_object_or_404(Route, name=route_slug) |
|
58 |
route_original = deepcopy(route_edit) |
|
59 |
if request.POST: |
|
60 |
form = RouteForm(request.POST, instance = route_edit) |
|
61 |
if form.is_valid(): |
|
62 |
route=form.save(commit=False) |
|
63 |
route.name = route_original.name |
|
64 |
route.applier = route_original.applier |
|
65 |
route.expires = route_original.expires |
|
66 |
route.is_active = route_original.is_active |
|
67 |
route.save() |
|
68 |
form.save_m2m() |
|
69 |
route.commit_edit() |
|
70 |
return HttpResponseRedirect(urlresolvers.reverse("user-routes")) |
|
71 |
else: |
|
72 |
return render_to_response('apply.html', {'form': form, 'edit':True}, |
|
73 |
context_instance=RequestContext(request)) |
|
74 |
else: |
|
75 |
dictionary = model_to_dict(route_edit, fields=[], exclude=[]) |
|
76 |
form = RouteForm(dictionary) |
|
77 |
return render_to_response('apply.html', {'form': form, 'edit':True}, |
|
78 |
context_instance=RequestContext(request)) |
|
79 |
|
|
80 |
@login_required |
|
81 |
def delete_route(request, route_slug): |
|
82 |
if request.is_ajax(): |
|
83 |
route = get_object_or_404(Route, name=route_slug) |
|
84 |
if route.applier == request.user: |
|
85 |
route.deactivate() |
|
86 |
route.commit_delete() |
|
87 |
return HttpResponseRedirect(urlresolvers.reverse("user-routes")) |
b/poller/README | ||
---|---|---|
1 |
An example of AJAX chat taken from Tornado demos and converted to use django and gevent. |
|
2 |
|
|
3 |
To start the server, run |
|
4 |
$ python run.py |
b/poller/application.py | ||
---|---|---|
1 |
#!/usr/bin/python |
|
2 |
from gevent import monkey; monkey.patch_all() |
|
3 |
import os |
|
4 |
import traceback |
|
5 |
from django.core.handlers.wsgi import WSGIHandler |
|
6 |
from django.core.signals import got_request_exception |
|
7 |
from django.core.management import call_command |
|
8 |
|
|
9 |
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' |
|
10 |
|
|
11 |
def exception_printer(sender, **kwargs): |
|
12 |
traceback.print_exc() |
|
13 |
|
|
14 |
got_request_exception.connect(exception_printer) |
|
15 |
|
|
16 |
call_command('syncdb') |
|
17 |
|
|
18 |
application = WSGIHandler() |
b/poller/run_uwsgi | ||
---|---|---|
1 |
#!/bin/sh |
|
2 |
# see http://projects.unbit.it/uwsgi and http://projects.unbit.it/uwsgi/wiki/Gevent |
|
3 |
exec uwsgi --loop gevent --http-socket :8000 --module application --async 1000 |
b/poller/urls.py | ||
---|---|---|
1 |
from django.conf.urls.defaults import * |
|
2 |
from django.conf import settings |
|
3 |
|
|
4 |
urlpatterns = patterns('flowspy.poller.views', |
|
5 |
('^$', 'main'), |
|
6 |
('^a/message/existing$', 'message_existing'), |
|
7 |
('^a/message/new$', 'message_new'), |
|
8 |
('^a/message/updates$', 'message_updates')) |
|
9 |
|
|
10 |
urlpatterns += patterns('', |
|
11 |
(r'^static/(?P<path>.*)', 'django.views.static.serve',\ |
|
12 |
{'document_root': settings.STATIC_URL}), |
|
13 |
) |
b/poller/views.py | ||
---|---|---|
1 |
from gevent import monkey |
|
2 |
monkey.patch_all() |
|
3 |
from gevent.pool import Pool |
|
4 |
|
|
5 |
import uuid |
|
6 |
import simplejson |
|
7 |
import datetime |
|
8 |
from django.shortcuts import render_to_response |
|
9 |
from django.template.loader import render_to_string |
|
10 |
from django.http import HttpResponse |
|
11 |
from gevent.event import Event |
|
12 |
from django.conf import settings |
|
13 |
from django.views.decorators.csrf import csrf_exempt |
|
14 |
|
|
15 |
from flowspy.utils import beanstalkc |
|
16 |
|
|
17 |
import logging |
|
18 |
|
|
19 |
FORMAT = '%(asctime)s %(levelname)s: %(message)s' |
|
20 |
logging.basicConfig(format=FORMAT) |
|
21 |
logger = logging.getLogger(__name__) |
|
22 |
logger.setLevel(logging.DEBUG) |
|
23 |
|
|
24 |
|
|
25 |
def create_message(from_, body): |
|
26 |
data = {'id': str(uuid.uuid4()), 'from': from_, 'body': body} |
|
27 |
data['html'] = render_to_string('poll_message.html', dictionary={'message': data}) |
|
28 |
return data |
|
29 |
|
|
30 |
|
|
31 |
def json_response(value, **kwargs): |
|
32 |
kwargs.setdefault('content_type', 'text/javascript; charset=UTF-8') |
|
33 |
return HttpResponse(simplejson.dumps(value), **kwargs) |
|
34 |
|
|
35 |
class Msgs(object): |
|
36 |
cache_size = 200 |
|
37 |
|
|
38 |
def __init__(self): |
|
39 |
self.cache = [] |
|
40 |
self.new_message_event = Event() |
|
41 |
|
|
42 |
def main(self, request): |
|
43 |
if self.cache: |
|
44 |
request.session['cursor'] = self.cache[-1]['id'] |
|
45 |
return render_to_response('poll.html', {'messages': self.cache}) |
|
46 |
|
|
47 |
@csrf_exempt |
|
48 |
def message_existing(self, request): |
|
49 |
if self.cache: |
|
50 |
request.session['cursor'] = self.cache[-1]['id'] |
|
51 |
return json_response({'messages': self.cache}) |
|
52 |
|
|
53 |
@csrf_exempt |
|
54 |
def message_new(self, request=None, mesg=None): |
|
55 |
if request: |
|
56 |
name = request.META.get('REMOTE_ADDR') or 'Anonymous' |
|
57 |
forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') |
|
58 |
if forwarded_for and name == '127.0.0.1': |
|
59 |
name = forwarded_for |
|
60 |
msg = create_message(name, request.POST['body']) |
|
61 |
if mesg: |
|
62 |
message = mesg |
|
63 |
now = datetime.datetime.now() |
|
64 |
msg = create_message("[%s]"%now.strftime("%Y-%m-%d %H:%M:%S"), message) |
|
65 |
self.cache.append(msg) |
|
66 |
if len(self.cache) > self.cache_size: |
|
67 |
self.cache = self.cache[-self.cache_size:] |
|
68 |
self.new_message_event.set() |
|
69 |
self.new_message_event.clear() |
|
70 |
return json_response(msg) |
|
71 |
|
|
72 |
@csrf_exempt |
|
73 |
def message_updates(self, request): |
|
74 |
cursor = request.session.get('cursor') |
|
75 |
if not self.cache or cursor == self.cache[-1]['id']: |
|
76 |
self.new_message_event.wait() |
|
77 |
assert cursor != self.cache[-1]['id'], cursor |
|
78 |
try: |
|
79 |
for index, m in enumerate(self.cache): |
|
80 |
if m['id'] == cursor: |
|
81 |
return json_response({'messages': self.cache[index + 1:]}) |
|
82 |
return json_response({'messages': self.cache}) |
|
83 |
finally: |
|
84 |
if self.cache: |
|
85 |
request.session['cursor'] = self.cache[-1]['id'] |
|
86 |
else: |
|
87 |
request.session.pop('cursor', None) |
|
88 |
|
|
89 |
def monitor_polls(self, polls=None): |
|
90 |
b = beanstalkc.Connection() |
|
91 |
b.watch(settings.POLLS_TUBE) |
|
92 |
while True: |
|
93 |
job = b.reserve() |
|
94 |
msg = job.body |
|
95 |
job.bury() |
|
96 |
self.message_new(None, msg) |
|
97 |
|
|
98 |
|
|
99 |
def start_polling(self): |
|
100 |
logger.info("Start Polling") |
|
101 |
p = Pool(10) |
|
102 |
while True: |
|
103 |
p.spawn(self.monitor_polls) |
|
104 |
|
|
105 |
msgs = Msgs() |
|
106 |
|
|
107 |
main = msgs.main |
|
108 |
|
|
109 |
message_new = msgs.message_new |
|
110 |
message_updates = msgs.message_updates |
|
111 |
message_existing = msgs.message_existing |
|
112 |
|
|
113 |
poll = msgs.start_polling |
|
114 |
poll() |
|
115 |
|
|
116 |
|
|
117 |
|
|
118 |
|
|
119 |
|
|
120 |
|
|
121 |
|
|
122 |
|
|
123 |
|
|
124 |
|
b/run_poller.py | ||
---|---|---|
1 |
#!/usr/bin/python |
|
2 |
from gevent.wsgi import WSGIServer |
|
3 |
from poller.application import application |
|
4 |
print 'Serving on 8000...' |
|
5 |
WSGIServer(('netdev.grnet.gr', 9090), application).serve_forever() |
b/static/css/poller.css | ||
---|---|---|
1 |
/* |
|
2 |
* Copyright 2009 FriendFeed |
|
3 |
* |
|
4 |
* Licensed under the Apache License, Version 2.0 (the "License"); you may |
|
5 |
* not use this file except in compliance with the License. You may obtain |
|
6 |
* a copy of the License at |
|
7 |
* |
|
8 |
* http://www.apache.org/licenses/LICENSE-2.0 |
|
9 |
* |
|
10 |
* Unless required by applicable law or agreed to in writing, software |
|
11 |
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|
12 |
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|
13 |
* License for the specific language governing permissions and limitations |
|
14 |
* under the License. |
|
15 |
*/ |
|
16 |
|
|
17 |
body { |
|
18 |
background: white; |
|
19 |
margin: 10px; |
|
20 |
} |
|
21 |
|
|
22 |
body, |
|
23 |
input { |
|
24 |
font-family: sans-serif; |
|
25 |
font-size: 10pt; |
|
26 |
color: black; |
|
27 |
} |
|
28 |
|
|
29 |
table { |
|
30 |
border-collapse: collapse; |
|
31 |
border: 0; |
|
32 |
} |
|
33 |
|
|
34 |
td { |
|
35 |
border: 0; |
|
36 |
padding: 0; |
|
37 |
} |
|
38 |
|
|
39 |
#body { |
|
40 |
position: absolute; |
|
41 |
bottom: 10px; |
|
42 |
left: 10px; |
|
43 |
right: 100px; |
|
44 |
} |
|
45 |
|
|
46 |
#input { |
|
47 |
margin-top: 0.5em; |
|
48 |
} |
|
49 |
|
|
50 |
#inbox .message { |
|
51 |
padding-top: 0.25em; |
|
52 |
} |
|
53 |
|
|
54 |
#nav { |
|
55 |
text-align: right; |
|
56 |
float: right; |
|
57 |
z-index: 99; |
|
58 |
} |
b/static/js/poller.js | ||
---|---|---|
1 |
// Copyright 2009 FriendFeed |
|
2 |
// |
|
3 |
// Licensed under the Apache License, Version 2.0 (the "License"); you may |
|
4 |
// not use this file except in compliance with the License. You may obtain |
|
5 |
// a copy of the License at |
|
6 |
// |
|
7 |
// http://www.apache.org/licenses/LICENSE-2.0 |
|
8 |
// |
|
9 |
// Unless required by applicable law or agreed to in writing, software |
|
10 |
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|
11 |
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|
12 |
// License for the specific language governing permissions and limitations |
|
13 |
// under the License. |
|
14 |
|
|
15 |
$(document).ready(function() { |
|
16 |
if (!window.console) window.console = {}; |
|
17 |
if (!window.console.log) window.console.log = function() {}; |
|
18 |
|
|
19 |
$("#messageform").live("submit", function() { |
|
20 |
newMessage($(this)); |
|
21 |
return false; |
|
22 |
}); |
|
23 |
$("#messageform").live("keypress", function(e) { |
|
24 |
if (e.keyCode == 13) { |
|
25 |
newMessage($(this)); |
|
26 |
return false; |
|
27 |
} |
|
28 |
}); |
|
29 |
$("#message").select(); |
|
30 |
updater.start(); |
|
31 |
updater.poll(); |
|
32 |
}); |
|
33 |
|
|
34 |
function newMessage(form) { |
|
35 |
var message = form.formToDict(); |
|
36 |
var disabled = form.find("input[type=submit]"); |
|
37 |
disabled.disable(); |
|
38 |
$.postJSON("/poll/a/message/new", message, function(response) { |
|
39 |
updater.showMessage(response); |
|
40 |
if (message.id) { |
|
41 |
form.parent().remove(); |
|
42 |
} else { |
|
43 |
form.find("input[type=text]").val("").select(); |
|
44 |
disabled.enable(); |
|
45 |
} |
|
46 |
}); |
|
47 |
} |
|
48 |
|
|
49 |
function getCookie(name) { |
|
50 |
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b"); |
|
51 |
return r ? r[1] : undefined; |
|
52 |
} |
|
53 |
|
|
54 |
jQuery.postJSON = function(url, args, callback) { |
|
55 |
args._xsrf = getCookie("_xsrf"); |
|
56 |
$.ajax({url: url, data: $.param(args), dataType: "text", type: "POST", |
|
57 |
success: function(response) { |
|
58 |
if (callback) callback(eval("(" + response + ")")); |
|
59 |
}, error: function(response) { |
|
60 |
console.log("ERROR:", response) |
|
61 |
}}); |
|
62 |
}; |
|
63 |
|
|
64 |
jQuery.fn.formToDict = function() { |
|
65 |
var fields = this.serializeArray(); |
|
66 |
var json = {} |
|
67 |
for (var i = 0; i < fields.length; i++) { |
|
68 |
json[fields[i].name] = fields[i].value; |
|
69 |
} |
|
70 |
if (json.next) delete json.next; |
|
71 |
return json; |
|
72 |
}; |
|
73 |
|
|
74 |
jQuery.fn.disable = function() { |
|
75 |
this.enable(false); |
|
76 |
return this; |
|
77 |
}; |
|
78 |
|
|
79 |
jQuery.fn.enable = function(opt_enable) { |
|
80 |
if (arguments.length && !opt_enable) { |
|
81 |
this.attr("disabled", "disabled"); |
|
82 |
} else { |
|
83 |
this.removeAttr("disabled"); |
|
84 |
} |
|
85 |
return this; |
|
86 |
}; |
|
87 |
|
|
88 |
var updater = { |
|
89 |
errorSleepTime: 500, |
|
90 |
cursor: null, |
|
91 |
|
|
92 |
start: function() { |
|
93 |
var args = {"_xsrf": getCookie("_xsrf")}; |
|
94 |
if (updater.cursor) args.cursor = updater.cursor; |
|
95 |
$.ajax({url: "/poll/a/message/existing", type: "POST", dataType: "text", |
|
96 |
data: $.param(args), success: updater.onFetchExisting, |
|
97 |
error: updater.onError}); |
|
98 |
}, |
|
99 |
|
|
100 |
poll: function() { |
|
101 |
var args = {"_xsrf": getCookie("_xsrf")}; |
|
102 |
if (updater.cursor) args.cursor = updater.cursor; |
|
103 |
$.ajax({url: "/poll/a/message/updates", type: "POST", dataType: "text", |
|
104 |
data: $.param(args), success: updater.onSuccess, |
|
105 |
error: updater.onError}); |
|
106 |
}, |
|
107 |
|
|
108 |
onSuccess: function(response) { |
|
109 |
try { |
|
110 |
updater.newMessages(eval("(" + response + ")")); |
|
111 |
} catch (e) { |
|
112 |
updater.onError(); |
|
113 |
return; |
|
114 |
} |
|
115 |
updater.errorSleepTime = 500; |
|
116 |
window.setTimeout(updater.poll, 0); |
|
117 |
}, |
|
118 |
|
|
119 |
onFetchExisting: function(response) { |
|
120 |
try { |
|
121 |
updater.existingMessages(eval("(" + response + ")")); |
|
122 |
} catch (e) { |
|
123 |
updater.onError(); |
|
124 |
return; |
|
125 |
} |
|
126 |
}, |
|
127 |
|
|
128 |
onError: function(response) { |
|
129 |
updater.errorSleepTime *= 2; |
|
130 |
console.log("Poll error; sleeping for", updater.errorSleepTime, "ms"); |
|
131 |
window.setTimeout(updater.poll, updater.errorSleepTime); |
|
132 |
}, |
|
133 |
|
|
134 |
newMessages: function(response) { |
|
135 |
if (!response.messages) return; |
|
136 |
updater.cursor = response.cursor; |
|
137 |
var messages = response.messages; |
|
138 |
updater.cursor = messages[messages.length - 1].id; |
|
139 |
console.log(messages.length, "new messages, cursor:", updater.cursor); |
|
140 |
|
|
141 |
for (var i = 0; i < messages.length; i++) { |
|
142 |
updater.showMessage(messages[i]); |
|
143 |
} |
|
144 |
if (($('#console').dialog('isOpen')) == false){ |
|
145 |
blink("#consolebutton"); |
|
146 |
} |
|
147 |
}, |
|
148 |
|
|
149 |
existingMessages: function(response) { |
|
150 |
if (!response.messages) return; |
|
151 |
updater.cursor = response.cursor; |
|
152 |
var messages = response.messages; |
|
153 |
updater.cursor = messages[messages.length - 1].id; |
|
154 |
for (var i = 0; i < messages.length; i++) { |
|
155 |
updater.showMessage(messages[i]); |
|
156 |
} |
|
157 |
}, |
|
158 |
|
|
159 |
showMessage: function(message) { |
|
160 |
var existing = $("#m" + message.id); |
|
161 |
if (existing.length > 0) return; |
|
162 |
var node = $(message.html); |
|
163 |
node.hide(); |
|
164 |
// $('#inbox').val($('#inbox').val()+message.text); |
|
165 |
$("#inbox").append(node); |
|
166 |
node.slideDown(); |
|
167 |
} |
|
168 |
}; |
|
169 |
|
|
170 |
function blink(selector){ |
|
171 |
$(selector).animate({ color: "red" }, 500, function(){ |
|
172 |
$(this).animate({ color: "#555555" }, 500, function(){ |
|
173 |
blink(this); |
|
174 |
}); |
|
175 |
}); |
|
176 |
} |
|
177 |
|
b/templates/apply.html | ||
---|---|---|
1 | 1 |
{% extends "base.html" %} |
2 | 2 |
{% load i18n %} |
3 |
{% block title %}{% trans "Create new Route" %}{% endblock %} |
|
4 |
{% block breadcrumbs %}:: {% trans "Create Route" %}{% endblock %} |
|
3 |
|
|
4 |
{% block title %} |
|
5 |
{% if edit %} |
|
6 |
{% trans "Edit Route" %} {{form.data.name}} |
|
7 |
{% else %} |
|
8 |
{% trans "Create new Route" %} |
|
9 |
{% endif %} |
|
10 |
{% endblock %} |
|
11 |
|
|
12 |
{% block breadcrumbs %}:: |
|
13 |
{% if edit %} |
|
14 |
{% trans "Edit route" %} {{form.data.name}} |
|
15 |
{% else %} |
|
16 |
{% trans "Create route" %} |
|
17 |
{% endif %} |
|
18 |
{% endblock %} |
|
19 |
|
|
5 | 20 |
{% block content %} |
6 | 21 |
<style type="text/css"> |
7 | 22 |
th { |
... | ... | |
17 | 32 |
</style> |
18 | 33 |
|
19 | 34 |
<div align="center"> |
35 |
{% if edit %} |
|
36 |
<h3>{% trans "Edit route" %}: {{form.data.name}}</h3> |
|
37 |
{% else %} |
|
20 | 38 |
<h3>{% trans "Apply for a new route" %}</h3> |
39 |
{% endif %} |
|
21 | 40 |
<form method="POST"> |
22 | 41 |
{% csrf_token %} |
23 | 42 |
{% if form.non_field_errors %} |
24 | 43 |
<p class="error">{{ form.non_field_errors|join:", "}}</p> |
25 | 44 |
{% endif %} |
26 |
<fieldset> |
|
45 |
|
|
46 |
<fieldset {% if edit %} style="display:none;" {% endif %}> |
|
27 | 47 |
<legend>{% trans "Route Basic Info" %}</legend> |
28 | 48 |
<table> |
29 | 49 |
<tr><th>{{ form.name.label_tag }}</th><td>{{ form.name }}<span class="error">{{ form.name.errors|join:", " }}</span></td></tr> |
b/templates/base.html | ||
---|---|---|
8 | 8 |
<link rel="stylesheet" type="text/css" href="/static/css/base.css"> |
9 | 9 |
<link rel="stylesheet" type="text/css" href="/static/css/smoothness/jquery-ui-1.8.13.custom.css"> |
10 | 10 |
<script type="text/javascript" src="/static/js/jquery-ui-1.8.12.custom.min.js"></script> |
11 |
<script type="text/javascript" src="/static/js/poller.js"></script> |
|
11 | 12 |
<script type="text/javascript"> |
12 | 13 |
|
13 | 14 |
function setlang(lang){ |
... | ... | |
79 | 80 |
<div class="info_content_title">{% if user.is_authenticated %}<a href="/">{% trans "My routes" %}</a>{% endif %} |
80 | 81 |
{% block breadcrumbs %}{% endblock %} |
81 | 82 |
</div> |
82 |
{% if messages %} |
|
83 |
<div id="messages"> |
|
84 |
{% for message in messages %} |
|
85 |
<span{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</span><br /> |
|
86 |
{% endfor %} |
|
87 |
</div> |
|
88 |
{% endif %} |
|
83 |
|
|
89 | 84 |
{% endblock %} |
90 | 85 |
{% block content %} |
91 | 86 |
{% endblock %} |
b/templates/poll.html | ||
---|---|---|
1 |
<div id="inbox" > |
|
2 |
{% for message in messages %} |
|
3 |
{% include "poll_message.html" %} |
|
4 |
{% endfor %} |
|
5 |
</div> |
b/templates/poll_message.html | ||
---|---|---|
1 |
<div class="message" id="m{{ message.id }}"><b>{{ message.from }}: </b>{{ message.body }}</div> |
b/templates/user_routes.html | ||
---|---|---|
4 | 4 |
<script type="text/javascript" src="/static/js/jquery.dataTables.js"></script> |
5 | 5 |
<script type="text/javascript"> |
6 | 6 |
$(document).ready( function(){ |
7 |
$('#create_dialog').dialog({
|
|
8 |
height: 400,
|
|
9 |
width: 500,
|
|
7 |
$('#dialog').dialog({ |
|
8 |
height: 220,
|
|
9 |
width: 300,
|
|
10 | 10 |
modal: true, |
11 | 11 |
autoOpen: false, |
12 |
buttons: { |
|
13 |
'Delete': function() { |
|
14 |
route = $('#route_to_delete').text(); |
|
15 |
route_url_id = '#del_route_'+route; |
|
16 |
console.log(route_url_id); |
|
17 |
url = $(route_url_id).attr('href'); |
|
18 |
console.log(route, url); |
|
19 |
$.ajax({ |
|
20 |
url: url , |
|
21 |
cache: false, |
|
22 |
success: function(data) { |
|
23 |
$('#dialog').dialog('close'); |
|
24 |
} |
|
25 |
}); |
|
26 |
// $('#dialog').dialog('close'); |
|
27 |
}, |
|
28 |
Cancel: function() { |
|
29 |
$('#dialog').dialog('close'); |
|
30 |
} |
|
31 |
} |
|
12 | 32 |
}); |
33 |
$('#console').dialog({ |
|
34 |
height: 250, |
|
35 |
width: 500, |
|
36 |
modal: true, |
|
37 |
autoOpen: false |
|
38 |
}); |
|
13 | 39 |
$('#routes_table').dataTable( { |
14 | 40 |
"bJQueryUI": true, |
15 | 41 |
"oLanguage": { |
16 |
"sLengthMenu": '{% trans "Display" %} <select><option value="25">25</option><option value="50">50</option><option value="-1">{% trans "All" %}</option></select> rules'
|
|
42 |
"sLengthMenu": '{% trans "Display" %} <select><option value="25">25</option><option value="50">50</option><option value="-1">{% trans "All" %}</option></select> routes'
|
|
17 | 43 |
}, |
18 | 44 |
"iDisplayLength": 25, |
19 | 45 |
} ); |
... | ... | |
22 | 48 |
primary: "ui-icon-circle-plus" |
23 | 49 |
}, |
24 | 50 |
}); |
51 |
$( " .edit_button" ).button({ |
|
52 |
icons: { |
|
53 |
primary: "ui-icon-wrench" |
|
54 |
}, |
|
55 |
}); |
|
56 |
$( " .del_button" ).button({ |
|
57 |
icons: { |
|
58 |
primary: "ui-icon-circle-close" |
|
59 |
}, |
|
60 |
}) |
|
61 |
.click(function(){ |
|
62 |
$('#dialog').dialog('open'); |
|
63 |
return false; |
|
64 |
}); |
|
65 |
$("#consolebutton").button({ |
|
66 |
icons: { |
|
67 |
primary: "ui-icon-image" |
|
68 |
}, |
|
69 |
}) |
|
70 |
.click(function(){ |
|
71 |
$("#consolebutton").stop().stop(); |
|
72 |
$("#consolebutton").css('color', '#555555'); |
|
73 |
$('#console').dialog('open'); |
|
74 |
return false; |
|
75 |
}); |
|
25 | 76 |
}); |
26 | 77 |
|
78 |
function delete_route(route){ |
|
79 |
route_name = route; |
|
80 |
$('#route_to_delete').text(route_name); |
|
81 |
return false; |
|
82 |
} |
|
27 | 83 |
|
28 | 84 |
|
29 | 85 |
|
30 | 86 |
</script> |
87 |
<style type="text/css"> |
|
88 |
#console { |
|
89 |
background: none repeat scroll 0 0 #36102a !important; |
|
90 |
color: #edeae8 !important; |
|
91 |
font-family: monospace !important; |
|
92 |
} |
|
93 |
.message { |
|
94 |
font-family: monospace !important; |
|
95 |
} |
|
96 |
|
|
97 |
</style> |
|
31 | 98 |
{% endblock %} |
32 | 99 |
{% block title %}{% trans "My routes" %}{% endblock %} |
33 | 100 |
{% block content %} |
... | ... | |
35 | 102 |
<h3 style="margin-top: 0px;">{% trans "My routes" %}</h3> |
36 | 103 |
</div> |
37 | 104 |
<div class='button_place' style="float:right"> |
38 |
<a href="{% url add-route %}" id="routebutton">Add Route</a> |
|
105 |
<button id="consolebutton">Console</button> <a href="{% url add-route %}" id="routebutton">Add Route</a>
|
|
39 | 106 |
</div> |
40 |
|
|
41 | 107 |
<table class="display" width="100%" id="routes_table"> |
42 | 108 |
<thead> |
43 | 109 |
<tr> |
... | ... | |
48 | 114 |
{% comment %}<th style="text-align: center;">{% trans "Details" %}</th>{% endcomment %} |
49 | 115 |
<th style="text-align: center;">{% trans "Expires" %}</th> |
50 | 116 |
<th style="text-align: center;">{% trans "Response" %}</th> |
117 |
<th style="text-align: center; width:120px;">{% trans "Actions" %}</th> |
|
51 | 118 |
</tr> |
52 | 119 |
</thead> |
53 | 120 |
|
... | ... | |
62 | 129 |
{% comment %}<td style="text-align: center;">{{ route.response }}</td>{% endcomment %} |
63 | 130 |
<td style="text-align: center;">{{ route.expires }}</td> |
64 | 131 |
<td style="text-align: center;">{{ route.response }}</td> |
132 |
<td style="text-align: center;"> |
|
133 |
{% if route.is_active %} |
|
134 |
<a href="{% url edit-route route.name %}" class="edit_button" id="edit_button_{{route.pk}}">Edit</a> |
|
135 |
<button class="del_button" id="{{route.name}}" onclick="javascript:delete_route(this.id)">Del</button> |
|
136 |
<a href="{% url delete-route route.name %}" style="display:none" id="del_route_{{route.name}}"></a> |
|
137 |
{% else %} |
|
138 |
- |
|
139 |
{% endif %}</td> |
|
65 | 140 |
</tr> |
66 | 141 |
|
67 | 142 |
{% endfor %} |
68 | 143 |
</tbody> |
69 | 144 |
</table> |
70 | 145 |
|
71 |
<div id="create_dialog" title="Add a new Route"> |
|
72 |
KOKO |
|
73 |
</div> |
|
74 |
|
|
146 |
<div id="dialog" title="Delete Route"> |
|
147 |
<p>You are about to delete route <strong><span id="route_to_delete"></span></strong></p> |
|
148 |
<p>Deleting the route will automatically remove the configuration from the network and mark this route as inactive.</p> |
|
149 |
<p>Are you sure you want to proceed?</p> |
|
150 |
</div> |
|
151 |
<div id="console" title="Console"> |
|
152 |
{% include "poll.html" %} |
|
153 |
</div> |
|
75 | 154 |
{% endblock %} |
b/urls.py | ||
---|---|---|
7 | 7 |
urlpatterns = patterns('', |
8 | 8 |
# Example: |
9 | 9 |
# (r'^flowspy/', include('flowspy.foo.urls')), |
10 |
(r'^poll/', include('flowspy.poller.urls')), |
|
10 | 11 |
url(r'^/?$', 'flowspy.flowspec.views.user_routes', name="user-routes"), |
11 | 12 |
url(r'^add/?$', 'flowspy.flowspec.views.add_route', name="add-route"), |
13 |
url(r'^edit/(?P<route_slug>\w+)/$', 'flowspy.flowspec.views.edit_route', name="edit-route"), |
|
14 |
url(r'^delete/(?P<route_slug>\w+)/$', 'flowspy.flowspec.views.delete_route', name="delete-route"), |
|
12 | 15 |
url(r'^user/login/?', 'django.contrib.auth.views.login', {'template_name': 'login.html'}, name="login"), |
13 | 16 |
url(r'^user/logout/?', 'django.contrib.auth.views.logout', {'next_page': '/'}, name="logout"), |
14 | 17 |
(r'^setlang/?$', 'django.views.i18n.set_language'), |
15 | 18 |
# Uncomment the admin/doc line below to enable admin documentation: |
16 | 19 |
(r'^admin/doc/', include('django.contrib.admindocs.urls')), |
17 | 20 |
|
21 |
|
|
18 | 22 |
# Uncomment the next line to enable the admin: |
19 | 23 |
(r'^admin/', include(admin.site.urls)), |
20 | 24 |
) |
b/utils/proxy.py | ||
---|---|---|
56 | 56 |
self.username = username |
57 | 57 |
self.password = password |
58 | 58 |
|
59 |
def to_xml(self): |
|
59 |
def to_xml(self, operation=None): |
|
60 |
logger.info("Operation: %s"%operation) |
|
60 | 61 |
if self.route_object: |
61 | 62 |
logger.info("Generating XML config") |
62 | 63 |
route_obj = self.route_object |
... | ... | |
66 | 67 |
flow.routes.append(route) |
67 | 68 |
device.routing_options.append(flow) |
68 | 69 |
route.name = route_obj.name |
70 |
if operation == "delete": |
|
71 |
logger.info("Requesting a delete operation") |
|
72 |
route.operation = operation |
|
73 |
device = device.export(netconf_config=True) |
|
74 |
return ET.tostring(device) |
|
69 | 75 |
if route_obj.source: |
70 | 76 |
route.match['source'].append(route_obj.source) |
71 | 77 |
if route_obj.destination: |
... | ... | |
109 | 115 |
route.then[thenaction.action] = thenaction.action_value |
110 | 116 |
else: |
111 | 117 |
route.then[thenaction.action] = True |
112 |
device = device.export(netconf_config=True) |
|
113 |
return ET.tostring(device) |
|
114 |
else: |
|
115 |
return False |
|
116 |
|
|
117 |
def delete_route(self): |
|
118 |
if self.route_object: |
|
119 |
logger.info("Generating XML config") |
|
120 |
route_obj = self.route_object |
|
121 |
device = np.Device() |
|
122 |
flow = np.Flow() |
|
123 |
route = np.Route() |
|
124 |
flow.routes.append(route) |
|
125 |
device.routing_options.append(flow) |
|
126 |
route.name = route_obj.name |
|
127 |
route.operation = 'delete' |
|
118 |
if operation == "replace": |
|
119 |
logger.info("Requesting a replace operation") |
|
120 |
route.operation = operation |
|
128 | 121 |
device = device.export(netconf_config=True) |
129 | 122 |
return ET.tostring(device) |
130 | 123 |
else: |
... | ... | |
147 | 140 |
else: |
148 | 141 |
return False |
149 | 142 |
|
150 |
def apply(self, configuration = None): |
|
143 |
def apply(self, configuration = None, operation=None):
|
|
151 | 144 |
reason = None |
152 | 145 |
if not configuration: |
153 |
configuration = self.to_xml() |
|
146 |
configuration = self.to_xml(operation=operation)
|
|
154 | 147 |
edit_is_successful = False |
155 | 148 |
commit_confirmed_is_successful = False |
156 | 149 |
commit_is_successful = False |
Also available in: Unified diff