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