blob: 2c1c129efc67ad95d43941f9b3cefda74f4a5154 [file] [log] [blame]
Srikanth Vavilapalli1725e492014-12-01 17:50:52 -08001#
2# Copyright (c) 2013 Big Switch Networks, Inc.
3#
4# Licensed under the Eclipse Public License, Version 1.0 (the
5# "License"); you may not use this file except in compliance with the
6# License. You may obtain a copy of the License at
7#
8# http://www.eclipse.org/legal/epl-v10.html
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,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied. See the License for the specific language governing
14# permissions and limitations under the License.
15#
16
17from django.db import models
18from django.contrib.auth.models import User
19
20from django.forms import ValidationError
21from django.core.validators import MaxValueValidator
22from sdncon.rest.validators import *
23
24import sys # for sys.maxint
25import time
26
27#
28# controller/models.py
29# ------------------------------------------------------------
30#
31# model description of the tables used to configure and
32# view the controller state.
33#
34# these tables are used both by sdnplatform and the rest api.
35# the cli uses the rest api to read and write these tables,
36# while sdnplatform/sdnplatform has a more direct interface to the
37# tables.
38#
39# for the cli, the names of the fields are described in the
40# 'class Rest' section of each of the tables.
41#
42# for sdnplatform the names of fields have a relationship to their
43# type for sdnplatform. If a field is a foreign key, then an '_id'
44# is appended to the name for the same field within sdnplatform. This
45# means if a field is promoted or demoted to/from a foreign key,
46# changes need to be made in the sdnplatform code to use the updated
47# name.
48#
49# Make sure to include the nested Rest class or else they won't be
50# accessible using the REST API.
51
52#
53#
54# ------------------------------------------------------------
55
56def get_timestamp():
57 """
58 For us in models where the method of ordering the row values
59 is based on the order of creation.
60
61 The field is exposed to the rest api so that the value can
62 be queries, and so that the rest api may choose to order the
63 row values using its own strategy by setting the field directly
64 """
65 return int(time.time()*1000000)
66
Srikanth Vavilapallib5c3ca52014-12-15 15:59:33 -080067class Tunnelset(models.Model):
68
69 id_max_length = 64
70 #
71 # fields ----------------------------------------
72
73 #
74 # Unique name of the Tunnel
75 #
76 tunnelset_id = models.CharField(
77 primary_key = True,
78 verbose_name = 'Tunnelset Id',
79 help_text = 'A unique Id for a Tunnelset',
80 validators = [ TenantNameValidator() ],
81 max_length = id_max_length)
82
83 #
84 # end fields ----------------------------------------
85
86 def __unicode__ (self):
87 return self.tunnelset_id
88
89 def delete(self):
90 super(Tunnelset, self).delete()
91 class Rest:
92 NAME = 'tunnelset-config'
93 FIELD_INFO = (
94 {'name': 'tunnelset_id', 'rest_name': 'tunnelset-id'},
95 )
96
Srikanth Vavilapalli1725e492014-12-01 17:50:52 -080097class Tunnel(models.Model):
98
99 id_max_length = 64
100 #
101 # fields ----------------------------------------
102
103 #
104 # Unique name of the Tunnel
105 #
106 tunnel_id = models.CharField(
107 primary_key = True,
108 verbose_name = 'Tunnel Id',
109 help_text = 'A unique Id for a Tunnel',
110 validators = [ TenantNameValidator() ],
111 max_length = id_max_length)
112
113 path_seq = models.CharField(
114 verbose_name = 'Path Sequence',
115 help_text = 'List of path identifiers: nodes or adjacencies',
116 blank = True,
117 null = True,
118 max_length = 500)
119
120 #
121 # end fields ----------------------------------------
122
123 def __unicode__ (self):
124 return self.tunnel_id
125
126 def delete(self):
127 super(Tunnel, self).delete()
128 class Rest:
129 NAME = 'tunnel-config'
130 FIELD_INFO = (
131 {'name': 'tunnel_id', 'rest_name': 'tunnel-id'},
132 {'name': 'path_seq', 'rest_name': 'path-seq'},
133 )
134
135class Policy(models.Model):
136
137 id_max_length = 64
138 #
139 # fields ----------------------------------------
140
141 #
142 # Unique name of the Tunnel
143 #
144 sr_policy_id = models.CharField(
145 primary_key = True,
146 verbose_name = 'SR Policy Id',
147 help_text = 'A unique Id for a SR Policy',
148 validators = [ TenantNameValidator() ],
149 max_length = id_max_length)
150
151 sr_policy_type = models.CharField(
152 verbose_name = 'SR Policy Type',
153 help_text = 'Type of SR Policy',
154 validators = [ TenantNameValidator() ],
155 max_length = id_max_length)
156 #
157 # end fields ----------------------------------------
158
159 def __unicode__ (self):
160 return self.sr_policy_id
161
162 def delete(self):
163 super(Policy, self).delete()
164
165 class Rest:
166 NAME = 'policy-config'
167 FIELD_INFO = (
168 {'name': 'sr_policy_id', 'rest_name': 'policy-id'},
169 {'name': 'sr_policy_type', 'rest_name': 'policy-type'},
170 )
171
172#
173# ------------------------------------------------------------
174
175class Feature(models.Model):
176
177 #
178 # fields ----------------------------------------
179
180 id = models.CharField(
181 primary_key=True,
182 verbose_name='feature',
183 default='feature',
184 max_length=16)
185
186 netvirt_feature = models.BooleanField(
187 verbose_name='Network virtualization Feature Enabled',
188 help_text='Enable network virtualization feature by setting to true',
189 default=True
190 )
191
192 static_flow_pusher_feature = models.BooleanField(
193 verbose_name='Static Flow Pusher Feature Enabled',
194 help_text='Enable Static Flow Pusher feature by setting to true',
195 default=True
196 )
197
198 performance_monitor_feature = models.BooleanField(
199 verbose_name='Performance Monitor',
200 help_text="Enable performance monitoring feature by setting to true",
201 default=False
202 )
203
204 #
205 # end fields ----------------------------------------
206
207 def __unicode__(self):
208 return "%s" % (self.id,)
209
210 def validate_unique(self, exclude = None):
211 if self.id != 'feature':
212 raise ValidationError("Only single feature record exists")
213
214 class Rest:
215 NAME = 'feature'
216 FIELD_INFO = (
217 {'name': 'netvirt_feature', 'rest_name': 'netvirt-feature'},
218 {'name': 'static_flow_pusher_feature', 'rest_name': 'static-flow-pusher-feature'},
219 {'name': 'performance_monitor_feature','rest_name': 'performance-monitor-feature'},
220 )
221
222"""
223#
224# ------------------------------------------------------------
225
226class GlobalConfig(models.Model):
227 #
228 # fields ----------------------------------------
229
230 name = models.CharField(
231 primary_key=True,
232 verbose_name='Global Config Name',
233 help_text="Unique name for the global config; it's a singleton, "
234 "so, by convention, the name should always be \"global\"",
235 default='global',
236 max_length=16)
237
238 cluster_name = models.CharField(
239 verbose_name='Cluster Name',
240 help_text='Name for the cluster',
241 default='SDNCluster',
242 max_length=256)
243
244 cluster_number = models.IntegerField(
245 verbose_name='Cluster Number',
246 help_text='Small integral (1-255) identifier for the cluster',
247 default=0)
248
249 ha_enabled = models.BooleanField(
250 verbose_name='High Availability (HA) Enabled',
251 help_text='Is high availability (HA) enabled',
252 default=False)
253
254 #
255 # end fields ----------------------------------------
256
257 class Rest:
258 NAME = 'global-config'
259 FIELD_INFO = (
260 {'name': 'cluster_name', 'rest_name': 'cluster-name'},
261 {'name': 'cluster_number', 'rest_name': 'cluster-number'},
262 {'name': 'ha_enabled', 'rest_name': 'ha-enabled'},
263 )
264
265#
266# ------------------------------------------------------------
267
268class TopologyConfig(models.Model):
269 #
270 # fields ----------------------------------------
271
272 id = models.CharField(
273 primary_key=True,
274 verbose_name='topology',
275 default='topology',
276 max_length=16)
277
278
279 autoportfast = models.BooleanField(
280 verbose_name='Auto PortFast',
281 help_text='Suppress sending LLDPs on fast ports.',
282 default=True
283 )
284
285 #
286 # end fields ----------------------------------------
287
288 def __unicode__(self):
289 return "%s" % (self.id,)
290
291 def validate_unique(self, exclude = None):
292 if self.id != 'topology':
293 raise ValidationError("Only single topology record exists")
294
295 class Rest:
296 NAME = 'topology-config'
297 FIELD_INFO = (
298 )
299
300
301#
302# ------------------------------------------------------------
303
304class ForwardingConfig(models.Model):
305 #
306 # fields ----------------------------------------
307
308 id = models.CharField(
309 primary_key=True,
310 verbose_name='forwarding',
311 default='forwarding',
312 max_length=16)
313
314 access_priority = models.IntegerField(
315 verbose_name='Access Flow Priority',
316 help_text='Priority for flows created by forwarding on access switches',
317 validators=[ RangeValidator(0,2**15-1) ],
318 default=10)
319
320 core_priority = models.IntegerField(
321 verbose_name='Core Flow Priority',
322 help_text='Priority for flows created by forwarding on core switches',
323 validators=[ RangeValidator(0,2**15-1) ],
324 default=20)
325
326 #
327 # end fields ----------------------------------------
328
329 def __unicode__(self):
330 return "%s" % (self.id,)
331
332 def validate_unique(self, exclude = None):
333 if self.id != 'forwarding':
334 raise ValidationError(
335 "Only a single forwarding configuration record is allowed"
336 )
337
338 class Rest:
339 NAME = 'forwarding-config'
340 FIELD_INFO = (
341 {'name': 'access_priority', 'rest_name': 'access-priority'},
342 {'name': 'core_priority', 'rest_name': 'core-priority'},
343 )
344"""
345#
346# ------------------------------------------------------------
347class Controller(models.Model):
348 #
349 # fields ----------------------------------------
350 #
351 # Note: This model should only contain config state for the controller VM,
352 # not any operational state.
353 #
354 # Note: The convention used to be that the controller ID was
355 # the IP address and port that the OpenFlow controller was listening
356 # on. In practice SDNPlatform listened on all interfaces and it's logic
357 # for determining its real IP address was unreliable, so it was
358 # changed/simplified to just always use "localhost" for the
359 # IP address. So the controller ID was pretty much always
360 # "localhost:6633". The use of "controller here caused some
361 # confusion about whether "controller" meant the
362 # OpenFlow controller (i.e. SDNPlatform/SDNPlatform) vs. the controller VM.
363 # In the old controller model most/all of the settings concerned the
364 # OpenFlow controller not the VM>
365 # Most of the settings we now want to associate with the controller are
366 # controller VM settings (e.g. IP address, DNS, time zone) and not
367 # OpenFlow controller settings. So we're repurposing the Controller
368 # model to be the controller VM config state and not the OpenFlow
369 # controller discovered state (which was sort of broken to begin with).
370 # Currently (as of 10/2011), since we only support single node operation
371 # the controller ID is hard-coded to be localhost (probably should be
372 # something different, e.g. "default", because localhost implies a
373 # an interface which is really something separate), but eventually for
374 # multi-node operation we'll need to have unique ids for each controller
375 # node in the cluster. The easiest way would be to have the user enter
376 # something at first time config. Or possibly we could do something
377 # with GUIDs. Needs more thought...
378 id = models.CharField(
379 primary_key=True,
380 verbose_name='Controller ID',
381 help_text='Unique identifier string for the controller node',
382 validators=[ validate_controller_id ],
383 max_length=256)
384
385 status = models.CharField(
386 verbose_name='Status',
387 help_text='cluster status of node',
388 max_length=256,
389 default='Provisioned',
390 )
391
392 domain_lookups_enabled = models.BooleanField(
393 verbose_name='Domain Lookups Enabled',
394 help_text='If domain lookups are enabled (default is True)',
395 default=True
396 )
397
398 domain_name = models.CharField(
399 verbose_name='Domain Name',
400 help_text='Domain name of the network',
401 max_length=256,
402 validators=[ DomainNameValidator() ],
403 default='',
404 blank=True)
405
406 default_gateway = models.CharField(
407 verbose_name='Default Gateway',
408 help_text='Default gateway',
409 max_length=256,
410 validators=[ IpValidator() ],
411 default='',
412 blank=True)
413
414 ntp_server = models.CharField(
415 verbose_name='NTP Server',
416 help_text='NTP server',
417 max_length=256,
418 validators=[ IpOrDomainNameValidator() ],
419 default='',
420 blank=True,
421 null=True)
422
423 time_zone = models.CharField(
424 verbose_name='Time Zone',
425 help_text='Time zone (e.g. America/Los_Angeles)',
426 max_length=256,
427 validators=[ TimeZoneValidator() ],
428 default='UTC')
429
430 logging_enabled = models.BooleanField(
431 verbose_name='Logging Enabled',
432 help_text='Logging enabled',
433 default=False
434 )
435
436 logging_server = models.CharField(
437 verbose_name='Logging Server',
438 help_text='Logging server',
439 max_length=256,
440 validators=[ IpOrDomainNameValidator() ],
441 default='',
442 blank=True,
443 null=True)
444
445 logging_level = models.CharField(
446 verbose_name='Logging Level',
447 help_text='Logging level',
448 max_length=16,
449 validators=[ EnumerationValidator(('emerg', 'alert', 'crit', 'err',
450 'warning', 'notice', 'info', 'debug', '0', '1', '2', '3', '4',
451 '5', '6', '7'))],
452 default='notice'
453 )
454
455 # IOS let's you specify the domain name(s) of the network in two
456 # ways. You can specify a single domain name with the "ip domain name <domain-name>"
457 # command. You can specify multiple domain names with multiple
458 # "ip domain list <domain-name>" commands. The domain names specified with
459 # "ip domain list" commands take precedence over the domain name specified with
460 # the "ip domain name" command, so the single "ip domain name" is only
461 # used if the domain name list is unspecified/empty/null. Kind of messy, but
462 # if we want to emulate IOS behavior I think we'll need to represent that in the
463 # model. But to simplify things for now we'll just support the single domain name.
464
465 #domain_name_list = models.TextField(
466 # verbose_name='Domain Name List',
467 # help_text='List of domain names for the network, one per line',
468 # null=True
469 # )
470
471 #
472 # end fields ----------------------------------------
473
474 def __unicode__(self):
475 return "%s" % (self.id,)
476
477 class Rest:
478 NAME = 'controller-node'
479 FIELD_INFO = (
480 {'name': 'domain_lookups_enabled', 'rest_name': 'domain-lookups-enabled'},
481 {'name': 'domain_name', 'rest_name': 'domain-name'},
482 {'name': 'default_gateway', 'rest_name': 'default-gateway'},
483 {'name': 'ntp_server', 'rest_name': 'ntp-server'},
484 {'name': 'time_zone', 'rest_name': 'time-zone'},
485 {'name': 'logging_enabled', 'rest_name': 'logging-enabled'},
486 {'name': 'logging_server', 'rest_name': 'logging-server'},
487 {'name': 'logging_level', 'rest_name': 'logging-level'},
488 )
489
490"""
491#
492# ------------------------------------------------------------
493
494class ControllerAlias(models.Model):
495
496 #
497 # fields ----------------------------------------
498
499 alias = models.CharField(
500 primary_key=True,
501 max_length=255,
502 help_text = "alias for controller",
503 verbose_name='alias',
504 validators=[SwitchAliasValidator()])
505
506 controller = models.ForeignKey(
507 Controller,
508 verbose_name='Controller')
509
510 #
511 # end fields ----------------------------------------
512
513 class Rest:
514 NAME = 'controller-alias'
515#
516# ------------------------------------------------------------
517
518class ControllerInterface(models.Model):
519
520 #
521 # fields ----------------------------------------
522
523 controller = models.ForeignKey(
524 Controller,
525 verbose_name="Controller ID")
526
527 type = models.CharField(
528 verbose_name='Interface Type',
529 help_text='Type of interface (e.g. "Ethernet")',
530 max_length=256,
531 default='Ethernet'
532 )
533
534 number = models.IntegerField(
535 verbose_name="Interface Number",
536 help_text='Number of interface (non-negative integer)',
537 default=0)
538
539 mode = models.CharField(
540 verbose_name='Mode',
541 help_text='Mode of configuring IP address ("dhcp" or "static")',
542 validators=[ EnumerationValidator(('dhcp', 'static'))],
543 max_length=32,
544 default='static')
545
546 ip = models.CharField(
547 verbose_name='IP Address',
548 help_text='IP Address for interface',
549 validators=[ IpValidator() ],
550 max_length=15,
551 default='',
552 blank=True)
553
554 netmask = models.CharField(
555 verbose_name='Netmask',
556 help_text='Netmask',
557 validators=[ IpValidator() ],
558 max_length=15,
559 default='',
560 blank=True)
561
562 # provide a link between the underlying interface layer
563 # and this model's 'index number' to help manage discovered_ip
564 mac = models.CharField(
565 verbose_name="MAC Address",
566 help_text="MAC Address",
567 max_length=17,
568 validators = [validate_mac_address],
569 blank=True,
570 null=True)
571
572 discovered_ip = models.CharField(
573 verbose_name='Discovered IP Address',
574 help_text='Discovered IP Address for interface',
575 validators=[ IpValidator() ],
576 max_length=15,
577 default='',
578 blank=True)
579
580
581# in_acl = models.ForeignKey(
582# ControllerAcl,
583# verbose_name = 'Controller input acl',
584# blank=True,
585# null=True)
586
587# out_acl = models.ForeignKey(
588# ControllerAcl,
589# verbose_name = 'Controller output acl',
590# blank=True,
591# null=True)
592
593 #
594 # end fields ----------------------------------------
595
596 class CassandraSettings:
597 COMPOUND_KEY_FIELDS = ('controller', 'type', 'number')
598
599 class Rest:
600 NAME = 'controller-interface'
601 FIELD_INFO = (
602 {'name': 'discovered_ip', 'rest_name': 'discovered-ip'},
603# {'name': 'in_acl', 'rest_name': 'in-acl'},
604# {'name': 'out_acl', 'rest_name': 'out-acl'},
605 )
606
607
608#
609# ------------------------------------------------------------
610
611class FirewallRule(models.Model):
612
613 #
614 # fields ----------------------------------------
615
616 interface = models.ForeignKey(
617 ControllerInterface,
618 verbose_name="Controller Interface")
619
620 action = models.CharField(
621 max_length=8,
622 validators=[ EnumerationValidator(('allow', 'deny', 'reject'))],
623 default='allow',
624 blank=True)
625
626 src_ip = models.CharField(
627 verbose_name='Source IP',
628 help_text='IP Address to allow traffic from',
629 validators=[ IpValidator() ],
630 max_length=15,
631 default='',
632 blank=True)
633
634 vrrp_ip = models.CharField(
635 verbose_name='Local IP',
636 help_text='(Local) IP Address to allow traffic to',
637 validators=[ IpValidator() ],
638 max_length=15,
639 default='',
640 blank=True)
641
642 port = models.IntegerField(
643 verbose_name='Port Number',
644 help_text='Port Number',
645 validators=[ RangeValidator(0,2**16-1) ],
646 default=0,
647 blank=True)
648
649 proto = models.CharField(
650 verbose_name="ip proto",
651 help_text="ip protocol (tcp, udp or vrrp)", #TODO validator
652 validators=[ UfwProtocolValditor() ],
653 max_length=4,
654 default='',
655 blank=True)
656
657 #
658 # end fields ----------------------------------------
659
660 class CassandraSettings:
661 COMPOUND_KEY_FIELDS = ('interface', 'src_ip', 'vrrp_ip', 'port', 'proto')
662
663 class Rest:
664 NAME = 'firewall-rule'
665 FIELD_INFO = (
666 {'name': 'src_ip', 'rest_name': 'src-ip'},
667 {'name': 'vrrp_ip', 'rest_name': 'vrrp-ip'},
668 )
669
670#
671# ------------------------------------------------------------
672
673class ControllerDomainNameServer(models.Model):
674
675 controller = models.ForeignKey(
676 Controller,
677 verbose_name="Controller ID",
678 default=None,
679 null=True)
680
681 ip = models.CharField(
682 verbose_name='IP Address',
683 help_text='IP Address of domain name server',
684 validators=[ IpValidator() ],
685 max_length=15,
686 default='')
687
688 timestamp = models.IntegerField(
689 verbose_name='timestamp',
690 help_text='Timestamp to determine order of domain name servers',
691 default = get_timestamp,
692 null=True,
693 blank=True,
694 )
695
696 #
697 # end fields ----------------------------------------
698
699 class CassandraSettings:
700 COMPOUND_KEY_FIELDS = ('controller', 'ip')
701
702 def validate_unique(self, exclude = None):
703 try:
704 exists = ControllerDomainNameServer.objects.get(controller = self.controller,
705 ip = self.ip)
706 if exists.timestamp != self.timestamp:
707 print 'SHAZAM', self.timestamp
708 self.timestamp = exists.timestamp
709 except:
710 pass
711
712 class Rest:
713 NAME = 'controller-domain-name-server'
714 FIELD_INFO = (
715 )
716"""
717#
718# ------------------------------------------------------------
719
720class Switch(models.Model):
721 switch_id_length = 23
722 #
723 # fields ----------------------------------------
724
725 dpid = models.CharField(
726 primary_key=True,
727 verbose_name='Switch DPID',
728 help_text='Switch DPID - 64-bit hex separated by :',
729 max_length=switch_id_length,
730 validators=[ validate_dpid ])
731
732 controller = models.ForeignKey(
733 Controller,
734 verbose_name='Controller ID',
735 blank=True,
736 null=True)
737
738 socket_address = models.CharField(
739 verbose_name='Socket Address',
740 help_text='Socket address used for connection to controller',
741 max_length=64,
742 blank=True,
743 null=True)
744
745 ip = models.CharField(
746 verbose_name='IP Address',
747 help_text='IP Address used for connection from controller',
748 validators=[ IpValidator() ],
749 max_length=15,
750 blank=True,
751 null=True)
752
753 active = models.BooleanField(
754 verbose_name='Active',
755 help_text='Switch is active (i.e. connected to a controller)',
756 default=False)
757
758 connected_since = models.DateTimeField(
759 verbose_name='Last Connect Time',
760 help_text='Time when the switch connected to the controller',
761 blank=True,
762 null=True)
763
764 dp_desc = models.CharField(
765 verbose_name='Description',
766 max_length=256,
767 blank=True,
768 null=True)
769
770 hw_desc = models.CharField(
771 verbose_name='Hardware Description',
772 max_length=256,
773 blank=True,
774 null=True)
775
776 sw_desc = models.CharField(
777 verbose_name='Software Description',
778 max_length=256,
779 blank=True,
780 null=True)
781
782 serial_num = models.CharField(
783 verbose_name='Serial Number',
784 max_length=32,
785 blank=True,
786 null=True)
787
788 capabilities = models.IntegerField(
789 verbose_name='Capabilities',
790 help_text='Openflow switch capabilities',
791 validators=[ RangeValidator(0,2**32-1) ],
792 default=0)
793
794 tunneling_supported = models.BooleanField(
795 default=False,
796 )
797
798 buffers = models.IntegerField(
799 verbose_name='Max Packets',
800 help_text='Maximum number of packets buffered by the switch',
801 validators=[ RangeValidator(0,2**32-1) ],
802 blank=True,
803 null=True)
804
805 tables = models.IntegerField(
806 verbose_name='Max Tables',
807 help_text='Number of tables supported by the switch',
808 validators=[ RangeValidator(0,2**8-1) ],
809 blank=True,
810 null=True)
811
812 actions = models.IntegerField(
813 verbose_name='Actions',
814 help_text='Actions supported by the switch',
815 validators=[ RangeValidator(0,2**32-1) ],
816 default=0)
817
818 #
819 # end fields ----------------------------------------
820
821 # LOOK! we should have an origin field to distinguish
822 # between user-specified switches and 'discovered' switches
823
824 def __unicode__(self):
825 return "%s" % self.dpid
826
827 class Rest:
828 NAME = 'switch'
829 FIELD_INFO = (
830 {'name': 'socket_address', 'rest_name': 'socket-address'},
831 {'name': 'ip', 'rest_name': 'ip-address'},
832 {'name': 'connected_since', 'rest_name': 'connected-since'},
833 {'name': 'dp_desc', 'rest_name': 'dp-desc'},
834 {'name': 'hw_desc', 'rest_name': 'hw-desc'},
835 {'name': 'sw_desc', 'rest_name': 'sw-desc'},
836 {'name': 'serial_num', 'rest_name': 'serial-num'},
837 {'name': 'tunneling_supported', 'rest_name': 'tunnel-supported'},
838 )
839#
840# ------------------------------------------------------------
841# SwitchConfig
842# Any 'configured' (non-discovered) state associated with
843# a switch.
844#
845# tunnel_termination; when enabled, tunnels are constructed
846# to any other tunnel_termination switch, building a mesh
847# of open-flow enabled switches. Typicall Used in virtualized
848# environments, where openflow switches are not intended to
849# exist in the path.
850#
851
852class SwitchConfig(models.Model):
853 switch_id_length = 23
854 #
855 # fields ----------------------------------------
856
857 dpid = models.CharField(
858 primary_key=True,
859 verbose_name='Switch DPID',
860 help_text='Switch DPID - 64-bit hex separated by :',
861 max_length=switch_id_length,
862 validators=[ validate_dpid ])
863
864 core_switch = models.BooleanField(
865 default=False,
866 help_text='Identify core switches'
867 )
868
869 tunnel_termination = models.CharField(
870 verbose_name='Tunnel Termination',
871 help_text='Tunnel Termination ("enabled" "disabled" "default")',
872 validators=[ EnumerationValidator(('enabled', 'disabled', 'default'))],
873 default = 'default',
874 max_length=16)
875
876 #
877 # end fields ----------------------------------------
878
879
880 def validate_unique(self, exclude = None):
881 self.dpid = self.dpid.lower()
882
883 class Rest:
884 NAME = 'switch-config'
885 FIELD_INFO = (
886 {'name': 'tunnel_termination', 'rest_name': 'tunnel-termination'},
887 {'name': 'core_switch', 'rest_name': 'core-switch'},
888 )
889
890#
891# ------------------------------------------------------------
892
893class SwitchAlias(models.Model):
894
895 #
896 # fields ----------------------------------------
897
898 id = models.CharField(
899 primary_key=True,
900 max_length=255,
901 help_text = "alias for switch",
902 verbose_name='alias',
903 validators=[SwitchAliasValidator()])
904
905 switch = models.ForeignKey(
906 SwitchConfig,
907 verbose_name='Switch DPID')
908
909 #
910 # end fields ----------------------------------------
911
912 class Rest:
913 NAME = 'switch-alias'
914
915"""
916#
917# ------------------------------------------------------------
918
919class Port(models.Model):
920 #
921 # fields ----------------------------------------
922 #
923 # This table isn't intended to be updated via the rest api,
924 # sdnplatform writes the table to describe a switch.
925 #
926 # The 'number' in the port model is the openflow port number,
927 # which is a value used in setting up flow entries. This is
928 # not an interface name; the 'interface name' is the 'name'
929 # field below. This table provides a mapping from the switch
930 # dpid and port number to an 'interface name'.
931 #
932 # Since interface names can be configured on demand by the
933 # switch, for example to name a tunnel, its not easy to
934 # "guess" interface names without the switch reporting
935 # what interface names exist. This leads to difficulty
936 # in preconfiguring any associations with <switch, interface name>
937 #
938
939 # Unique identifier for the port
940 # combination of the switch DPID and the port number
941 id = models.CharField(
942 primary_key=True,
943 verbose_name='Port ID',
944 help_text = '#|switch|number',
945 max_length=48)
946
947 switch = models.ForeignKey(
948 Switch,
949 verbose_name='Switch DPID')
950
951 number = models.IntegerField(
952 verbose_name='OF #',
953 help_text="Port open flow number",
954 validators=[ RangeValidator(0,2**16-1) ])
955
956 hardware_address = models.CharField(
957 verbose_name="MAC Address",
958 help_text="Port MAC Address",
959 max_length=17,
960 validators = [validate_mac_address],
961 blank=True,
962 null=True)
963
964 name = models.CharField(
965 verbose_name='Name',
966 help_text="Port name",
967 max_length=32,
968 blank=True,
969 null=True)
970
971 config = models.IntegerField(
972 verbose_name='Configuration',
973 help_text='Configuration Flags',
974 validators=[ RangeValidator(0,2**32-1) ],
975 default=0)
976
977 state = models.IntegerField(
978 verbose_name="State",
979 help_text="State Flags",
980 validators=[ RangeValidator(0,2**32-1) ],
981 default=0)
982
983 current_features = models.IntegerField(
984 verbose_name='Current',
985 help_text='Current Features',
986 validators=[ RangeValidator(0,2**32-1) ],
987 default=0)
988
989 advertised_features = models.IntegerField(
990 verbose_name='Advertised',
991 help_text='Advertised Features',
992 validators=[ RangeValidator(0,2**32-1) ],
993 default=0)
994
995 supported_features = models.IntegerField(
996 verbose_name='Supported',
997 help_text='Supported Features',
998 validators=[ RangeValidator(0,2**32-1) ],
999 default=0)
1000
1001 peer_features = models.IntegerField(
1002 verbose_name='Peer',
1003 help_text='Peer Features',
1004 validators=[ RangeValidator(0,2**32-1) ],
1005 default=0)
1006
1007 #
1008 # end fields ----------------------------------------
1009
1010 def __unicode__(self):
1011 return "%s" % (self.id,)
1012
1013 class Rest:
1014 NAME = 'port'
1015 FIELD_INFO = (
1016 {'name': 'hardware_address', 'rest_name': 'hardware-address'},
1017 {'name': 'current_features', 'rest_name': 'current-features'},
1018 {'name': 'advertised_features', 'rest_name': 'advertised-features'},
1019 {'name': 'supported_features', 'rest_name': 'supported-features'},
1020 {'name': 'peer_features', 'rest_name': 'peer-features'},
1021 )
1022
1023#
1024# ------------------------------------------------------------
1025
1026class PortAlias(models.Model):
1027
1028 #
1029 # fields ----------------------------------------
1030
1031 id = models.CharField(
1032 primary_key=True,
1033 max_length=255,
1034 help_text = "alias for port",
1035 verbose_name='alias',
1036 validators=[PortAliasValidator()])
1037
1038 port = models.ForeignKey(
1039 Port,
1040 help_text = "foreign key for port alias",
1041 verbose_name='Port ID')
1042
1043 #
1044 # end fields ----------------------------------------
1045
1046 class Rest:
1047 NAME = 'port-alias'
1048#
1049# ------------------------------------------------------------
1050
1051class SwitchInterfaceConfig(models.Model):
1052 if_name_len = 32
1053 #
1054 # fields ----------------------------------------
1055
1056 switch = models.ForeignKey(
1057 SwitchConfig,
1058 verbose_name='Switch')
1059
1060 if_name = models.CharField(
1061 verbose_name='If Name',
1062 validators=[ SafeForPrimaryKeyValidator() ],
1063 max_length=32)
1064
1065 mode = models.CharField(
1066 verbose_name='Mode',
1067 help_text='Interface Mode ("external", "default")',
1068 validators=[ EnumerationValidator(('external','default'))],
1069 max_length=32,
1070 default='default')
1071
1072 broadcast = models.BooleanField(
1073 default=False,
1074 verbose_name='Broadcast',
1075 help_text='True when interfaces is an uplink to a legacy net',
1076 )
1077
1078 #
1079 # end fields ----------------------------------------
1080
1081 def __unicode__(self):
1082 return "%s" % (self.id)
1083
1084 class CassandraSettings:
1085 COMPOUND_KEY_FIELDS = ('switch', 'if_name')
1086
1087 def validate_unique(self, exclude = None):
1088 self.broadcast = False
1089 if self.mode == 'external':
1090 self.broadcast = True
1091
1092 class Rest:
1093 NAME = 'switch-interface-config'
1094 FIELD_INFO = (
1095 # By using 'name' here, the 'name' from the port is identified
1096 # in the complete-from-another procedure
1097 {'name': 'if_name', 'rest_name': 'name'},
1098 )
1099
1100
1101#
1102# ------------------------------------------------------------
1103
1104class SwitchInterfaceAlias(models.Model):
1105 switch_interface_alias_length = 255
1106 #
1107 # fields ----------------------------------------
1108
1109 id = models.CharField(
1110 primary_key = True,
1111 verbose_name = 'Switch Interface Alias',
1112 help_text = 'alias',
1113 max_length = switch_interface_alias_length,
1114 validators=[HostAliasValidator()])
1115
1116 switch_interface = models.ForeignKey(
1117 SwitchInterfaceConfig,
1118 verbose_name='Switch Interface',
1119 )
1120
1121 #
1122 # end fields ----------------------------------------
1123
1124 class Rest:
1125 NAME = 'switch-interface-alias'
1126 FIELD_INFO = (
1127 {'name': 'switch_interface', 'rest_name': 'switch-interface'},
1128 )
1129#
1130# ------------------------------------------------------------
1131
1132class StaticFlowTableEntry(models.Model):
1133 #
1134 # fields ----------------------------------------
1135
1136 name = models.CharField(
1137 primary_key=True,
1138 verbose_name='Name',
1139 max_length=80)
1140
1141 switch = models.ForeignKey(
1142 SwitchConfig,
1143 verbose_name="Switch DPID")
1144
1145 active = models.BooleanField(
1146 default=False)
1147
1148 # LOOK! we should hide the timeout values for static flow entry
1149 # definition as they are overwritten (unless we allow these to
1150 # overwrite the default that static flow pusher uses)
1151 idle_timeout = models.IntegerField(
1152 verbose_name="Idle Timeout",
1153 help_text="Expire flow after this many seconds of inactivity - default: 60",
1154 default=60,
1155 validators=[ RangeValidator(0, 2**16-1) ])
1156
1157 hard_timeout = models.IntegerField(
1158 verbose_name="Hard Timeout",
1159 help_text="Seconds to expire flow, regardless of activity - default: 0",
1160 default=0,
1161 validators=[ RangeValidator(0, 2**16-1) ])
1162
1163 priority = models.IntegerField(
1164 verbose_name="Priority",
1165 help_text="Priority of the flow entry",
1166 default=32768,
1167 validators=[ RangeValidator(0, 2**16-1) ])
1168
1169 cookie = models.IntegerField(
1170 verbose_name="Cookie",
1171 default=0) # 64-bit
1172
1173 #
1174 # match fields
1175 #
1176
1177 # LOOK! Need a file of python openflow constants
1178
1179 # LOOK! we should hide this also or at least
1180 # for static flow entries say it is ignored
1181 wildcards = models.IntegerField(
1182 verbose_name="Wildcards",
1183 default=0,
1184 validators=[ RangeValidator(0,2**32-1) ])
1185
1186 in_port = models.IntegerField(
1187 verbose_name="Ingress Port",
1188 blank=True,
1189 help_text="Open flow port number of ingress port",
1190 null=True,
1191 validators = [ RangeValidator(0, 2**16-1) ] )
1192
1193 dl_src = models.CharField(
1194 verbose_name="Src MAC",
1195 help_text="This is a 48-bit quantity specified in xx:xx:xx:xx:xx:xx format",
1196 max_length=17,
1197 blank=True,
1198 null=True,
1199 validators = [ validate_mac_address ] )
1200
1201 dl_dst = models.CharField(
1202 verbose_name="Dst MAC",
1203 help_text="Destination MAC address in the frames",
1204 max_length=17,
1205 blank=True,
1206 null=True,
1207 validators = [ validate_mac_address ] )
1208
1209 dl_vlan = models.IntegerField(
1210 verbose_name="VLAN ID",
1211 help_text="VLAN ID in the frames",
1212 blank=True,
1213 null=True,
1214 validators = [ RangeValidator(0, 2**12-1) ])
1215
1216 dl_vlan_pcp = models.IntegerField(
1217 verbose_name="VLAN Priority",
1218 help_text="VLAN ID in the frames",
1219 blank=True,
1220 null=True,
1221 validators = [ RangeValidator(0, 2**3-1) ])
1222
1223 dl_type = models.IntegerField(
1224 verbose_name="Ether Type",
1225 help_text="Ether(L3) type",
1226 blank=True,
1227 null=True,
1228 validators = [ RangeValidator(0, 2**16-1) ])
1229
1230 nw_tos = models.IntegerField(
1231 verbose_name="TOS Bits",
1232 help_text="TOS bits in the frame",
1233 blank=True,
1234 null=True,
1235 validators = [ RangeValidator(0, 2**6-1) ]) # 6-bit DSCP value
1236
1237 nw_proto = models.IntegerField(
1238 verbose_name="Protocol",
1239 help_text="IP (L4) protocol in the packets",
1240 blank=True,
1241 null=True,
1242 validators = [ RangeValidator(0, 2**8-1) ])
1243
1244 nw_src = models.CharField(
1245 verbose_name="Src IP",
1246 help_text="IP v4 source address in dotted decimal a.b.c.d w/ optional mask (ex: /24)",
1247 max_length=18,
1248 validators = [ CidrValidator(mask_required=False) ],
1249 blank=True,
1250 null=True)
1251
1252 nw_dst = models.CharField(
1253 verbose_name="Dst IP",
1254 help_text="IP v4 destination address in dotted decimal a.b.c.d w/ optional mask (ex: /24)",
1255 validators=[ CidrValidator(mask_required=False) ],
1256 max_length=18,
1257 blank=True,
1258 null=True )
1259
1260 tp_src = models.IntegerField(
1261 verbose_name="Src Port",
1262 help_text="Source (TCP/UDP) port",
1263 blank=True,
1264 null=True,
1265 validators=[ RangeValidator(0, 2**16-1) ])
1266
1267 tp_dst = models.IntegerField(
1268 verbose_name="Dst Port",
1269 help_text="Destination (TCP/UDP) port",
1270 blank=True,
1271 null=True,
1272 validators=[ RangeValidator(0, 2**16-1) ])
1273
1274 # LOOK! have to figure out how to expose actions in the CLI - this is ugly/brittle
1275 actions = models.CharField(
1276 verbose_name = "Actions",
1277 help_text="This is a comma-separated list of actions - a la dpctl",
1278 max_length=1024,
1279 blank=True,
1280 null=True)
1281
1282 #
1283 # end fields ----------------------------------------
1284
1285 def __unicode__(self):
1286 return self.name
1287
1288
1289 class Rest:
1290 NAME = 'flow-entry'
1291 FIELD_INFO = (
1292 {'name': 'idle_timeout', 'rest_name': 'idle-timeout'},
1293 {'name': 'hard_timeout', 'rest_name': 'hard-timeout'},
1294 {'name': 'in_port', 'rest_name': 'ingress-port'},
1295 {'name': 'dl_src', 'rest_name': 'src-mac'},
1296 {'name': 'dl_dst', 'rest_name': 'dst-mac'},
1297 {'name': 'dl_vlan', 'rest_name': 'vlan-id'},
1298 {'name': 'dl_vlan_pcp', 'rest_name': 'vlan-priority'},
1299 {'name': 'dl_type', 'rest_name': 'ether-type'},
1300 {'name': 'nw_tos', 'rest_name': 'tos-bits'},
1301 {'name': 'nw_proto', 'rest_name': 'protocol'},
1302 {'name': 'nw_src', 'rest_name': 'src-ip'},
1303 {'name': 'nw_dst', 'rest_name': 'dst-ip'},
1304 {'name': 'tp_src', 'rest_name': 'src-port'},
1305 {'name': 'tp_dst', 'rest_name': 'dst-port'},
1306 {'name': 'actions', 'rest_name': 'actions'},
1307 )
1308
1309#
1310# ------------------------------------------------------------
1311
1312class Link(models.Model):
1313 #
1314 # fields ----------------------------------------
1315
1316 id = models.CharField(
1317 primary_key=True,
1318 verbose_name='Link ID',
1319 max_length=64)
1320
1321 src_switch = models.ForeignKey(
1322 Switch,
1323 verbose_name='Src Switch DPID',
1324 related_name='src_link_set')
1325 #src_switch_id = models.CharField(
1326 # verbose_name='Src Switch ID',
1327 # max_length=32)
1328
1329 name = models.CharField(
1330 verbose_name='Name',
1331 help_text="Link name",
1332 max_length=32,
1333 blank=True,
1334 null=True)
1335
1336 src_port = models.IntegerField(
1337 verbose_name="Src Port",
1338 validators=[ RangeValidator(0, 2**16-1) ])
1339
1340 src_port_state = models.IntegerField(
1341 verbose_name="Src Port State",
1342 help_text="Source Port State Flags",
1343 validators=[ RangeValidator(0,2**32-1) ],
1344 default=0)
1345
1346 dst_switch = models.ForeignKey(
1347 Switch,
1348 verbose_name='Dst Switch DPID',
1349 related_name='dst_link_set')
1350 #dst_switch_id = models.CharField(
1351 # verbose_name='Dst Switch ID',
1352 # max_length=32)
1353
1354 dst_port = models.IntegerField(
1355 verbose_name="Dst Port",
1356 help_text="Destination Port",
1357 validators=[ RangeValidator(0, 2**16-1) ])
1358
1359 dst_port_state = models.IntegerField(
1360 verbose_name="Dst Port State",
1361 help_text="Destination Port State Flags",
1362 validators=[ RangeValidator(0,2**32-1) ],
1363 default=0)
1364
1365 link_type = models.CharField(
1366 verbose_name='Link Type',
1367 help_text="Link type",
1368 max_length=10,
1369 blank=True,
1370 null=True)
1371 #
1372 # end fields ----------------------------------------
1373
1374 def __unicode__(self):
1375 return "%s" % self.id
1376
1377 class Rest:
1378 NAME = 'link'
1379 FIELD_INFO = (
1380 {'name': 'src_switch', 'rest_name': 'src-switch'},
1381 {'name': 'src_port', 'rest_name': 'src-port'},
1382 {'name': 'src_port_state', 'rest_name': 'src-port-state'},
1383 {'name': 'dst_switch', 'rest_name': 'dst-switch'},
1384 {'name': 'dst_port', 'rest_name': 'dst-port'},
1385 {'name': 'dst_port_state', 'rest_name': 'dst-port-state'},
1386 {'name': 'link_type', 'rest_name': 'link-type'}
1387 )
1388
1389"""
1390"""
1391#
1392# ------------------------------------------------------------
1393# An address-space separation
1394
1395class AddressSpace (models.Model):
1396
1397 id_max_length = 64
1398
1399 #
1400 # fields ----------------------------------------
1401
1402 #
1403 # Unique name of the address-space
1404 #
1405 name = models.CharField(
1406 primary_key = True,
1407 verbose_name = 'Address Space Name',
1408 help_text = 'A unique name for an Address Space Seperation',
1409 validators = [ AddressSpaceNameValidator() ],
1410 max_length = id_max_length)
1411
1412 #
1413 # Verbose description of this rule.
1414 #
1415 description = models.CharField(
1416 verbose_name = 'Description',
1417 help_text = "Description of the address-space",
1418 max_length = 128,
1419 blank = True,
1420 null = True)
1421
1422 #
1423 # Whether the configuration is active ? By default, it is active
1424 # Used to disable the configuration without having to delete the entire
1425 # address-space configuration construct.
1426 #
1427 active = models.BooleanField(
1428 verbose_name = 'Active',
1429 help_text = 'Is this Address Space active (default is True)',
1430 default = True)
1431
1432 #
1433 # Priority of this address-space during device matching when
1434 # compared to other address-spaces. Those at the same priority
1435 # are used in a determinisitc alphanetical order.
1436 #
1437 priority = models.IntegerField(
1438 verbose_name = 'Priority',
1439 help_text = 'Priority for this Address Space ' +
1440 '(higher numbers are higher priority)',
1441 default = 1000,
1442 validators = [ RangeValidator(0, 65535) ])
1443
1444 #
1445 # Seperator tag of this address-space in the data plane, such as vlan id.
1446 #
1447 vlan_tag_on_egress = models.IntegerField(
1448 verbose_name = 'Egress VLAN tag',
1449 help_text = 'Vlan Tag value used for this Address Space separation',
1450 default = None,
1451 blank = True,
1452 null = True,
1453 validators = [ RangeValidator(0, 4095) ])
1454
1455 #
1456 # Origin of this configured item
1457 #
1458 origin = models.CharField(
1459 verbose_name = "Origin",
1460 help_text = "Values: cli, rest, other packages",
1461 max_length = 64, # in future we might use SW GUIDs for this field
1462 blank = True,
1463 null = True)
1464
1465 #
1466 # end fields ----------------------------------------
1467
1468 def __unicode__ (self):
1469 return self.name
1470
1471 class Rest:
1472 NAME = 'address-space'
1473 FIELD_INFO = (
1474 {'name': 'vlan_tag_on_egress', 'rest_name':'vlan-tag-on-egress'},
1475 )
1476
1477#
1478# ------------------------------------------------------------
1479# An identifier rule in address-space separation
1480
1481class AddressSpaceIdentifierRule (models.Model):
1482 rule_max_length = 32
1483
1484 #
1485 # fields ----------------------------------------
1486
1487 #
1488 # Create a reference to the enclosing address-space construct.
1489 #
1490 address_space = models.ForeignKey(
1491 AddressSpace,
1492 verbose_name = 'Address Space Identifier')
1493
1494 #
1495 # Unique rule identifier name under this address-space.
1496 #
1497 rule = models.CharField(
1498 verbose_name = 'Address Space Rule Identifier',
1499 max_length = rule_max_length)
1500
1501 #
1502 # Verbose description of this rule.
1503 #
1504 description = models.CharField(
1505 verbose_name = 'Description',
1506 help_text = "Description of rule",
1507 max_length = 128,
1508 blank = True,
1509 null = True)
1510
1511 #
1512 # Whether the configuration is active ? By default, it is active
1513 # Used to disable the configuration without having to delete the entire
1514 # address-space identifier-rule configuration construct.
1515 #
1516 active = models.BooleanField(
1517 verbose_name = 'Active',
1518 help_text = 'If this interface is active (default is True)',
1519 default = True)
1520
1521 #
1522 # Priority of this address-space during device matching when
1523 # compared to other address-spaces. Those at the same priority
1524 # are used in a determinisitc alphanetical order.
1525 #
1526 priority = models.IntegerField(
1527 verbose_name = 'Priority',
1528 help_text = 'Priority for this interface rule ' +
1529 '(higher numbers are higher priority)',
1530 default = 32768,
1531 validators = [ RangeValidator(0, 65535) ])
1532
1533 #
1534 # DPID of the Switch which sent the packet
1535 #
1536 switch = models.CharField(
1537 verbose_name = 'Switch DPID',
1538 max_length = Switch.switch_id_length,
1539 help_text = 'Switch DPID or switch alias',
1540 validators = [ validate_dpid ],
1541 null = True,
1542 blank = True)
1543
1544 #
1545 # Range of ports in which the packet came from
1546 #
1547 ports = models.CharField(
1548 verbose_name = "Port Range Spec",
1549 help_text = 'Port range (e.g. C12 or B1,A22-25)',
1550 max_length = 256,
1551 validators = [ PortRangeSpecValidator() ],
1552 blank = True,
1553 null = True)
1554
1555 #
1556 # Range of VLAN tags
1557 #
1558 vlans = models.CharField(
1559 verbose_name = "VLAN Range Spec",
1560 help_text = "VLAN(s) (e.g. 5 or 5-10,4010-4050)",
1561 max_length = 256,
1562 validators = [ VLANRangeSpecValidator() ],
1563 blank = True,
1564 null = True)
1565
1566 #
1567 # NameValue pair of tags
1568 #
1569 tag = models.CharField(
1570 verbose_name = "Tag Spec",
1571 help_text = "Tag values (e.g. namespace.tagname=value)",
1572 max_length = 256,
1573 validators = [ TagSpecValidator() ],
1574 blank = True,
1575 null = True)
1576
1577 #
1578 # end fields ----------------------------------------
1579
1580 def __unicode__ (self):
1581 return self.id
1582
1583 class CassandraSettings:
1584 COMPOUND_KEY_FIELDS = ('address_space', 'rule')
1585
1586 class Rest:
1587 NAME = 'address-space-identifier-rule'
1588 FIELD_INFO = (
1589 {'name': 'description', 'rest_name': 'description'},
1590 {'name': 'address_space', 'rest_name': 'address-space'},
1591 {'name': 'active', 'rest_name': 'active'},
1592 {'name': 'priority', 'rest_name': 'priority'},
1593 {'name': 'switch', 'rest_name': 'switch'},
1594 {'name': 'ports', 'rest_name': 'ports'},
1595 {'name': 'vlans', 'rest_name': 'vlans'},
1596 {'name': 'tag', 'rest_name': 'tag'},
1597 )
1598#
1599# ------------------------------------------------------------
1600"""
1601class HostConfig(models.Model):
1602 host_id_length = 17
1603 #
1604 # fields ----------------------------------------
1605
1606 #address_space = models.ForeignKey(
1607 # AddressSpace,
1608 # verbose_name = "Address space name")
1609
1610 mac = models.CharField(
1611 verbose_name="MAC Address",
1612 max_length=host_id_length,
1613 validators = [validate_mac_address])
1614
1615 vlan = models.CharField(
1616 verbose_name='VLAN',
1617 help_text='VLAN Associated with host',
1618 max_length=4,
1619 validators=[RangeValidator(1, 4095)],
1620 blank=True,
1621 default='')
1622
1623 #
1624 # end fields ----------------------------------------
1625
1626 def __unicode__(self):
1627 self.mac = self.mac.lower()
1628 #return "%s::%s" % (self.addressSpace, self.mac)
1629 return "%s" % (self.addressSpace)
1630
1631 class CassandraSettings:
1632 #COMPOUND_KEY_FIELDS = ('address_space', 'vlan', 'mac')
1633 COMPOUND_KEY_FIELDS = ('vlan', 'mac')
1634
1635 def validate_unique(self, exclude = None):
1636 # Invoke the default validator; error out if the vns already exists
1637 super(HostConfig, self).validate_unique(exclude)
1638 #if self.vlan and str(self.address_space) != 'default':
1639 # raise ValidationError('host: vlan configured for '
1640 # 'address-space other than "default" %s' % self.address_space)
1641
1642 class Rest:
1643 NAME = 'host-config'
1644 FIELD_INFO = (
1645 #{'name': 'address_space', 'rest_name': 'address-space'},
1646 )
1647"""
1648#
1649# ------------------------------------------------------------
1650
1651class HostSecurityIpAddress(models.Model):
1652 host = models.ForeignKey(
1653 HostConfig,
1654 verbose_name='Host ID')
1655
1656 ip = models.CharField(
1657 verbose_name='IP Address',
1658 help_text='IP Address used to associate with host',
1659 validators=[ IpValidator() ],
1660 max_length=15,
1661 blank=True,
1662 null=True)
1663
1664 #
1665 # end fields ----------------------------------------
1666
1667 def __unicode__(self):
1668 return self.id
1669
1670 class CassandraSettings:
1671 COMPOUND_KEY_FIELDS = ('host', 'ip')
1672
1673 class Rest:
1674 NAME = 'host-security-ip-address'
1675 FIELD_INFO = (
1676 {'name': 'ip', 'rest_name': 'ip-address'},
1677 )
1678
1679
1680#
1681# ------------------------------------------------------------
1682
1683class HostSecurityAttachmentPoint(models.Model):
1684 host = models.ForeignKey(
1685 HostConfig,
1686 verbose_name='Host ID')
1687
1688 dpid = models.CharField(
1689 verbose_name = 'Switch DPID',
1690 max_length = Switch.switch_id_length,
1691 help_text = 'Switch DPID or switch alias',
1692 validators = [ validate_dpid ],
1693 null = True,
1694 blank = True)
1695
1696 if_name_regex = models.CharField(
1697 verbose_name='If Name Regex',
1698 help_text='Interface name regular expression',
1699 max_length=64,
1700 validators = [SafeForPrimaryKeyValidator(), IsRegexValidator()],
1701 blank = True,
1702 null = False,
1703 )
1704
1705
1706 #
1707 # end fields ----------------------------------------
1708
1709 def __unicode__(self):
1710 return self.id
1711
1712 class CassandraSettings:
1713 COMPOUND_KEY_FIELDS = ('host', 'dpid', 'if_name_regex')
1714
1715 class Rest:
1716 NAME = 'host-security-attachment-point'
1717 FIELD_INFO = (
1718 {'name': 'if_name_regex', 'rest_name': 'if-name-regex'},
1719 )
1720
1721#
1722# ------------------------------------------------------------
1723
1724class HostAlias(models.Model):
1725 host_alias_length = 255
1726 #
1727 # fields ----------------------------------------
1728
1729 id = models.CharField(
1730 primary_key = True,
1731 verbose_name = 'Host Alias',
1732 help_text = 'alias',
1733 max_length = host_alias_length,
1734 validators=[HostAliasValidator()])
1735
1736 host = models.ForeignKey(
1737 HostConfig,
1738 verbose_name='Host ID')
1739
1740 #
1741 # end fields ----------------------------------------
1742
1743 class Rest:
1744 NAME = 'host-alias'
1745
1746class VlanConfig(models.Model):
1747 #
1748 # fields ----------------------------------------
1749 vlan = models.IntegerField(
1750 primary_key = True,
1751 verbose_name='VLAN',
1752 help_text='VLAN Number',
1753 validators=[RangeValidator(0, 4095)],
1754 )
1755
1756 #
1757 # end fields ----------------------------------------
1758
1759 def __unicode__(self):
1760 if self.vlan:
1761 return "%s vlan %s" % (self.vlan)
1762 else:
1763 return "%s vlan %s" % (self.vlan)
1764
1765 class Rest:
1766 NAME = 'vlan-config'
1767
1768#
1769# ------------------------------------------------------------
1770# A Static ARP table separation
1771
1772class StaticArp (models.Model):
1773
1774 id_max_length = 64
1775 #
1776 # fields ----------------------------------------
1777 ip = models.CharField(
1778 primary_key=True,
1779 verbose_name='IP Address',
1780 validators=[ IpValidator() ],
1781 max_length=15)
1782 mac = models.CharField(
1783 verbose_name="MAC Address",
1784 max_length=17,
1785 validators = [validate_mac_address])
1786 #
1787 # Origin of this configured item
1788 #
1789 origin = models.CharField(
1790 verbose_name = "Origin",
1791 help_text = "Values: cli, rest, other packages",
1792 max_length = 64, # in future we might use SW GUIDs for this field
1793 blank = True,
1794 null = True)
1795
1796 #
1797 # end fields ----------------------------------------
1798
1799 def __unicode__ (self):
1800 return self.id
1801
1802 class Rest:
1803 NAME = 'static-arp'
1804
1805
1806
1807#
1808# ------------------------------------------------------------
1809# A Tenant separation
1810
1811class Tenant (models.Model):
1812
1813 id_max_length = 64
1814 #
1815 # fields ----------------------------------------
1816
1817 #
1818 # Unique name of the tenant
1819 #
1820 name = models.CharField(
1821 primary_key = True,
1822 verbose_name = 'Tenant Name',
1823 help_text = 'A unique name for an Tenant',
1824 validators = [ TenantNameValidator() ],
1825 max_length = id_max_length)
1826
1827 #
1828 # Verbose description of this tenant.
1829 #
1830 description = models.CharField(
1831 verbose_name = 'Description',
1832 help_text = "Description of the tenant",
1833 max_length = 128,
1834 blank = True,
1835 null = True)
1836
1837 #
1838 # Whether the configuration is active ? By default, it is active
1839 # Used to disable the configuration without having to delete the entire
1840 # tenant configuration construct.
1841 #
1842 active = models.BooleanField(
1843 verbose_name = 'Active',
1844 help_text = 'Is this Tenant active (default is True)',
1845 default = True)
1846
1847 #
1848 # Origin of this configured item
1849 #
1850 origin = models.CharField(
1851 verbose_name = "Origin",
1852 help_text = "Values: cli, rest, other packages",
1853 max_length = 64, # in future we might use SW GUIDs for this field
1854 blank = True,
1855 null = True)
1856
1857 #
1858 # end fields ----------------------------------------
1859
1860 def __unicode__ (self):
1861 return self.name
1862
1863 def delete(self):
1864 if self.name=='default' or self.name =='system' or self.name =='external':
1865 raise ValidationError("Default/External/System Tenant can't be deleted")
1866 super(Tenant, self).delete()
1867 class Rest:
1868 NAME = 'tenant'
1869
1870
1871#
1872# ------------------------------------------------------------
1873# A virtual router separation
1874
1875class VirtualRouter (models.Model):
1876
1877 id_max_length = 64
1878 #
1879 # fields ----------------------------------------
1880 vrname = models.CharField(
1881 verbose_name = 'Virtual Router Name',
1882 help_text = 'A unique name for a virtual router',
1883 validators = [ GeneralNameValidator() ],
1884 max_length = id_max_length
1885 )
1886
1887 tenant = models.ForeignKey(
1888 Tenant,
1889 verbose_name='Tenant Name',
1890 )
1891 description = models.CharField(
1892 verbose_name = 'Description',
1893 help_text = "Description of the virtual router",
1894 max_length = 128,
1895 blank = True,
1896 null = True,
1897 )
1898
1899 #
1900 # Origin of this configured item
1901 #
1902 origin = models.CharField(
1903 verbose_name = "Origin",
1904 help_text = "Values: cli, rest, other packages",
1905 max_length = 64, # in future we might use SW GUIDs for this field
1906 blank = True,
1907 null = True)
1908
1909 #
1910 # end fields ----------------------------------------
1911
1912 def __unicode__ (self):
1913 return self.id
1914
1915 def validate_unique(self, exclude = None):
1916 # in fat tire, only one router per tenant can be defined. this can be removed later.
1917 error=False
1918 try:
1919 exists = VirtualRouter.objects.get(tenant = self.tenant)
1920 if exists.vrname !=self.vrname:
1921 error=True
1922 except:
1923 pass
1924 if error:
1925 raise ValidationError(" Virtual router %s has been defined for tenant %s, only one virtual router per tenant supported" % (exists.vrname,self.tenant))
1926
1927 class CassandraSettings:
1928 COMPOUND_KEY_FIELDS = ('tenant', 'vrname')
1929
1930 class Rest:
1931 NAME = 'virtualrouter'
1932
1933#
1934# ------------------------------------------------------------
1935# A virtual network segment
1936
1937class VNS(models.Model):
1938 id_max_length = 64
1939 #
1940 # fields ----------------------------------------
1941 vnsname = models.CharField(
1942 verbose_name='VNS ID',
1943 help_text='A unique name for a Virtual Network Segment',
1944 validators=[GeneralNameValidator()],
1945 max_length=id_max_length)
1946 tenant=models.ForeignKey(
1947 Tenant,
1948 verbose_name='Tenant ID',
1949 default='default')
1950 #
1951 # Verbose description of this rule.
1952 #
1953 description = models.CharField(
1954 verbose_name = 'Description',
1955 help_text = "Description of the VNS",
1956 max_length = 128,
1957 blank = True,
1958 null = True)
1959
1960 #
1961 # Reference to the address-space item. By default, we
1962 # implicitly use 'default' if this is not explicitly provided.
1963 #
1964 vns_address_space = models.ForeignKey(
1965 AddressSpace,
1966 verbose_name='Address Space Association',
1967 blank=True,
1968 null=True)
1969
1970 active = models.BooleanField(
1971 verbose_name='Active',
1972 help_text='If this VNS is active (default is True)',
1973 default=True)
1974
1975 priority = models.IntegerField(
1976 verbose_name='Priority',
1977 help_text='Priority for this VNS (higher numbers are higher priority)',
1978 default = 1000,
1979 validators=[RangeValidator(0, 65535)])
1980
1981 origin = models.CharField(
1982 verbose_name = "The origin/creator interface for this VNS",
1983 help_text="Values: cli, rest, other packages",
1984 max_length=64, # in future we might use SW GUIDs for this field
1985 blank=True,
1986 null=True)
1987
1988 arp_mode = models.CharField(
1989 verbose_name = "ARP Manager Config Mode",
1990 help_text="Values: always-flood, flood-if-unknown, drop-if-unknown",
1991 max_length=32,
1992 validators=[VnsArpModeValidator()],
1993 default='flood-if-unknown')
1994
1995 dhcp_mode = models.CharField(
1996 verbose_name = "DHCP Manager Config Mode",
1997 help_text = "Values: always-flood, flood-if-unknown, static",
1998 max_length = 20,
1999 validators=[VnsDhcpModeValidator()],
2000 default='flood-if-unknown')
2001
2002 dhcp_ip = models.CharField(
2003 verbose_name='DHCP IP Address',
2004 help_text='IP Address of DHCP Server',
2005 validators=[ IpValidator() ],
2006 max_length=15,
2007 blank=True,
2008 null=True)
2009
2010 broadcast = models.CharField(
2011 verbose_name = "Broadcast (non ARP/DHCP) Config Mode",
2012 help_text = "Values: always-flood, forward-to-known, drop",
2013 max_length = 20,
2014 validators=[VnsBroadcastModeValidator()],
2015 default='forward-to-known')
2016
2017 #
2018 # end fields ----------------------------------------
2019
2020 def __unicode__(self):
2021 return self.id
2022
2023 def delete(self):
2024 #default VNS can't be deleted
2025 if self.id=='default|default':
2026 raise ValidationError("Default VNS can't be deleted")
2027 #while address space still exist, address space default vns can't be deleted
2028 #for fat tire, relationship between address space and tenant are unclear yet, the following part may need revisit
2029 suffix = '-default'
2030 if self.vnsname.endswith(suffix):
2031 print self.vnsname
2032 address_space_name = self.vnsname[:-len(suffix)]
2033 error=False
2034 try:
2035 self.vns_address_space = AddressSpace.objects.get(name = address_space_name)
2036 error=True
2037 except Exception, e:
2038 pass
2039 if error:
2040 raise ValidationError('vns %s is the default VNS of address space: %s, can not be deleted ' %
2041 (self.vnsname,address_space_name))
2042 super(VNS, self).delete()
2043 # manage a magic association between vns names and
2044 # address space for vns's which end in -default
2045 def validate_unique(self, exclude = None):
2046 # Invoke the default validator; error out if the vns already exists
2047 #for fat tire, relationship between address space and tenant are unclear yet, the following part may need revisit
2048 super(VNS, self).validate_unique(exclude)
2049 suffix = '-default'
2050 if not 'vns_address_space' in exclude:
2051 if self.vns_address_space:
2052 if self.vnsname.endswith(suffix):
2053 if str(self.vns_address_space) != self.vnsname[:-len(suffix)]:
2054 raise ValidationError('vns names %s ending in -default '
2055 'must have address_space names with the same prefix: %s '
2056 % (self.vnsname, self.vns_address_space))
2057 elif self.vnsname.endswith(suffix):
2058 address_space_name = self.vnsname[:-len(suffix)]
2059 try:
2060 self.vns_address_space = AddressSpace.objects.get(name = address_space_name)
2061 except Exception, e:
2062 print e
2063 if self.vns_address_space == None:
2064 raise ValidationError('vns %s has no matching address-space %s ' %
2065 (self.vnsname, address_space_name))
2066
2067 class CassandraSettings:
2068 COMPOUND_KEY_FIELDS = ('tenant', 'vnsname')
2069
2070 class Rest:
2071 NAME = 'vns-definition'
2072 FIELD_INFO = (
2073 {'name': 'vns_address_space', 'rest_name': 'address-space'},
2074 {'name': 'arp_mode', 'rest_name': 'arp-mode'},
2075 {'name': 'dhcp_mode', 'rest_name': 'dhcp-mode'},
2076 {'name': 'dhcp_ip', 'rest_name': 'dhcp-ip'},
2077 )
2078
2079#
2080# ------------------------------------------------------------
2081# An interface rule on a VNS
2082
2083class VNSInterfaceRule(models.Model):
2084 rule_max_length = 32
2085
2086 #
2087 # fields ----------------------------------------
2088
2089 vns = models.ForeignKey(
2090 VNS,
2091 verbose_name='VNS ID')
2092
2093 rule = models.CharField(
2094 verbose_name='VNS Rule ID',
2095 max_length=rule_max_length)
2096
2097 description = models.CharField(
2098 verbose_name='Description',
2099 help_text="Description of rule",
2100 max_length=128,
2101 blank=True,
2102 null=True)
2103
2104 vlan_tag_on_egress = models.BooleanField(
2105 verbose_name='Egress Vlan Tagging',
2106 help_text='Tag with VLAN at egress point (default is False)',
2107 default=False)
2108
2109 allow_multiple = models.BooleanField(
2110 verbose_name='Allow Multiple',
2111 help_text='If this interface allows hosts to be on multiple VNS (default is False)',
2112 default=False)
2113
2114 active = models.BooleanField(
2115 verbose_name='Active',
2116 help_text='If this interface is active (default is True)',
2117 default=True)
2118
2119 priority = models.IntegerField(
2120 verbose_name='Priority',
2121 help_text='Priority for this interface rule (higher numbers are higher priority)',
2122 default = 32768,
2123 validators=[RangeValidator(0, 65535)])
2124
2125 mac = models.CharField(
2126 verbose_name="MAC Address",
2127 help_text='MAC Address or host alias',
2128 max_length=17,
2129 validators = [validate_mac_address],
2130 blank=True,
2131 null=True)
2132
2133 ip_subnet = models.CharField(
2134 verbose_name="IP Subnet",
2135 help_text='IP address or subnet (e.g. 192.168.1.1 or 192.168.1.0/24)',
2136 max_length=31,
2137 validators = [CidrValidator(False)],
2138 blank=True,
2139 null=True)
2140
2141 switch = models.CharField(
2142 verbose_name='Switch DPID',
2143 max_length= Switch.switch_id_length,
2144 help_text='Switch DPID or switch alias',
2145 validators=[ validate_dpid ],
2146 null=True,
2147 blank=True)
2148
2149 ports = models.CharField(
2150 verbose_name="Port Range Spec",
2151 help_text='Port range (e.g. C12 or B1,A22-25)',
2152 max_length=256,
2153 validators = [PortRangeSpecValidator()],
2154 blank=True,
2155 null=True)
2156
2157 vlans = models.CharField(
2158 verbose_name="VLAN Range Spec",
2159 help_text="VLAN(s) (e.g. 5 or 5-10,4010-4050)",
2160 max_length=256,
2161 validators = [VLANRangeSpecValidator()],
2162 blank=True,
2163 null=True)
2164
2165 tags = models.CharField(
2166 verbose_name="Tag Spec",
2167 help_text="Tag values (e.g. namespace.tagname=value)",
2168 max_length=256,
2169 validators = [TagSpecValidator()],
2170 blank=True,
2171 null=True)
2172
2173
2174 #
2175 # end fields ----------------------------------------
2176
2177 def __unicode__(self):
2178 return self.id
2179
2180 class CassandraSettings:
2181 COMPOUND_KEY_FIELDS = ('vns', 'rule')
2182
2183 class Rest:
2184 NAME = 'vns-interface-rule'
2185 FIELD_INFO = (
2186 {'name': 'description', 'rest_name': 'description'},
2187 {'name': 'allow_multiple', 'rest_name': 'allow-multiple'},
2188 {'name': 'active', 'rest_name': 'active'},
2189 {'name': 'priority', 'rest_name': 'priority'},
2190 {'name': 'mac', 'rest_name': 'mac'},
2191 {'name': 'ip_subnet', 'rest_name': 'ip-subnet'},
2192 {'name': 'switch', 'rest_name': 'switch'},
2193 {'name': 'ports', 'rest_name': 'ports'},
2194 {'name': 'vlans', 'rest_name': 'vlans'},
2195 {'name': 'tags', 'rest_name': 'tags'},
2196 {'name': 'vlan_tag_on_egress', 'rest_name': 'vlan-tag-on-egress'},
2197 )
2198
2199#
2200# ------------------------------------------------------------
2201
2202class VNSInterfaceConfig(models.Model):
2203 name_max_length = 32
2204 #
2205 # fields ----------------------------------------
2206
2207 vns = models.ForeignKey(
2208 VNS,
2209 verbose_name='VNS ID')
2210
2211 interface = models.CharField(
2212 verbose_name='VNS Interface Name',
2213 max_length=name_max_length,
2214 validators = [VnsInterfaceNameValidator()])
2215
2216 rule = models.ForeignKey(
2217 VNSInterfaceRule,
2218 verbose_name='VNS Rule ID',
2219 blank=True,
2220 null=True)
2221
2222 #
2223 # end fields ----------------------------------------
2224
2225 def __unicode__(self):
2226 return self.id
2227
2228 class CassandraSettings:
2229 COMPOUND_KEY_FIELDS = ('vns', 'interface')
2230
2231 class Rest:
2232 NAME = 'vns-interface-config'
2233 FIELD_INFO = (
2234 {'name': 'rule', 'rest_name': 'rule'},
2235 )
2236
2237
2238#
2239# ------------------------------------------------------------
2240
2241class VNSAcl(models.Model):
2242 name_max_length=32
2243 #
2244 # fields ----------------------------------------
2245
2246 vns = models.ForeignKey(
2247 VNS,
2248 verbose_name='VNS ID')
2249
2250 name = models.CharField(
2251 help_text='Acl Name',
2252 validators=[VnsAclNameValidator()],
2253 max_length=name_max_length)
2254
2255 priority = models.IntegerField(
2256 verbose_name='Priority',
2257 help_text='Priority for this ACL (higher numbers are higher priority)',
2258 default = 32768,
2259 validators=[RangeValidator(0, 65535)])
2260
2261 description = models.CharField(
2262 verbose_name='Description',
2263 help_text="Description of the ACL",
2264 max_length=128,
2265 blank=True,
2266 null=True)
2267
2268 #
2269 # end fields ----------------------------------------
2270
2271 class CassandraSettings:
2272 COMPOUND_KEY_FIELDS = ('vns', 'name')
2273
2274 def __unicode__(self):
2275 return self.id
2276
2277 class Rest:
2278 NAME = 'vns-access-list'
2279 FIELD_INFO = (
2280 {'name': 'name', 'rest_name': 'name'},
2281 {'name': 'priority', 'rest_name': 'priority'},
2282 {'name': 'description', 'rest_name': 'description'},
2283 )
2284
2285#
2286# ------------------------------------------------------------
2287
2288class VNSAclEntry(models.Model):
2289 #
2290 # fields ----------------------------------------
2291
2292 rule = models.CharField(
2293 help_text='Rule ID',
2294 validators=[VnsRuleNameValidator()],
2295 max_length=15)
2296
2297 vns_acl = models.ForeignKey(
2298 VNSAcl,
2299 verbose_name='VNS Acl name')
2300
2301 action = models.CharField(
2302 verbose_name='permit or deny',
2303 help_text="'permit' or 'deny'",
2304 max_length=16,
2305 validators=[ VnsAclEntryActionValidator() ])
2306
2307 type = models.CharField(
2308 verbose_name='mac/ip/<0-255>/udp/tcp/icmp',
2309 help_text="ACLtype either mac or ip or udp or tcp or icmp or ip-protocol-type",
2310 max_length=16)
2311
2312 src_ip = models.CharField(
2313 verbose_name='Source IP',
2314 help_text='Source IP Address to match',
2315 validators=[ IpValidator() ],
2316 max_length=15,
2317 blank=True,
2318 null=True)
2319
2320 src_ip_mask = models.CharField(
2321 verbose_name='Source IP Mask',
2322 help_text='Mask to match source IP',
2323 validators=[ IpValidator() ],
2324 max_length=15,
2325 blank=True,
2326 null=True)
2327
2328 src_tp_port_op = models.CharField(
2329 verbose_name='Source Port comparison op',
2330 help_text='Compare with tcp/udp port eq/neq/any',
2331 max_length=5,
2332 blank=True,
2333 null=True)
2334
2335 src_tp_port = models.IntegerField(
2336 verbose_name='Source UDP/TCP Port',
2337 help_text='Source port value to compare',
2338 validators=[RangeValidator(0, 65535)],
2339 blank=True,
2340 null=True)
2341
2342 dst_ip = models.CharField(
2343 verbose_name='Destination IP',
2344 help_text='Destination IP Address to match',
2345 validators=[ IpValidator() ],
2346 max_length=15,
2347 blank=True,
2348 null=True)
2349
2350 dst_ip_mask = models.CharField(
2351 verbose_name='Destination IP Mask',
2352 help_text='Mask to match destination IP',
2353 validators=[ IpValidator() ],
2354 max_length=15,
2355 blank=True,
2356 null=True)
2357
2358 dst_tp_port_op = models.CharField(
2359 verbose_name='Destination Port comparison op',
2360 help_text='Compare with tcp/udp port eq/neq/any',
2361 max_length=3,
2362 blank=True,
2363 null=True)
2364
2365 dst_tp_port = models.IntegerField(
2366 verbose_name='Destination UDP/TCP Port',
2367 help_text='Destination port value to compare',
2368 validators=[RangeValidator(0, 65535)],
2369 blank=True,
2370 null=True)
2371
2372 icmp_type = models.IntegerField(
2373 verbose_name='ICMP Type',
2374 help_text='Matching ICMP type icmp (blank matches all)',
2375 validators=[RangeValidator(0, 255)],
2376 blank=True,
2377 null=True)
2378
2379 src_mac = models.CharField(
2380 verbose_name="Source MAC Address",
2381 help_text="Colon separated hex string (blank matches all)",
2382 max_length=17,
2383 validators = [validate_mac_address],
2384 blank=True,
2385 null=True)
2386
2387 dst_mac = models.CharField(
2388 verbose_name="Destination MAC Address",
2389 help_text="Colon separated hex string (blank matches all)",
2390 max_length=17,
2391 validators = [validate_mac_address],
2392 blank=True,
2393 null=True)
2394
2395 ether_type = models.IntegerField(
2396 verbose_name='Ethernet Packet Type',
2397 help_text='Standard ether type (blank matches all)',
2398 validators=[RangeValidator(1536, 65535)],
2399 blank=True,
2400 null=True)
2401
2402 vlan = models.IntegerField(
2403 verbose_name="VLAN ID",
2404 help_text='Standard ether type (blank matches all)',
2405 blank=True,
2406 null=True,
2407 validators = [ RangeValidator(0, 4095) ])
2408
2409 #
2410 # end fields ----------------------------------------
2411
2412 def __unicode__(self):
2413 return self.id
2414
2415 class CassandraSettings:
2416 COMPOUND_KEY_FIELDS = ('vns_acl', 'rule')
2417
2418 def validate_unique(self, exclude = None):
2419 #
2420 # there are three types of entries:
2421 # - mac based rules
2422 # - ip based rules
2423 # - tcp/udp based rules
2424 #
2425 # verify that for each rules, unexpected fields are not
2426 # populated
2427
2428 if self.type == 'mac':
2429 if self.src_ip or self.src_ip_mask:
2430 raise ValidationError("vns-access-list-entry mac rule:"
2431 " src-ip/src-ip-mask specified"
2432 "(ought to be null)")
2433 if self.dst_ip or self.dst_ip_mask:
2434 raise ValidationError("vns-access-list-entry mac rule:"
2435 " dst-ip/dst-ip-mask specified "
2436 "(ought to be null)")
2437 if self.src_tp_port_op or self.src_tp_port:
2438 raise ValidationError("vns-access-list-entry mac rule:"
2439 " src-tp-port-op/src-to-port specified "
2440 "(ought to be null)")
2441 if self.dst_tp_port_op or self.dst_tp_port:
2442 raise ValidationError("vns-access-list-entry mac rule:"
2443 " dst-tp-port-op/dst-to-port specified "
2444 "(ought to be null)")
2445 if self.icmp_type:
2446 raise ValidationError("vns-access-list-entry mac rule:"
2447 " icmp_type specified "
2448 "(ought to be null)")
2449 elif self.type == 'ip' or re.match('[\d]+', self.type) or \
2450 self.type == 'icmp':
2451 if (self.src_tp_port_op != None or self.src_tp_port != None) and \
2452 ((self.src_tp_port_op == None) or (self.src_tp_port == None)):
2453 raise ValidationError("vns-access-list-entry ip rule:"
2454 " src-tp-port-op/src-to-port specified "
2455 "(both or neither)")
2456 if (self.dst_tp_port_op != None or self.dst_tp_port != None) and \
2457 ((self.dst_tp_port_op == None) or (self.dst_tp_port == None)):
2458 raise ValidationError("vns-access-list-entry ip rule:"
2459 " dst-tp-port-op/dst-to-port specified "
2460 "(both or neither)")
2461 if self.src_mac or self.dst_mac:
2462 raise ValidationError("vns-access-list-entry ip rule:"
2463 " src_mac/dst_mac specified "
2464 "(ought to be null)")
2465 if self.ether_type or self.vlan:
2466 raise ValidationError("vns-access-list-entry ip rule:"
2467 " ether-type/vlan specified "
2468 "(ought to be null)")
2469
2470 elif self.type == 'tcp' or self.type == 'udp':
2471 if self.src_mac or self.dst_mac:
2472 raise ValidationError("vns-access-list-entry ip rule:"
2473 " src_mac/dst_mac specified "
2474 "(ought to be null)")
2475 if self.ether_type or self.vlan:
2476 raise ValidationError("vns-access-list-entry ip rule:"
2477 " ether-type/vlan specified "
2478 "(ought to be null)")
2479
2480
2481 class Rest:
2482 NAME = 'vns-access-list-entry'
2483 FIELD_INFO = (
2484 {'name': 'vns_acl', 'rest_name': 'vns-access-list'},
2485 {'name': 'src_ip', 'rest_name': 'src-ip'},
2486 {'name': 'src_ip_mask', 'rest_name': 'src-ip-mask'},
2487 {'name': 'dst_ip', 'rest_name': 'dst-ip'},
2488 {'name': 'dst_ip_mask', 'rest_name': 'dst-ip-mask'},
2489 {'name': 'src_tp_port_op', 'rest_name': 'src-tp-port-op'},
2490 {'name': 'src_tp_port', 'rest_name': 'src-tp-port'},
2491 {'name': 'dst_tp_port_op', 'rest_name': 'dst-tp-port-op'},
2492 {'name': 'dst_tp_port', 'rest_name': 'dst-tp-port'},
2493 {'name': 'icmp_type', 'rest_name': 'icmp-type'},
2494 {'name': 'src_mac', 'rest_name': 'src-mac'},
2495 {'name': 'dst_mac', 'rest_name': 'dst-mac'},
2496 {'name': 'ether_type', 'rest_name': 'ether-type'},
2497 )
2498
2499#
2500# ------------------------------------------------------------
2501
2502class VNSInterfaceAcl(models.Model):
2503 in_out_length = 4
2504
2505 #
2506 # fields ----------------------------------------
2507
2508 vns_acl = models.ForeignKey(
2509 VNSAcl,
2510 verbose_name='VNS Acl name')
2511
2512 vns_interface = models.ForeignKey(
2513 VNSInterfaceConfig,
2514 verbose_name='VNS Interface ID',
2515 help_text='[vns id]|[interface long name]')
2516
2517 in_out = models.CharField(
2518 verbose_name='in/out',
2519 help_text='Match on packet input or output',
2520 validators=[VnsInterfaceAclInOutValidator()],
2521 max_length=in_out_length,
2522 )
2523
2524 #
2525 # end fields ----------------------------------------
2526
2527 def __unicode__(self):
2528 return self.id
2529
2530 class CassandraSettings:
2531 COMPOUND_KEY_FIELDS = ('vns_interface', 'vns_acl', 'in_out')
2532
2533 def validate_unique(self, exclude = None):
2534 acl_parts = str(self.vns_acl).split('|')
2535 intf_parts = str(self.vns_interface).split('|')
2536 # validate two vns parts
2537 if acl_parts[0] != intf_parts[0]:
2538 raise ValidationError("acl's vns %s doen't match interface vns %s" %
2539 (acl_parts[0], intf_parts[0]))
2540 error = False
2541 try:
2542 exists = VNSInterfaceAcl.objects.get(vns_interface=self.vns_interface, in_out=self.in_out)
2543 if exists:
2544 if exists.vns_acl != self.vns_acl:
2545 error = True
2546 except:
2547 pass
2548 if error:
2549 raise ValidationError("Interface %s already has an ACL in the %s direction, only one ACL per direction allowed" % (self.vns_interface, self.in_out))
2550
2551 class Rest:
2552 NAME = 'vns-interface-access-list'
2553 FIELD_INFO = (
2554 {'name': 'vns_acl', 'rest_name': 'vns-access-list'},
2555 {'name': 'vns_interface', 'rest_name': 'vns-interface'},
2556 {'name': 'in_out', 'rest_name': 'in-out'},
2557 )
2558
2559#
2560# ------------------------------------------------------------
2561# A virtual router interface separation
2562
2563class VirtualRouterInterface (models.Model):
2564
2565 id_max_length = 64
2566 #
2567 # fields ----------------------------------------
2568 virtual_router = models.ForeignKey(
2569 VirtualRouter,
2570 verbose_name='Virtual Router ID')
2571 #
2572 # Unique name of the interface
2573 #
2574 vriname = models.CharField(
2575 verbose_name = 'Interface Name',
2576 help_text = 'A unique name for a virtual router interface',
2577 validators = [ GeneralNameValidator() ],
2578 max_length = id_max_length)
2579 #
2580 # Origin of this configured item
2581 #
2582 origin = models.CharField(
2583 verbose_name = "Origin",
2584 help_text = "Values: cli, rest, other packages",
2585 max_length = 64, # in future we might use SW GUIDs for this field
2586 blank = True,
2587 null = True)
2588 #
2589 # Whether the configuration is active ? By default, it is active
2590 # Used to disable the configuration without having to delete the entire
2591 # interface configuration construct.
2592 #
2593 active = models.BooleanField(
2594 verbose_name = 'Active',
2595 help_text = 'Is this interface active (default is True)',
2596 default = True)
2597 vns_connected = models.ForeignKey(
2598 VNS,
2599 verbose_name='VNS connected to',
2600 blank =True,
2601 null =True)
2602 router_connected = models.ForeignKey(
2603 VirtualRouter,
2604 related_name='router_connected',
2605 verbose_name='Virtual Router connected to',
2606 blank =True,
2607 null =True)
2608
2609 #
2610 # end fields ----------------------------------------
2611
2612 def __unicode__ (self):
2613 return self.id
2614
2615 def validate_unique(self, exclude = None):
2616 def is_set(value):
2617 if value != None and value != '':
2618 return True
2619
2620 # for vns connection, verify that only the VNSs under the same tenant can be connected
2621 error=False
2622 if not 'vns_connected' in exclude:
2623 if is_set(self.vns_connected):
2624 tenant_vns_parts = str(self.vns_connected).split('|')
2625 tenant_router_parts = str(self.virtual_router).split('|')
2626 if tenant_vns_parts[0] != tenant_router_parts[0]:
2627 raise ValidationError(" VNS %s belongs to tenant %s, doesn't match virtual router tenant %s" %
2628 (tenant_vns_parts[1],tenant_vns_parts[0], tenant_router_parts[0]))
2629 # verify there can only be one connection for one VNS
2630 try:
2631 exists = VirtualRouterInterface.objects.get(virtual_router = self.virtual_router, vns_connected=self.vns_connected)
2632 if exists:
2633 if exists.vriname!=self.vriname:
2634 error=True
2635 except:
2636 pass
2637 if error:
2638 raise ValidationError(" VNS %s has been connected, multiple connections is not allowed" % self.vns_connected)
2639 error = False
2640 # for router connection, verify that the same virtual router as myself can't be connected
2641 if not 'router_connected' in exclude:
2642 if is_set(self.router_connected):
2643 tenant_router_parts = str(self.router_connected).split('|')
2644 tenant_myrouter_parts = str(self.virtual_router).split('|')
2645 if tenant_router_parts[0] == tenant_myrouter_parts[0]:
2646 raise ValidationError(" Local loop conncetion is not allowed.")
2647 # verify there can only be one connection for one virtual router
2648 try:
2649 exists = VirtualRouterInterface.objects.get(virtual_router = self.virtual_router,router_connected=self.router_connected)
2650 if exists:
2651 if exists.vriname!=self.vriname:
2652 error=True
2653 except:
2654 pass
2655 if error:
2656 raise ValidationError(" Virtual Router %s has been connected, multiple connections is not allowed" % self.router_connected)
2657
2658 class CassandraSettings:
2659 COMPOUND_KEY_FIELDS = ('virtual_router', 'vriname')
2660
2661 class Rest:
2662 NAME = 'virtualrouter-interface'
2663 FIELD_INFO = (
2664 {'name': 'vns_connected', 'rest_name': 'vns-connected'},
2665 {'name': 'router_connected', 'rest_name': 'router-connected'},
2666 {'name': 'virtual_router', 'rest_name': 'virtual-router'},
2667
2668 )
2669
2670#
2671# ------------------------------------------------------------
2672# A virtual router interface address pool separation
2673
2674class VRInterfaceIpAddressPool (models.Model):
2675
2676 id_max_length = 64
2677 #
2678 # fields ----------------------------------------
2679 virtual_router_interface = models.ForeignKey(
2680 VirtualRouterInterface,
2681 verbose_name='Virtual Router Interface ID')
2682 #
2683 # Origin of this configured item
2684 #
2685 origin = models.CharField(
2686 verbose_name = "Origin",
2687 help_text = "Values: cli, rest, other packages",
2688 max_length = 64,
2689 blank = True,
2690 null = True)
2691 ip_address = models.CharField(
2692 verbose_name='Source IP',
2693 help_text='Interface IP Address',
2694 validators=[ IpValidator() ],
2695 max_length=15,
2696 blank=True,
2697 null=True)
2698 subnet_mask = models.CharField(
2699 verbose_name='Subnet IP Mask',
2700 validators=[ IpMaskValidator() ],
2701 max_length=15,
2702 blank=True,
2703 null=True)
2704
2705 #
2706 # end fields ----------------------------------------
2707
2708 def __unicode__ (self):
2709 return self.id
2710
2711 class CassandraSettings:
2712 COMPOUND_KEY_FIELDS = ('virtual_router_interface', 'ip_address')
2713
2714 class Rest:
2715 NAME = 'interface-address-pool'
2716 FIELD_INFO = (
2717 {'name': 'virtual_router_interface', 'rest_name': 'virtual-router-interface'},
2718 {'name': 'ip_address', 'rest_name': 'ip-address'},
2719 {'name': 'subnet_mask', 'rest_name': 'subnet-mask'},
2720 )
2721
2722
2723#
2724# ------------------------------------------------------------
2725
2726class VirtualRouterGWPool (models.Model):
2727
2728 id_max_length = 64
2729 #
2730 # fields ----------------------------------------
2731 virtual_router = models.ForeignKey(
2732 VirtualRouter,
2733 verbose_name='Virtual Router ID')
2734 #
2735 # Unique name of the gateway pool
2736 #
2737 vrgwname = models.CharField(
2738 verbose_name = 'Gateway Pool Name',
2739 help_text = 'A unique name for a virtual router gateway pool',
2740 validators = [ GeneralNameValidator() ],
2741 max_length = id_max_length)
2742
2743 #
2744 # end fields ----------------------------------------
2745
2746 def __unicode__ (self):
2747 return self.id
2748
2749 def validate_unique(self, exclude = None):
2750 def is_set(value):
2751 if value != None and value != '':
2752 return True
2753
2754 class CassandraSettings:
2755 COMPOUND_KEY_FIELDS = ('virtual_router', 'vrgwname')
2756
2757 class Rest:
2758 NAME = 'virtualrouter-gwpool'
2759 FIELD_INFO = (
2760 {'name': 'virtual_router', 'rest_name': 'virtual-router'},
2761 )
2762
2763#
2764# ------------------------------------------------------------
2765# A virtual router gateway address pool separation
2766
2767class VRGatewayIpAddressPool (models.Model):
2768
2769 id_max_length = 64
2770 #
2771 # fields ----------------------------------------
2772 virtual_router_gwpool = models.ForeignKey(
2773 VirtualRouterGWPool,
2774 verbose_name='Virtual Router Gateway Pool ID')
2775 ip_address = models.CharField(
2776 verbose_name='Gateway IP',
2777 help_text='Gateway IP Address',
2778 validators=[ IpValidator() ],
2779 max_length=15,
2780 blank=True,
2781 null=True)
2782
2783 #
2784 # end fields ----------------------------------------
2785
2786 def __unicode__ (self):
2787 return self.id
2788
2789 class CassandraSettings:
2790 COMPOUND_KEY_FIELDS = ('virtual_router_gwpool', 'ip_address')
2791
2792 class Rest:
2793 NAME = 'gateway-address-pool'
2794 FIELD_INFO = (
2795 {'name': 'virtual_router_gwpool', 'rest_name': 'virtual-router-gwpool'},
2796 {'name': 'ip_address', 'rest_name': 'ip-address'},
2797 )
2798
2799class VirtualRoutingRule (models.Model):
2800
2801 id_max_length = 64
2802 #
2803 # fields ----------------------------------------
2804 virtual_router = models.ForeignKey(
2805 VirtualRouter,
2806 verbose_name='Virtual Router ID')
2807 #
2808 # Origin of this configured item
2809 #
2810 origin = models.CharField(
2811 verbose_name = "Origin",
2812 help_text = "Values: cli, rest, other packages",
2813 max_length = 64,
2814 blank = True,
2815 null = True)
2816 src_host = models.ForeignKey(
2817 HostConfig,
2818 verbose_name='source Host ID',
2819 help_text='Source Host ID to match',
2820 blank =True,
2821 null =True)
2822 src_tenant = models.ForeignKey(
2823 Tenant,
2824 verbose_name='source tenant ID',
2825 help_text='Source tenant ID to match',
2826 blank =True,
2827 null =True)
2828 src_vns = models.ForeignKey(
2829 VNS,
2830 verbose_name='source VNS ID',
2831 help_text='Source VNS ID to match',
2832 blank =True,
2833 null =True)
2834 src_ip = models.CharField(
2835 verbose_name='Source IP',
2836 help_text='Source IP Address to match',
2837 validators=[ IpValidator() ],
2838 max_length=15,
2839 blank=True,
2840 null=True)
2841 src_ip_mask = models.CharField(
2842 verbose_name='Source IP Mask',
2843 help_text='Mask to match source IP',
2844 validators=[ IpMaskValidator() ],
2845 max_length=15,
2846 blank=True,
2847 null=True)
2848 dst_host = models.ForeignKey(
2849 HostConfig,
2850 verbose_name='Destination Host ID',
2851 help_text='Destination Host ID to match',
2852 related_name='dest_host',
2853 blank =True,
2854 null =True)
2855 dst_tenant = models.ForeignKey(
2856 Tenant,
2857 verbose_name='Destination tenant ID',
2858 related_name='dest_tenant',
2859 blank =True,
2860 null =True)
2861 dst_vns = models.ForeignKey(
2862 VNS,
2863 verbose_name='Destination VNS ID',
2864 related_name='dest_vns',
2865 blank =True,
2866 null =True)
2867 dst_ip = models.CharField(
2868 verbose_name='Destination IP',
2869 help_text='Destination IP Address to match',
2870 validators=[ IpValidator() ],
2871 max_length=15,
2872 blank=True,
2873 null=True)
2874 dst_ip_mask = models.CharField(
2875 verbose_name='Destination IP Mask',
2876 help_text='Mask to match destination IP',
2877 validators=[ IpMaskValidator() ],
2878 max_length=15,
2879 blank=True,
2880 null=True)
2881 outgoing_intf = models.ForeignKey(
2882 VirtualRouterInterface,
2883 verbose_name='Outgoing Interface',
2884 blank =True,
2885 null =True)
2886 gateway_pool = models.ForeignKey(
2887 VirtualRouterGWPool,
2888 verbose_name='Gateway pool',
2889 blank =True,
2890 null =True)
2891 nh_ip = models.CharField(
2892 verbose_name='Next Hop IP',
2893 help_text='Next Hop IP Address',
2894 validators=[ IpValidator() ],
2895 max_length=15,
2896 blank=True,
2897 null=True)
2898 action = models.CharField(
2899 verbose_name='permit or deny',
2900 help_text="'permit' or 'deny'",
2901 default='permit',
2902 max_length=16,
2903 validators=[ VnsAclEntryActionValidator() ])
2904 #
2905 # end fields ----------------------------------------
2906
2907 def __unicode__ (self):
2908 return self.id
2909
2910 def validate_unique(self, exclude = None):
2911 def is_set(value):
2912 if value != None and value != '':
2913 return True
2914 #verify the outgoing interface can only be on the local virtual router interface
2915 if not 'outgoing_intf' in exclude:
2916 if is_set(self.outgoing_intf):
2917 router_parts = str(self.outgoing_intf).split('|')
2918 myrouter_parts = str(self.virtual_router).split('|')
2919 if (router_parts[0] != myrouter_parts[0]) or (router_parts[1] != myrouter_parts[1]):
2920 raise ValidationError(" outgoing interface has to be local to virtual router: %s|%s" %
2921 (myrouter_parts[0],myrouter_parts[1]))
2922 #verify the gateway pool belongs to the local virtual router
2923 if not 'gateway_pool' in exclude:
2924 if is_set(self.gateway_pool):
2925 router_parts = str(self.gateway_pool).split('|')
2926 myrouter_parts = str(self.virtual_router).split('|')
2927 if (router_parts[0] != myrouter_parts[0]) or (router_parts[1] != myrouter_parts[1]):
2928 raise ValidationError(" gateway pool has to be local to virtual router: %s|%s" %
2929 (myrouter_parts[0],myrouter_parts[1]))
2930
2931 class CassandraSettings:
2932 COMPOUND_KEY_FIELDS = ('virtual_router', 'src_host', 'src_tenant','src_vns','src_ip','src_ip_mask', 'dst_host', 'dst_tenant','dst_vns','dst_ip','dst_ip_mask')
2933
2934 class Rest:
2935 NAME = 'virtualrouter-routingrule'
2936 FIELD_INFO = (
2937 {'name': 'virtual_router', 'rest_name': 'virtual-router'},
2938 {'name': 'src_tenant', 'rest_name': 'src-tenant'},
2939 {'name': 'dst_tenant', 'rest_name': 'dst-tenant'},
2940 {'name': 'src_vns', 'rest_name': 'src-vns'},
2941 {'name': 'src_ip', 'rest_name': 'src-ip'},
2942 {'name': 'src_ip_mask', 'rest_name': 'src-ip-mask'},
2943 {'name': 'dst_ip', 'rest_name': 'dst-ip'},
2944 {'name': 'dst_ip_mask', 'rest_name': 'dst-ip-mask'},
2945 {'name': 'nh_ip', 'rest_name': 'nh-ip'},
2946 {'name': 'outgoing_intf', 'rest_name': 'outgoing-intf'},
2947 {'name': 'dst_host', 'rest_name': 'dst-host'},
2948 {'name': 'src_host', 'rest_name': 'src-host'},
2949 {'name': 'dst_vns', 'rest_name': 'dst-vns'},
2950 {'name': 'gateway_pool', 'rest_name': 'gateway-pool'},
2951 )
2952#
2953# ------------------------------------------------------------
2954"""
2955class Tag(models.Model):
2956 namespace_length = 64
2957 name_length = 64
2958 value_length = 64
2959
2960 #
2961 # fields ----------------------------------------
2962
2963 namespace = models.CharField(
2964 verbose_name='Tag Namespace',
2965 help_text="Namespace of the tag",
2966 max_length=namespace_length)
2967
2968 name = models.CharField(
2969 verbose_name='Tag Name',
2970 help_text="Name of the tag",
2971 max_length=name_length)
2972
2973 value = models.CharField(
2974 verbose_name='Tag Value',
2975 help_text="Value of the tag",
2976 max_length=value_length)
2977
2978 persist = models.BooleanField(
2979 verbose_name='persist',
2980 help_text='For any cli configured tag, include in running-config',
2981 default=True)
2982
2983 #
2984 # end fields ----------------------------------------
2985
2986 def __unicode__(self):
2987 return self.id
2988
2989 class CassandraSettings:
2990 COMPOUND_KEY_FIELDS = ('namespace', 'name', 'value')
2991
2992 class Rest:
2993 NAME = 'tag'
2994
2995#
2996# ------------------------------------------------------------
2997
2998class TagMapping(models.Model):
2999 host_id_length = 17
3000 switch_id_length = 23
3001 if_name_len = 32
3002 vlan_str_len = 4
3003 #
3004 # fields ----------------------------------------
3005
3006 tag = models.ForeignKey(
3007 Tag)
3008
3009 mac = models.CharField(
3010 verbose_name="MAC Address",
3011 max_length=host_id_length,
3012 validators = [validate_mac_address],
3013 default="",
3014 blank=True)
3015
3016 vlan = models.CharField(
3017 verbose_name='VLAN',
3018 max_length=vlan_str_len,
3019 help_text='VLAN Number, in the range of 1-4095. 4095 means untagged',
3020 validators=[VlanStringValidator()],
3021 default="",
3022 blank=True)
3023
3024 dpid = models.CharField(
3025 verbose_name='Switch DPID',
3026 help_text='Switch DPID - 64-bit hex separated by :',
3027 max_length=switch_id_length,
3028 validators=[ validate_dpid ],
3029 default="",
3030 blank=True)
3031
3032 ifname = models.CharField(
3033 verbose_name='If Name regular expression',
3034 max_length=if_name_len,
3035 default="",
3036 validators=[ SafeForPrimaryKeyValidator() ],
3037 blank=True)
3038
3039 #
3040 # end fields ----------------------------------------
3041
3042 def __unicode__(self):
3043 return self.id
3044
3045 class CassandraSettings:
3046 COMPOUND_KEY_FIELDS = ('tag', 'mac', 'vlan', 'dpid', 'ifname')
3047
3048 def validate_unique(self, exclude = None):
3049 if self.mac != '':
3050 self.mac = self.mac.lower()
3051
3052 if self.dpid != '':
3053 self.dpid = self.dpid.lower()
3054
3055 # don't allow all default values for the association
3056 if self.mac == '' and self.vlan == '' and \
3057 self.dpid == '' and self.ifname == '':
3058 raise ValidationError("Match without any matching fields")
3059
3060 class Rest:
3061 NAME = 'tag-mapping'
3062"""
3063#
3064# ------------------------------------------------------------
3065class TechSupportConf(models.Model):
3066
3067 #
3068 # fields ----------------------------------------
3069
3070 cmd_type = models.CharField(
3071 verbose_name='Type of command',
3072 help_text='Enter cli or shell',
3073 max_length=32)
3074
3075 cmd = models.CharField(
3076 max_length=256,
3077 verbose_name='Command name',
3078 help_text = 'Command excuted by show tech-support')
3079
3080
3081 #
3082 # end fields ----------------------------------------
3083
3084 def __unicode__(self):
3085 return self.id
3086
3087 class CassandraSettings:
3088 COMPOUND_KEY_FIELDS = ('cmd_type', 'cmd')
3089
3090 class Rest:
3091 NAME = 'tech-support-config'
3092 FIELD_INFO = (
3093 {'name': 'cmd_type', 'rest_name': 'cmd-type'},
3094 )
3095
3096#
3097# ------------------------------------------------------------
3098
3099class TacacsPlusConfig(models.Model):
3100 #
3101 # fields ----------------------------------------
3102
3103 id = models.CharField(
3104 primary_key=True,
3105 verbose_name='tacacs singleton',
3106 default='tacacs',
3107 max_length=16)
3108
3109 tacacs_plus_authn = models.BooleanField(
3110 verbose_name='TACACS+ Authentication Enabled',
3111 help_text='Enable TACACS+ authentication by setting to true',
3112 default=False
3113 )
3114
3115 tacacs_plus_authz = models.BooleanField(
3116 verbose_name='TACACS+ Authorization Enabled',
3117 help_text='Enable TACACS+ authorization by setting to true',
3118 default=False
3119 )
3120
3121 tacacs_plus_acct = models.BooleanField(
3122 verbose_name='TACACS+ Accounting Enabled',
3123 help_text='Enable TACACS+ accounting by setting to true',
3124 default=False
3125 )
3126
3127 local_authn = models.BooleanField(
3128 verbose_name='Local Authentication Enabled',
3129 help_text='Enable local authentication by setting to true',
3130 default=True
3131 )
3132
3133 local_authz = models.BooleanField(
3134 verbose_name='Local Authorization Enabled',
3135 help_text='Enable local authorization by setting to true',
3136 default=True
3137 )
3138
3139 timeout = models.IntegerField(
3140 verbose_name="TACACS+ Server Timeout",
3141 help_text='Set TACACS+ server timeout in seconds',
3142 default=0,
3143 validators=[ RangeValidator(0, 2**16-1) ])
3144
3145 key = models.CharField(
3146 verbose_name='TACACS+ Pre-shared Key',
3147 help_text='pre-shared key to connect to TACACS+ server(s)',
3148 max_length=256,
3149 blank=True,
3150 default="")
3151
3152 #
3153 # end fields ----------------------------------------
3154
3155 class Rest:
3156 NAME = 'tacacs-plus-config'
3157 FIELD_INFO = (
3158 {'name': 'tacacs_plus_authn', 'rest_name': 'tacacs-plus-authn'},
3159 {'name': 'tacacs_plus_authz', 'rest_name': 'tacacs-plus-authz'},
3160 {'name': 'tacacs_plus_acct', 'rest_name': 'tacacs-plus-acct'},
3161 {'name': 'local_authn', 'rest_name': 'local-authn'},
3162 {'name': 'local_authz', 'rest_name': 'local-authz'},
3163 )
3164
3165#
3166# ------------------------------------------------------------
3167
3168class TacacsPlusHost(models.Model):
3169 #
3170 # fields ----------------------------------------
3171
3172 ip = models.CharField(
3173 primary_key=True,
3174 verbose_name='IP Address',
3175 help_text='IP Address for TACACS+ server',
3176 validators=[ IpValidator() ],
3177 max_length=15)
3178
3179 timestamp = models.PositiveIntegerField(
3180 verbose_name='timestamp',
3181 help_text='Timestamp to order the tacacs servers',
3182 default = get_timestamp,
3183 )
3184
3185 key = models.CharField(
3186 verbose_name='TACACS+ Per-host Pre-shared Key',
3187 help_text='pre-shared key to connect to this TACACS+ server',
3188 max_length=256,
3189 blank=True,
3190 default="")
3191
3192 #
3193 # end fields ----------------------------------------
3194
3195 def __unicode__(self):
3196 return "%s" % self.ip
3197
3198 def validate_unique(self, exclude = None):
3199 try:
3200 exists = TacacsPlusHost.objects.get(ip = self.ip)
3201
3202 if exists.timestamp != self.timestamp:
3203 self.timestamp = exists.timestamp
3204 except:
3205 pass
3206
3207 class Rest:
3208 NAME = 'tacacs-plus-host'
3209 FIELD_INFO = (
3210 )
3211
3212#
3213#---------------------------------------------------------
3214
3215class SnmpServerConfig(models.Model):
3216 #
3217 # fields ----------------------------------------
3218
3219 # we just have one row entry in the table to update, so static primary key
3220 id = models.CharField(
3221 primary_key=True,
3222 verbose_name='snmp',
3223 # default='snmp',
3224 max_length=16)
3225
3226 #
3227 # Enable state of the SNMP server/agent on the controller
3228 #
3229 server_enable = models.BooleanField(
3230 verbose_name='SNMP Server enabled',
3231 help_text='Enable SNMP server by setting to true',
3232 default=False
3233 )
3234 #
3235 # Community string for accessing the SNMP server on the controller
3236 #
3237 community = models.CharField(
3238 verbose_name = 'Community String',
3239 help_text = "Community String to access SNMP data",
3240 max_length = 128,
3241 null = True,
3242 blank = True,
3243 )
3244 #
3245 # Location string of the SNMP server on the controller
3246 #
3247 location = models.CharField(
3248 verbose_name = 'System Location',
3249 help_text = "Location information for the controller appliance",
3250 max_length = 128,
3251 null = True,
3252 blank = True
3253 )
3254 #
3255 # Contact string of the SNMP server on the controller
3256 #
3257 contact = models.CharField(
3258 verbose_name = 'System Contact',
3259 help_text = "Contact information for the controller appliance",
3260 max_length = 128,
3261 null = True,
3262 blank = True,
3263 )
3264 #
3265 # end fields ----------------------------------------
3266
3267 def __unicode__(self):
3268 return self.id
3269
3270 def validate_unique(self, exclude = None):
3271 if self.id != 'snmp':
3272 raise ValidationError("Only single snmp record exists")
3273
3274 class Rest:
3275 NAME = 'snmp-server-config'
3276 FIELD_INFO = (
3277 {'name': 'server_enable', 'rest_name': 'server-enable'},
3278 )
3279
3280#
3281# ------------------------------------------------------------
3282
3283class ImageDropUser(models.Model):
3284 #
3285 # fields ----------------------------------------
3286
3287 # we just have one row entry in the table to update, so static primary key
3288 id = models.CharField(
3289 primary_key=True,
3290 verbose_name='imagedropuser',
3291 default='imagedropuser',
3292 max_length=16)
3293
3294 images_user_ssh_key = models.CharField(
3295 verbose_name='Image drop user SSH public key',
3296 help_text='The SSH public RSA key for the images user',
3297 default='',
3298 max_length=600
3299 )
3300 #
3301 # end fields ----------------------------------------
3302
3303 def __unicode__(self):
3304 return self.id
3305
3306 def validate_unique(self, exclude = None):
3307 if self.id != 'imagedropuser':
3308 raise ValidationError("Only single ImageDropUser record exists")
3309
3310 class Rest:
3311 NAME = 'image-drop-user'
3312 FIELD_INFO = (
3313 {'name': 'images_user_ssh_key', 'rest_name': 'images-user-ssh-key'},
3314 )
3315
3316# robv: Commenting out for now. Doesn't work with Cassandra backend
3317#class ConsoleUser(User):
3318#
3319# class Meta:
3320# proxy = True
3321#
3322# class Rest:
3323# NAME = 'user'
3324"""