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