the big switch from subscriber settings in vcpe to CordSubscriberRoot
diff --git a/xos/cord/models.py b/xos/cord/models.py
index 3dc4a67..2f412c4 100644
--- a/xos/cord/models.py
+++ b/xos/cord/models.py
@@ -71,7 +71,8 @@
                           "url_filter_rules": "allow all",
                           "url_filter_level": "PG",
                           "cdn_enable": False,
-                          "users": [] }
+                          "users": [],
+                          "is_demo_user": False }
 
     sync_attributes = ("firewall_enable",
                        "firewall_rules",
@@ -92,7 +93,7 @@
 
         # always return the same object when possible
         if (self.cached_volt) and (self.cached_volt.id == volt.id):
-            return self.cached_vcpe
+            return self.cached_volt
 
         #volt.caller = self.creator
         self.cached_volt = volt
@@ -224,6 +225,14 @@
             # 2) trigger vcpe observer to wake up
             self.volt.vcpe.save()
 
+    @property
+    def is_demo_user(self):
+        return self.get_attribute("is_demo_user", self.default_attributes["is_demo_user"])
+
+    @is_demo_user.setter
+    def is_demo_user(self, value):
+        self.set_attribute("is_demo_user", value)
+
 # -------------------------------------------
 # VOLT
 # -------------------------------------------
@@ -242,9 +251,7 @@
 
     KIND = VOLT_KIND
 
-    default_attributes = {"vlan_id": None,
-                          "is_demo_user": False }
-
+    default_attributes = {"vlan_id": None, }
     def __init__(self, *args, **kwargs):
         volt_services = VOLTService.get_service_objects().all()
         if volt_services:
@@ -309,14 +316,6 @@
             self.cached_creator=None
         self.set_attribute("creator_id", value)
 
-    @property
-    def is_demo_user(self):
-        return self.get_attribute("is_demo_user", self.default_attributes["is_demo_user"])
-
-    @is_demo_user.setter
-    def is_demo_user(self, value):
-        self.set_attribute("is_demo_user", value)
-
     def manage_vcpe(self):
         # Each VOLT object owns exactly one VCPE object
 
@@ -422,25 +421,14 @@
 
     KIND = VCPE_KIND
 
-    sync_attributes = ("firewall_enable",
-                       "firewall_rules",
-                       "url_filter_enable",
-                       "url_filter_rules",
-                       "cdn_enable",
-                       "nat_ip",
+    sync_attributes = ("nat_ip",
                        "lan_ip",
                        "wan_ip",
                        "private_ip",
                        "hpc_client_ip",
                        "wan_mac")
 
-    default_attributes = {"firewall_enable": False,
-                          "firewall_rules": "accept all anywhere anywhere",
-                          "url_filter_enable": False,
-                          "url_filter_rules": "allow all",
-                          "url_filter_level": "PG",
-                          "cdn_enable": False,
-                          "sliver_id": None,
+    default_attributes = {"sliver_id": None,
                           "users": [],
                           "bbs_account": None,
                           "last_ansible_hash": None}
@@ -536,137 +524,6 @@
             return None
         return volts[0]
 
-    # *** to be moved to CordSubscriberRoot
-
-    # TODO:
-    #    1) delete the stuff in this section
-    #    2) remove the associated fields from sync_attributes and default_attributes
-    #    3) in core/xoslib/methods/cordsubscriber.py, rename CordSubscriberNew to CordSubscriber
-    #    4) in observers/vcpe/steps/sync_vcpetenant, remove the stuff marked 'legacy'
-    #    5) update manage_bbs_account
-    #    6) come up with a way to auto-create Subscriber from vOLT, for backward compatibility
-
-    @property
-    def firewall_enable(self):
-        return self.get_attribute("firewall_enable", self.default_attributes["firewall_enable"])
-
-    @firewall_enable.setter
-    def firewall_enable(self, value):
-        self.set_attribute("firewall_enable", value)
-
-    @property
-    def firewall_rules(self):
-        return self.get_attribute("firewall_rules", self.default_attributes["firewall_rules"])
-
-    @firewall_rules.setter
-    def firewall_rules(self, value):
-        self.set_attribute("firewall_rules", value)
-
-    @property
-    def url_filter_enable(self):
-        return self.get_attribute("url_filter_enable", self.default_attributes["url_filter_enable"])
-
-    @url_filter_enable.setter
-    def url_filter_enable(self, value):
-        self.set_attribute("url_filter_enable", value)
-
-    @property
-    def url_filter_level(self):
-        return self.get_attribute("url_filter_level", self.default_attributes["url_filter_level"])
-
-    @url_filter_level.setter
-    def url_filter_level(self, value):
-        self.set_attribute("url_filter_level", value)
-
-    @property
-    def url_filter_rules(self):
-        return self.get_attribute("url_filter_rules", self.default_attributes["url_filter_rules"])
-
-    @url_filter_rules.setter
-    def url_filter_rules(self, value):
-        self.set_attribute("url_filter_rules", value)
-
-    @property
-    def cdn_enable(self):
-        return self.get_attribute("cdn_enable", self.default_attributes["cdn_enable"])
-
-    @cdn_enable.setter
-    def cdn_enable(self, value):
-        self.set_attribute("cdn_enable", value)
-
-    @property
-    def users(self):
-        return self.get_attribute("users", self.default_attributes["users"])
-
-    @users.setter
-    def users(self, value):
-        self.set_attribute("users", value)
-
-    def find_user(self, uid):
-        uid = int(uid)
-        for user in self.users:
-            if user["id"] == uid:
-                return user
-        return None
-
-    def update_user(self, uid, **kwargs):
-        # kwargs may be "level" or "mac"
-        #    Setting one of these to None will cause None to be stored in the db
-        uid = int(uid)
-        users = self.users
-        for user in users:
-            if user["id"] == uid:
-                for arg in kwargs.keys():
-                    user[arg] = kwargs[arg]
-                    self.users = users
-                return user
-        raise ValueError("User %d not found" % uid)
-
-    def create_user(self, **kwargs):
-        if "name" not in kwargs:
-            raise XOSMissingField("The name field is required")
-
-        for user in self.users:
-            if kwargs["name"] == user["name"]:
-                raise XOSDuplicateKey("User %s already exists" % kwargs["name"])
-
-        uids = [x["id"] for x in self.users]
-        if uids:
-            uid = max(uids)+1
-        else:
-            uid = 0
-        newuser = kwargs.copy()
-        newuser["id"] = uid
-
-        users = self.users
-        users.append(newuser)
-        self.users = users
-
-        return newuser
-
-    def delete_user(self, uid):
-        uid = int(uid)
-        users = self.users
-        for user in users:
-            if user["id"]==uid:
-                users.remove(user)
-                self.users = users
-                return
-
-        raise ValueError("Users %d not found" % uid)
-
-    @property
-    def services(self):
-        return {"cdn": self.cdn_enable,
-                "url_filter": self.url_filter_enable,
-                "firewall": self.firewall_enable}
-
-    @services.setter
-    def services(self, value):
-        pass
-
-    # *** end of stuff to be moved to CordSubscriberRoot
-
     @property
     def bbs_account(self):
         return self.get_attribute("bbs_account", self.default_attributes["bbs_account"])
@@ -839,7 +696,7 @@
         if self.deleted:
             return
 
-        if self.url_filter_enable:
+        if self.volt and self.volt.subscriber and self.volt.subscriber.url_filter_enable:
             if not self.bbs_account:
                 # make sure we use the proxied VCPEService object, not the generic Service object
                 vcpe_service = VCPEService.objects.get(id=self.provider_service.id)
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index eae1dcc..320922a 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -162,6 +162,12 @@
     service_specific_attribute = models.TextField(blank=True, null=True)
     service_specific_id = StrippedCharField(max_length=30, blank=True, null=True)
 
+    def __init__(self, *args, **kwargs):
+        # for subclasses, set the default kind appropriately
+        self._meta.get_field("kind").default = self.KIND
+        super(TenantRoot, self).__init__(*args, **kwargs)
+
+
     # helper for extracting things from a json-encoded attribute
     def get_attribute(self, name, default=None):
         if self.service_specific_attribute:
diff --git a/xos/core/xoslib/methods/cordsubscriber.py b/xos/core/xoslib/methods/cordsubscriber.py
index 6b993e1..a825824 100644
--- a/xos/core/xoslib/methods/cordsubscriber.py
+++ b/xos/core/xoslib/methods/cordsubscriber.py
@@ -9,7 +9,7 @@
 from core.models import *
 from django.forms import widgets
 from django.conf.urls import patterns, url
-from cord.models import VOLTTenant, VBNGTenant
+from cord.models import VOLTTenant, VBNGTenant, CordSubscriberRoot
 from core.xoslib.objects.cordsubscriber import CordSubscriber
 from plus import PlusSerializerMixin
 from django.shortcuts import get_object_or_404
@@ -116,7 +116,7 @@
     def post(self, request, format=None):
         data = request.DATA
         subscriber = CordSubscriber.get_tenant_objects().get(id=int(data["subscriber"]))
-        user = subscriber.vcpe.create_user(name=data["name"],
+        user = subscriber.create_user(name=data["name"],
                                     level=data["level"],
                                     mac=data["mac"])
         subscriber.save()
@@ -137,7 +137,7 @@
     def delete(self, request, pk):
         parts = pk.split("-")
         subscriber = CordSubscriber.get_tenant_objects().get(id=int(parts[0]))
-        subscriber.vcpe.delete_user(parts[1])
+        subscriber.delete_user(parts[1])
         subscriber.save()
         return Response("okay")
 
@@ -152,7 +152,7 @@
 
         parts = pk.split("-")
         subscriber = CordSubscriber.get_tenant_objects().get(id=int(parts[0]))
-        user = subscriber.vcpe.update_user(parts[1], **kwargs)
+        user = subscriber.update_user(parts[1], **kwargs)
         subscriber.save()
         return Response(serialize_user(subscriber,user))
 
@@ -253,8 +253,8 @@
         return Response({"users": subscriber.users})
 
     def get_user_level(self, request, pk=None, uid=None):
-        vcpe = self.get_vcpe()
-        user = vcpe.find_user(uid)
+        subscriber = self.get_object()
+        user = subscriber.find_user(uid)
         if user and user.get("level", None):
             level = user["level"]
         else:
@@ -263,14 +263,12 @@
         return Response( {"id": uid, "level": level} )
 
     def set_user_level(self, request, pk=None, uid=None, level=None):
-        vcpe = self.get_vcpe()
-        vcpe.update_user(uid, level=level)
-        vcpe.save()
+        subscriber = self.get_object()
+        subscriber.update_user(uid, level=level)
+        subscriber.save()
         return self.get_user_level(request, pk, uid)
 
     def create_user(self, request, pk=None):
-        vcpe = self.get_vcpe()
-
         data = request.DATA
         name = data.get("name",None)
         mac = data.get("mac",None)
@@ -279,23 +277,23 @@
         if (not mac):
              raise XOSMissingField("mac must be specified when creating user")
 
-        newuser = vcpe.create_user(name=name, mac=mac)
-        vcpe.save()
+        subscriber = self.get_object()
+        newuser = subscriber.create_user(name=name, mac=mac)
+        subscriber.save()
 
         return Response(newuser)
 
     def delete_user(self, request, pk=None, uid=None):
-        vcpe = self.get_vcpe()
-
-        vcpe.delete_user(uid)
-        vcpe.save()
+        subscriber = self.get_object()
+        subscriber.delete_user(uid)
+        subscriber.save()
 
         return Response( {"id": uid, "deleted": True} )
 
     def clear_users(self, request, pk=None):
-        vcpe = self.get_vcpe()
-        vcpe.users = []
-        vcpe.save()
+        subscriber = self.get_object()
+        subscriber.users = []
+        subscriber.save()
 
         return Response( "Okay" )
 
@@ -324,61 +322,58 @@
 
     def get_bbsdump(self, request, pk=None):
         subscriber = self.get_object()
-        if not subsciber.vcpe:
+        if not subsciber.volt or not subscriber.volt.vcpe:
             raise XOSMissingField("subscriber has no vCPE")
-        if not subscriber.vcpe.bbs_account:
+        if not subscriber.volt.vcpe.bbs_account:
             raise XOSMissingField("subscriber has no bbs_account")
 
-        result=subprocess.check_output(["python", "/opt/xos/observers/vcpe/broadbandshield.py", "dump", subscriber.vcpe.\
-bbs_account, "123"])

+        result=subprocess.check_output(["python", "/opt/xos/observers/vcpe/broadbandshield.py", "dump", subscriber.volt.vcpe.bbs_account, "123"])
         if request.GET.get("theformat",None)=="text":

             from django.http import HttpResponse

             return HttpResponse(result, content_type="text/plain")

         else:

             return Response( {"bbs_dump": result } )
 
-    def setup_demo_vcpe(self, voltTenant):
+    def setup_demo_subscriber(self, subscriber):
         # nuke the users and start over
-        voltTenant.vcpe.users = []
-        voltTenant.vcpe.create_user(name="Mom's PC",      mac="010203040506", level="PG_13")
-        voltTenant.vcpe.create_user(name="Dad's PC",      mac="90E2Ba82F975", level="PG_13")
-        voltTenant.vcpe.create_user(name="Jack's Laptop", mac="685B359D91D5", level="PG_13")
-        voltTenant.vcpe.create_user(name="Jill's Laptop", mac="34363BC9B6A6", level="PG_13")
-        voltTenant.vcpe.save()
+        subscriber.users = []
+        subscriber.create_user(name="Mom's PC",      mac="010203040506", level="PG_13")
+        subscriber.create_user(name="Dad's PC",      mac="90E2Ba82F975", level="PG_13")
+        subscriber.create_user(name="Jack's Laptop", mac="685B359D91D5", level="PG_13")
+        subscriber.create_user(name="Jill's Laptop", mac="34363BC9B6A6", level="PG_13")
+        subscriber.save()
 
     def initdemo(self, request):
-        object_list = VOLTTenant.get_tenant_objects().all()
+        object_list = CordSubscriber.get_tenant_objects().all()
 
         # reset the parental controls in any existing demo vCPEs
         for o in object_list:
             if str(o.service_specific_id) in ["0", "1"]:
-                if o.vcpe is not None:
-                    self.setup_demo_vcpe(o)
+                self.setup_demo_subscriber(o)
 
         demo_subscribers = [o for o in object_list if o.is_demo_user]
 
         if demo_subscribers:
             return Response({"id": demo_subscribers[0].id})
 
-        voltTenant = VOLTTenant(service_specific_id=1234,
-                                vlan_id=1234,
-                                is_demo_user=True)
-        voltTenant.caller = User.objects.get(email="padmin@vicci.org")
-        voltTenant.save()
+        subscriber = CordSubscriberRoot(service_specific_id=1234,
+                                        name="demo-subscriber",)
+        subscriber.is_demo_user = True
+        subscriber.save()
 
-        self.setup_demo_vcpe(voltTenant)
+        self.setup_demo_subscriber(subscriber)
 
-        return Response({"id": voltTenant.id})
+        return Response({"id": subscriber.id})
 
     def ssidlist(self, request):
-        object_list = VOLTTenant.get_tenant_objects().all()
+        object_list = CordSubscriber.get_tenant_objects().all()
 
         ssidmap = [ {"service_specific_id": x.service_specific_id, "subscriber_id": x.id} for x in object_list ]
 
         return Response({"ssidmap": ssidmap})
 
     def ssiddetail(self, pk=None, ssid=None):
-        object_list = VOLTTenant.get_tenant_objects().all()
+        object_list = CordSubscriber.get_tenant_objects().all()
 
         ssidmap = [ {"service_specific_id": x.service_specific_id, "subscriber_id": x.id} for x in object_list if str(x.service_specific_id)==str(ssid) ]
 
diff --git a/xos/core/xoslib/objects/cordsubscriber.py b/xos/core/xoslib/objects/cordsubscriber.py
index 144fa63..63dfec2 100644
--- a/xos/core/xoslib/objects/cordsubscriber.py
+++ b/xos/core/xoslib/objects/cordsubscriber.py
@@ -18,7 +18,7 @@
 c=CordSubscriber.get_tenant_objects().select_related().all()[0]
 """
 
-class CordSubscriber(VOLTTenant, PlusObjectMixin):
+class CordSubscriberOld(VOLTTenant, PlusObjectMixin):
     class Meta:
         proxy = True
 
@@ -93,7 +93,7 @@
                 print "attr", self.vcpe.vbng.service_specific_attribute
                 self.vcpe.vbng.save()
 
-class CordSubscriberNew(CordSubscriberRoot):
+class CordSubscriber(CordSubscriberRoot):
     class Meta:
         proxy = True
 
@@ -109,11 +109,13 @@
                      # ("url_filter_enable", "vcpe.url_filter_enable"),
                      # ("url_filter_rules", "vcpe.url_filter_rules"),
                      # ("url_filter_level", "vcpe.url_filter_level"),
-                     # ("bbs_account", "vcpe.bbs_account"),
                      # ("users", "vcpe.users"),
                      # ("services", "vcpe.services"),
                      # ("cdn_enable", "vcpe.cdn_enable"),
 
+                     ("vlan_id", "volt.vlan_id"),
+
+                     ("bbs_account", "volt.vcpe.bbs_account"),
                      ("ssh_command", "volt.vcpe.ssh_command"),
                      ("image", "volt.vcpe.image.id"),
                      ("image_name", "volt.vcpe.image.name"),
@@ -131,6 +133,7 @@
                      )
 
     def __getattr__(self, key):
+        #print "XXX getattr", self, key
         for (member_name, passthrough_name) in self.passthroughs:
             if key==member_name:
                 parts = passthrough_name.split(".")
@@ -165,13 +168,13 @@
         if (self.volt):
             print "save volt"
             self.volt.save()
-            if (self.vcpe):
+            if (self.volt.vcpe):
                 print "save vcpe"
-                self.vcpe.save()
-                if (self.vcpe.vbng):
-                    print "save vbng", self.vcpe.vbng
-                    print "attr", self.vcpe.vbng.service_specific_attribute
-                    self.vcpe.vbng.save()
+                self.volt.vcpe.save()
+                if (self.volt.vcpe.vbng):
+                    print "save vbng", self.volt.vcpe.vbng
+                    print "attr", self.volt.vcpe.vbng.service_specific_attribute
+                    self.volt.vcpe.vbng.save()
 
 
 
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.py b/xos/observers/vcpe/steps/sync_vcpetenant.py
index 97784a5..ccab3d6 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.py
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.py
@@ -130,10 +130,8 @@
             for attribute_name in o.sync_attributes:
                 fields[attribute_name] = getattr(o, attribute_name)
 
-        # legacy code, to be deleted
-        url_filter_enable = o.url_filter_enable
-        url_filter_level = o.url_filter_level
-        url_filter_users = o.users
+        # only enable filtering if we have a subscriber object (see below)
+        url_filter_enable = False
 
         # for attributes that come from CordSubscriberRoot
         if o.volt and o.volt.subscriber and hasattr(o.volt.subscriber, "sync_attributes"):