Revision 97e42c7d
b/apache/django.wsgi | ||
---|---|---|
1 |
import os |
|
2 |
import sys |
|
3 |
|
|
4 |
sys.path.append('/home/leopoul/projects/') |
|
5 |
sys.path.append('/home/leopoul/projects/flowspy') |
|
6 |
|
|
7 |
os.environ['DJANGO_SETTINGS_MODULE'] = 'flowspy.settings' |
|
8 |
|
|
9 |
from gevent import monkey; monkey.patch_all() |
|
10 |
|
|
11 |
|
|
12 |
import django.core.handlers.wsgi |
|
13 |
application = django.core.handlers.wsgi.WSGIHandler() |
b/djangobackends/shibauthBackend.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- vim:encoding=utf-8: |
|
2 |
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab |
|
3 |
|
|
4 |
from django.contrib.auth.models import User, UserManager, Permission, Group |
|
5 |
from django.conf import settings |
|
6 |
from flowspy.peers.models import * |
|
7 |
from flowspy.accounts.models import * |
|
8 |
|
|
9 |
class shibauthBackend: |
|
10 |
def authenticate(self, **kwargs): |
|
11 |
|
|
12 |
username = kwargs.get('username') |
|
13 |
firstname = kwargs.get('firstname') |
|
14 |
lastname = kwargs.get('lastname') |
|
15 |
mail = kwargs.get('mail') |
|
16 |
affiliation = kwargs.get('affiliation') |
|
17 |
organization = kwargs.get('organization') |
|
18 |
user = self._auth_user(username, firstname, lastname, mail, affiliation, organization) |
|
19 |
if not user: |
|
20 |
return None |
|
21 |
return user |
|
22 |
|
|
23 |
def _auth_user(self, username, firstname, lastname, mail, affiliation, organization): |
|
24 |
|
|
25 |
try: |
|
26 |
user = User.objects.get(username__exact=username) |
|
27 |
# The user did not exist. Create one with no privileges |
|
28 |
except: |
|
29 |
user = User.objects.create_user(username, mail, None) |
|
30 |
user.first_name = firstname |
|
31 |
user.last_name = lastname |
|
32 |
user.is_staff = False |
|
33 |
user.is_superuser = False |
|
34 |
# if organization == settings.SHIB_ADMIN_DOMAIN: |
|
35 |
# user.is_staff = True |
|
36 |
# user.is_superuser = True |
|
37 |
user.is_active = True |
|
38 |
try: |
|
39 |
peer = Peer.objects.get(domain_name=organization) |
|
40 |
up = UserProfile.objects.get_or_create(user=user,peer=peer) |
|
41 |
except: |
|
42 |
pass |
|
43 |
return user |
|
44 |
|
|
45 |
def get_user(self, user_id): |
|
46 |
try: |
|
47 |
return User.objects.get(pk=user_id) |
|
48 |
except User.DoesNotExist: |
|
49 |
return None |
b/flowspec/admin.py | ||
---|---|---|
3 | 3 |
from flowspy.accounts.models import * |
4 | 4 |
from utils import proxy as PR |
5 | 5 |
|
6 |
class RouteAdmin(admin.ModelAdmin): |
|
7 |
|
|
8 |
actions = ['deactivate'] |
|
9 |
|
|
10 |
def deactivate(self, request, queryset): |
|
11 |
applier = PR.Applier(route_objects=queryset) |
|
12 |
commit, response = applier.apply(configuration=applier.delete_routes()) |
|
13 |
if commit: |
|
14 |
rows = queryset.update(is_online=False, is_active=False) |
|
15 |
queryset.update(response="Successfully removed route from network") |
|
16 |
self.message_user(request, "Successfully removed %s routes from network" % rows) |
|
17 |
else: |
|
18 |
self.message_user(request, "Could not remove routes from network") |
|
19 |
deactivate.short_description = "Deactivate selected routes from network" |
|
20 |
|
|
21 |
list_display = ('name', 'is_online', 'applier', 'get_match', 'get_then', 'response') |
|
22 |
fieldsets = [ |
|
23 |
(None, {'fields': ['name','applier']}), |
|
24 |
("Match", {'fields': ['source', 'sourceport', 'destination', 'destinationport', 'port']}), |
|
25 |
('Advanced Match Statements', {'fields': ['dscp', 'fragmenttype', 'icmpcode', 'icmptype', 'packetlength', 'protocol', 'tcpflag'], 'classes': ['collapse']}), |
|
26 |
("Then", {'fields': ['then' ]}), |
|
27 |
(None, {'fields': ['comments',]}), |
|
28 |
|
|
29 |
] |
|
6 |
#class RouteAdmin(admin.ModelAdmin):
|
|
7 |
# |
|
8 |
# actions = ['deactivate']
|
|
9 |
# |
|
10 |
# def deactivate(self, request, queryset):
|
|
11 |
# applier = PR.Applier(route_objects=queryset)
|
|
12 |
# commit, response = applier.apply(configuration=applier.delete_routes())
|
|
13 |
# if commit:
|
|
14 |
# rows = queryset.update(is_online=False, is_active=False)
|
|
15 |
# queryset.update(response="Successfully removed route from network")
|
|
16 |
# self.message_user(request, "Successfully removed %s routes from network" % rows)
|
|
17 |
# else:
|
|
18 |
# self.message_user(request, "Could not remove routes from network")
|
|
19 |
# deactivate.short_description = "Deactivate selected routes from network"
|
|
20 |
# |
|
21 |
# list_display = ('name', 'is_online', 'applier', 'get_match', 'get_then', 'response')
|
|
22 |
# fieldsets = [
|
|
23 |
# (None, {'fields': ['name','applier']}),
|
|
24 |
# ("Match", {'fields': ['source', 'sourceport', 'destination', 'destinationport', 'port']}),
|
|
25 |
# ('Advanced Match Statements', {'fields': ['dscp', 'fragmenttype', 'icmpcode', 'icmptype', 'packetlength', 'protocol', 'tcpflag'], 'classes': ['collapse']}),
|
|
26 |
# ("Then", {'fields': ['then' ]}),
|
|
27 |
# (None, {'fields': ['comments',]}),
|
|
28 |
# |
|
29 |
# ]
|
|
30 | 30 |
# fields = ('name', 'applier', 'expires') |
31 | 31 |
|
32 | 32 |
#def formfield_for_dbfield(self, db_field, **kwargs): |
... | ... | |
47 | 47 |
admin.site.register(ThenAction) |
48 | 48 |
#admin.site.register(ThenStatement) |
49 | 49 |
#admin.site.register(MatchStatement) |
50 |
admin.site.register(Route, RouteAdmin)
|
|
50 |
admin.site.register(Route) |
|
51 | 51 |
|
52 | 52 |
admin.site.disable_action('delete_selected') |
53 | 53 |
|
b/flowspec/forms.py | ||
---|---|---|
3 | 3 |
from django.utils.translation import ugettext as _ |
4 | 4 |
from django.utils.translation import ugettext_lazy |
5 | 5 |
from django.template.defaultfilters import filesizeformat |
6 |
|
|
7 | 6 |
from flowspy.flowspec.models import * |
8 | 7 |
from ipaddr import * |
8 |
from django.contrib.auth.models import User |
|
9 | 9 |
|
10 | 10 |
class RouteForm(forms.ModelForm): |
11 | 11 |
# name = forms.CharField(help_text=ugettext_lazy("A unique route name," |
... | ... | |
44 | 44 |
ports = self.cleaned_data.get('port', None) |
45 | 45 |
destination = self.cleaned_data.get('destination', None) |
46 | 46 |
destinationports = self.cleaned_data.get('destinationport', None) |
47 |
user = self.cleaned_data.get('applier', None) |
|
48 |
networks = user.get_profile().peer.networks.all() |
|
49 |
mynetwork = False |
|
50 |
if destination: |
|
51 |
for network in networks: |
|
52 |
net = IPNetwork(network.network) |
|
53 |
if IPNetwork(destination) in net: |
|
54 |
mynetwork = True |
|
55 |
if not mynetwork: |
|
56 |
raise forms.ValidationError('Destination address/network should belong to your administrative address space. Check My Profile to review your networks') |
|
47 | 57 |
if (sourceports and ports): |
48 | 58 |
raise forms.ValidationError('Cannot create rule for source ports and ports at the same time. Select either ports or source ports') |
49 | 59 |
if (destinationports and ports): |
... | ... | |
54 | 64 |
raise forms.ValidationError('Once destination port is matched, destination has to be filled as well. Either deselect destination port or fill destination address') |
55 | 65 |
if not (source or sourceports or ports or destination or destinationports): |
56 | 66 |
raise forms.ValidationError('Fill at least a Route Match Condition') |
57 |
return self.cleaned_data |
|
67 |
return self.cleaned_data |
|
68 |
|
|
69 |
class ThenPlainForm(forms.ModelForm): |
|
70 |
# action = forms.CharField(initial='rate-limit') |
|
71 |
class Meta: |
|
72 |
model = ThenAction |
|
73 |
|
|
74 |
def clean_action_value(self): |
|
75 |
action_value = self.cleaned_data['action_value'] |
|
76 |
if action_value: |
|
77 |
try: |
|
78 |
assert(int(action_value)) |
|
79 |
return "%s" %self.cleaned_data["action_value"] |
|
80 |
except: |
|
81 |
raise forms.ValidationError('Rate-limiting should be an integer') |
|
82 |
if int(action_value) < 50: |
|
83 |
raise forms.ValidationError('Rate-limiting cannot be < 50kbps') |
|
84 |
else: |
|
85 |
raise forms.ValidationError('Cannot be empty') |
|
86 |
|
|
87 |
def clean_action(self): |
|
88 |
action = self.cleaned_data['action'] |
|
89 |
if action != 'rate-limit': |
|
90 |
raise forms.ValidationError('Cannot select something other than rate-limit') |
|
91 |
else: |
|
92 |
return self.cleaned_data["action"] |
|
93 |
|
|
94 |
class PortPlainForm(forms.ModelForm): |
|
95 |
# action = forms.CharField(initial='rate-limit') |
|
96 |
class Meta: |
|
97 |
model = MatchPort |
|
98 |
|
|
99 |
def clean_port(self): |
|
100 |
port = self.cleaned_data['port'] |
|
101 |
if port: |
|
102 |
try: |
|
103 |
assert(int(port)) |
|
104 |
return "%s" %self.cleaned_data["port"] |
|
105 |
except: |
|
106 |
raise forms.ValidationError('Port should be an integer') |
|
107 |
else: |
|
108 |
raise forms.ValidationError('Cannot be empty') |
b/flowspec/models.py | ||
---|---|---|
12 | 12 |
from time import sleep |
13 | 13 |
|
14 | 14 |
from flowspy.utils import beanstalkc |
15 |
from flowspy.utils.randomizer import id_generator as id_gen |
|
15 | 16 |
|
16 | 17 |
|
17 | 18 |
FORMAT = '%(asctime)s %(levelname)s: %(message)s' |
... | ... | |
38 | 39 |
("sample", "Sample") |
39 | 40 |
) |
40 | 41 |
|
42 |
ROUTE_STATES = ( |
|
43 |
("ACTIVE", "ACTIVE"), |
|
44 |
("ERROR", "ERROR"), |
|
45 |
("EXPIRED", "EXPIRED"), |
|
46 |
("PENDING", "PENDING"), |
|
47 |
("OUTOFSYNC", "OUTOFSYNC"), |
|
48 |
("INACTIVE", "INACTIVE"), |
|
49 |
) |
|
50 |
|
|
41 | 51 |
|
42 | 52 |
def days_offset(): return datetime.now() + timedelta(days = settings.EXPIRATION_DAYS_OFFSET) |
43 | 53 |
|
44 | 54 |
class MatchPort(models.Model): |
45 |
port = models.CharField(max_length=24) |
|
55 |
port = models.CharField(max_length=24, unique=True)
|
|
46 | 56 |
def __unicode__(self): |
47 | 57 |
return self.port |
48 | 58 |
class Meta: |
... | ... | |
60 | 70 |
action = models.CharField(max_length=60, choices=THEN_CHOICES, verbose_name="Action") |
61 | 71 |
action_value = models.CharField(max_length=255, blank=True, null=True, verbose_name="Action Value") |
62 | 72 |
def __unicode__(self): |
63 |
return "%s: %s" %(self.action, self.action_value) |
|
73 |
ret = "%s:%s" %(self.action, self.action_value) |
|
74 |
return ret.rstrip(":") |
|
64 | 75 |
class Meta: |
65 | 76 |
db_table = u'then_action' |
66 | 77 |
|
... | ... | |
82 | 93 |
then = models.ManyToManyField(ThenAction, verbose_name="Then") |
83 | 94 |
filed = models.DateTimeField(auto_now_add=True) |
84 | 95 |
last_updated = models.DateTimeField(auto_now=True) |
85 |
is_online = models.BooleanField(default=False) |
|
86 |
is_active = models.BooleanField(default=False) |
|
96 |
status = models.CharField(max_length=20, choices=ROUTE_STATES, blank=True, null=True, verbose_name="Status", default="PENDING") |
|
97 |
# is_online = models.BooleanField(default=False) |
|
98 |
# is_active = models.BooleanField(default=False) |
|
87 | 99 |
expires = models.DateField(default=days_offset, blank=True, null=True,) |
88 | 100 |
response = models.CharField(max_length=512, blank=True, null=True) |
89 | 101 |
comments = models.TextField(null=True, blank=True, verbose_name="Comments") |
... | ... | |
93 | 105 |
return self.name |
94 | 106 |
|
95 | 107 |
class Meta: |
96 |
unique_together = (("name", "is_active"),) |
|
97 | 108 |
db_table = u'route' |
98 | 109 |
|
110 |
def save(self, *args, **kwargs): |
|
111 |
if not self.pk: |
|
112 |
hash = id_gen() |
|
113 |
self.name = "%s_%s" %(self.name, hash) |
|
114 |
super(Route, self).save(*args, **kwargs) # Call the "real" save() method. |
|
115 |
|
|
116 |
|
|
99 | 117 |
def clean(self, *args, **kwargs): |
100 | 118 |
from django.core.exceptions import ValidationError |
101 | 119 |
if self.destination: |
... | ... | |
122 | 140 |
# logger.info("Got save job id: %s" %response) |
123 | 141 |
|
124 | 142 |
def commit_add(self, *args, **kwargs): |
125 |
send_message("Adding route %s. Please wait..." %self.name, self.applier) |
|
143 |
peer = self.applier.get_profile().peer.domain_name |
|
144 |
send_message("[%s] Adding route %s. Please wait..." %(self.applier.username, self.name), peer) |
|
126 | 145 |
response = add.delay(self) |
127 | 146 |
logger.info("Got save job id: %s" %response) |
128 | 147 |
|
129 | 148 |
def deactivate(self): |
130 |
self.is_online = False |
|
131 |
self.is_active = False |
|
149 |
self.status = "INACTIVE" |
|
132 | 150 |
self.save() |
133 | 151 |
# def delete(self, *args, **kwargs): |
134 | 152 |
# response = delete.delay(self) |
135 | 153 |
# logger.info("Got delete job id: %s" %response) |
136 | 154 |
|
137 | 155 |
def commit_edit(self, *args, **kwargs): |
138 |
send_message("Editing route %s. Please wait..." %self.name, self.applier) |
|
156 |
peer = self.applier.get_profile().peer.domain_name |
|
157 |
send_message("[%s] Editing route %s. Please wait..." %(self.applier.username, self.name), peer) |
|
139 | 158 |
response = edit.delay(self) |
140 | 159 |
logger.info("Got edit job id: %s" %response) |
141 | 160 |
|
142 | 161 |
def commit_delete(self, *args, **kwargs): |
143 |
send_message("Removing route %s. Please wait..." %self.name, self.applier) |
|
162 |
peer = self.applier.get_profile().peer.domain_name |
|
163 |
send_message("[%s] Removing route %s. Please wait..." %(self.applier.username, self.name), peer) |
|
144 | 164 |
response = delete.delay(self) |
145 | 165 |
logger.info("Got edit job id: %s" %response) |
146 | 166 |
# |
147 | 167 |
# def delete(self, *args, **kwargs): |
148 | 168 |
# response = delete.delay(self) |
149 | 169 |
# logger.info("Got delete job id: %s" %response) |
150 |
def is_synced(self): |
|
151 |
|
|
170 |
|
|
171 |
def is_synced(self): |
|
152 | 172 |
found = False |
153 | 173 |
get_device = PR.Retriever() |
154 | 174 |
device = get_device.fetch_device() |
155 | 175 |
try: |
156 | 176 |
routes = device.routing_options[0].routes |
157 | 177 |
except Exception as e: |
158 |
self.is_online = False
|
|
178 |
self.status = "EXPIRED"
|
|
159 | 179 |
self.save() |
160 | 180 |
logger.error("No routing options on device. Exception: %s" %e) |
161 | 181 |
return False |
... | ... | |
230 | 250 |
logger.info('Protocol fields do not match') |
231 | 251 |
except: |
232 | 252 |
pass |
233 |
if found and not self.is_online:
|
|
253 |
if found and self.status != "ACTIVE":
|
|
234 | 254 |
logger.error('Rule is applied on device but appears as offline') |
235 | 255 |
found = False |
236 | 256 |
|
... | ... | |
252 | 272 |
def get_match(self): |
253 | 273 |
ret = '' |
254 | 274 |
if self.destination: |
255 |
ret = '%s Destination Address:<strong>%s</strong><br/>' %(ret, self.destination)
|
|
275 |
ret = '%s Dst Addr:<strong>%s</strong><br/>' %(ret, self.destination)
|
|
256 | 276 |
if self.fragmenttype: |
257 | 277 |
ret = "%s Fragment Type:<strong>%s</strong><br/>" %(ret, self.fragmenttype) |
258 | 278 |
if self.icmpcode: |
... | ... | |
264 | 284 |
if self.protocol: |
265 | 285 |
ret = "%s Protocol:<strong>%s</strong><br/>" %(ret, self.protocol) |
266 | 286 |
if self.source: |
267 |
ret = "%s Source Address:<strong>%s</strong><br/>" %(ret, self.source)
|
|
287 |
ret = "%s Src Addr:<strong>%s</strong><br/>" %(ret, self.source)
|
|
268 | 288 |
if self.tcpflag: |
269 | 289 |
ret = "%s TCP flag:<strong>%s</strong><br/>" %(ret, self.tcpflag) |
270 | 290 |
if self.port: |
... | ... | |
272 | 292 |
ret = ret + "Port:<strong>%s</strong><br/>" %(port) |
273 | 293 |
if self.destinationport: |
274 | 294 |
for port in self.destinationport.all(): |
275 |
ret = ret + "Destination Port:<strong>%s</strong><br/>" %(port)
|
|
295 |
ret = ret + "Dst Port:<strong>%s</strong><br/>" %(port)
|
|
276 | 296 |
if self.sourceport: |
277 | 297 |
for port in self.sourceport.all(): |
278 |
ret = ret +"Source Port:<strong>%s</strong><br/>" %(port)
|
|
298 |
ret = ret +"Src Port:<strong>%s</strong><br/>" %(port)
|
|
279 | 299 |
if self.dscp: |
280 | 300 |
for dscp in self.dscp.all(): |
281 | 301 |
ret = ret + "%s Port:<strong>%s</strong><br/>" %(ret, dscp) |
... | ... | |
285 | 305 |
get_match.allow_tags = True |
286 | 306 |
|
287 | 307 |
def send_message(msg, user): |
288 |
username = user.username |
|
308 |
# username = user.username |
|
309 |
peer = user |
|
289 | 310 |
b = beanstalkc.Connection() |
290 | 311 |
b.use(settings.POLLS_TUBE) |
291 |
tube_message = json.dumps({'message': str(msg), 'username':username})
|
|
312 |
tube_message = json.dumps({'message': str(msg), 'username':peer})
|
|
292 | 313 |
b.put(tube_message) |
293 | 314 |
b.close() |
b/flowspec/sql/then_action.sql | ||
---|---|---|
1 |
INSERT INTO `then_action` (`id`, `action`, `action_value`) VALUES |
|
2 |
(1, 'accept', ''), |
|
3 |
(2, 'discard', ''); |
b/flowspec/tasks.py | ||
---|---|---|
18 | 18 |
applier = PR.Applier(route_object=route) |
19 | 19 |
commit, response = applier.apply() |
20 | 20 |
if commit: |
21 |
is_online = True |
|
22 |
is_active = True |
|
21 |
status = "ACTIVE" |
|
23 | 22 |
else: |
24 |
is_online = False |
|
25 |
is_active = True |
|
26 |
route.is_online = is_online |
|
27 |
route.is_active = is_active |
|
23 |
status = "ERROR" |
|
24 |
route.status = status |
|
28 | 25 |
route.response = response |
29 |
subtask(announce).delay("Route add: %s - Result: %s" %(route.name, response), route.applier)
|
|
26 |
subtask(announce).delay("[%s] Route add: %s - Result: %s" %(route.applier, route.name, response), route.applier)
|
|
30 | 27 |
route.save() |
31 | 28 |
|
32 | 29 |
@task |
... | ... | |
34 | 31 |
applier = PR.Applier(route_object=route) |
35 | 32 |
commit, response = applier.apply(operation="replace") |
36 | 33 |
if commit: |
37 |
is_online = True
|
|
34 |
status = "ACTIVE"
|
|
38 | 35 |
else: |
39 |
is_online = False |
|
40 |
route.is_active = True |
|
41 |
route.is_online = is_online |
|
36 |
status = "ERROR" |
|
37 |
route.status = status |
|
42 | 38 |
route.response = response |
43 | 39 |
route.save() |
44 |
subtask(announce).delay("Route edit: %s - Result: %s" %(route.name, response), route.applier)
|
|
40 |
subtask(announce).delay("[%s] Route edit: %s - Result: %s"%(route.applier, route.name, response), route.applier)
|
|
45 | 41 |
|
46 | 42 |
|
47 | 43 |
|
... | ... | |
50 | 46 |
applier = PR.Applier(route_object=route) |
51 | 47 |
commit, response = applier.apply(operation="delete") |
52 | 48 |
if commit: |
53 |
is_online = False |
|
54 |
is_active = False |
|
49 |
status = "INACTIVE" |
|
55 | 50 |
else: |
56 |
is_online = route.is_online |
|
57 |
is_active = route.is_active |
|
58 |
route.is_online = is_online |
|
59 |
route.is_active = is_active |
|
51 |
status = "ERROR" |
|
52 |
route.status = status |
|
60 | 53 |
route.response = response |
61 | 54 |
route.save() |
62 |
subtask(announce).delay("Route delete: %s - Result %s" %(route.name, response), route.applier)
|
|
55 |
subtask(announce).delay("[%s] Route delete: %s - Result %s" %(route.applier, route.name, response), route.applier)
|
|
63 | 56 |
|
64 | 57 |
|
65 | 58 |
|
66 | 59 |
@task |
67 | 60 |
def announce(messg, user): |
68 | 61 |
messg = str(messg) |
69 |
username = user.username |
|
62 |
# username = user.username |
|
63 |
username = user.get_profile().peer.domain_name |
|
70 | 64 |
b = beanstalkc.Connection() |
71 | 65 |
b.use(settings.POLLS_TUBE) |
72 | 66 |
tube_message = json.dumps({'message': messg, 'username':username}) |
73 | 67 |
b.put(tube_message) |
74 | 68 |
b.close() |
75 | 69 |
|
76 |
|
|
70 |
@task |
|
71 |
def check_sync(route_name=None, selected_routes = []): |
|
72 |
if not selected_routes: |
|
73 |
routes = Route.objects.all() |
|
74 |
else: |
|
75 |
routes = selected_routes |
|
76 |
if route_name: |
|
77 |
routes = routes.filter(name=route_name) |
|
78 |
for route in roures: |
|
79 |
if route.is_synced(): |
|
80 |
logger.info("Route %s is synced" %route.name) |
|
81 |
else: |
|
82 |
logger.warn("Route %s is out of sync" %route.name) |
|
77 | 83 |
#def delete(route): |
78 | 84 |
# |
79 | 85 |
# applier = PR.Applier(route_object=route) |
b/flowspec/views.py | ||
---|---|---|
2 | 2 |
import urllib2 |
3 | 3 |
import re |
4 | 4 |
import socket |
5 |
import json |
|
5 | 6 |
from django import forms |
6 | 7 |
from django.views.decorators.csrf import csrf_exempt |
7 | 8 |
from django.core import urlresolvers |
9 |
from django.core import serializers |
|
8 | 10 |
from django.contrib.auth.decorators import login_required |
9 | 11 |
from django.http import HttpResponseRedirect, HttpResponseForbidden, HttpResponse |
10 | 12 |
from django.shortcuts import get_object_or_404, render_to_response |
... | ... | |
16 | 18 |
from django.contrib import messages |
17 | 19 |
from flowspy.accounts.models import * |
18 | 20 |
|
21 |
from django.contrib.auth import authenticate, login |
|
22 |
|
|
19 | 23 |
from django.forms.models import model_to_dict |
20 | 24 |
|
21 | 25 |
from flowspy.flowspec.forms import * |
22 | 26 |
from flowspy.flowspec.models import * |
23 | 27 |
|
24 | 28 |
from copy import deepcopy |
29 |
from flowspy.utils.decorators import shib_required |
|
25 | 30 |
|
26 | 31 |
def days_offset(): return datetime.now() + timedelta(days = settings.EXPIRATION_DAYS_OFFSET) |
27 | 32 |
|
28 | 33 |
@login_required |
29 | 34 |
def user_routes(request): |
30 |
if request.user.is_anonymous(): |
|
31 |
return HttpResponseRedirect(reverse('login')) |
|
32 | 35 |
user_routes = Route.objects.filter(applier=request.user) |
33 | 36 |
return render_to_response('user_routes.html', {'routes': user_routes}, |
34 | 37 |
context_instance=RequestContext(request)) |
35 | 38 |
|
36 | 39 |
@login_required |
37 | 40 |
def group_routes(request): |
38 |
if request.user.is_anonymous(): |
|
39 |
return HttpResponseRedirect(reverse('login')) |
|
41 |
group_routes = [] |
|
40 | 42 |
peer = request.user.get_profile().peer |
41 | 43 |
if peer: |
42 | 44 |
peer_members = UserProfile.objects.filter(peer=peer) |
... | ... | |
48 | 50 |
|
49 | 51 |
@login_required |
50 | 52 |
def add_route(request): |
53 |
applier = request.user.pk |
|
51 | 54 |
if request.method == "GET": |
52 | 55 |
form = RouteForm() |
53 |
return render_to_response('apply.html', {'form': form}, |
|
56 |
return render_to_response('apply.html', {'form': form, 'applier': applier},
|
|
54 | 57 |
context_instance=RequestContext(request)) |
55 | 58 |
|
56 | 59 |
else: |
... | ... | |
59 | 62 |
route=form.save(commit=False) |
60 | 63 |
route.applier = request.user |
61 | 64 |
route.expires = days_offset() |
65 |
route.status = "PENDING" |
|
62 | 66 |
route.save() |
63 | 67 |
form.save_m2m() |
64 | 68 |
route.commit_add() |
65 |
return HttpResponseRedirect(urlresolvers.reverse("user-routes"))
|
|
69 |
return HttpResponseRedirect(reverse("group-routes"))
|
|
66 | 70 |
else: |
67 |
return render_to_response('apply.html', {'form': form}, |
|
71 |
return render_to_response('apply.html', {'form': form, 'applier':applier},
|
|
68 | 72 |
context_instance=RequestContext(request)) |
73 |
|
|
74 |
@login_required |
|
75 |
def add_then(request): |
|
76 |
applier = request.user.pk |
|
77 |
if request.method == "GET": |
|
78 |
form = RouteForm() |
|
79 |
return render_to_response('apply.html', {'form': form, 'applier': applier}, |
|
80 |
context_instance=RequestContext(request)) |
|
81 |
|
|
82 |
else: |
|
83 |
form = RouteForm(request.POST) |
|
84 |
if form.is_valid(): |
|
85 |
route=form.save(commit=False) |
|
86 |
route.applier = request.user |
|
87 |
route.expires = days_offset() |
|
88 |
route.save() |
|
89 |
form.save_m2m() |
|
90 |
route.commit_add() |
|
91 |
return HttpResponseRedirect(reverse("group-routes")) |
|
92 |
else: |
|
93 |
return render_to_response('apply.html', {'form': form, 'applier':applier}, |
|
94 |
context_instance=RequestContext(request)) |
|
95 |
|
|
69 | 96 |
@login_required |
70 | 97 |
def edit_route(request, route_slug): |
98 |
applier = request.user.pk |
|
71 | 99 |
route_edit = get_object_or_404(Route, name=route_slug) |
72 | 100 |
route_original = deepcopy(route_edit) |
73 | 101 |
if request.POST: |
... | ... | |
75 | 103 |
if form.is_valid(): |
76 | 104 |
route=form.save(commit=False) |
77 | 105 |
route.name = route_original.name |
78 |
route.applier = route_original.applier
|
|
106 |
route.applier = request.user
|
|
79 | 107 |
route.expires = route_original.expires |
80 |
route.is_active = route_original.is_active
|
|
108 |
route.status = "PENDING"
|
|
81 | 109 |
route.save() |
82 | 110 |
form.save_m2m() |
83 | 111 |
route.commit_edit() |
84 |
return HttpResponseRedirect(urlresolvers.reverse("user-routes"))
|
|
112 |
return HttpResponseRedirect(reverse("group-routes"))
|
|
85 | 113 |
else: |
86 |
return render_to_response('apply.html', {'form': form, 'edit':True}, |
|
114 |
return render_to_response('apply.html', {'form': form, 'edit':True, 'applier': applier},
|
|
87 | 115 |
context_instance=RequestContext(request)) |
88 | 116 |
else: |
89 | 117 |
dictionary = model_to_dict(route_edit, fields=[], exclude=[]) |
118 |
#form = RouteForm(instance=route_edit) |
|
90 | 119 |
form = RouteForm(dictionary) |
91 |
return render_to_response('apply.html', {'form': form, 'edit':True}, |
|
120 |
return render_to_response('apply.html', {'form': form, 'edit':True, 'applier': applier},
|
|
92 | 121 |
context_instance=RequestContext(request)) |
93 | 122 |
|
94 | 123 |
@login_required |
95 | 124 |
def delete_route(request, route_slug): |
96 | 125 |
if request.is_ajax(): |
97 | 126 |
route = get_object_or_404(Route, name=route_slug) |
98 |
if route.applier == request.user: |
|
127 |
applier_peer = route.applier.get_profile().peer |
|
128 |
requester_peer = request.user.get_profile().peer |
|
129 |
if applier_peer == requester_peer: |
|
99 | 130 |
route.deactivate() |
100 | 131 |
route.commit_delete() |
101 |
return HttpResponseRedirect(urlresolvers.reverse("user-routes")) |
|
132 |
html = "<html><body>Done</body></html>" |
|
133 |
return HttpResponse(html) |
|
134 |
else: |
|
135 |
return HttpResponseRedirect(reverse("group-routes")) |
|
136 |
|
|
137 |
@login_required |
|
138 |
def user_profile(request): |
|
139 |
user = request.user |
|
140 |
peer = request.user.get_profile().peer |
|
141 |
|
|
142 |
return render_to_response('profile.html', {'user': user, 'peer':peer}, |
|
143 |
context_instance=RequestContext(request)) |
|
144 |
|
|
145 |
|
|
146 |
def user_login(request): |
|
147 |
try: |
|
148 |
error_username = None |
|
149 |
error_orgname = None |
|
150 |
username = request.META['HTTP_EPPN'] |
|
151 |
if not username: |
|
152 |
error_username = True |
|
153 |
firstname = request.META['HTTP_SHIB_INETORGPERSON_GIVENNAME'] |
|
154 |
lastname = request.META['HTTP_SHIB_PERSON_SURNAME'] |
|
155 |
mail = request.META['HTTP_SHIB_INETORGPERSON_MAIL'] |
|
156 |
organization = request.META['HTTP_SHIB_HOMEORGANIZATION'] |
|
157 |
if not organization: |
|
158 |
error_orgname = True |
|
159 |
|
|
160 |
if error_orgname or error_username: |
|
161 |
error = "Your idP should release the HTTP_EPPN, HTTP_SHIB_HOMEORGANIZATION attributes towards this service" |
|
162 |
return render_to_response('error.html', {'error': error,}, |
|
163 |
context_instance=RequestContext(request)) |
|
164 |
user = authenticate(username=username, firstname=firstname, lastname=lastname, mail=mail, organization=organization, affiliation=None) |
|
165 |
if user is not None: |
|
166 |
login(request, user) |
|
167 |
return HttpResponseRedirect(reverse("group-routes")) |
|
168 |
# Redirect to a success page. |
|
169 |
# Return a 'disabled account' error message |
|
170 |
else: |
|
171 |
html = "<html><body>Invalid User</body></html>" |
|
172 |
return HttpResponse(html) |
|
173 |
except Exception as e: |
|
174 |
html = "<html><body>Invalid Login Procedure %s </body></html>" %e |
|
175 |
return HttpResponse(html) |
|
176 |
# Return an 'invalid login' error message. |
|
177 |
# return HttpResponseRedirect(reverse("user-routes")) |
|
178 |
|
|
179 |
@login_required |
|
180 |
def add_rate_limit(request): |
|
181 |
if request.method == "GET": |
|
182 |
form = ThenPlainForm() |
|
183 |
return render_to_response('add_rate_limit.html', {'form': form,}, |
|
184 |
context_instance=RequestContext(request)) |
|
185 |
|
|
186 |
else: |
|
187 |
form = ThenPlainForm(request.POST) |
|
188 |
if form.is_valid(): |
|
189 |
then=form.save(commit=False) |
|
190 |
then.action_value = "%sk"%then.action_value |
|
191 |
then.save() |
|
192 |
response_data = {} |
|
193 |
response_data['pk'] = "%s" %then.pk |
|
194 |
response_data['value'] = "%s:%s" %(then.action, then.action_value) |
|
195 |
return HttpResponse(simplejson.dumps(response_data), mimetype='application/json') |
|
196 |
else: |
|
197 |
return render_to_response('add_rate_limit.html', {'form': form,}, |
|
198 |
context_instance=RequestContext(request)) |
|
199 |
|
|
200 |
@login_required |
|
201 |
def add_port(request): |
|
202 |
if request.method == "GET": |
|
203 |
form = PortPlainForm() |
|
204 |
return render_to_response('add_port.html', {'form': form,}, |
|
205 |
context_instance=RequestContext(request)) |
|
206 |
|
|
207 |
else: |
|
208 |
form = PortPlainForm(request.POST) |
|
209 |
if form.is_valid(): |
|
210 |
port=form.save() |
|
211 |
response_data = {} |
|
212 |
response_data['value'] = "%s" %port.pk |
|
213 |
response_data['text'] = "%s" %port.port |
|
214 |
return HttpResponse(simplejson.dumps(response_data), mimetype='application/json') |
|
215 |
else: |
|
216 |
return render_to_response('add_port.html', {'form': form,}, |
|
217 |
context_instance=RequestContext(request)) |
|
218 |
|
|
219 |
@login_required |
|
220 |
def user_logout(request): |
|
221 |
return HttpResponseRedirect(settings.SHIB_LOGOUT_URL) |
|
222 |
|
|
223 |
|
|
224 |
def load_jscript(request, file): |
|
225 |
return render_to_response('%s.js' % file, context_instance=RequestContext(request), mimetype="text/javascript") |
b/monkey_patch/models.py | ||
---|---|---|
1 |
from django.contrib.auth.models import User |
|
2 |
from django.core.validators import MaxLengthValidator |
|
3 |
|
|
4 |
NEW_USERNAME_LENGTH = 255 |
|
5 |
|
|
6 |
def monkey_patch_username(): |
|
7 |
username = User._meta.get_field("username") |
|
8 |
username.max_length = NEW_USERNAME_LENGTH |
|
9 |
for v in username.validators: |
|
10 |
if isinstance(v, MaxLengthValidator): |
|
11 |
v.limit_value = NEW_USERNAME_LENGTH |
|
12 |
|
|
13 |
monkey_patch_username() |
b/poller/urls.py | ||
---|---|---|
3 | 3 |
|
4 | 4 |
urlpatterns = patterns('flowspy.poller.views', |
5 | 5 |
('^$', 'main'), |
6 |
('^a/message/existing$', 'message_existing'),
|
|
7 |
('^a/message/new$', 'message_new'),
|
|
8 |
('^a/message/updates$', 'message_updates'))
|
|
6 |
url('^a/message/existing$', 'message_existing', name='fetch-existing'),
|
|
7 |
url('^a/message/new$', 'message_new',name='fetch-new'),
|
|
8 |
url('^a/message/updates$', 'message_updates', name='fetch-updates'))
|
|
9 | 9 |
|
10 | 10 |
urlpatterns += patterns('', |
11 | 11 |
(r'^static/(?P<path>.*)', 'django.views.static.serve',\ |
b/poller/views.py | ||
---|---|---|
12 | 12 |
from gevent.event import Event |
13 | 13 |
from django.conf import settings |
14 | 14 |
from django.views.decorators.csrf import csrf_exempt |
15 |
from django.http import HttpResponseRedirect |
|
16 |
from django.core.urlresolvers import reverse |
|
17 |
|
|
15 | 18 |
|
16 | 19 |
from flowspy.utils import beanstalkc |
17 | 20 |
|
... | ... | |
37 | 40 |
cache_size = 200 |
38 | 41 |
|
39 | 42 |
def __init__(self): |
43 |
self.user = None |
|
40 | 44 |
self.user_cache = {} |
41 | 45 |
self.user_cursor = {} |
42 | 46 |
self.cache = [] |
... | ... | |
47 | 51 |
if self.user_cache: |
48 | 52 |
request.session['cursor'] = self.user_cache[-1]['id'] |
49 | 53 |
return render_to_response('poll.html', {'messages': self.user_cache}) |
50 |
|
|
54 |
|
|
51 | 55 |
@csrf_exempt |
52 | 56 |
def message_existing(self, request): |
53 |
|
|
54 |
try: |
|
55 |
user = request.user.username |
|
56 |
except: |
|
57 |
user = None |
|
58 |
self.new_message_user_event[user] = Event() |
|
59 |
try: |
|
60 |
if self.user_cache[user]: |
|
61 |
self.user_cursor[user] = self.user_cache[user][-1]['id'] |
|
62 |
except: |
|
63 |
self.user_cache[user] = [] |
|
64 |
self.user_cursor[user] = '' |
|
65 |
return json_response({'messages': self.user_cache[user]}) |
|
57 |
if request.is_ajax(): |
|
58 |
try: |
|
59 |
user = request.user.get_profile().peer.domain_name |
|
60 |
except: |
|
61 |
user = None |
|
62 |
return False |
|
63 |
try: |
|
64 |
assert(self.new_message_user_event[user]) |
|
65 |
except: |
|
66 |
self.new_message_user_event[user] = Event() |
|
67 |
# self.new_message_user_event[user] = Event() |
|
68 |
try: |
|
69 |
if self.user_cache[user]: |
|
70 |
self.user_cursor[user] = self.user_cache[user][-1]['id'] |
|
71 |
except: |
|
72 |
self.user_cache[user] = [] |
|
73 |
self.user_cursor[user] = '' |
|
74 |
return json_response({'messages': self.user_cache[user]}) |
|
75 |
return HttpResponseRedirect(reverse('login')) |
|
66 | 76 |
|
67 | 77 |
@csrf_exempt |
68 | 78 |
def message_new(self, mesg=None): |
... | ... | |
89 | 99 |
|
90 | 100 |
@csrf_exempt |
91 | 101 |
def message_updates(self, request): |
92 |
cursor = {} |
|
93 |
try: |
|
94 |
user = request.user.username |
|
95 |
except: |
|
96 |
user = None |
|
97 |
|
|
98 |
cursor[user] = self.user_cursor[user] |
|
99 |
|
|
100 |
try: |
|
101 |
if not isinstance(self.user_cache[user], list): |
|
102 |
if request.is_ajax(): |
|
103 |
cursor = {} |
|
104 |
try: |
|
105 |
# user = request.user.username |
|
106 |
user = request.user.get_profile().peer.domain_name |
|
107 |
except: |
|
108 |
user = None |
|
109 |
return False |
|
110 |
cursor[user] = self.user_cursor[user] |
|
111 |
|
|
112 |
try: |
|
113 |
if not isinstance(self.user_cache[user], list): |
|
114 |
self.user_cache[user] = [] |
|
115 |
except: |
|
102 | 116 |
self.user_cache[user] = [] |
103 |
except: |
|
104 |
self.user_cache[user] = [] |
|
105 |
if not self.user_cache[user] or cursor[user] == self.user_cache[user][-1]['id']: |
|
106 |
self.new_message_user_event[user].wait() |
|
107 |
# self.new_message_event.wait() |
|
108 |
# assert cursor[user] != self.user_cache[user][-1]['id'], cursor[user] |
|
109 |
try: |
|
110 |
for index, m in enumerate(self.user_cache[user]): |
|
111 |
if m['id'] == cursor[user]: |
|
112 |
return json_response({'messages': self.user_cache[user][index + 1:]}) |
|
113 |
return json_response({'messages': self.user_cache[user]}) |
|
114 |
finally: |
|
115 |
if self.user_cache[user]: |
|
116 |
self.user_cursor[user] = self.user_cache[user][-1]['id'] |
|
117 |
# else: |
|
118 |
# request.session.pop('cursor', None) |
|
117 |
if not self.user_cache[user] or cursor[user] == self.user_cache[user][-1]['id']: |
|
118 |
self.new_message_user_event[user].wait() |
|
119 |
# self.new_message_event.wait() |
|
120 |
# assert cursor[user] != self.user_cache[user][-1]['id'], cursor[user] |
|
121 |
try: |
|
122 |
for index, m in enumerate(self.user_cache[user]): |
|
123 |
if m['id'] == cursor[user]: |
|
124 |
return json_response({'messages': self.user_cache[user][index + 1:]}) |
|
125 |
return json_response({'messages': self.user_cache[user]}) |
|
126 |
finally: |
|
127 |
if self.user_cache[user]: |
|
128 |
self.user_cursor[user] = self.user_cache[user][-1]['id'] |
|
129 |
return HttpResponseRedirect(reverse('login')) |
|
130 |
# else: |
|
131 |
# request.session.pop('cursor', None) |
|
119 | 132 |
|
120 | 133 |
def monitor_polls(self, polls=None): |
121 | 134 |
b = beanstalkc.Connection() |
122 | 135 |
b.watch(settings.POLLS_TUBE) |
123 | 136 |
while True: |
124 | 137 |
job = b.reserve() |
125 |
print job.body |
|
126 | 138 |
msg = json.loads(job.body) |
127 | 139 |
job.bury() |
128 | 140 |
self.message_new(msg) |
b/run_poller.py | ||
---|---|---|
1 | 1 |
#!/usr/bin/python |
2 | 2 |
from gevent.wsgi import WSGIServer |
3 | 3 |
from poller.application import application |
4 |
print 'Serving on 8000...' |
|
5 |
WSGIServer(('netdev.grnet.gr', 9090), application).serve_forever() |
|
4 |
server="localhost" |
|
5 |
port=8081 |
|
6 |
print 'Serving on port %s...' % port |
|
7 |
WSGIServer((server,port ), application).serve_forever() |
b/static/css/base.css | ||
---|---|---|
455 | 455 |
#cluster_overview_table_wrapper{ |
456 | 456 |
width: 100%; |
457 | 457 |
} |
458 |
|
|
458 |
/* |
|
459 |
* jQuery UI specific styling |
|
460 |
*/ |
|
459 | 461 |
|
460 | 462 |
.paging_two_button .ui-button { |
461 | 463 |
float: left; |
... | ... | |
518 | 520 |
} |
519 | 521 |
|
520 | 522 |
|
523 |
|
|
524 |
|
|
525 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
526 |
* |
|
527 |
* Everything below this line is the same as demo_table.css. This file is |
|
528 |
* required for 'cleanliness' of the markup |
|
529 |
* |
|
530 |
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
|
531 |
|
|
532 |
|
|
533 |
|
|
534 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
535 |
* DataTables features |
|
536 |
*/ |
|
537 |
|
|
521 | 538 |
.dataTables_wrapper { |
522 | 539 |
position: relative; |
523 | 540 |
min-height: 302px; |
524 |
float: left; |
|
525 |
clear: both; |
|
526 | 541 |
_height: 302px; |
527 |
width: 100%; |
|
528 |
zoom: 1; /* Feeling sorry for IE */ |
|
542 |
clear: both; |
|
529 | 543 |
} |
530 | 544 |
|
531 | 545 |
.dataTables_processing { |
532 | 546 |
position: absolute; |
533 |
top: 50%;
|
|
547 |
top: 0px;
|
|
534 | 548 |
left: 50%; |
535 | 549 |
width: 250px; |
536 |
height: 30px; |
|
537 | 550 |
margin-left: -125px; |
538 |
margin-top: -15px; |
|
539 |
padding: 14px 0 2px 0; |
|
540 | 551 |
border: 1px solid #ddd; |
541 | 552 |
text-align: center; |
542 | 553 |
color: #999; |
543 |
font-size: 14px;
|
|
544 |
background-color: white;
|
|
554 |
font-size: 11px;
|
|
555 |
padding: 2px 0;
|
|
545 | 556 |
} |
546 | 557 |
|
547 | 558 |
.dataTables_length { |
... | ... | |
556 | 567 |
} |
557 | 568 |
|
558 | 569 |
.dataTables_info { |
559 |
width: 60%;
|
|
570 |
width: 50%;
|
|
560 | 571 |
float: left; |
561 | 572 |
} |
562 | 573 |
|
563 | 574 |
.dataTables_paginate { |
564 |
width: 44px; |
|
565 |
* width: 50px; |
|
566 | 575 |
float: right; |
567 | 576 |
text-align: right; |
568 | 577 |
} |
... | ... | |
593 | 602 |
|
594 | 603 |
|
595 | 604 |
|
596 |
|
|
605 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
606 |
* DataTables display |
|
607 |
*/ |
|
597 | 608 |
table.display { |
598 | 609 |
margin: 0 auto; |
599 |
clear: both; |
|
600 | 610 |
width: 100%; |
611 |
clear: both; |
|
601 | 612 |
border-collapse: collapse; |
602 |
|
|
603 |
} |
|
604 |
|
|
605 |
table.display thead th { |
|
606 |
padding: 3px 18px 3px 10px; |
|
607 |
font-weight: normal; |
|
608 |
cursor: pointer; |
|
609 |
* cursor: hand; |
|
610 | 613 |
} |
611 | 614 |
|
612 | 615 |
table.display tfoot th { |
613 |
padding: 3px 18px 3px 10px; |
|
614 |
border-top: 1px solid black; |
|
616 |
padding: 3px 0px 3px 10px; |
|
615 | 617 |
font-weight: bold; |
618 |
font-weight: normal; |
|
616 | 619 |
} |
617 | 620 |
|
618 | 621 |
table.display tr.heading2 td { |
... | ... | |
623 | 626 |
padding: 3px 10px; |
624 | 627 |
} |
625 | 628 |
|
626 |
table.display td a { |
|
627 |
font-weight: normal; |
|
628 |
} |
|
629 |
|
|
630 | 629 |
table.display td.center { |
631 | 630 |
text-align: center; |
632 | 631 |
} |
633 | 632 |
|
634 |
table.display tfoot th { |
|
635 |
padding: 3px 0px 3px 10px; |
|
636 |
font-weight: bold; |
|
637 |
font-weight: normal; |
|
638 |
} |
|
639 | 633 |
|
640 |
table.display tr.heading2 td { |
|
641 |
border-bottom: 1px solid #aaa; |
|
642 |
} |
|
643 |
|
|
644 |
table.display td { |
|
645 |
padding: 3px 10px; |
|
646 |
} |
|
647 | 634 |
|
648 |
table.display td.center {
|
|
649 |
text-align: center;
|
|
650 |
}
|
|
635 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
636 |
* DataTables sorting
|
|
637 |
*/
|
|
651 | 638 |
|
652 | 639 |
.sorting_asc { |
653 | 640 |
background: url('../images/sort_asc.png') no-repeat center right; |
... | ... | |
669 | 656 |
background: url('../images/sort_desc_disabled.png') no-repeat center right; |
670 | 657 |
} |
671 | 658 |
|
659 |
|
|
660 |
|
|
661 |
|
|
662 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
663 |
* DataTables row classes |
|
664 |
*/ |
|
672 | 665 |
table.display tr.odd.gradeA { |
673 |
background-color: #EEEEEE;
|
|
666 |
background-color: #ddffdd;
|
|
674 | 667 |
} |
675 | 668 |
|
676 | 669 |
table.display tr.even.gradeA { |
677 |
background-color: #F6F6F6; |
|
670 |
background-color: #eeffee; |
|
671 |
} |
|
672 |
|
|
673 |
|
|
674 |
|
|
675 |
|
|
676 |
table.display tr.odd.gradeA { |
|
677 |
background-color: #ddffdd; |
|
678 |
} |
|
679 |
|
|
680 |
table.display tr.even.gradeA { |
|
681 |
background-color: #eeffee; |
|
678 | 682 |
} |
679 | 683 |
|
680 | 684 |
table.display tr.odd.gradeC { |
... | ... | |
710 | 714 |
background-color: white; |
711 | 715 |
} |
712 | 716 |
|
717 |
|
|
718 |
|
|
719 |
|
|
720 |
|
|
721 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
722 |
* Misc |
|
723 |
*/ |
|
713 | 724 |
.dataTables_scroll { |
714 | 725 |
clear: both; |
715 | 726 |
} |
716 | 727 |
|
717 |
.dataTables_scrollBody { |
|
718 |
*margin-top: -1px; |
|
719 |
} |
|
720 |
|
|
721 | 728 |
.top, .bottom { |
722 | 729 |
padding: 15px; |
723 | 730 |
background-color: #F5F5F5; |
... | ... | |
762 | 769 |
width: 40%; |
763 | 770 |
} |
764 | 771 |
|
765 |
.paging_full_numbers { |
|
766 |
width: 400px; |
|
767 |
height: 22px; |
|
768 |
line-height: 22px; |
|
769 |
} |
|
770 |
|
|
771 | 772 |
.paging_full_numbers span.paginate_button, |
772 | 773 |
.paging_full_numbers span.paginate_active { |
773 | 774 |
border: 1px solid #aaa; |
... | ... | |
799 | 800 |
background-color: #9FAFD1; |
800 | 801 |
} |
801 | 802 |
|
803 |
|
|
804 |
/* |
|
805 |
* Sorting classes for columns |
|
806 |
*/ |
|
807 |
/* For the standard odd/even */ |
|
802 | 808 |
tr.odd td.sorting_1 { |
803 | 809 |
background-color: #D3D6FF; |
804 | 810 |
} |
... | ... | |
823 | 829 |
background-color: #F9F9FF; |
824 | 830 |
} |
825 | 831 |
|
832 |
|
|
833 |
/* For the Conditional-CSS grading rows */ |
|
834 |
/* |
|
835 |
Colour calculations (based off the main row colours) |
|
836 |
Level 1: |
|
837 |
dd > c4 |
|
838 |
ee > d5 |
|
839 |
Level 2: |
|
840 |
dd > d1 |
|
841 |
ee > e2 |
|
842 |
*/ |
|
826 | 843 |
tr.odd.gradeA td.sorting_1 { |
827 |
background-color: #E1E1E1;
|
|
844 |
background-color: #c4ffc4;
|
|
828 | 845 |
} |
829 | 846 |
|
830 | 847 |
tr.odd.gradeA td.sorting_2 { |
831 |
background-color: #E8E8E8;
|
|
848 |
background-color: #d1ffd1;
|
|
832 | 849 |
} |
833 | 850 |
|
834 | 851 |
tr.odd.gradeA td.sorting_3 { |
835 |
background-color: #E8E8E8;
|
|
852 |
background-color: #d1ffd1;
|
|
836 | 853 |
} |
837 | 854 |
|
838 | 855 |
tr.even.gradeA td.sorting_1 { |
839 |
background-color: #EAEAEA;
|
|
856 |
background-color: #d5ffd5;
|
|
840 | 857 |
} |
841 | 858 |
|
842 | 859 |
tr.even.gradeA td.sorting_2 { |
843 |
background-color: #F0F0F0;
|
|
860 |
background-color: #e2ffe2;
|
|
844 | 861 |
} |
845 | 862 |
|
846 | 863 |
tr.even.gradeA td.sorting_3 { |
847 |
background-color: #F0F0F0;
|
|
864 |
background-color: #e2ffe2;
|
|
848 | 865 |
} |
849 | 866 |
|
850 | 867 |
tr.odd.gradeC td.sorting_1 { |
... | ... | |
919 | 936 |
background-color: #e2e2e2; |
920 | 937 |
} |
921 | 938 |
|
939 |
|
|
940 |
/* |
|
941 |
* Row highlighting example |
|
942 |
*/ |
|
922 | 943 |
.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted { |
923 | 944 |
background-color: #ECFFB3; |
924 | 945 |
} |
... | ... | |
927 | 948 |
background-color: #E6FF99; |
928 | 949 |
} |
929 | 950 |
|
930 |
.ex_highlight_row #example tr.even:hover { |
|
931 |
background-color: #ECFFB3; |
|
932 |
} |
|
933 |
|
|
934 |
.ex_highlight_row #example tr.even:hover td.sorting_1 { |
|
935 |
background-color: #DDFF75; |
|
936 |
} |
|
937 |
|
|
938 |
.ex_highlight_row #example tr.even:hover td.sorting_2 { |
|
939 |
background-color: #E7FF9E; |
|
940 |
} |
|
941 |
|
|
942 |
.ex_highlight_row #example tr.even:hover td.sorting_3 { |
|
943 |
background-color: #E2FF89; |
|
944 |
} |
|
945 |
|
|
946 |
.ex_highlight_row #example tr.odd:hover { |
|
947 |
background-color: #E6FF99; |
|
948 |
} |
|
949 |
|
|
950 |
.ex_highlight_row #example tr.odd:hover td.sorting_1 { |
|
951 |
background-color: #D6FF5C; |
|
952 |
} |
|
953 |
|
|
954 |
.ex_highlight_row #example tr.odd:hover td.sorting_2 { |
|
955 |
background-color: #E0FF84; |
|
956 |
} |
|
957 |
|
|
958 |
.ex_highlight_row #example tr.odd:hover td.sorting_3 { |
|
959 |
background-color: #DBFF70; |
|
960 |
} |
|
961 |
|
|
962 |
table.KeyTable td { |
|
963 |
border: 3px solid transparent; |
|
964 |
} |
|
965 |
|
|
966 |
table.KeyTable td.focus { |
|
967 |
border: 3px solid #3366FF; |
|
968 |
} |
|
969 |
|
|
970 |
table.display tr.gradeA { |
|
971 |
background-color: #eeffee; |
|
972 |
} |
|
973 |
|
|
974 |
table.display tr.gradeC { |
|
975 |
background-color: #ddddff; |
|
976 |
} |
|
977 |
|
|
978 |
table.display tr.gradeX { |
|
979 |
background-color: #ffdddd; |
|
980 |
} |
|
981 |
|
|
982 |
table.display tr.gradeU { |
|
983 |
background-color: #ddd; |
|
984 |
} |
|
985 |
|
|
986 | 951 |
div.box { |
987 | 952 |
height: 100px; |
988 | 953 |
padding: 10px; |
b/templates/add_port.html | ||
---|---|---|
1 |
<form id="add_port_form" method="POST"> |
|
2 |
{% csrf_token %} |
|
3 |
<table> |
|
4 |
<tr><th>Port</th><td>{{ form.port }}<span class="error" id='rl_error'>{{ form.port.errors|join:", " }}</span></td></tr> |
|
5 |
</table> |
|
6 |
</form> |
b/templates/add_rate_limit.html | ||
---|---|---|
1 |
<form id="add_rl_form" method="POST"> |
|
2 |
{% csrf_token %} |
|
3 |
<table> |
|
4 |
<input type="hidden" id="id_action" name="action" value="rate-limit"/> |
|
5 |
<tr><th>Value</th><td>{{ form.action_value }}Kbps<span class="error" id='rl_error'>{{ form.action_value.errors|join:", " }}</span></td></tr> |
|
6 |
<tr class="help"><td></td><td>Value should be >50Kbps</td></tr> |
|
7 |
</table> |
|
8 |
</form> |
b/templates/apply.html | ||
---|---|---|
1 | 1 |
{% extends "base.html" %} |
2 | 2 |
{% load i18n %} |
3 |
|
|
3 |
|
|
4 | 4 |
{% block title %} |
5 | 5 |
{% if edit %} |
6 | 6 |
{% trans "Edit Route" %} {{form.data.name}} |
... | ... | |
16 | 16 |
{% trans "Create route" %} |
17 | 17 |
{% endif %} |
18 | 18 |
{% endblock %} |
19 |
|
|
19 |
{% block extrahead %} |
|
20 |
<script> |
|
21 |
$(document).ready( function(){ |
|
22 |
|
|
23 |
$("#id_sourceport").css('width', '100px').attr('size', '5'); |
|
24 |
$("#id_port").css('width', '100px').attr('size', '5'); |
|
25 |
$("#id_destinationport").css('width', '100px').attr('size', '5'); |
|
26 |
$('#id_then').attr("multiple", ""); |
|
27 |
$('#then_diag').dialog({ |
|
28 |
height: 220, |
|
29 |
width: 340, |
|
30 |
modal: true, |
|
31 |
autoOpen: false, |
|
32 |
buttons: { |
|
33 |
'Add': function() { |
|
34 |
console.log($("#add_rl_form").serialize()); |
|
35 |
$.ajax({ |
|
36 |
url:"{% url add-rate-limit %}", |
|
37 |
data:$("#add_rl_form").serialize(), |
|
38 |
type: "POST", |
|
39 |
cache: false, |
|
40 |
success:function(data){ |
|
41 |
try { |
|
42 |
value = data.pk; |
|
43 |
text = data.value; |
|
44 |
$('#id_then').append($("<option></option>").attr("value",value).text(text)); |
|
45 |
$('#then_diag').dialog('close'); |
|
46 |
} |
|
47 |
catch (exception) { |
|
48 |
$('#then_diag').html(data); |
|
49 |
} |
|
50 |
} |
|
51 |
}); |
|
52 |
}, |
|
53 |
Cancel: function() { |
|
54 |
$('#then_diag').dialog('close'); |
|
55 |
} |
|
56 |
} |
|
57 |
}); |
|
58 |
|
|
59 |
$('#port_diag').dialog({ |
|
60 |
height: 220, |
|
61 |
width: 340, |
|
62 |
modal: true, |
|
63 |
autoOpen: false, |
|
64 |
buttons: { |
|
65 |
'Add': function() { |
|
66 |
console.log($("#add_port_form").serialize()); |
|
67 |
$.ajax({ |
|
68 |
url:"{% url add-port %}", |
|
69 |
data:$("#add_port_form").serialize(), |
|
70 |
type: "POST", |
|
71 |
cache: false, |
|
72 |
success:function(data){ |
|
73 |
try { |
|
74 |
value = data.value; |
|
75 |
text = data.text; |
|
76 |
$('#id_port').append($("<option></option>").attr("value",value).text(text)); |
|
77 |
$('#id_destinationport').append($("<option></option>").attr("value",value).text(text)); |
|
78 |
$('#id_sourceport').append($("<option></option>").attr("value",value).text(text)); |
|
79 |
$('#port_diag').dialog('close'); |
|
80 |
} |
|
81 |
catch (exception) { |
|
82 |
$('#port_diag').html(data); |
|
83 |
} |
|
84 |
} |
|
85 |
}); |
|
86 |
}, |
|
87 |
Cancel: function() { |
|
88 |
$('#port_diag').dialog('close'); |
|
89 |
} |
|
90 |
} |
|
91 |
}); |
|
92 |
|
|
93 |
|
|
94 |
$("#new_then_actions").button({ |
|
95 |
icons: { |
|
96 |
primary: "ui-icon-plusthick" |
|
97 |
}, |
|
98 |
}) |
|
99 |
.click(function(){ |
|
100 |
$.ajax({ |
|
101 |
url: "{% url add-rate-limit %}", |
|
102 |
cache: false, |
|
103 |
success: function(data){ |
|
104 |
$("#then_diag").html(data); |
|
105 |
} |
|
106 |
}); |
|
107 |
$('#then_diag').dialog('open'); |
|
108 |
return false; |
|
109 |
}); |
|
110 |
|
|
111 |
|
|
112 |
$(".new_port").button({ |
|
113 |
icons: { |
|
114 |
primary: "ui-icon-plusthick" |
|
115 |
}, |
|
116 |
}) |
|
117 |
.click(function(){ |
|
118 |
$.ajax({ |
|
119 |
url: "{% url add-port %}", |
|
120 |
cache: false, |
|
121 |
success: function(data){ |
|
122 |
$("#port_diag").html(data); |
|
123 |
} |
|
124 |
}); |
|
125 |
$('#port_diag').dialog('open'); |
|
126 |
return false; |
|
127 |
}); |
|
128 |
}); |
|
129 |
|
|
130 |
</script> |
|
131 |
{% endblock %} |
|
20 | 132 |
{% block content %} |
21 | 133 |
<style type="text/css"> |
22 | 134 |
th { |
... | ... | |
30 | 142 |
|
31 | 143 |
} |
32 | 144 |
</style> |
33 |
|
|
34 | 145 |
<div align="center"> |
35 | 146 |
{% if edit %} |
36 | 147 |
<h3>{% trans "Edit route" %}: {{form.data.name}}</h3> |
... | ... | |
47 | 158 |
<legend>{% trans "Route Basic Info" %}</legend> |
48 | 159 |
<table> |
49 | 160 |
<tr><th>{{ form.name.label_tag }}</th><td>{{ form.name }}<span class="error">{{ form.name.errors|join:", " }}</span></td></tr> |
50 |
<tr class="help"><td></td><td>{{ form.name.help_text }}</td></tr>
|
|
161 |
<tr class="help"><td></td><td>A unique identifier will be added as a name_suffix</td></tr>
|
|
51 | 162 |
</table> |
52 | 163 |
</fieldset> |
53 | 164 |
|
54 | 165 |
<fieldset> |
55 | 166 |
<legend>{% trans "Route Match Conditions" %}</legend> |
56 | 167 |
<table> |
168 |
<input type="hidden" id="id_applier" name="applier" value="{{applier}}"/> |
|
57 | 169 |
<tr><th>{{ form.source.label_tag }}</th><td>{{ form.source }}<span class="error">{{ form.source.errors|join:", " }}</span></td></tr> |
58 | 170 |
<tr class="help"><td></td><td>{{ form.source.help_text }}</td></tr> |
59 |
<tr><th>{{ form.sourceport.label_tag }}</th><td>{{ form.sourceport }}<span class="error">{{ form.sourceport.errors|join:", " }}</span></td></tr> |
|
171 |
<tr><th>{{ form.sourceport.label_tag }}</th><td>{{ form.sourceport }} <button class="new_port">Port</button><span class="error">{{ form.sourceport.errors|join:", " }}</span></td></tr>
|
|
60 | 172 |
<tr class="help"><td></td><td>{{ form.sourceport.help_text }}</td></tr> |
61 | 173 |
<tr><th>{{ form.destination.label_tag }}</th><td>{{ form.destination }}<span class="error">{{ form.destination.errors|join:", " }}</span></td></tr> |
62 | 174 |
<tr class="help"><td></td><td>{{ form.destination.help_text }}</td></tr> |
63 |
<tr><th>{{ form.destinationport.label_tag }}</th><td>{{ form.destinationport }}<span class="error">{{ form.destinationport.errors|join:", " }}</span></td></tr> |
|
175 |
<tr><th>{{ form.destinationport.label_tag }}</th><td>{{ form.destinationport }} <button class="new_port">Port</button><span class="error">{{ form.destinationport.errors|join:", " }}</span></td></tr>
|
|
64 | 176 |
<tr class="help"><td></td><td>{{ form.destinationport.help_text }}</td></tr> |
65 |
<tr><th>{{ form.port.label_tag }}</th><td>{{ form.port }}<span class="error">{{ form.port.errors|join:", " }}</span></td></tr> |
|
177 |
<tr><th>{{ form.port.label_tag }}</th><td>{{ form.port }} <button class="new_port">Port</button><span class="error">{{ form.port.errors|join:", " }}</span></td></tr>
|
|
66 | 178 |
<tr class="help"><td></td><td>{{ form.port.help_text }}</td></tr> |
67 | 179 |
</table> |
68 | 180 |
</fieldset> |
69 | 181 |
<fieldset> |
70 | 182 |
<legend>{% trans "Route Actions" %}</legend> |
71 | 183 |
<table> |
72 |
<tr><th>{{ form.then.label_tag }}</th><td>{{ form.then }}<span class="error">{{ form.then.errors|join:", " }}</span></td></tr> |
|
73 |
<tr class="help"><td></td><td>{{ form.then.help_text }}</td></tr> |
|
184 |
<tr><th>{{ form.then.label_tag }}</th><td>{{ form.then }} <button id="new_then_actions">Rate-limit</button><span class="error">{{ form.then.errors|join:", " }}</span></td></tr> |
|
74 | 185 |
</table> |
75 | 186 |
</fieldset> |
76 | 187 |
<fieldset> |
... | ... | |
87 | 198 |
</form> |
88 | 199 |
</div> |
89 | 200 |
|
201 |
<div id="then_diag" title="Add new rate-limit value"> |
|
202 |
</div> |
|
203 |
|
|
204 |
<div id="port_diag" title="Add new port"> |
|
205 |
</div> |
|
206 |
|
|
90 | 207 |
{% endblock %} |
Also available in: Unified diff