Merge branch 'master' of github.com:open-cloud/xos into feature/diagnostic
diff --git a/xos/configurations/cord/README-VTN.md b/xos/configurations/cord/README-VTN.md
index b3c0c61..a3c4e69 100644
--- a/xos/configurations/cord/README-VTN.md
+++ b/xos/configurations/cord/README-VTN.md
@@ -112,7 +112,12 @@
     ovs-vsctl del-br br-flat-lan-1
     ifconfig eth2 10.123.0.1
     iptables --table nat --append POSTROUTING --out-interface br-ex -j MASQUERADE
-    arp -s 10.123.0.3 fa:16:3e:ea:11:0a
+    #arp -s 10.123.0.3 fa:16:3e:ea:11:0a
+    sysctl net.ipv4.conf.all.send_redirects
+    sysctl net.ipv4.conf.all.send_redirects=0
+    sysctl net.ipv4.conf.default.send_redirects=0
+    sysctl net.ipv4.conf.eth0.send_redirects=0
+    sysctl net.ipv4.conf.br-ex.send_redirects=0
     
 Substitute for your installation:
 
diff --git a/xos/configurations/devel/docker-compose.yml b/xos/configurations/devel/docker-compose.yml
index 803e57c..9ef6fc7 100644
--- a/xos/configurations/devel/docker-compose.yml
+++ b/xos/configurations/devel/docker-compose.yml
@@ -15,6 +15,7 @@
         - ctl:${MYIP}
     volumes:
         - ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
+        - ./images:/opt/xos/images:ro
 
 # FUTURE
 #xos_swarm_synchronizer:
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 6ece1b3..51f9b3c 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -32,6 +32,17 @@
         return attributes.get(name, default)
 
     @classmethod
+    def get_default_attribute(cls, name):
+        for (attrname, default) in cls.simple_attributes:
+            if attrname==name:
+                return default
+        if hasattr(cls,"default_attributes"):
+            if attrname in cls.default_attributes:
+                return cls.default_attributes[attrname]
+        else:
+            return None
+
+    @classmethod
     def setup_simple_attributes(cls):
         for (attrname, default) in cls.simple_attributes:
             setattr(cls, attrname, property(lambda self, attrname=attrname, default=default: self.get_attribute(attrname, default),
diff --git a/xos/core/xoslib/methods/cordsubscriber.py b/xos/core/xoslib/methods/cordsubscriber.py
index 0615024..f0bee26 100644
--- a/xos/core/xoslib/methods/cordsubscriber.py
+++ b/xos/core/xoslib/methods/cordsubscriber.py
@@ -47,6 +47,12 @@
         ssh_command = ReadOnlyField()
         bbs_account = ReadOnlyField()
 
+        wan_container_ip = ReadOnlyField()
+        uplink_speed = serializers.CharField(required=False)
+        downlink_speed = serializers.CharField(required=False)
+        status = serializers.CharField()
+        enable_uverse = serializers.BooleanField()
+
         lan_ip = ReadOnlyField()
         wan_ip = ReadOnlyField()
         nat_ip = ReadOnlyField()
@@ -68,7 +74,9 @@
                       'bbs_account',
                       'ssh_command',
                       'vcpe_synced',
-                      'cdn_enable', 'vbng_id', 'routeable_subnet', 'nat_ip', 'lan_ip', 'wan_ip', 'private_ip', 'wan_mac')
+                      'cdn_enable', 'vbng_id', 'routeable_subnet', 'nat_ip', 'lan_ip', 'wan_ip', 'private_ip', 'wan_mac',
+                      'wan_container_ip',
+                      'uplink_speed', 'downlink_speed', 'status', 'enable_uverse')
 
 
         def getHumanReadableName(self, obj):
