Base net-virt CLI files on top of which ONOS specific changes will be done
diff --git a/cli/sdncon/controller/models.py b/cli/sdncon/controller/models.py
new file mode 100755
index 0000000..c6dcc9f
--- /dev/null
+++ b/cli/sdncon/controller/models.py
@@ -0,0 +1,3225 @@
+#
+# Copyright (c) 2013 Big Switch Networks, Inc.
+#
+# Licensed under the Eclipse Public License, Version 1.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+#      http://www.eclipse.org/legal/epl-v10.html
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# permissions and limitations under the License.
+#
+
+from django.db import models
+from django.contrib.auth.models import User
+
+from django.forms import ValidationError
+from django.core.validators import MaxValueValidator
+from sdncon.rest.validators import *
+
+import sys # for sys.maxint
+import time
+
+#
+# controller/models.py
+# ------------------------------------------------------------
+#
+# model description of the tables used to configure and 
+# view the controller state.
+#
+# these tables are used both by sdnplatform and the rest api.
+# the cli uses the rest api to read and write these tables,
+# while sdnplatform/sdnplatform has a more direct interface to the
+# tables.
+#
+# for the cli, the names of the fields are described in the
+# 'class Rest' section of each of the tables.
+#
+# for sdnplatform the names of fields have a relationship to their
+# type for sdnplatform.   If a field is a foreign key, then an '_id'
+# is appended to the name for the same field within sdnplatform.  This
+# means if a field is promoted or demoted to/from a foreign key,
+# changes need to be made in the sdnplatform code to use the updated
+# name.
+#
+# Make sure to include the nested Rest class or else they won't be
+# accessible using the REST API.
+
+#
+#
+# ------------------------------------------------------------
+
+def get_timestamp():
+    """
+    For us in models where the method of ordering the row values
+    is based on the order of creation.  
+    
+    The field is exposed to the rest api so that the value can
+    be queries, and so that the rest api may choose to order the
+    row values using its own strategy by setting the field directly
+    """
+    return int(time.time()*1000000)
+
+
+#
+# ------------------------------------------------------------
+
+class Feature(models.Model):
+
+    #
+    # fields ----------------------------------------
+
+    id = models.CharField(
+        primary_key=True,
+        verbose_name='feature',
+        default='feature',
+        max_length=16)
+
+    netvirt_feature = models.BooleanField(
+        verbose_name='Network virtualization Feature Enabled',
+        help_text='Enable network virtualization feature by setting to true',
+        default=True
+        )
+
+    static_flow_pusher_feature = models.BooleanField(
+        verbose_name='Static Flow Pusher Feature Enabled',
+        help_text='Enable Static Flow Pusher feature by setting to true',
+        default=True
+        )
+    
+    performance_monitor_feature = models.BooleanField(
+        verbose_name='Performance Monitor',
+        help_text="Enable performance monitoring feature by setting to true",
+        default=False
+        )
+    
+    #
+    # end fields ----------------------------------------
+    
+    def __unicode__(self):
+        return "%s" % (self.id,)
+
+    def validate_unique(self, exclude = None):
+        if self.id != 'feature':
+            raise ValidationError("Only single feature record exists")
+
+    class Rest:
+        NAME = 'feature'
+        FIELD_INFO = (
+            {'name': 'netvirt_feature',                'rest_name': 'netvirt-feature'},
+            {'name': 'static_flow_pusher_feature', 'rest_name': 'static-flow-pusher-feature'},
+            {'name': 'performance_monitor_feature','rest_name': 'performance-monitor-feature'},
+            )
+
+#
+# ------------------------------------------------------------
+
+class GlobalConfig(models.Model):
+    #
+    # fields ----------------------------------------
+    
+    name = models.CharField(
+        primary_key=True,
+        verbose_name='Global Config Name',
+        help_text="Unique name for the global config; it's a singleton, "
+                  "so, by convention, the name should always be \"global\"",
+        default='global',
+        max_length=16)
+    
+    cluster_name = models.CharField(
+        verbose_name='Cluster Name',
+        help_text='Name for the cluster',
+        default='SDNCluster',
+        max_length=256)
+
+    cluster_number = models.IntegerField(
+        verbose_name='Cluster Number',
+        help_text='Small integral (1-255) identifier for the cluster',
+        default=0)
+
+    ha_enabled = models.BooleanField(
+        verbose_name='High Availability (HA) Enabled',
+        help_text='Is high availability (HA) enabled',
+        default=False)
+
+    #
+    # end fields ----------------------------------------
+
+    class Rest:
+        NAME = 'global-config'
+        FIELD_INFO = (
+            {'name': 'cluster_name',    'rest_name': 'cluster-name'},
+            {'name': 'cluster_number',  'rest_name': 'cluster-number'},
+            {'name': 'ha_enabled',      'rest_name': 'ha-enabled'},
+            )
+    
+#
+# ------------------------------------------------------------
+
+class TopologyConfig(models.Model):
+    #
+    # fields ----------------------------------------
+
+    id = models.CharField(
+        primary_key=True,
+        verbose_name='topology',
+        default='topology',
+        max_length=16)
+
+
+    autoportfast = models.BooleanField(
+        verbose_name='Auto PortFast',
+        help_text='Suppress sending LLDPs on fast ports.',
+        default=True
+        )
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return "%s" % (self.id,)
+
+    def validate_unique(self, exclude = None):
+        if self.id != 'topology':
+            raise ValidationError("Only single topology record exists")
+
+    class Rest:
+        NAME = 'topology-config'
+        FIELD_INFO = (
+        )
+
+
+#
+# ------------------------------------------------------------
+
+class ForwardingConfig(models.Model):
+    #
+    # fields ----------------------------------------
+
+    id = models.CharField(
+        primary_key=True,
+        verbose_name='forwarding',
+        default='forwarding',
+        max_length=16)
+
+    access_priority = models.IntegerField(
+        verbose_name='Access Flow Priority',
+        help_text='Priority for flows created by forwarding on access switches',
+        validators=[ RangeValidator(0,2**15-1) ],
+        default=10)
+
+    core_priority = models.IntegerField(
+        verbose_name='Core Flow Priority',
+        help_text='Priority for flows created by forwarding on core switches',
+        validators=[ RangeValidator(0,2**15-1) ],
+        default=20)
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return "%s" % (self.id,)
+
+    def validate_unique(self, exclude = None):
+        if self.id != 'forwarding':
+            raise ValidationError(
+                "Only a single forwarding configuration record is allowed"
+        )
+
+    class Rest:
+        NAME = 'forwarding-config'
+        FIELD_INFO = (
+            {'name': 'access_priority',    'rest_name': 'access-priority'},
+            {'name': 'core_priority',      'rest_name': 'core-priority'},
+        )
+
+
+#
+# ------------------------------------------------------------
+
+class Controller(models.Model):
+    #
+    # fields ----------------------------------------
+    #
+    # Note: This model should only contain config state for the controller VM,
+    # not any operational state.
+    #
+    # Note: The convention used to be that the controller ID was
+    # the IP address and port that the OpenFlow controller was listening
+    # on. In practice SDNPlatform listened on all interfaces and it's logic
+    # for determining its real IP address was unreliable, so it was
+    # changed/simplified to just always use "localhost" for the 
+    # IP address. So the controller ID was pretty much always
+    # "localhost:6633". The use of "controller here caused some
+    # confusion about whether "controller" meant the
+    # OpenFlow controller (i.e. SDNPlatform/SDNPlatform) vs. the controller VM.
+    # In the old controller model most/all of the settings concerned the
+    # OpenFlow controller not the VM>
+    # Most of the settings we now want to associate with the controller are
+    # controller VM settings (e.g. IP address, DNS, time zone) and not
+    # OpenFlow controller settings. So we're repurposing the Controller
+    # model to be the controller VM config state and not the OpenFlow
+    # controller discovered state (which was sort of broken to begin with).
+    # Currently (as of 10/2011), since we only support single node operation
+    # the controller ID is hard-coded to be localhost (probably should be
+    # something different, e.g. "default", because localhost implies a
+    # an interface which is really something separate), but eventually for
+    # multi-node operation we'll need to have unique ids for each controller
+    # node in the cluster. The easiest way would be to have the user enter
+    # something at first time config. Or possibly we could do something
+    # with GUIDs. Needs more thought...
+    id = models.CharField(
+        primary_key=True,
+        verbose_name='Controller ID',
+        help_text='Unique identifier string for the controller node',
+        validators=[ validate_controller_id ],
+        max_length=256)
+    
+    status = models.CharField(
+        verbose_name='Status',
+        help_text='cluster status of node',
+        max_length=256,
+        default='Provisioned',
+        )
+
+    domain_lookups_enabled = models.BooleanField(
+        verbose_name='Domain Lookups Enabled',
+        help_text='If domain lookups are enabled (default is True)',
+        default=True
+        )
+
+    domain_name = models.CharField(
+        verbose_name='Domain Name',
+        help_text='Domain name of the network',
+        max_length=256,
+        validators=[ DomainNameValidator() ],
+        default='',
+        blank=True)
+
+    default_gateway = models.CharField(
+        verbose_name='Default Gateway',
+        help_text='Default gateway',
+        max_length=256,
+        validators=[ IpValidator() ],
+        default='',
+        blank=True)
+    
+    ntp_server = models.CharField(
+        verbose_name='NTP Server',
+        help_text='NTP server',
+        max_length=256,
+        validators=[ IpOrDomainNameValidator() ],
+        default='',
+        blank=True,
+        null=True)
+
+    time_zone = models.CharField(
+        verbose_name='Time Zone',
+        help_text='Time zone (e.g. America/Los_Angeles)',
+        max_length=256,
+        validators=[ TimeZoneValidator() ],
+        default='UTC')
+
+    logging_enabled = models.BooleanField(
+        verbose_name='Logging Enabled',
+        help_text='Logging enabled',
+        default=False
+    )
+    
+    logging_server = models.CharField(
+        verbose_name='Logging Server',
+        help_text='Logging server',
+        max_length=256,
+        validators=[ IpOrDomainNameValidator() ],
+        default='',
+        blank=True,
+        null=True)
+    
+    logging_level = models.CharField(
+        verbose_name='Logging Level',
+        help_text='Logging level',
+        max_length=16,
+        validators=[ EnumerationValidator(('emerg', 'alert', 'crit', 'err',
+            'warning', 'notice', 'info', 'debug', '0', '1', '2', '3', '4',
+            '5', '6', '7'))],
+        default='notice'
+        )
+    
+    # IOS let's you specify the domain name(s) of the network in two
+    # ways. You can specify a single domain name with the "ip domain name <domain-name>"
+    # command. You can specify multiple domain names with multiple
+    # "ip domain list <domain-name>" commands. The domain names specified with
+    # "ip domain list" commands take precedence over the domain name specified with
+    # the "ip domain name" command, so the single "ip domain name" is only
+    # used if the domain name list is unspecified/empty/null. Kind of messy, but
+    # if we want to emulate IOS behavior I think we'll need to represent that in the
+    # model. But to simplify things for now we'll just support the single domain name.
+    
+    #domain_name_list = models.TextField(
+    #    verbose_name='Domain Name List',
+    #    help_text='List of domain names for the network, one per line',
+    #    null=True
+    #    )
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return "%s" % (self.id,)
+
+    class Rest:
+        NAME = 'controller-node'
+        FIELD_INFO = (
+            {'name': 'domain_lookups_enabled',  'rest_name': 'domain-lookups-enabled'},
+            {'name': 'domain_name',             'rest_name': 'domain-name'},
+            {'name': 'default_gateway',         'rest_name': 'default-gateway'},
+            {'name': 'ntp_server',              'rest_name': 'ntp-server'},
+            {'name': 'time_zone',               'rest_name': 'time-zone'},
+            {'name': 'logging_enabled',         'rest_name': 'logging-enabled'},
+            {'name': 'logging_server',          'rest_name': 'logging-server'},
+            {'name': 'logging_level',           'rest_name': 'logging-level'},
+            )
+
+
+#
+# ------------------------------------------------------------
+
+class ControllerAlias(models.Model):
+    
+    #
+    # fields ----------------------------------------
+
+    alias = models.CharField(
+        primary_key=True,
+        max_length=255,
+        help_text = "alias for controller",
+        verbose_name='alias',
+        validators=[SwitchAliasValidator()])
+    
+    controller = models.ForeignKey(
+        Controller,
+        verbose_name='Controller')
+
+    #
+    # end fields ----------------------------------------
+
+    class Rest:
+        NAME = 'controller-alias'
+
+#
+# ------------------------------------------------------------
+
+class ControllerInterface(models.Model):
+    
+    #
+    # fields ----------------------------------------
+    
+    controller = models.ForeignKey(
+        Controller,
+        verbose_name="Controller ID")
+    
+    type = models.CharField(
+        verbose_name='Interface Type',
+        help_text='Type of interface (e.g. "Ethernet")',
+        max_length=256,
+        default='Ethernet'
+        )
+    
+    number = models.IntegerField(
+        verbose_name="Interface Number",
+        help_text='Number of interface (non-negative integer)',
+        default=0)
+    
+    mode = models.CharField(
+        verbose_name='Mode',
+        help_text='Mode of configuring IP address ("dhcp" or "static")',
+        validators=[ EnumerationValidator(('dhcp', 'static'))],
+        max_length=32,
+        default='static')
+    
+    ip = models.CharField(
+        verbose_name='IP Address',
+        help_text='IP Address for interface',
+        validators=[ IpValidator() ],
+        max_length=15,
+        default='',
+        blank=True)
+    
+    netmask = models.CharField(
+        verbose_name='Netmask',
+        help_text='Netmask',
+        validators=[ IpValidator() ],
+        max_length=15,
+        default='',
+        blank=True)
+    
+    # provide a link between the underlying interface layer
+    # and this model's 'index number' to help manage discovered_ip
+    mac = models.CharField(
+        verbose_name="MAC Address",
+        help_text="MAC Address",
+        max_length=17, 
+        validators = [validate_mac_address],
+        blank=True,
+        null=True)
+
+    discovered_ip = models.CharField(
+        verbose_name='Discovered IP Address',
+        help_text='Discovered IP Address for interface',
+        validators=[ IpValidator() ],
+        max_length=15,
+        default='',
+        blank=True)
+    
+
+#    in_acl = models.ForeignKey(
+#        ControllerAcl,
+#        verbose_name = 'Controller input acl',
+#        blank=True,
+#        null=True)
+
+#    out_acl = models.ForeignKey(
+#        ControllerAcl,
+#        verbose_name = 'Controller output acl',
+#        blank=True,
+#        null=True)
+        
+    #
+    # end fields ----------------------------------------
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('controller', 'type', 'number')
+    
+    class Rest:
+        NAME = 'controller-interface'
+        FIELD_INFO = (
+             {'name': 'discovered_ip', 'rest_name': 'discovered-ip'},
+#            {'name': 'in_acl',        'rest_name': 'in-acl'},
+#            {'name': 'out_acl',       'rest_name': 'out-acl'},
+             )
+        
+
+#
+# ------------------------------------------------------------
+
+class FirewallRule(models.Model):
+
+    #
+    # fields ----------------------------------------
+    
+    interface = models.ForeignKey(
+        ControllerInterface,
+        verbose_name="Controller Interface")
+
+    action = models.CharField(
+        max_length=8,
+        validators=[ EnumerationValidator(('allow', 'deny', 'reject'))],
+        default='allow',
+        blank=True)
+
+    src_ip = models.CharField(
+        verbose_name='Source IP',
+        help_text='IP Address to allow traffic from',
+        validators=[ IpValidator() ],
+        max_length=15,
+        default='',
+        blank=True)
+    
+    vrrp_ip = models.CharField(
+        verbose_name='Local IP',
+        help_text='(Local) IP Address to allow traffic to',
+        validators=[ IpValidator() ],
+        max_length=15,
+        default='',
+        blank=True)
+    
+    port = models.IntegerField(
+        verbose_name='Port Number',
+        help_text='Port Number',
+        validators=[ RangeValidator(0,2**16-1) ],
+        default=0,
+        blank=True)
+    
+    proto = models.CharField(
+        verbose_name="ip proto",
+        help_text="ip protocol (tcp, udp or vrrp)", #TODO validator
+        validators=[ UfwProtocolValditor() ],
+        max_length=4,
+        default='',
+        blank=True)
+    
+    #
+    # end fields ----------------------------------------
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('interface', 'src_ip', 'vrrp_ip', 'port', 'proto')
+        
+    class Rest:
+        NAME = 'firewall-rule'
+        FIELD_INFO = (
+             {'name': 'src_ip', 'rest_name': 'src-ip'},
+             {'name': 'vrrp_ip', 'rest_name': 'vrrp-ip'},
+        )
+
+#
+# ------------------------------------------------------------
+
+class ControllerDomainNameServer(models.Model):
+    
+    controller = models.ForeignKey(
+        Controller,
+        verbose_name="Controller ID",
+        default=None,
+        null=True)
+    
+    ip = models.CharField(
+        verbose_name='IP Address',
+        help_text='IP Address of domain name server',
+        validators=[ IpValidator() ],
+        max_length=15,
+        default='')
+
+    timestamp = models.IntegerField(
+        verbose_name='timestamp',
+        help_text='Timestamp to determine order of domain name servers',
+        default = get_timestamp,
+        null=True,
+        blank=True,
+        )
+
+    #
+    # end fields ----------------------------------------
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('controller', 'ip')
+        
+    def validate_unique(self, exclude = None):
+        try:
+            exists = ControllerDomainNameServer.objects.get(controller = self.controller,
+                                                        ip = self.ip)
+            if exists.timestamp != self.timestamp:
+                print 'SHAZAM', self.timestamp
+                self.timestamp = exists.timestamp
+        except:
+            pass
+
+    class Rest:
+        NAME = 'controller-domain-name-server'
+        FIELD_INFO = (
+            )
+
+
+#
+# ------------------------------------------------------------
+
+class Switch(models.Model):
+    switch_id_length = 23
+    #
+    # fields ----------------------------------------
+
+    dpid = models.CharField(
+        primary_key=True,
+        verbose_name='Switch DPID',
+        help_text='Switch DPID - 64-bit hex separated by :',
+        max_length=switch_id_length, 
+        validators=[ validate_dpid ])
+
+    controller = models.ForeignKey(
+        Controller,
+        verbose_name='Controller ID',
+        blank=True,                           
+        null=True)
+    
+    socket_address = models.CharField(
+        verbose_name='Socket Address',
+        help_text='Socket address used for connection to controller',
+        max_length=64,
+        blank=True,
+        null=True)
+    
+    ip = models.CharField(
+        verbose_name='IP Address',
+        help_text='IP Address used for connection from controller',
+        validators=[ IpValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+
+    active = models.BooleanField(
+        verbose_name='Active',
+        help_text='Switch is active (i.e. connected to a controller)',
+        default=False)
+
+    connected_since = models.DateTimeField(
+        verbose_name='Last Connect Time',
+        help_text='Time when the switch connected to the controller',
+        blank=True,
+        null=True)
+    
+    dp_desc = models.CharField(
+        verbose_name='Description',
+        max_length=256,
+        blank=True,
+        null=True)
+
+    hw_desc = models.CharField(
+        verbose_name='Hardware Description',
+        max_length=256,
+        blank=True,
+        null=True)
+
+    sw_desc = models.CharField(
+        verbose_name='Software Description',
+        max_length=256,
+        blank=True,
+        null=True)
+
+    serial_num = models.CharField(
+        verbose_name='Serial Number',
+        max_length=32,
+        blank=True,
+        null=True)
+
+    capabilities = models.IntegerField(
+        verbose_name='Capabilities',
+        help_text='Openflow switch capabilities',
+        validators=[ RangeValidator(0,2**32-1) ],
+        default=0)
+
+    tunneling_supported = models.BooleanField(
+        default=False,
+        )
+
+    buffers = models.IntegerField(
+        verbose_name='Max Packets',
+        help_text='Maximum number of packets buffered by the switch',
+        validators=[ RangeValidator(0,2**32-1) ],
+        blank=True,
+        null=True)
+
+    tables = models.IntegerField(
+        verbose_name='Max Tables',
+        help_text='Number of tables supported by the switch',
+        validators=[ RangeValidator(0,2**8-1) ],
+        blank=True,
+        null=True)
+
+    actions = models.IntegerField(
+        verbose_name='Actions',
+        help_text='Actions supported by the switch',
+        validators=[ RangeValidator(0,2**32-1) ],
+        default=0)
+    
+    #
+    # end fields ----------------------------------------
+
+    # LOOK! we should have an origin field to distinguish
+    # between user-specified switches and 'discovered' switches
+
+    def __unicode__(self):
+        return "%s" % self.dpid
+
+    class Rest:
+        NAME = 'switch'
+        FIELD_INFO = (
+            {'name': 'socket_address',    'rest_name': 'socket-address'},
+            {'name': 'ip',                'rest_name': 'ip-address'},
+            {'name': 'connected_since',   'rest_name': 'connected-since'},
+            {'name': 'dp_desc',           'rest_name': 'dp-desc'},
+            {'name': 'hw_desc',           'rest_name': 'hw-desc'},
+            {'name': 'sw_desc',           'rest_name': 'sw-desc'},
+            {'name': 'serial_num',        'rest_name': 'serial-num'},
+            {'name': 'tunneling_supported', 'rest_name': 'tunnel-supported'},
+            )
+
+#
+# ------------------------------------------------------------
+# SwitchConfig
+#  Any 'configured' (non-discovered) state associated with
+#  a switch.
+#
+#  tunnel_termination; when enabled, tunnels are constructed
+#   to any other tunnel_termination switch, building a mesh
+#   of open-flow enabled switches.  Typicall Used in virtualized
+#   environments, where openflow switches are not intended to
+#   exist in the path.
+#
+
+class SwitchConfig(models.Model):
+    switch_id_length = 23
+    #
+    # fields ----------------------------------------
+
+    dpid = models.CharField(
+        primary_key=True,
+        verbose_name='Switch DPID',
+        help_text='Switch DPID - 64-bit hex separated by :',
+        max_length=switch_id_length, 
+        validators=[ validate_dpid ])
+
+    core_switch = models.BooleanField(
+        default=False,
+        help_text='Identify core switches'
+        )
+
+    tunnel_termination = models.CharField(
+        verbose_name='Tunnel Termination',
+        help_text='Tunnel Termination ("enabled" "disabled" "default")',
+        validators=[ EnumerationValidator(('enabled', 'disabled', 'default'))],
+        default = 'default',
+        max_length=16)
+
+    #
+    # end fields ----------------------------------------
+
+
+    def validate_unique(self, exclude = None):
+        self.dpid = self.dpid.lower()
+
+    class Rest:
+        NAME = 'switch-config'
+        FIELD_INFO = (
+            {'name': 'tunnel_termination', 'rest_name': 'tunnel-termination'},
+            {'name': 'core_switch',       'rest_name': 'core-switch'},
+            )
+
+
+#
+# ------------------------------------------------------------
+
+class SwitchAlias(models.Model):
+    
+    #
+    # fields ----------------------------------------
+
+    id = models.CharField(
+        primary_key=True,
+        max_length=255,
+        help_text = "alias for switch",
+        verbose_name='alias',
+        validators=[SwitchAliasValidator()])
+    
+    switch = models.ForeignKey(
+        SwitchConfig,
+        verbose_name='Switch DPID')
+
+    #
+    # end fields ----------------------------------------
+
+    class Rest:
+        NAME = 'switch-alias'
+
+
+#
+# ------------------------------------------------------------
+
+class Port(models.Model):
+    #
+    # fields ----------------------------------------
+    #
+    # This table isn't intended to be updated via the rest api,
+    #  sdnplatform writes the table to describe a switch.
+    #
+    # The 'number' in the port model is the openflow port number,
+    #  which is a value used in setting up flow entries.  This is
+    #  not an interface name;  the 'interface name' is the 'name'
+    #  field below.  This table provides a mapping from the switch
+    #  dpid and port number to an 'interface name'.
+    #
+    # Since interface names can be configured on demand by the
+    #  switch, for example to name a tunnel, its not easy to
+    #  "guess" interface names without the switch reporting 
+    #  what interface names exist.  This leads to difficulty
+    #  in preconfiguring any associations with <switch, interface name>
+    #
+
+    # Unique identifier for the port
+    # combination of the switch DPID and the port number
+    id = models.CharField(
+        primary_key=True,
+        verbose_name='Port ID',
+        help_text = '#|switch|number',
+        max_length=48)
+    
+    switch = models.ForeignKey(
+        Switch,
+        verbose_name='Switch DPID')
+
+    number = models.IntegerField(
+        verbose_name='OF #',
+        help_text="Port open flow number",
+        validators=[ RangeValidator(0,2**16-1) ])
+    
+    hardware_address = models.CharField(
+        verbose_name="MAC Address",
+        help_text="Port MAC Address",
+        max_length=17, 
+        validators = [validate_mac_address],
+        blank=True,
+        null=True)
+    
+    name = models.CharField(
+        verbose_name='Name',
+        help_text="Port name",
+        max_length=32,
+        blank=True,
+        null=True)
+
+    config = models.IntegerField(
+        verbose_name='Configuration',
+        help_text='Configuration Flags',
+        validators=[ RangeValidator(0,2**32-1) ],
+        default=0)
+
+    state = models.IntegerField(
+        verbose_name="State",
+        help_text="State Flags",
+        validators=[ RangeValidator(0,2**32-1) ],
+        default=0)
+    
+    current_features = models.IntegerField(
+        verbose_name='Current',
+        help_text='Current Features',
+        validators=[ RangeValidator(0,2**32-1) ],
+        default=0)
+
+    advertised_features = models.IntegerField(
+        verbose_name='Advertised',
+        help_text='Advertised Features',
+        validators=[ RangeValidator(0,2**32-1) ],
+        default=0)
+
+    supported_features = models.IntegerField(
+        verbose_name='Supported',
+        help_text='Supported Features',
+        validators=[ RangeValidator(0,2**32-1) ],
+        default=0)
+
+    peer_features = models.IntegerField(
+        verbose_name='Peer',
+        help_text='Peer Features',
+        validators=[ RangeValidator(0,2**32-1) ],
+        default=0)
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return "%s" % (self.id,)
+
+    class Rest:
+        NAME = 'port'
+        FIELD_INFO = (
+            {'name': 'hardware_address',     'rest_name': 'hardware-address'},
+            {'name': 'current_features',     'rest_name': 'current-features'},
+            {'name': 'advertised_features',  'rest_name': 'advertised-features'},
+            {'name': 'supported_features',   'rest_name': 'supported-features'},
+            {'name': 'peer_features',        'rest_name': 'peer-features'},
+            )
+
+#
+# ------------------------------------------------------------
+
+class PortAlias(models.Model):
+    
+    #
+    # fields ----------------------------------------
+
+    id = models.CharField(
+        primary_key=True,
+        max_length=255,
+        help_text = "alias for port",
+        verbose_name='alias',
+        validators=[PortAliasValidator()])
+    
+    port = models.ForeignKey(
+        Port,
+        help_text = "foreign key for port alias",
+        verbose_name='Port ID')
+
+    #
+    # end fields ----------------------------------------
+
+    class Rest:
+        NAME = 'port-alias'
+#
+# ------------------------------------------------------------
+
+class SwitchInterfaceConfig(models.Model):
+    if_name_len = 32
+    #
+    # fields ----------------------------------------
+
+    switch = models.ForeignKey(
+        SwitchConfig,
+        verbose_name='Switch')
+
+    if_name = models.CharField(
+        verbose_name='If Name',
+        validators=[ SafeForPrimaryKeyValidator() ],
+        max_length=32)
+
+    mode = models.CharField(
+        verbose_name='Mode',
+        help_text='Interface Mode ("external", "default")',
+        validators=[ EnumerationValidator(('external','default'))],
+        max_length=32,
+        default='default')
+    
+    broadcast = models.BooleanField(
+        default=False,
+        verbose_name='Broadcast',
+        help_text='True when interfaces is an uplink to a legacy net',
+        )
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return "%s" % (self.id)
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('switch', 'if_name')
+
+    def validate_unique(self, exclude = None):
+        self.broadcast = False
+        if self.mode == 'external':
+            self.broadcast = True
+
+    class Rest:
+        NAME = 'switch-interface-config'
+        FIELD_INFO = (
+            # By using 'name' here, the 'name' from the port is identified
+            #  in the complete-from-another procedure
+            {'name': 'if_name',             'rest_name': 'name'},
+            )
+
+
+#
+# ------------------------------------------------------------
+
+class SwitchInterfaceAlias(models.Model):
+    switch_interface_alias_length = 255
+    #
+    # fields ----------------------------------------
+
+    id = models.CharField(
+        primary_key = True,
+        verbose_name = 'Switch Interface Alias',
+        help_text = 'alias',
+        max_length = switch_interface_alias_length,
+        validators=[HostAliasValidator()])
+
+    switch_interface = models.ForeignKey(
+        SwitchInterfaceConfig,
+        verbose_name='Switch Interface',
+        )
+
+    #
+    # end fields ----------------------------------------
+
+    class Rest:
+        NAME = 'switch-interface-alias'
+        FIELD_INFO = (
+            {'name': 'switch_interface', 'rest_name': 'switch-interface'},
+        )
+#
+# ------------------------------------------------------------
+
+class StaticFlowTableEntry(models.Model):
+    #
+    # fields ----------------------------------------
+
+    name = models.CharField(
+        primary_key=True,
+        verbose_name='Name', 
+        max_length=80)
+
+    switch = models.ForeignKey(
+        SwitchConfig, 
+        verbose_name="Switch DPID")
+
+    active = models.BooleanField(
+        default=False)
+    
+    # LOOK! we should hide the timeout values for static flow entry
+    # definition as they are overwritten (unless we allow these to
+    # overwrite the default that static flow pusher uses)
+    idle_timeout = models.IntegerField(
+        verbose_name="Idle Timeout", 
+        help_text="Expire flow after this many seconds of inactivity - default: 60",
+        default=60,
+        validators=[ RangeValidator(0, 2**16-1) ])
+                                       
+    hard_timeout = models.IntegerField(
+        verbose_name="Hard Timeout",
+        help_text="Seconds to expire flow, regardless of activity - default: 0",
+        default=0,
+        validators=[ RangeValidator(0, 2**16-1) ])
+
+    priority = models.IntegerField(
+        verbose_name="Priority", 
+        help_text="Priority of the flow entry",
+        default=32768,
+        validators=[ RangeValidator(0, 2**16-1) ])
+
+    cookie = models.IntegerField(
+        verbose_name="Cookie",
+        default=0) # 64-bit
+
+    #
+    # match fields
+    #
+
+    # LOOK! Need a file of python openflow constants 
+
+    # LOOK! we should hide this also or at least
+    #       for static flow entries say it is ignored
+    wildcards = models.IntegerField(
+        verbose_name="Wildcards",
+        default=0,
+        validators=[ RangeValidator(0,2**32-1) ])
+
+    in_port = models.IntegerField(
+            verbose_name="Ingress Port", 
+            blank=True, 
+            help_text="Open flow port number of ingress port",
+            null=True, 
+            validators = [ RangeValidator(0, 2**16-1) ] ) 
+
+    dl_src = models.CharField(
+            verbose_name="Src MAC", 
+            help_text="This is a 48-bit quantity specified in xx:xx:xx:xx:xx:xx format", 
+            max_length=17, 
+            blank=True, 
+            null=True,
+            validators = [ validate_mac_address ] )
+
+    dl_dst = models.CharField(
+            verbose_name="Dst MAC", 
+            help_text="Destination MAC address in the frames",
+            max_length=17, 
+            blank=True, 
+            null=True, 
+            validators = [ validate_mac_address ] )
+
+    dl_vlan = models.IntegerField(
+            verbose_name="VLAN ID",
+            help_text="VLAN ID in the frames",
+            blank=True, 
+            null=True, 
+            validators = [ RangeValidator(0, 2**12-1) ]) 
+
+    dl_vlan_pcp = models.IntegerField(
+            verbose_name="VLAN Priority", 
+            help_text="VLAN ID in the frames",
+            blank=True, 
+            null=True, 
+            validators = [ RangeValidator(0, 2**3-1) ]) 
+
+    dl_type = models.IntegerField(
+            verbose_name="Ether Type", 
+            help_text="Ether(L3) type",
+            blank=True, 
+            null=True, 
+            validators = [ RangeValidator(0, 2**16-1) ]) 
+
+    nw_tos = models.IntegerField(
+            verbose_name="TOS Bits",
+            help_text="TOS bits in the frame",
+            blank=True,
+            null=True,
+            validators = [ RangeValidator(0, 2**6-1) ]) # 6-bit DSCP value
+
+    nw_proto = models.IntegerField(
+            verbose_name="Protocol", 
+            help_text="IP (L4) protocol in the packets",
+            blank=True,
+            null=True, 
+            validators = [ RangeValidator(0, 2**8-1) ]) 
+
+    nw_src = models.CharField(
+            verbose_name="Src IP", 
+            help_text="IP v4 source address in dotted decimal a.b.c.d w/ optional mask (ex: /24)", 
+            max_length=18,
+            validators = [ CidrValidator(mask_required=False) ],
+            blank=True, 
+            null=True)
+
+    nw_dst = models.CharField(
+            verbose_name="Dst IP", 
+            help_text="IP v4 destination address in dotted decimal a.b.c.d w/ optional mask (ex: /24)", 
+            validators=[ CidrValidator(mask_required=False) ],
+            max_length=18,
+            blank=True, 
+            null=True )
+
+    tp_src = models.IntegerField(
+            verbose_name="Src Port", 
+            help_text="Source (TCP/UDP) port",
+            blank=True, 
+            null=True,
+            validators=[ RangeValidator(0, 2**16-1) ])
+
+    tp_dst = models.IntegerField(
+            verbose_name="Dst Port", 
+            help_text="Destination (TCP/UDP) port",
+            blank=True, 
+            null=True, 
+            validators=[ RangeValidator(0, 2**16-1) ])
+
+    # LOOK! have to figure out how to expose actions in the CLI - this is ugly/brittle
+    actions = models.CharField(
+            verbose_name = "Actions",
+            help_text="This is a comma-separated list of actions - a la dpctl", 
+            max_length=1024, 
+            blank=True, 
+            null=True)
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.name
+
+
+    class Rest:
+        NAME = 'flow-entry'
+        FIELD_INFO = (
+            {'name': 'idle_timeout',    'rest_name': 'idle-timeout'},
+            {'name': 'hard_timeout',    'rest_name': 'hard-timeout'},
+            {'name': 'in_port',         'rest_name': 'ingress-port'},
+            {'name': 'dl_src',          'rest_name': 'src-mac'},
+            {'name': 'dl_dst',          'rest_name': 'dst-mac'},
+            {'name': 'dl_vlan',         'rest_name': 'vlan-id'},
+            {'name': 'dl_vlan_pcp',     'rest_name': 'vlan-priority'},
+            {'name': 'dl_type',         'rest_name': 'ether-type'},
+            {'name': 'nw_tos',          'rest_name': 'tos-bits'},
+            {'name': 'nw_proto',        'rest_name': 'protocol'},
+            {'name': 'nw_src',          'rest_name': 'src-ip'},
+            {'name': 'nw_dst',          'rest_name': 'dst-ip'},
+            {'name': 'tp_src',          'rest_name': 'src-port'},
+            {'name': 'tp_dst',          'rest_name': 'dst-port'},
+            {'name': 'actions',         'rest_name': 'actions'},
+            )
+
+
+#
+# ------------------------------------------------------------
+
+class Link(models.Model):
+    #
+    # fields ----------------------------------------
+
+    id = models.CharField(
+        primary_key=True,
+        verbose_name='Link ID',
+        max_length=64)
+    
+    src_switch = models.ForeignKey(
+        Switch,
+        verbose_name='Src Switch DPID',
+        related_name='src_link_set')
+    #src_switch_id = models.CharField(
+    #    verbose_name='Src Switch ID',
+    #    max_length=32)
+    
+    name = models.CharField(
+        verbose_name='Name',
+        help_text="Link name",
+        max_length=32,
+        blank=True,
+        null=True)
+    
+    src_port = models.IntegerField(
+        verbose_name="Src Port",
+        validators=[ RangeValidator(0, 2**16-1) ])
+
+    src_port_state = models.IntegerField(
+        verbose_name="Src Port State",
+        help_text="Source Port State Flags",
+        validators=[ RangeValidator(0,2**32-1) ],
+        default=0)
+
+    dst_switch = models.ForeignKey(
+        Switch,
+        verbose_name='Dst Switch DPID',
+        related_name='dst_link_set')
+    #dst_switch_id = models.CharField(
+    #    verbose_name='Dst Switch ID',
+    #    max_length=32)
+   
+    dst_port = models.IntegerField(
+        verbose_name="Dst Port",
+        help_text="Destination Port",
+        validators=[ RangeValidator(0, 2**16-1) ])
+
+    dst_port_state = models.IntegerField(
+        verbose_name="Dst Port State",
+        help_text="Destination Port State Flags",
+        validators=[ RangeValidator(0,2**32-1) ],
+        default=0)
+
+    link_type = models.CharField(
+                            verbose_name='Link Type',
+                            help_text="Link type",
+                            max_length=10,
+                            blank=True,
+                            null=True)
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return "%s" % self.id
+
+    class Rest:
+        NAME = 'link'
+        FIELD_INFO = (
+            {'name': 'src_switch',      'rest_name': 'src-switch'},
+            {'name': 'src_port',        'rest_name': 'src-port'},
+            {'name': 'src_port_state',  'rest_name': 'src-port-state'},
+            {'name': 'dst_switch',      'rest_name': 'dst-switch'},
+            {'name': 'dst_port',        'rest_name': 'dst-port'},
+            {'name': 'dst_port_state',  'rest_name': 'dst-port-state'},
+            {'name': 'link_type',       'rest_name': 'link-type'}
+            )
+            
+#
+# ------------------------------------------------------------
+# An address-space separation
+
+class AddressSpace (models.Model):
+
+    id_max_length = 64
+
+    #
+    # fields ----------------------------------------
+
+    #
+    # Unique name of the address-space
+    #
+    name = models.CharField(
+        primary_key  = True,
+        verbose_name = 'Address Space Name',
+        help_text    = 'A unique name for an Address Space Seperation',
+        validators   = [ AddressSpaceNameValidator() ],
+        max_length   = id_max_length)
+
+    #
+    # Verbose description of this rule.
+    #
+    description = models.CharField(
+        verbose_name = 'Description',
+        help_text    = "Description of the address-space",
+        max_length   = 128,
+        blank        = True,
+        null         = True)
+   
+    #
+    # Whether the configuration is active ? By default, it is active
+    # Used to disable the configuration without having to delete the entire
+    # address-space configuration construct.
+    #
+    active = models.BooleanField(
+        verbose_name = 'Active',
+        help_text    = 'Is this Address Space active (default is True)',
+        default      = True)
+
+    #
+    # Priority of this address-space during device matching when
+    # compared to other address-spaces. Those at the same priority
+    # are used in a determinisitc alphanetical order.
+    #
+    priority = models.IntegerField(
+        verbose_name = 'Priority',
+        help_text    = 'Priority for this Address Space ' +
+                       '(higher numbers are higher priority)',
+        default      = 1000,
+        validators   = [ RangeValidator(0, 65535) ])
+
+    #
+    # Seperator tag of this address-space in the data plane, such as vlan id.
+    #
+    vlan_tag_on_egress = models.IntegerField(
+        verbose_name = 'Egress VLAN tag',
+        help_text    = 'Vlan Tag value used for this Address Space separation',
+        default      = None,
+        blank        = True,
+        null         = True,
+        validators   = [ RangeValidator(0, 4095) ])
+
+    #
+    # Origin of this configured item
+    # 
+    origin = models.CharField(
+        verbose_name = "Origin",
+        help_text    = "Values: cli, rest, other packages",
+        max_length   = 64, # in future we might use SW GUIDs for this field
+        blank        = True,
+        null         = True)
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__ (self):
+        return self.name
+
+    class Rest:
+        NAME       = 'address-space'
+        FIELD_INFO = (
+            {'name': 'vlan_tag_on_egress', 'rest_name':'vlan-tag-on-egress'},
+        )
+
+#
+# ------------------------------------------------------------
+# An identifier rule in address-space separation
+
+class AddressSpaceIdentifierRule (models.Model):
+    rule_max_length = 32 
+
+    #
+    # fields ----------------------------------------
+
+    #
+    # Create a reference to the enclosing address-space construct.
+    #
+    address_space = models.ForeignKey(
+        AddressSpace,
+        verbose_name = 'Address Space Identifier')
+
+    #
+    # Unique rule identifier name under this address-space.
+    #
+    rule = models.CharField(
+        verbose_name = 'Address Space Rule Identifier',
+        max_length   = rule_max_length)
+
+    #
+    # Verbose description of this rule.
+    #
+    description = models.CharField(
+        verbose_name = 'Description',
+        help_text    = "Description of rule",
+        max_length   = 128,
+        blank        = True,
+        null         = True)
+
+    #
+    # Whether the configuration is active ? By default, it is active
+    # Used to disable the configuration without having to delete the entire
+    # address-space identifier-rule configuration construct.
+    #
+    active = models.BooleanField(
+        verbose_name = 'Active',
+        help_text    = 'If this interface is active (default is True)',
+        default      = True)
+
+    #
+    # Priority of this address-space during device matching when
+    # compared to other address-spaces. Those at the same priority
+    # are used in a determinisitc alphanetical order.
+    #
+    priority = models.IntegerField(
+        verbose_name = 'Priority',
+        help_text    = 'Priority for this interface rule ' +
+                       '(higher numbers are higher priority)',
+        default      = 32768,
+        validators   = [ RangeValidator(0, 65535) ])
+
+    #
+    # DPID of the Switch which sent the packet
+    #
+    switch = models.CharField(
+        verbose_name = 'Switch DPID',
+        max_length   = Switch.switch_id_length,
+        help_text    = 'Switch DPID or switch alias',
+        validators   = [ validate_dpid ],
+        null         = True,
+        blank        = True)
+
+    #
+    # Range of ports in which the packet came from
+    #
+    ports = models.CharField(
+        verbose_name = "Port Range Spec",
+        help_text    = 'Port range (e.g. C12 or B1,A22-25)',
+        max_length   = 256,
+        validators   = [ PortRangeSpecValidator() ],
+        blank        = True,
+        null         = True)
+    
+    #
+    # Range of VLAN tags
+    #
+    vlans = models.CharField(
+        verbose_name = "VLAN Range Spec",
+        help_text    = "VLAN(s) (e.g. 5 or 5-10,4010-4050)",
+        max_length   = 256,
+        validators   = [ VLANRangeSpecValidator() ],
+        blank        = True,
+        null         = True)
+
+    #
+    # NameValue pair of tags
+    #
+    tag = models.CharField(
+        verbose_name = "Tag Spec",
+        help_text    = "Tag values (e.g. namespace.tagname=value)",
+        max_length   = 256,
+        validators   = [ TagSpecValidator() ],
+        blank        = True,
+        null         = True)
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__ (self):
+        return self.id
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('address_space', 'rule')
+
+    class Rest:
+        NAME = 'address-space-identifier-rule'
+        FIELD_INFO = (
+            {'name': 'description',     'rest_name': 'description'},
+            {'name': 'address_space',   'rest_name': 'address-space'},
+            {'name': 'active',          'rest_name': 'active'},
+            {'name': 'priority',        'rest_name': 'priority'},
+            {'name': 'switch',          'rest_name': 'switch'},
+            {'name': 'ports',           'rest_name': 'ports'},
+            {'name': 'vlans',           'rest_name': 'vlans'},
+            {'name': 'tag',             'rest_name': 'tag'},
+            )
+
+#
+# ------------------------------------------------------------
+
+class HostConfig(models.Model):
+    host_id_length = 17
+    #
+    # fields ----------------------------------------
+
+    address_space = models.ForeignKey(
+        AddressSpace,
+        verbose_name = "Address space name")
+
+    mac = models.CharField(
+        verbose_name="MAC Address",
+        max_length=host_id_length, 
+        validators = [validate_mac_address])
+
+    vlan = models.CharField(
+        verbose_name='VLAN',
+        help_text='VLAN Associated with host',
+        max_length=4,
+        validators=[RangeValidator(1, 4095)],
+        blank=True,
+        default='')
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        self.mac = self.mac.lower()
+        return "%s::%s" % (self.addressSpace, self.mac)
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('address_space', 'vlan', 'mac')
+
+    def validate_unique(self, exclude = None):
+        # Invoke the default validator; error out if the vns already exists
+        super(HostConfig, self).validate_unique(exclude)
+        if self.vlan and str(self.address_space) != 'default': 
+            raise ValidationError('host: vlan configured for '
+                                  'address-space other than "default" %s' % self.address_space)
+
+    class Rest:
+        NAME = 'host-config'
+        FIELD_INFO = (
+            {'name': 'address_space', 'rest_name': 'address-space'},
+            )
+
+#
+# ------------------------------------------------------------
+
+class HostSecurityIpAddress(models.Model):
+    host = models.ForeignKey(
+        HostConfig,
+        verbose_name='Host ID')
+
+    ip = models.CharField(
+        verbose_name='IP Address',
+        help_text='IP Address used to associate with host',
+        validators=[ IpValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.id
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('host', 'ip')
+
+    class Rest:
+        NAME = 'host-security-ip-address'
+        FIELD_INFO = (
+            {'name': 'ip', 'rest_name': 'ip-address'},
+            )
+
+
+#
+# ------------------------------------------------------------
+
+class HostSecurityAttachmentPoint(models.Model):
+    host = models.ForeignKey(
+        HostConfig,
+        verbose_name='Host ID')
+
+    dpid = models.CharField(
+        verbose_name = 'Switch DPID',
+        max_length   = Switch.switch_id_length,
+        help_text    = 'Switch DPID or switch alias',
+        validators   = [ validate_dpid ],
+        null         = True,
+        blank        = True)
+
+    if_name_regex = models.CharField(
+        verbose_name='If Name Regex',
+        help_text='Interface name regular expression',
+        max_length=64,
+        validators = [SafeForPrimaryKeyValidator(), IsRegexValidator()],
+        blank = True,
+        null = False,
+        )
+
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.id
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('host', 'dpid', 'if_name_regex')
+
+    class Rest:
+        NAME = 'host-security-attachment-point'
+        FIELD_INFO = (
+            {'name': 'if_name_regex', 'rest_name': 'if-name-regex'},
+            )
+
+#
+# ------------------------------------------------------------
+
+class HostAlias(models.Model):
+    host_alias_length = 255
+    #
+    # fields ----------------------------------------
+
+    id = models.CharField(
+        primary_key = True,
+        verbose_name = 'Host Alias',
+        help_text = 'alias',
+        max_length = host_alias_length,
+        validators=[HostAliasValidator()])
+
+    host = models.ForeignKey(
+        HostConfig,
+        verbose_name='Host ID')
+
+    #
+    # end fields ----------------------------------------
+
+    class Rest:
+        NAME = 'host-alias'
+
+
+class VlanConfig(models.Model):
+    #
+    # fields ----------------------------------------    
+    vlan = models.IntegerField(
+        primary_key = True,                       
+        verbose_name='VLAN',
+        help_text='VLAN Number',
+        validators=[RangeValidator(0, 4095)],
+        )
+    
+    #
+    # end fields ----------------------------------------
+    
+    def __unicode__(self):
+        if self.vlan:
+            return "%s vlan %s" % (self.vlan)
+        else:
+            return "%s vlan %s" % (self.vlan)
+    
+    class Rest:
+        NAME = 'vlan-config'
+
+#
+# ------------------------------------------------------------
+# A Static ARP table separation
+
+class StaticArp (models.Model):
+
+    id_max_length = 64
+    #
+    # fields ----------------------------------------
+    ip = models.CharField(
+        primary_key=True,
+        verbose_name='IP Address',
+        validators=[ IpValidator() ],
+        max_length=15)
+    mac = models.CharField(
+        verbose_name="MAC Address",
+        max_length=17, 
+        validators = [validate_mac_address]) 
+    #
+    # Origin of this configured item
+    # 
+    origin = models.CharField(
+        verbose_name = "Origin",
+        help_text    = "Values: cli, rest, other packages",
+        max_length   = 64, # in future we might use SW GUIDs for this field
+        blank        = True,
+        null         = True)
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__ (self):
+        return self.id
+    
+    class Rest:
+        NAME = 'static-arp'
+        
+
+
+#
+# ------------------------------------------------------------
+# A Tenant separation
+
+class Tenant (models.Model):
+
+    id_max_length = 64
+    #
+    # fields ----------------------------------------
+
+    #
+    # Unique name of the tenant
+    #
+    name = models.CharField(
+        primary_key  = True,
+        verbose_name = 'Tenant Name',
+        help_text    = 'A unique name for an Tenant',
+        validators   = [ TenantNameValidator() ],
+        max_length   = id_max_length)
+
+    #
+    # Verbose description of this tenant.
+    #
+    description = models.CharField(
+        verbose_name = 'Description',
+        help_text    = "Description of the tenant",
+        max_length   = 128,
+        blank        = True,
+        null         = True)
+   
+    #
+    # Whether the configuration is active ? By default, it is active
+    # Used to disable the configuration without having to delete the entire
+    # tenant configuration construct.
+    #
+    active = models.BooleanField(
+        verbose_name = 'Active',
+        help_text    = 'Is this Tenant active (default is True)',
+        default      = True)
+
+    #
+    # Origin of this configured item
+    # 
+    origin = models.CharField(
+        verbose_name = "Origin",
+        help_text    = "Values: cli, rest, other packages",
+        max_length   = 64, # in future we might use SW GUIDs for this field
+        blank        = True,
+        null         = True)
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__ (self):
+        return self.name
+    
+    def delete(self):
+        if self.name=='default' or self.name =='system' or self.name =='external':
+            raise ValidationError("Default/External/System Tenant can't be deleted")
+        super(Tenant, self).delete()
+    class Rest:
+        NAME = 'tenant'
+        
+
+#
+# ------------------------------------------------------------
+# A virtual router separation
+
+class VirtualRouter (models.Model):
+
+    id_max_length = 64
+    #
+    # fields ----------------------------------------
+    vrname = models.CharField(
+        verbose_name = 'Virtual Router Name',
+        help_text    = 'A unique name for a virtual router',
+        validators   = [ GeneralNameValidator() ],
+        max_length   = id_max_length
+        )
+    
+    tenant = models.ForeignKey(
+        Tenant,
+        verbose_name='Tenant Name',
+        )
+    description = models.CharField(
+        verbose_name = 'Description',
+        help_text    = "Description of the virtual router",
+        max_length   = 128,
+        blank        = True,
+        null         = True,
+        )
+   
+    #
+    # Origin of this configured item
+    # 
+    origin = models.CharField(
+        verbose_name = "Origin",
+        help_text    = "Values: cli, rest, other packages",
+        max_length   = 64, # in future we might use SW GUIDs for this field
+        blank        = True,
+        null         = True)
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__ (self):
+        return self.id
+    
+    def validate_unique(self, exclude = None):
+        # in fat tire, only one router per tenant can be defined. this can be removed later.
+        error=False
+        try:
+            exists = VirtualRouter.objects.get(tenant = self.tenant)
+            if exists.vrname !=self.vrname:
+                error=True
+        except:
+            pass
+        if error:
+            raise ValidationError(" Virtual router %s has been defined for tenant %s, only one virtual router per tenant supported" % (exists.vrname,self.tenant))
+    
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('tenant', 'vrname')
+
+    class Rest:
+        NAME = 'virtualrouter'
+
+#
+# ------------------------------------------------------------
+# A virtual network segment
+
+class VNS(models.Model):
+    id_max_length = 64
+    #
+    # fields ----------------------------------------
+    vnsname = models.CharField(
+        verbose_name='VNS ID',
+        help_text='A unique name for a Virtual Network Segment',
+        validators=[GeneralNameValidator()],
+        max_length=id_max_length)
+    tenant=models.ForeignKey(
+        Tenant,
+        verbose_name='Tenant ID',
+        default='default')
+    #
+    # Verbose description of this rule.
+    #
+    description = models.CharField(
+        verbose_name = 'Description',
+        help_text    = "Description of the VNS",
+        max_length   = 128,
+        blank        = True,
+        null         = True)
+
+    #
+    # Reference to the address-space item. By default, we 
+    # implicitly use 'default' if this is not explicitly provided.
+    #
+    vns_address_space = models.ForeignKey(
+        AddressSpace,
+        verbose_name='Address Space Association',
+        blank=True,
+        null=True)
+
+    active = models.BooleanField(
+        verbose_name='Active',
+        help_text='If this VNS is active (default is True)',
+        default=True)
+
+    priority = models.IntegerField(
+        verbose_name='Priority',
+        help_text='Priority for this VNS (higher numbers are higher priority)',
+        default = 1000,
+        validators=[RangeValidator(0, 65535)])
+ 
+    origin = models.CharField(
+        verbose_name = "The origin/creator interface for this VNS",
+        help_text="Values: cli, rest, other packages",
+        max_length=64, # in future we might use SW GUIDs for this field
+        blank=True,
+        null=True)
+
+    arp_mode = models.CharField(
+        verbose_name = "ARP Manager Config Mode",
+        help_text="Values: always-flood, flood-if-unknown, drop-if-unknown", 
+        max_length=32, 
+        validators=[VnsArpModeValidator()],
+        default='flood-if-unknown')
+
+    dhcp_mode = models.CharField(
+        verbose_name = "DHCP Manager Config Mode",
+        help_text = "Values: always-flood, flood-if-unknown, static",
+        max_length = 20,
+        validators=[VnsDhcpModeValidator()],
+        default='flood-if-unknown')
+    
+    dhcp_ip = models.CharField(
+        verbose_name='DHCP IP Address',
+        help_text='IP Address of DHCP Server',
+        validators=[ IpValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+
+    broadcast = models.CharField(
+        verbose_name = "Broadcast (non ARP/DHCP) Config Mode",
+        help_text = "Values: always-flood, forward-to-known, drop",
+        max_length = 20,
+        validators=[VnsBroadcastModeValidator()],
+        default='forward-to-known')
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.id
+
+    def delete(self):
+        #default VNS can't be deleted
+        if self.id=='default|default':
+            raise ValidationError("Default VNS can't be deleted")
+        #while address space still exist, address space default vns can't be deleted
+        #for fat tire, relationship between address space and tenant are unclear yet, the following part may need revisit
+        suffix = '-default'
+        if self.vnsname.endswith(suffix):
+            print self.vnsname
+            address_space_name = self.vnsname[:-len(suffix)]
+            error=False
+            try:
+                self.vns_address_space = AddressSpace.objects.get(name = address_space_name)
+                error=True
+            except Exception, e:
+                pass
+            if error:
+                raise ValidationError('vns %s is the default VNS of address space: %s, can not be deleted ' %
+                                         (self.vnsname,address_space_name))
+        super(VNS, self).delete()
+    # manage a magic association between vns names and 
+    # address space for vns's which end in -default
+    def validate_unique(self, exclude = None):
+        # Invoke the default validator; error out if the vns already exists
+        #for fat tire, relationship between address space and tenant are unclear yet, the following part may need revisit
+        super(VNS, self).validate_unique(exclude)
+        suffix = '-default'
+        if not 'vns_address_space' in exclude:
+            if self.vns_address_space:
+                if self.vnsname.endswith(suffix):
+                    if str(self.vns_address_space) != self.vnsname[:-len(suffix)]:
+                        raise ValidationError('vns names %s ending in -default '
+                                'must have address_space names with the same prefix: %s '
+                                % (self.vnsname, self.vns_address_space))
+            elif self.vnsname.endswith(suffix):
+                address_space_name = self.vnsname[:-len(suffix)]
+                try:
+                    self.vns_address_space = AddressSpace.objects.get(name = address_space_name)
+                except Exception, e:
+                    print e
+                    if self.vns_address_space == None:
+                        raise ValidationError('vns %s has no matching address-space %s ' %
+                                             (self.vnsname, address_space_name))
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('tenant', 'vnsname')
+
+    class Rest:
+        NAME = 'vns-definition'
+        FIELD_INFO = (
+            {'name': 'vns_address_space', 'rest_name': 'address-space'},
+            {'name': 'arp_mode',          'rest_name': 'arp-mode'},
+            {'name': 'dhcp_mode',         'rest_name': 'dhcp-mode'},
+            {'name': 'dhcp_ip',           'rest_name': 'dhcp-ip'},
+            )
+    
+#
+# ------------------------------------------------------------
+# An interface rule on a VNS
+
+class VNSInterfaceRule(models.Model):
+    rule_max_length = 32 
+
+    #
+    # fields ----------------------------------------
+
+    vns = models.ForeignKey(
+        VNS,
+        verbose_name='VNS ID')
+
+    rule = models.CharField(
+        verbose_name='VNS Rule ID',
+        max_length=rule_max_length)
+
+    description = models.CharField(
+        verbose_name='Description',
+        help_text="Description of rule",
+        max_length=128,
+        blank=True,
+        null=True)
+
+    vlan_tag_on_egress = models.BooleanField(
+        verbose_name='Egress Vlan Tagging',
+        help_text='Tag with VLAN at egress point (default is False)',
+        default=False)
+
+    allow_multiple = models.BooleanField(
+        verbose_name='Allow Multiple',
+        help_text='If this interface allows hosts to be on multiple VNS (default is False)',
+        default=False)
+
+    active = models.BooleanField(
+        verbose_name='Active',
+        help_text='If this interface is active (default is True)',
+        default=True)
+
+    priority = models.IntegerField(
+        verbose_name='Priority',
+        help_text='Priority for this interface rule (higher numbers are higher priority)',
+        default = 32768,
+        validators=[RangeValidator(0, 65535)])
+
+    mac = models.CharField(
+        verbose_name="MAC Address",
+        help_text='MAC Address or host alias',
+        max_length=17, 
+        validators = [validate_mac_address],
+        blank=True,
+        null=True)
+
+    ip_subnet = models.CharField(
+        verbose_name="IP Subnet",
+        help_text='IP address or subnet (e.g. 192.168.1.1 or 192.168.1.0/24)',
+        max_length=31,
+        validators = [CidrValidator(False)],
+        blank=True,
+        null=True)
+
+    switch = models.CharField(
+        verbose_name='Switch DPID',
+        max_length= Switch.switch_id_length,
+        help_text='Switch DPID or switch alias',
+        validators=[ validate_dpid ],
+        null=True,
+        blank=True)
+
+    ports = models.CharField(
+        verbose_name="Port Range Spec",
+        help_text='Port range (e.g. C12 or B1,A22-25)',
+        max_length=256,
+        validators = [PortRangeSpecValidator()],
+        blank=True,
+        null=True)
+
+    vlans = models.CharField(
+        verbose_name="VLAN Range Spec",
+        help_text="VLAN(s) (e.g. 5 or 5-10,4010-4050)",
+        max_length=256,
+        validators = [VLANRangeSpecValidator()],
+        blank=True,
+        null=True)
+
+    tags = models.CharField(
+        verbose_name="Tag Spec",
+        help_text="Tag values (e.g. namespace.tagname=value)",
+        max_length=256,
+        validators = [TagSpecValidator()],
+        blank=True,
+        null=True)
+
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.id
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('vns', 'rule')
+
+    class Rest:
+        NAME = 'vns-interface-rule'
+        FIELD_INFO = (
+            {'name': 'description',        'rest_name': 'description'},
+            {'name': 'allow_multiple',     'rest_name': 'allow-multiple'},
+            {'name': 'active',             'rest_name': 'active'},
+            {'name': 'priority',           'rest_name': 'priority'},
+            {'name': 'mac',                'rest_name': 'mac'},
+            {'name': 'ip_subnet',          'rest_name': 'ip-subnet'},
+            {'name': 'switch',             'rest_name': 'switch'},
+            {'name': 'ports',              'rest_name': 'ports'},
+            {'name': 'vlans',              'rest_name': 'vlans'},
+            {'name': 'tags',               'rest_name': 'tags'},
+            {'name': 'vlan_tag_on_egress', 'rest_name': 'vlan-tag-on-egress'},            
+            )
+
+#
+# ------------------------------------------------------------
+
+class VNSInterfaceConfig(models.Model):
+    name_max_length = 32
+    #
+    # fields ----------------------------------------
+
+    vns = models.ForeignKey(
+        VNS,
+        verbose_name='VNS ID')
+
+    interface = models.CharField(
+        verbose_name='VNS Interface Name',
+        max_length=name_max_length,
+        validators = [VnsInterfaceNameValidator()])
+
+    rule = models.ForeignKey(
+        VNSInterfaceRule,
+        verbose_name='VNS Rule ID',
+        blank=True,
+        null=True)
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.id
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('vns', 'interface')
+
+    class Rest:
+        NAME = 'vns-interface-config'
+        FIELD_INFO = (
+            {'name': 'rule',  'rest_name': 'rule'},
+            )
+
+
+#
+# ------------------------------------------------------------
+
+class VNSAcl(models.Model):
+    name_max_length=32
+    #
+    # fields ----------------------------------------
+
+    vns = models.ForeignKey(
+        VNS,
+        verbose_name='VNS ID')
+
+    name = models.CharField(
+        help_text='Acl Name',
+        validators=[VnsAclNameValidator()],
+        max_length=name_max_length)
+
+    priority = models.IntegerField(
+        verbose_name='Priority',
+        help_text='Priority for this ACL (higher numbers are higher priority)',
+        default = 32768,
+        validators=[RangeValidator(0, 65535)])
+
+    description = models.CharField(
+        verbose_name='Description',
+        help_text="Description of the ACL",
+        max_length=128,
+        blank=True,
+        null=True)
+
+    #
+    # end fields ----------------------------------------
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('vns', 'name')
+
+    def __unicode__(self):
+        return self.id
+
+    class Rest:
+        NAME = 'vns-access-list'
+        FIELD_INFO = (
+            {'name': 'name',         'rest_name': 'name'},
+            {'name': 'priority',     'rest_name': 'priority'},
+            {'name': 'description',  'rest_name': 'description'},
+            )
+
+#
+# ------------------------------------------------------------
+
+class VNSAclEntry(models.Model):
+    #
+    # fields ----------------------------------------
+
+    rule = models.CharField(
+        help_text='Rule ID',
+        validators=[VnsRuleNameValidator()],
+        max_length=15)
+
+    vns_acl = models.ForeignKey(
+        VNSAcl,
+        verbose_name='VNS Acl name')
+
+    action = models.CharField(
+        verbose_name='permit or deny',
+        help_text="'permit' or 'deny'",
+        max_length=16,
+        validators=[ VnsAclEntryActionValidator() ])
+
+    type = models.CharField(
+        verbose_name='mac/ip/<0-255>/udp/tcp/icmp',
+        help_text="ACLtype either mac or ip or udp or tcp or icmp or ip-protocol-type",
+        max_length=16)
+
+    src_ip = models.CharField(
+        verbose_name='Source IP',
+        help_text='Source IP Address to match',
+        validators=[ IpValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+
+    src_ip_mask = models.CharField(
+        verbose_name='Source IP Mask',
+        help_text='Mask to match source IP',
+        validators=[ IpValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+
+    src_tp_port_op = models.CharField(
+        verbose_name='Source Port comparison op',
+        help_text='Compare with tcp/udp port eq/neq/any',
+        max_length=5,
+        blank=True,
+        null=True)
+    
+    src_tp_port = models.IntegerField(
+        verbose_name='Source UDP/TCP Port',
+        help_text='Source port value to compare',
+        validators=[RangeValidator(0, 65535)],
+        blank=True,
+        null=True)
+   
+    dst_ip = models.CharField(
+        verbose_name='Destination IP',
+        help_text='Destination IP Address to match',
+        validators=[ IpValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+
+    dst_ip_mask = models.CharField(
+        verbose_name='Destination IP Mask',
+        help_text='Mask to match destination IP',
+        validators=[ IpValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+
+    dst_tp_port_op = models.CharField(
+        verbose_name='Destination Port comparison op',
+        help_text='Compare with tcp/udp port eq/neq/any',
+        max_length=3,
+        blank=True,
+        null=True)
+    
+    dst_tp_port = models.IntegerField(
+        verbose_name='Destination UDP/TCP Port',
+        help_text='Destination port value to compare',
+        validators=[RangeValidator(0, 65535)],
+        blank=True,
+        null=True)
+
+    icmp_type = models.IntegerField(
+        verbose_name='ICMP Type',
+        help_text='Matching ICMP type icmp (blank matches all)',
+        validators=[RangeValidator(0, 255)],
+        blank=True,
+        null=True)
+
+    src_mac = models.CharField(
+        verbose_name="Source MAC Address",
+        help_text="Colon separated hex string (blank matches all)",
+        max_length=17, 
+        validators = [validate_mac_address],
+        blank=True,
+        null=True)
+    
+    dst_mac = models.CharField(
+        verbose_name="Destination MAC Address",
+        help_text="Colon separated hex string (blank matches all)",
+        max_length=17, 
+        validators = [validate_mac_address],
+        blank=True,
+        null=True)
+    
+    ether_type = models.IntegerField(
+        verbose_name='Ethernet Packet Type',
+        help_text='Standard ether type (blank matches all)',
+        validators=[RangeValidator(1536, 65535)],
+        blank=True,
+        null=True)
+
+    vlan = models.IntegerField(
+        verbose_name="VLAN ID",
+        help_text='Standard ether type (blank matches all)',
+        blank=True, 
+        null=True, 
+        validators = [ RangeValidator(0, 4095) ]) 
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.id
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('vns_acl', 'rule')
+
+    def validate_unique(self, exclude = None):
+        #
+        # there are three types of entries:
+        # - mac based rules
+        # - ip based rules
+        # - tcp/udp based rules
+        # 
+        # verify that for each rules, unexpected fields are not
+        #  populated
+
+        if self.type == 'mac':
+            if self.src_ip or self.src_ip_mask:
+                raise ValidationError("vns-access-list-entry mac rule:"
+                                      " src-ip/src-ip-mask specified"
+                                      "(ought to be null)")
+            if self.dst_ip or self.dst_ip_mask:
+                raise ValidationError("vns-access-list-entry mac rule:"
+                                      " dst-ip/dst-ip-mask specified "
+                                      "(ought to be null)")
+            if self.src_tp_port_op or self.src_tp_port:
+                raise ValidationError("vns-access-list-entry mac rule:"
+                                      " src-tp-port-op/src-to-port specified "
+                                      "(ought to be null)")
+            if self.dst_tp_port_op or self.dst_tp_port:
+                raise ValidationError("vns-access-list-entry mac rule:"
+                                      " dst-tp-port-op/dst-to-port specified "
+                                      "(ought to be null)")
+            if self.icmp_type:
+                raise ValidationError("vns-access-list-entry mac rule:"
+                                      " icmp_type specified "
+                                      "(ought to be null)")
+        elif self.type == 'ip' or re.match('[\d]+', self.type) or \
+            self.type == 'icmp':
+            if (self.src_tp_port_op != None or self.src_tp_port != None) and \
+                ((self.src_tp_port_op == None) or (self.src_tp_port == None)):
+                raise ValidationError("vns-access-list-entry ip rule:"
+                                      " src-tp-port-op/src-to-port specified "
+                                      "(both or neither)")
+            if (self.dst_tp_port_op != None or self.dst_tp_port != None) and \
+                ((self.dst_tp_port_op == None) or (self.dst_tp_port == None)):
+                raise ValidationError("vns-access-list-entry ip rule:"
+                                      " dst-tp-port-op/dst-to-port specified "
+                                      "(both or neither)")
+            if self.src_mac or self.dst_mac:
+                raise ValidationError("vns-access-list-entry ip rule:"
+                                      " src_mac/dst_mac specified "
+                                      "(ought to be null)")
+            if self.ether_type or self.vlan:
+                raise ValidationError("vns-access-list-entry ip rule:"
+                                      " ether-type/vlan specified "
+                                      "(ought to be null)")
+
+        elif self.type == 'tcp' or self.type == 'udp':
+            if self.src_mac or self.dst_mac:
+                raise ValidationError("vns-access-list-entry ip rule:"
+                                      " src_mac/dst_mac specified "
+                                      "(ought to be null)")
+            if self.ether_type or self.vlan:
+                raise ValidationError("vns-access-list-entry ip rule:"
+                                      " ether-type/vlan specified "
+                                      "(ought to be null)")
+
+
+    class Rest:
+        NAME = 'vns-access-list-entry'
+        FIELD_INFO = (
+            {'name': 'vns_acl',        'rest_name': 'vns-access-list'},
+            {'name': 'src_ip',         'rest_name': 'src-ip'},
+            {'name': 'src_ip_mask',    'rest_name': 'src-ip-mask'},
+            {'name': 'dst_ip',         'rest_name': 'dst-ip'},
+            {'name': 'dst_ip_mask',    'rest_name': 'dst-ip-mask'},
+            {'name': 'src_tp_port_op', 'rest_name': 'src-tp-port-op'},
+            {'name': 'src_tp_port',    'rest_name': 'src-tp-port'},
+            {'name': 'dst_tp_port_op', 'rest_name': 'dst-tp-port-op'},
+            {'name': 'dst_tp_port',    'rest_name': 'dst-tp-port'},
+            {'name': 'icmp_type',      'rest_name': 'icmp-type'},
+            {'name': 'src_mac',        'rest_name': 'src-mac'},
+            {'name': 'dst_mac',        'rest_name': 'dst-mac'},
+            {'name': 'ether_type',     'rest_name': 'ether-type'},
+            )
+
+#
+# ------------------------------------------------------------
+
+class VNSInterfaceAcl(models.Model):
+    in_out_length = 4
+
+    #
+    # fields ----------------------------------------
+
+    vns_acl = models.ForeignKey(
+        VNSAcl,
+        verbose_name='VNS Acl name')
+
+    vns_interface = models.ForeignKey(
+        VNSInterfaceConfig,
+        verbose_name='VNS Interface ID',
+        help_text='[vns id]|[interface long name]')
+
+    in_out = models.CharField(
+        verbose_name='in/out',
+        help_text='Match on packet input or output',
+        validators=[VnsInterfaceAclInOutValidator()],
+        max_length=in_out_length,
+        )
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.id
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('vns_interface', 'vns_acl', 'in_out')
+
+    def validate_unique(self, exclude = None):
+        acl_parts = str(self.vns_acl).split('|')
+        intf_parts = str(self.vns_interface).split('|')
+        # validate two vns parts
+        if acl_parts[0] != intf_parts[0]:
+            raise ValidationError("acl's vns %s doen't match interface vns %s" %
+                                  (acl_parts[0], intf_parts[0]))
+        error = False
+        try:
+            exists = VNSInterfaceAcl.objects.get(vns_interface=self.vns_interface, in_out=self.in_out)
+            if exists:
+                if exists.vns_acl != self.vns_acl:
+                    error = True
+        except:
+            pass
+        if error:
+            raise ValidationError("Interface %s already has an ACL in the %s direction, only one ACL per direction allowed" % (self.vns_interface, self.in_out))
+
+    class Rest:
+        NAME = 'vns-interface-access-list'
+        FIELD_INFO = (
+            {'name': 'vns_acl',       'rest_name': 'vns-access-list'},
+            {'name': 'vns_interface', 'rest_name': 'vns-interface'},
+            {'name': 'in_out',        'rest_name': 'in-out'},
+            )
+
+#
+# ------------------------------------------------------------
+# A virtual router interface separation
+
+class VirtualRouterInterface (models.Model):
+
+    id_max_length = 64
+    #
+    # fields ----------------------------------------
+    virtual_router = models.ForeignKey(
+        VirtualRouter,
+        verbose_name='Virtual Router ID')
+    #
+    # Unique name of the interface
+    #
+    vriname = models.CharField(
+        verbose_name = 'Interface Name',
+        help_text    = 'A unique name for a virtual router interface',
+        validators   = [ GeneralNameValidator() ],
+        max_length   = id_max_length)
+    #
+    # Origin of this configured item
+    # 
+    origin = models.CharField(
+        verbose_name = "Origin",
+        help_text    = "Values: cli, rest, other packages",
+        max_length   = 64, # in future we might use SW GUIDs for this field
+        blank        = True,
+        null         = True)
+    #
+    # Whether the configuration is active ? By default, it is active
+    # Used to disable the configuration without having to delete the entire
+    # interface configuration construct.
+    #
+    active = models.BooleanField(
+        verbose_name = 'Active',
+        help_text    = 'Is this interface active (default is True)',
+        default      = True)
+    vns_connected = models.ForeignKey(
+        VNS,
+        verbose_name='VNS connected to',
+        blank       =True,
+        null        =True)
+    router_connected = models.ForeignKey(
+        VirtualRouter,
+        related_name='router_connected',
+        verbose_name='Virtual Router connected to',
+        blank       =True,
+        null        =True)
+    
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__ (self):
+        return self.id
+    
+    def validate_unique(self, exclude = None):
+        def is_set(value):
+            if value != None and value != '':
+                return True
+
+        # for vns connection, verify that only the VNSs under the same tenant can be connected
+        error=False
+        if not 'vns_connected' in exclude:
+            if is_set(self.vns_connected):
+                tenant_vns_parts = str(self.vns_connected).split('|')
+                tenant_router_parts = str(self.virtual_router).split('|')
+                if tenant_vns_parts[0] != tenant_router_parts[0]:
+                    raise ValidationError(" VNS %s belongs to tenant %s, doesn't match virtual router tenant %s" %
+                                      (tenant_vns_parts[1],tenant_vns_parts[0], tenant_router_parts[0]))
+                    # verify there can only be one connection for one VNS
+                try:
+                    exists = VirtualRouterInterface.objects.get(virtual_router = self.virtual_router, vns_connected=self.vns_connected)
+                    if exists:
+                        if exists.vriname!=self.vriname:
+                            error=True
+                except:
+                    pass
+                if error:
+                    raise ValidationError(" VNS %s has been connected, multiple connections is not allowed" % self.vns_connected)
+        error = False    
+        # for router connection, verify that the same virtual router as myself can't be connected        
+        if not 'router_connected' in exclude:
+            if is_set(self.router_connected):
+                tenant_router_parts = str(self.router_connected).split('|')
+                tenant_myrouter_parts = str(self.virtual_router).split('|')
+                if tenant_router_parts[0] == tenant_myrouter_parts[0]:
+                    raise ValidationError(" Local loop conncetion is not allowed.")
+            # verify there can only be one connection for one virtual router
+                try:
+                    exists = VirtualRouterInterface.objects.get(virtual_router = self.virtual_router,router_connected=self.router_connected)
+                    if exists:
+                        if exists.vriname!=self.vriname:
+                            error=True
+                except:
+                    pass
+                if error:
+                    raise ValidationError(" Virtual Router %s has been connected, multiple connections is not allowed" % self.router_connected)
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('virtual_router', 'vriname')
+
+    class Rest:
+        NAME = 'virtualrouter-interface'
+        FIELD_INFO = (
+                      {'name': 'vns_connected',           'rest_name': 'vns-connected'},
+                      {'name': 'router_connected',        'rest_name': 'router-connected'},
+                      {'name': 'virtual_router',          'rest_name': 'virtual-router'},
+                      
+            )
+
+#
+# ------------------------------------------------------------
+# A virtual router interface address pool separation
+
+class VRInterfaceIpAddressPool (models.Model):
+
+    id_max_length = 64
+    #
+    # fields ----------------------------------------
+    virtual_router_interface = models.ForeignKey(
+        VirtualRouterInterface,
+        verbose_name='Virtual Router Interface ID')
+    #
+    # Origin of this configured item
+    # 
+    origin = models.CharField(
+        verbose_name = "Origin",
+        help_text    = "Values: cli, rest, other packages",
+        max_length   = 64,
+        blank        = True,
+        null         = True)
+    ip_address = models.CharField(
+        verbose_name='Source IP',
+        help_text='Interface IP Address',
+        validators=[ IpValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+    subnet_mask = models.CharField(
+        verbose_name='Subnet IP Mask',
+        validators=[ IpMaskValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+    
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__ (self):
+        return self.id
+    
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('virtual_router_interface', 'ip_address')
+
+    class Rest:
+        NAME = 'interface-address-pool'
+        FIELD_INFO = (
+                      {'name': 'virtual_router_interface',           'rest_name': 'virtual-router-interface'},
+                      {'name': 'ip_address',                         'rest_name': 'ip-address'},
+                      {'name': 'subnet_mask',                        'rest_name': 'subnet-mask'}, 
+            )
+
+
+#
+# ------------------------------------------------------------
+
+class VirtualRouterGWPool (models.Model):
+
+    id_max_length = 64
+    #
+    # fields ----------------------------------------
+    virtual_router = models.ForeignKey(
+        VirtualRouter,
+        verbose_name='Virtual Router ID')
+    #
+    # Unique name of the gateway pool
+    #
+    vrgwname = models.CharField(
+        verbose_name = 'Gateway Pool Name',
+        help_text    = 'A unique name for a virtual router gateway pool',
+        validators   = [ GeneralNameValidator() ],
+        max_length   = id_max_length)
+    
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__ (self):
+        return self.id
+    
+    def validate_unique(self, exclude = None):
+        def is_set(value):
+            if value != None and value != '':
+                return True
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('virtual_router', 'vrgwname')
+
+    class Rest:
+        NAME = 'virtualrouter-gwpool'
+        FIELD_INFO = (
+                      {'name': 'virtual_router',          'rest_name': 'virtual-router'},
+            )
+
+#
+# ------------------------------------------------------------
+# A virtual router gateway address pool separation
+
+class VRGatewayIpAddressPool (models.Model):
+
+    id_max_length = 64
+    #
+    # fields ----------------------------------------
+    virtual_router_gwpool = models.ForeignKey(
+        VirtualRouterGWPool,
+        verbose_name='Virtual Router Gateway Pool ID')
+    ip_address = models.CharField(
+        verbose_name='Gateway IP',
+        help_text='Gateway IP Address',
+        validators=[ IpValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+    
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__ (self):
+        return self.id
+    
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('virtual_router_gwpool', 'ip_address')
+
+    class Rest:
+        NAME = 'gateway-address-pool'
+        FIELD_INFO = (
+                      {'name': 'virtual_router_gwpool', 'rest_name': 'virtual-router-gwpool'},
+                      {'name': 'ip_address', 'rest_name': 'ip-address'},
+            )
+
+class VirtualRoutingRule (models.Model):
+
+    id_max_length = 64
+    #
+    # fields ----------------------------------------
+    virtual_router = models.ForeignKey(
+        VirtualRouter,
+        verbose_name='Virtual Router ID')
+    #
+    # Origin of this configured item
+    # 
+    origin = models.CharField(
+        verbose_name = "Origin",
+        help_text    = "Values: cli, rest, other packages",
+        max_length   = 64,
+        blank        = True,
+        null         = True)
+    src_host = models.ForeignKey(
+        HostConfig,
+        verbose_name='source Host ID',
+        help_text='Source Host ID to match',
+        blank       =True,
+        null        =True)
+    src_tenant = models.ForeignKey(
+        Tenant,
+        verbose_name='source tenant ID',
+        help_text='Source tenant ID to match',
+        blank       =True,
+        null        =True)
+    src_vns = models.ForeignKey(
+        VNS,
+        verbose_name='source VNS ID',
+        help_text='Source VNS ID to match',
+        blank       =True,
+        null        =True)
+    src_ip = models.CharField(
+        verbose_name='Source IP',
+        help_text='Source IP Address to match',
+        validators=[ IpValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+    src_ip_mask = models.CharField(
+        verbose_name='Source IP Mask',
+        help_text='Mask to match source IP',
+        validators=[ IpMaskValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+    dst_host = models.ForeignKey(
+        HostConfig,
+        verbose_name='Destination Host ID',
+        help_text='Destination Host ID to match',
+        related_name='dest_host',
+        blank       =True,
+        null        =True)
+    dst_tenant = models.ForeignKey(
+        Tenant,
+        verbose_name='Destination tenant ID',
+        related_name='dest_tenant',
+        blank       =True,
+        null        =True)
+    dst_vns = models.ForeignKey(
+        VNS,
+        verbose_name='Destination VNS ID',
+        related_name='dest_vns',
+        blank       =True,
+        null        =True)
+    dst_ip = models.CharField(
+        verbose_name='Destination IP',
+        help_text='Destination IP Address to match',
+        validators=[ IpValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+    dst_ip_mask = models.CharField(
+        verbose_name='Destination IP Mask',
+        help_text='Mask to match destination IP',
+        validators=[ IpMaskValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+    outgoing_intf = models.ForeignKey(
+        VirtualRouterInterface,
+        verbose_name='Outgoing Interface',
+        blank       =True,
+        null        =True)
+    gateway_pool = models.ForeignKey(
+        VirtualRouterGWPool,
+        verbose_name='Gateway pool',
+        blank       =True,
+        null        =True)
+    nh_ip = models.CharField(
+        verbose_name='Next Hop IP',
+        help_text='Next Hop IP Address',
+        validators=[ IpValidator() ],
+        max_length=15,
+        blank=True,
+        null=True)
+    action = models.CharField(
+        verbose_name='permit or deny',
+        help_text="'permit' or 'deny'",
+        default='permit',
+        max_length=16,
+        validators=[ VnsAclEntryActionValidator() ])
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__ (self):
+        return self.id
+    
+    def validate_unique(self, exclude = None):
+        def is_set(value):
+            if value != None and value != '':
+                return True
+    #verify the outgoing interface can only be on the local virtual router interface
+        if not 'outgoing_intf' in exclude:
+            if is_set(self.outgoing_intf):
+                router_parts = str(self.outgoing_intf).split('|')
+                myrouter_parts = str(self.virtual_router).split('|')
+                if (router_parts[0] != myrouter_parts[0]) or (router_parts[1] != myrouter_parts[1]):
+                    raise ValidationError(" outgoing interface has to be local to virtual router: %s|%s" %
+                                  (myrouter_parts[0],myrouter_parts[1]))
+        #verify the gateway pool belongs to the local virtual router
+        if not 'gateway_pool' in exclude:
+            if is_set(self.gateway_pool):
+                router_parts = str(self.gateway_pool).split('|')
+                myrouter_parts = str(self.virtual_router).split('|')
+                if (router_parts[0] != myrouter_parts[0]) or (router_parts[1] != myrouter_parts[1]):
+                    raise ValidationError(" gateway pool has to be local to virtual router: %s|%s" %
+                                  (myrouter_parts[0],myrouter_parts[1]))
+
+    class CassandraSettings:
+        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')
+
+    class Rest:
+        NAME = 'virtualrouter-routingrule'
+        FIELD_INFO = (
+                      {'name': 'virtual_router',           'rest_name': 'virtual-router'},
+                      {'name': 'src_tenant',               'rest_name': 'src-tenant'},
+                      {'name': 'dst_tenant',               'rest_name': 'dst-tenant'},
+                      {'name': 'src_vns',                  'rest_name': 'src-vns'},
+                      {'name': 'src_ip',                   'rest_name': 'src-ip'},
+                      {'name': 'src_ip_mask',              'rest_name': 'src-ip-mask'},
+                      {'name': 'dst_ip',                   'rest_name': 'dst-ip'},
+                      {'name': 'dst_ip_mask',              'rest_name': 'dst-ip-mask'},
+                      {'name': 'nh_ip',                    'rest_name': 'nh-ip'},
+                      {'name': 'outgoing_intf',            'rest_name': 'outgoing-intf'},
+                      {'name': 'dst_host',                 'rest_name': 'dst-host'},
+                      {'name': 'src_host',                 'rest_name': 'src-host'},   
+                      {'name': 'dst_vns',                  'rest_name': 'dst-vns'},
+                      {'name': 'gateway_pool',             'rest_name': 'gateway-pool'},
+            )
+
+#
+# ------------------------------------------------------------
+
+class Tag(models.Model):
+    namespace_length = 64
+    name_length = 64
+    value_length = 64
+
+    #
+    # fields ----------------------------------------
+
+    namespace = models.CharField(
+        verbose_name='Tag Namespace',
+        help_text="Namespace of the tag",
+        max_length=namespace_length)
+
+    name = models.CharField(
+        verbose_name='Tag Name',
+        help_text="Name of the tag",
+        max_length=name_length)
+
+    value = models.CharField(
+        verbose_name='Tag Value',
+        help_text="Value of the tag",
+        max_length=value_length)
+
+    persist = models.BooleanField(
+        verbose_name='persist',
+        help_text='For any cli configured tag, include in running-config',
+        default=True)
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.id
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('namespace', 'name', 'value')
+
+    class Rest:
+        NAME = 'tag'
+
+#
+# ------------------------------------------------------------
+
+class TagMapping(models.Model):
+    host_id_length = 17
+    switch_id_length = 23
+    if_name_len = 32
+    vlan_str_len = 4
+    #
+    # fields ----------------------------------------
+
+    tag = models.ForeignKey(
+            Tag)
+
+    mac = models.CharField(
+        verbose_name="MAC Address",
+        max_length=host_id_length, 
+        validators = [validate_mac_address],
+        default="",
+        blank=True)
+
+    vlan = models.CharField(
+        verbose_name='VLAN',
+        max_length=vlan_str_len, 
+        help_text='VLAN Number, in the range of 1-4095. 4095 means untagged',
+        validators=[VlanStringValidator()],
+        default="",
+        blank=True)
+    
+    dpid = models.CharField(
+        verbose_name='Switch DPID',
+        help_text='Switch DPID - 64-bit hex separated by :',
+        max_length=switch_id_length, 
+        validators=[ validate_dpid ],
+        default="",
+        blank=True)
+
+    ifname = models.CharField(
+        verbose_name='If Name regular expression',
+        max_length=if_name_len,
+        default="",
+        validators=[ SafeForPrimaryKeyValidator() ],
+        blank=True)
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.id
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('tag', 'mac', 'vlan', 'dpid', 'ifname')
+
+    def validate_unique(self, exclude = None):
+        if self.mac != '':
+            self.mac = self.mac.lower()
+
+        if self.dpid != '':
+            self.dpid = self.dpid.lower()
+
+        # don't allow all default values for the association
+        if self.mac == '' and self.vlan == '' and \
+            self.dpid == '' and self.ifname == '':
+           raise ValidationError("Match without any matching fields")
+
+    class Rest:
+        NAME = 'tag-mapping'
+
+#
+# ------------------------------------------------------------
+
+class TechSupportConf(models.Model):
+
+    #
+    # fields ----------------------------------------
+
+    cmd_type = models.CharField(
+        verbose_name='Type of command',
+        help_text='Enter cli or shell',
+        max_length=32)
+
+    cmd = models.CharField(
+        max_length=256,
+        verbose_name='Command name',
+        help_text = 'Command excuted by show tech-support')
+
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.id
+
+    class CassandraSettings:
+        COMPOUND_KEY_FIELDS = ('cmd_type', 'cmd')
+
+    class Rest:
+        NAME = 'tech-support-config'
+        FIELD_INFO = (
+            {'name': 'cmd_type',  'rest_name': 'cmd-type'},
+            )
+
+#
+# ------------------------------------------------------------
+
+class TacacsPlusConfig(models.Model):
+    #
+    # fields ----------------------------------------
+
+    id = models.CharField(
+        primary_key=True,
+        verbose_name='tacacs singleton',
+        default='tacacs',
+        max_length=16)
+
+    tacacs_plus_authn = models.BooleanField(
+        verbose_name='TACACS+ Authentication Enabled',
+        help_text='Enable TACACS+ authentication by setting to true',
+        default=False
+        )
+
+    tacacs_plus_authz = models.BooleanField(
+        verbose_name='TACACS+ Authorization Enabled',
+        help_text='Enable TACACS+ authorization by setting to true',
+        default=False
+        )
+
+    tacacs_plus_acct = models.BooleanField(
+        verbose_name='TACACS+ Accounting Enabled',
+        help_text='Enable TACACS+ accounting by setting to true',
+        default=False
+        )
+
+    local_authn = models.BooleanField(
+        verbose_name='Local Authentication Enabled',
+        help_text='Enable local authentication by setting to true',
+        default=True
+        )
+
+    local_authz = models.BooleanField(
+        verbose_name='Local Authorization Enabled',
+        help_text='Enable local authorization by setting to true',
+        default=True
+        )
+
+    timeout = models.IntegerField(
+        verbose_name="TACACS+ Server Timeout",
+        help_text='Set TACACS+ server timeout in seconds',
+        default=0,
+        validators=[ RangeValidator(0, 2**16-1) ])
+    
+    key = models.CharField(
+        verbose_name='TACACS+ Pre-shared Key',
+        help_text='pre-shared key to connect to TACACS+ server(s)',
+        max_length=256,
+        blank=True,
+        default="")    
+
+    #
+    # end fields ----------------------------------------
+
+    class Rest:
+        NAME = 'tacacs-plus-config'
+        FIELD_INFO = (
+            {'name': 'tacacs_plus_authn', 'rest_name': 'tacacs-plus-authn'},
+            {'name': 'tacacs_plus_authz', 'rest_name': 'tacacs-plus-authz'},
+            {'name': 'tacacs_plus_acct',  'rest_name': 'tacacs-plus-acct'},
+            {'name': 'local_authn',       'rest_name': 'local-authn'},
+            {'name': 'local_authz',       'rest_name': 'local-authz'},
+            )
+
+#
+# ------------------------------------------------------------
+
+class TacacsPlusHost(models.Model):
+    #
+    # fields ----------------------------------------
+
+    ip = models.CharField(
+        primary_key=True,
+        verbose_name='IP Address',
+        help_text='IP Address for TACACS+ server',
+        validators=[ IpValidator() ],
+        max_length=15)
+    
+    timestamp = models.PositiveIntegerField(
+        verbose_name='timestamp',
+        help_text='Timestamp to order the tacacs servers',
+        default = get_timestamp,
+        )
+    
+    key = models.CharField(
+        verbose_name='TACACS+ Per-host Pre-shared Key',
+        help_text='pre-shared key to connect to this TACACS+ server',
+        max_length=256,
+        blank=True,
+        default="")    
+
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return "%s" % self.ip
+
+    def validate_unique(self, exclude = None):
+        try:
+            exists = TacacsPlusHost.objects.get(ip = self.ip)
+
+            if exists.timestamp != self.timestamp:
+                self.timestamp = exists.timestamp
+        except:
+            pass
+
+    class Rest:
+        NAME = 'tacacs-plus-host'
+        FIELD_INFO = (
+            )
+
+#
+#---------------------------------------------------------
+
+class SnmpServerConfig(models.Model):
+    #
+    # fields ----------------------------------------
+
+    # we just have one row entry in the table to update, so static primary key
+    id = models.CharField(
+        primary_key=True,
+        verbose_name='snmp',
+        # default='snmp',
+        max_length=16)
+
+    #
+    # Enable state of the SNMP server/agent on the controller
+    #
+    server_enable = models.BooleanField(
+        verbose_name='SNMP Server enabled',
+        help_text='Enable SNMP server by setting to true',
+        default=False
+        )
+    #
+    # Community string for accessing the SNMP server on the controller
+    #
+    community = models.CharField(
+        verbose_name = 'Community String',
+        help_text    = "Community String to access SNMP data",
+        max_length   = 128,
+        null         = True,
+        blank        = True,
+        )
+    #
+    # Location string of the SNMP server on the controller
+    #
+    location = models.CharField(
+        verbose_name = 'System Location',
+        help_text    = "Location information for the controller appliance",
+        max_length   = 128,
+        null         = True,
+        blank        = True
+        )
+    #
+    # Contact string of the SNMP server on the controller
+    #
+    contact = models.CharField(
+        verbose_name = 'System Contact',
+        help_text    = "Contact information for the controller appliance",
+        max_length   = 128,
+        null         = True,
+        blank        = True,
+        )
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.id
+
+    def validate_unique(self, exclude = None):
+        if self.id != 'snmp':
+            raise ValidationError("Only single snmp record exists")
+
+    class Rest:
+        NAME = 'snmp-server-config'
+        FIELD_INFO = (
+            {'name': 'server_enable',       'rest_name': 'server-enable'},
+            )
+
+#
+# ------------------------------------------------------------
+
+class ImageDropUser(models.Model):
+    #
+    # fields ----------------------------------------
+
+    # we just have one row entry in the table to update, so static primary key
+    id = models.CharField(
+        primary_key=True,
+        verbose_name='imagedropuser',
+        default='imagedropuser',
+        max_length=16)
+
+    images_user_ssh_key = models.CharField(
+        verbose_name='Image drop user SSH public key',
+        help_text='The SSH public RSA key for the images user',
+        default='',
+        max_length=600
+        )
+    #
+    # end fields ----------------------------------------
+
+    def __unicode__(self):
+        return self.id
+ 
+    def validate_unique(self, exclude = None):
+        if self.id != 'imagedropuser':
+            raise ValidationError("Only single ImageDropUser record exists")
+
+    class Rest:
+        NAME = 'image-drop-user'
+        FIELD_INFO = (
+            {'name': 'images_user_ssh_key', 'rest_name': 'images-user-ssh-key'},
+            )
+
+# robv: Commenting out for now. Doesn't work with Cassandra backend
+#class ConsoleUser(User):
+#
+#    class Meta:
+#        proxy = True
+#
+#    class Rest:
+#        NAME = 'user'