diff --git a/xos/core/xoslib/objects/cordsubscriber.py b/xos/core/xoslib/objects/cordsubscriber.py
index d859cd2..27596b7 100644
--- a/xos/core/xoslib/objects/cordsubscriber.py
+++ b/xos/core/xoslib/objects/cordsubscriber.py
@@ -18,81 +18,6 @@
 c=CordSubscriber.get_tenant_objects().select_related().all()[0]
 """
 
-class CordSubscriberOld(VOLTTenant, PlusObjectMixin):
-    class Meta:
-        proxy = True
-
-    def __init__(self, *args, **kwargs):
-        super(CordSubscriber, self).__init__(*args, **kwargs)
-
-    def __unicode__(self):
-        return u"cordSubscriber-%s" % str(self.id)
-
-    passthroughs = ( ("firewall_enable", "vcpe.firewall_enable"),
-                     ("firewall_rules", "vcpe.firewall_rules"),
-                     ("url_filter_enable", "vcpe.url_filter_enable"),
-                     ("url_filter_rules", "vcpe.url_filter_rules"),
-                     ("url_filter_level", "vcpe.url_filter_level"),
-                     ("ssh_command", "vcpe.ssh_command"),
-                     ("bbs_account", "vcpe.bbs_account"),
-                     ("users", "vcpe.users"),
-                     ("services", "vcpe.services"),
-                     ("cdn_enable", "vcpe.cdn_enable"),
-                     ("image", "vcpe.image.id"),
-                     ("image_name", "vcpe.image.name"),
-                     ("instance", "vcpe.instance.id"),
-                     ("instance_name", "vcpe.instance.name"),
-                     ("routeable_subnet", "vcpe.vbng.routeable_subnet"),
-                     ("vcpe_id", "vcpe.id"),
-                     ("vbng_id", "vcpe.vbng.id"),
-                     ("nat_ip", "vcpe.nat_ip"),
-                     ("lan_ip", "vcpe.lan_ip"),
-                     ("private_ip", "vcpe.private_ip"),
-                     ("wan_ip", "vcpe.wan_ip"),
-                     ("wan_mac", "vcpe.wan_mac"),
-                     ("vcpe_synced", "vcpe.is_synced"),
-                     )
-
-    def __getattr__(self, key):
-        for (member_name, passthrough_name) in self.passthroughs:
-            if key==member_name:
-                parts = passthrough_name.split(".")
-                obj = self
-                for part in parts[:-1]:
-                    obj = getattr(obj, part)
-                    if not obj:
-                        return None
-                return getattr(obj, parts[-1])
-
-        raise AttributeError("getattr: %r object has no attribute %r" %
-                         (self.__class__, key))
-
-    def __setattr__(self, key, value):
-        for (member_name, passthrough_name) in self.passthroughs:
-            if key==member_name:
-                parts = passthrough_name.split(".")
-                obj = self
-                for part in parts[:-1]:
-                     obj = getattr(obj, part)
-                     if not obj:
-                         return
-                setattr(obj, parts[-1], value)
-
-        super(CordSubscriber, self).__setattr__(key, value)
-
-    def save(self, *args, **kwargs):
-        super(CordSubscriber, self).save(*args, **kwargs)
-
-        # in case the vcpe or vbng fields were altered
-        #   TODO: dirty detection?
-        if (self.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()
-
 class CordSubscriber(CordSubscriberRoot):
     class Meta:
         proxy = True
@@ -112,6 +37,7 @@
                      # ("users", "vcpe.users"),
                      # ("services", "vcpe.services"),
                      # ("cdn_enable", "vcpe.cdn_enable"),
+                     # uplink_speed, downlink_speed, status, enable_uverse
 
                      ("vlan_id", "volt.vlan_id"),      # XXX remove this
                      ("c_tag", "volt.c_tag"),
@@ -132,6 +58,7 @@
                      ("wan_ip", "volt.vcpe.wan_ip"),
                      ("wan_mac", "volt.vcpe.wan_mac"),
                      ("vcpe_synced", "volt.vcpe.is_synced"),
+                     ("wan_container_ip", "volt.vcpe.wan_container_ip"),
                      )
 
     def __getattr__(self, key):
diff --git a/xos/services/cord/admin.py b/xos/services/cord/admin.py
index 76b505c..4ec4c6f 100644
--- a/xos/services/cord/admin.py
+++ b/xos/services/cord/admin.py
@@ -344,18 +344,34 @@
 
 class CordSubscriberRootForm(forms.ModelForm):
     url_filter_level = forms.CharField(required = False)
+    uplink_speed = forms.CharField(required = False)
+    downlink_speed = forms.CharField(required = False)
+    status = forms.ChoiceField(choices=CordSubscriberRoot.status_choices, required=True)
+    enable_uverse = forms.BooleanField(required=False)
 
     def __init__(self,*args,**kwargs):
         super (CordSubscriberRootForm,self ).__init__(*args,**kwargs)
         self.fields['kind'].widget.attrs['readonly'] = True
         if self.instance:
             self.fields['url_filter_level'].initial = self.instance.url_filter_level
+            self.fields['uplink_speed'].initial = self.instance.uplink_speed
+            self.fields['downlink_speed'].initial = self.instance.downlink_speed
+            self.fields['status'].initial = self.instance.status
+            self.fields['enable_uverse'].initial = self.instance.enable_uverse
         if (not self.instance) or (not self.instance.pk):
             # default fields for an 'add' form
             self.fields['kind'].initial = CORD_SUBSCRIBER_KIND
+            self.fields['uplink_speed'].initial = CordSubscriberRoot.get_default_attribute("uplink_speed")
+            self.fields['downlink_speed'].initial = CordSubscriberRoot.get_default_attribute("downlink_speed")
+            self.fields['status'].initial = CordSubscriberRoot.get_default_attribute("status")
+            self.fields['enable_uverse'].initial = CordSubscriberRoot.get_default_attribute("enable_uverse")
 
     def save(self, commit=True):
         self.instance.url_filter_level = self.cleaned_data.get("url_filter_level")
+        self.instance.uplink_speed = self.cleaned_data.get("uplink_speed")
+        self.instance.downlink_speed = self.cleaned_data.get("downlink_speed")
+        self.instance.status = self.cleaned_data.get("status")
+        self.instance.enable_uverse = self.cleaned_data.get("enable_uverse")
         return super(CordSubscriberRootForm, self).save(commit=commit)
 
     class Meta:
@@ -365,9 +381,9 @@
     list_display = ('backend_status_icon', 'id',  'name', )
     list_display_links = ('backend_status_icon', 'id', 'name', )
     fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'name', 'service_specific_id', # 'service_specific_attribute',
-                                     'url_filter_level'],
+                                     'url_filter_level', "uplink_speed", "downlink_speed", "status", "enable_uverse"],
                           'classes':['suit-tab suit-tab-general']})]
-    readonly_fields = ('backend_status_text', 'service_specific_attribute', 'bbs_account')
+    readonly_fields = ('backend_status_text', 'service_specific_attribute',)
     form = CordSubscriberRootForm
     inlines = (VOLTTenantInline, TenantRootPrivilegeInline)
 
diff --git a/xos/services/cord/models.py b/xos/services/cord/models.py
index 37ee78e..39592be 100644
--- a/xos/services/cord/models.py
+++ b/xos/services/cord/models.py
@@ -32,20 +32,38 @@
 
     KIND = CORD_SUBSCRIBER_KIND
 
-    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,
-                          "users": [],
-                          "is_demo_user": False }
+    status_choices = (("enabled", "Enabled"),
+                      ("suspended", "Suspended"),
+                      ("delinquent", "Delinquent"),
+                      ("copyrightviolation", "Copyright Violation"))
+
+    # 'simple_attributes' will be expanded into properties and setters that
+    # store the attribute using self.set_attribute / self.get_attribute.
+
+    simple_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),
+                          ("users", []),
+                          ("is_demo_user", False),
+
+                          ("uplink_speed", "1000000000"),  # 1 gigabit, a reasonable default?
+                          ("downlink_speed", "1000000000"),
+                          ("enable_uverse", True) )
+
+    default_attributes = {"status": "enabled"}
 
     sync_attributes = ("firewall_enable",
                        "firewall_rules",
                        "url_filter_enable",
                        "url_filter_rules",
-                       "cdn_enable",)
+                       "cdn_enable",
+                       "uplink_speed",
+                       "downlink_speed",
+                       "enable_uverse",
+                       "status")
 
     def __init__(self, *args, **kwargs):
         super(CordSubscriberRoot, self).__init__(*args, **kwargs)
@@ -67,60 +85,14 @@
         return volt
 
     @property
-    def firewall_enable(self):
-        return self.get_attribute("firewall_enable", self.default_attributes["firewall_enable"])
+    def status(self):
+        return self.get_attribute("status", self.default_attributes["status"])
 
-    @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)
+    @status.setter
+    def status(self, value):
+        if not value in [x[0] for x in self.status_choices]:
+            raise Exception("invalid status %s" % value)
+        self.set_attribute("status", value)
 
     def find_user(self, uid):
         uid = int(uid)
@@ -195,13 +167,7 @@
             # 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)
+CordSubscriberRoot.setup_simple_attributes()
 
 # -------------------------------------------
 # VOLT
diff --git a/xos/synchronizers/vcpe/steps/sync_vcpetenant.yaml b/xos/synchronizers/vcpe/steps/sync_vcpetenant.yaml
index 585f68a..945e7cb 100644
--- a/xos/synchronizers/vcpe/steps/sync_vcpetenant.yaml
+++ b/xos/synchronizers/vcpe/steps/sync_vcpetenant.yaml
@@ -58,6 +58,10 @@
         {% for mac in safe_browsing_macs %}
         - {{ mac }}
         {% endfor %}
+      uplink_speed: {{ uplink_speed }}
+      downlink_speed: {{ downlink_speed }}
+      status: {{ status }}
+      enable_uverse: {{ enable_uverse }}
 
   tasks:
 {% if full_setup %}
diff --git a/xos/synchronizers/vcpe/steps/sync_vcpetenant_new.yaml b/xos/synchronizers/vcpe/steps/sync_vcpetenant_new.yaml
index 071c30a..fe11405 100644
--- a/xos/synchronizers/vcpe/steps/sync_vcpetenant_new.yaml
+++ b/xos/synchronizers/vcpe/steps/sync_vcpetenant_new.yaml
@@ -59,6 +59,10 @@
         {% for mac in safe_browsing_macs %}
         - {{ mac }}
         {% endfor %}
+      uplink_speed: {{ uplink_speed }}
+      downlink_speed: {{ downlink_speed }}
+      status: {{ status }}
+      enable_uverse: {{ enable_uverse }}
 
   tasks:
   - name: Verify if vcpe_stats_notifier ([] is to avoid capturing the shell process) cron job is already running
diff --git a/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml b/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml
index 819dcc5..3db0010 100644
--- a/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml
+++ b/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml
@@ -63,6 +63,11 @@
         {% for mac in safe_browsing_macs %}
         - {{ mac }}
         {% endfor %}
+      uplink_speed: {{ uplink_speed }}
+      downlink_speed: {{ downlink_speed }}
+      status: {{ status }}
+      enable_uverse: {{ enable_uverse }}
+
 
   tasks:
   - name: Check to see if network is setup