Add wan_mac
diff --git a/xos/cord/models.py b/xos/cord/models.py
index c491a54..ca010cb 100644
--- a/xos/cord/models.py
+++ b/xos/cord/models.py
@@ -68,7 +68,8 @@
KIND = "vOLT"
- default_attributes = {"vlan_id": None, }
+ default_attributes = {"vlan_id": None,
+ "is_demo_user": False }
def __init__(self, *args, **kwargs):
volt_services = VOLTService.get_service_objects().all()
@@ -129,6 +130,14 @@
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
@@ -215,7 +224,9 @@
"nat_ip",
"lan_ip",
"wan_ip",
- "private_ip")
+ "private_ip",
+ "hpc_client_ip",
+ "wan_mac")
default_attributes = {"firewall_enable": False,
"firewall_rules": "accept all anywhere anywhere",
@@ -234,7 +245,8 @@
@property
def image(self):
- LOOK_FOR_IMAGES=["Ubuntu 14.04 LTS", # portal
+ LOOK_FOR_IMAGES=["ubuntu-vcpe2", # ONOS demo machine -- preferred vcpe image
+ "Ubuntu 14.04 LTS", # portal
"Ubuntu-14.04-LTS", # ONOS demo machine
]
for image_name in LOOK_FOR_IMAGES:
@@ -376,6 +388,17 @@
def bbs_account(self, value):
return self.set_attribute("bbs_account", value)
+ @property
+ def ssh_command(self):
+ if self.sliver:
+ return self.sliver.get_ssh_command()
+ else:
+ return "no-sliver"
+
+ @ssh_command.setter
+ def ssh_command(self, value):
+ pass
+
def find_user(self, uid):
uid = int(uid)
for user in self.users:
@@ -393,10 +416,17 @@
for arg in kwargs.keys():
user[arg] = kwargs[arg]
self.users = users
- return
+ 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
@@ -447,6 +477,8 @@
addresses["private"] = ns.ip
elif "nat" in ns.network.name.lower():
addresses["nat"] = ns.ip
+ elif "hpc_client" in ns.network.name.lower():
+ addresses["hpc_client"] = ns.ip
return addresses
@property
@@ -462,9 +494,25 @@
return self.addresses.get("wan",None)
@property
+ def wan_mac(self):
+ ip = self.wan_ip
+ if not ip:
+ return None
+ try:
+ (a,b,c,d) = ip.split('.')
+ wan_mac = "02:42:%2x:%2x:%2x:%2x" % (int(a), int(b), int(c), int(d))
+ except:
+ wan_mac = "Exception"
+ return wan_mac
+
+ @property
def private_ip(self):
return self.addresses.get("private",None)
+ @property
+ def hpc_client_ip(self):
+ return self.addresses.get("hpc_client",None)
+
def pick_node(self):
nodes = list(Node.objects.all())
# TODO: logic to filter nodes by which nodes are up, and which
@@ -591,7 +639,8 @@
KIND = "vBNG"
- default_attributes = {"routeable_subnet": ""}
+ default_attributes = {"routeable_subnet": "",
+ "mapped_ip": ""}
@property
def routeable_subnet(self):
@@ -600,3 +649,11 @@
@routeable_subnet.setter
def routeable_subnet(self, value):
self.set_attribute("routeable_subnet", value)
+
+ @property
+ def mapped_ip(self):
+ return self.get_attribute("mapped_ip", self.default_attributes["mapped_ip"])
+
+ @mapped_ip.setter
+ def mapped_ip(self, value):
+ self.set_attribute("mapped_ip", value)
diff --git a/xos/core/views/legacyapi.py b/xos/core/views/legacyapi.py
index 5216351..3c9f50a 100644
--- a/xos/core/views/legacyapi.py
+++ b/xos/core/views/legacyapi.py
@@ -157,13 +157,26 @@
ip = socket.gethostbyname(ps_node.name.strip())
- # search for a dedicated public IP address
+ # If the slice has a network that's labeled for hpc_client, then
+ # return that network.
+ found_labeled_network = False
for networkSliver in ps_sliver.networkslivers.all():
if (not networkSliver.ip):
continue
- template = networkSliver.network.template
- if (template.visibility=="public") and (template.translation=="none"):
+ if (networkSliver.network.owner != ps_slice):
+ continue
+ if networkSliver.network.labels and ("hpc_client" in networkSliver.network.labels):
ip=networkSliver.ip
+ found_labeled_network = True
+
+ if not found_labeled_network:
+ # search for a dedicated public IP address
+ for networkSliver in ps_sliver.networkslivers.all():
+ if (not networkSliver.ip):
+ continue
+ template = networkSliver.network.template
+ if (template.visibility=="public") and (template.translation=="none"):
+ ip=networkSliver.ip
if return_nat:
ip = None
@@ -218,6 +231,7 @@
slices = GetSlices({"name": slicename}, slice_remap=slice_remap)
perhost = {}
allinterfaces = {}
+ hostprivmap = {}
hostipmap = {}
hostnatmap = {}
nodes = []
diff --git a/xos/core/xoslib/methods/cordsubscriber.py b/xos/core/xoslib/methods/cordsubscriber.py
index 19823dc..5742c9c 100644
--- a/xos/core/xoslib/methods/cordsubscriber.py
+++ b/xos/core/xoslib/methods/cordsubscriber.py
@@ -5,10 +5,11 @@
from rest_framework import generics
from rest_framework import viewsets
from rest_framework.decorators import detail_route, list_route
+from rest_framework.views import APIView
from core.models import *
from django.forms import widgets
from django.conf.urls import patterns, url
-from cord.models import VOLTTenant
+from cord.models import VOLTTenant, VBNGTenant
from core.xoslib.objects.cordsubscriber import CordSubscriber
from plus import PlusSerializerMixin
from django.shortcuts import get_object_or_404
@@ -40,6 +41,7 @@
sliver_name = ReadOnlyField()
image_name = ReadOnlyField()
routeable_subnet = serializers.CharField(required=False)
+ ssh_command = ReadOnlyField()
bbs_account = ReadOnlyField()
lan_ip = ReadOnlyField()
@@ -47,6 +49,8 @@
nat_ip = ReadOnlyField()
private_ip = ReadOnlyField()
+ wan_mac = ReadOnlyField()
+
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
class Meta:
@@ -57,12 +61,18 @@
'firewall_enable', 'firewall_rules',
'url_filter_enable', 'url_filter_rules', 'url_filter_level',
'bbs_account',
- 'cdn_enable', 'vbng_id', 'routeable_subnet', 'nat_ip', 'lan_ip', 'wan_ip', 'private_ip')
+ 'ssh_command',
+ 'cdn_enable', 'vbng_id', 'routeable_subnet', 'nat_ip', 'lan_ip', 'wan_ip', 'private_ip', 'wan_mac')
def getHumanReadableName(self, obj):
return obj.__unicode__()
+#------------------------------------------------------------------------------
+# The "old" API
+# This is used by the xoslib-based GUI
+#------------------------------------------------------------------------------
+
class CordSubscriberList(XOSListCreateAPIView):
queryset = CordSubscriber.get_tenant_objects().select_related().all()
serializer_class = CordSubscriberIdSerializer
@@ -77,6 +87,71 @@
method_kind = "detail"
method_name = "cordsubscriber"
+# We fake a user object by pulling the user data struct out of the
+# subscriber object...
+
+def serialize_user(subscriber, user):
+ return {"id": "%d-%d" % (subscriber.id, user["id"]),
+ "name": user["name"],
+ "level": user.get("level",""),
+ "mac": user.get("mac", ""),
+ "subscriber": subscriber.id }
+
+class CordUserList(APIView):
+ method_kind = "list"
+ method_name = "corduser"
+
+ def get(self, request, format=None):
+ instances=[]
+ for subscriber in CordSubscriber.get_tenant_objects().all():
+ for user in subscriber.users:
+ instances.append( serialize_user(subscriber, user) )
+
+ return Response(instances)
+
+ 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"],
+ level=data["level"],
+ mac=data["mac"])
+ subscriber.save()
+
+ return Response(serialize_user(subscriber,user))
+
+class CordUserDetail(APIView):
+ method_kind = "detail"
+ method_name = "corduser"
+
+ def get(self, request, format=None, pk=0):
+ parts = pk.split("-")
+ subscriber = CordSubscriber.get_tenant_objects().filter(id=parts[0])
+ for user in subscriber.users:
+ return Response( [ serialize_user(subscriber, user) ] )
+ raise XOSNotFound("Failed to find user %s" % pk)
+
+ 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.save()
+ return Response("okay")
+
+ def put(self, request, pk):
+ kwargs={}
+ if "name" in request.DATA:
+ kwargs["name"] = request.DATA["name"]
+ if "level" in request.DATA:
+ kwargs["level"] = request.DATA["level"]
+ if "mac" in request.DATA:
+ kwargs["mac"] = request.DATA["mac"]
+
+ parts = pk.split("-")
+ subscriber = CordSubscriber.get_tenant_objects().get(id=int(parts[0]))
+ user = subscriber.vcpe.update_user(parts[1], **kwargs)
+ subscriber.save()
+ return Response(serialize_user(subscriber,user))
+
# this may be moved into plus.py...
class XOSViewSet(viewsets.ModelViewSet):
@@ -87,6 +162,12 @@
name=self.base_name+"_"+name)
@classmethod
+ def list_url(self, pattern, viewdict, name):
+ return url(r'^' + self.method_name + r'/' + pattern,
+ self.as_view(viewdict),
+ name=self.base_name+"_"+name)
+
+ @classmethod
def get_urlpatterns(self):
patterns = []
@@ -95,7 +176,10 @@
return patterns
-# the "new" API with many more REST endpoints.
+#------------------------------------------------------------------------------
+# The "new" API with many more REST endpoints.
+# This is for integration with with the subscriber GUI
+#------------------------------------------------------------------------------
class CordSubscriberViewSet(XOSViewSet):
base_name = "subscriber"
@@ -114,18 +198,25 @@
def get_urlpatterns(self):
patterns = super(CordSubscriberViewSet, self).get_urlpatterns()
patterns.append( self.detail_url("url_filter/$", {"get": "get_url_filter"}, "url_filter") )
- patterns.append( self.detail_url("url_filter/(?P<level>[a-zA-Z0-9\-]+)/$", {"put": "set_url_filter"}, "url_filter") )
+ patterns.append( self.detail_url("url_filter/(?P<level>[a-zA-Z0-9\-_]+)/$", {"put": "set_url_filter"}, "url_filter") )
patterns.append( self.detail_url("services/$", {"get": "get_services"}, "services") )
- patterns.append( self.detail_url("services/(?P<service>[a-zA-Z0-9\-]+)/$", {"get": "get_service"}, "get_service") )
- patterns.append( self.detail_url("services/(?P<service>[a-zA-Z0-9\-]+)/true/$", {"put": "enable_service"}, "enable_service") )
- patterns.append( self.detail_url("services/(?P<service>[a-zA-Z0-9\-]+)/false/$", {"put": "disable_service"}, "disable_service") )
+ patterns.append( self.detail_url("services/(?P<service>[a-zA-Z0-9\-_]+)/$", {"get": "get_service"}, "get_service") )
+ patterns.append( self.detail_url("services/(?P<service>[a-zA-Z0-9\-_]+)/true/$", {"put": "enable_service"}, "enable_service") )
+ patterns.append( self.detail_url("services/(?P<service>[a-zA-Z0-9\-_]+)/false/$", {"put": "disable_service"}, "disable_service") )
patterns.append( self.detail_url("users/$", {"get": "get_users", "post": "create_user"}, "users") )
patterns.append( self.detail_url("users/clearusers/$", {"get": "clear_users", "put": "clear_users", "post": "clear_users"}, "clearusers") )
patterns.append( self.detail_url("users/newuser/$", {"put": "create_user", "post": "create_user"}, "newuser") )
patterns.append( self.detail_url("users/(?P<uid>[0-9\-]+)/$", {"delete": "delete_user"}, "user") )
patterns.append( self.detail_url("users/(?P<uid>[0-9\-]+)/url_filter/$", {"get": "get_user_level"}, "user_level") )
- patterns.append( self.detail_url("users/(?P<uid>[0-9\-]+)/url_filter/(?P<level>[a-zA-Z0-9\-]+)/$", {"put": "set_user_level"}, "set_user_level") )
+ patterns.append( self.detail_url("users/(?P<uid>[0-9\-]+)/url_filter/(?P<level>[a-zA-Z0-9\-_]+)/$", {"put": "set_user_level"}, "set_user_level") )
+
+ patterns.append( url("^rs/initdemo/$", self.as_view({"put": "initdemo", "get": "initdemo"}), name="initdemo") )
+
+ patterns.append( url("^rs/subidlookup/(?P<ssid>[0-9\-]+)/$", self.as_view({"get": "ssiddetail"}), name="ssiddetail") )
+ patterns.append( url("^rs/subidlookup/$", self.as_view({"get": "ssidlist"}), name="ssidlist") )
+
+ patterns.append( url("^rs/vbng_mapping/$", self.as_view({"get": "get_vbng_mapping"}), name="vbng_mapping") )
return patterns
@@ -220,6 +311,53 @@
subscriber.save()
return Response({service: getattr(subscriber, service_attr)})
+ def initdemo(self, request):
+ object_list = VOLTTenant.get_tenant_objects().all()
+ 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()
+
+ voltTenant.vcpe.create_user(name="Mom's PC", mac="01020303040506", level="R")
+ voltTenant.vcpe.create_user(name="Dad's PC", mac="01020304040507", level="R")
+ voltTenant.vcpe.create_user(name="Jack's iPhone", mac="01020304050508", level="PG")
+ voltTenant.vcpe.create_user(name="Jill's iPad", mac="01020304050609", level="G")
+ voltTenant.vcpe.save()
+
+ return Response({"id": voltTenant.id})
+
+ def ssidlist(self, request):
+ object_list = VOLTTenant.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()
+
+ 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) ]
+
+ if len(ssidmap)==0:
+ raise XOSNotFound("didn't find ssid %s" % str(ssid))
+
+ return Response( ssidmap[0] )
+
+ def get_vbng_mapping(self, request):
+ object_list = VBNGTenant.get_tenant_objects().all()
+
+ mappings = []
+ for vbng in object_list:
+ if vbng.mapped_ip and vbng.routeable_subnet:
+ mappings.append( {"private_ip": vbng.mapped_ip, "routeable_subnet": vbng.routeable_subnet} )
+
+ return Response( {"vbng_mapping": mappings} )
diff --git a/xos/core/xoslib/methods/volttenant.py b/xos/core/xoslib/methods/volttenant.py
index a927c3f..a1ae656 100644
--- a/xos/core/xoslib/methods/volttenant.py
+++ b/xos/core/xoslib/methods/volttenant.py
@@ -30,13 +30,24 @@
humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ computeNodeName = serializers.SerializerMethodField("getComputeNodeName")
+
class Meta:
model = VOLTTenant
- fields = ('humanReadableName', 'id', 'provider_service', 'service_specific_id', 'vlan_id' )
+ fields = ('humanReadableName', 'id', 'provider_service', 'service_specific_id', 'vlan_id', 'computeNodeName' )
def getHumanReadableName(self, obj):
return obj.__unicode__()
+ def getComputeNodeName(self, obj):
+ vcpe = obj.vcpe
+ if not vcpe:
+ return None
+ sliver = vcpe.sliver
+ if not sliver:
+ return None
+ return sliver.node.name
+
class VOLTTenantList(XOSListCreateAPIView):
serializer_class = VOLTTenantIdSerializer
diff --git a/xos/core/xoslib/objects/cordsubscriber.py b/xos/core/xoslib/objects/cordsubscriber.py
index 1301f90..4719a13 100644
--- a/xos/core/xoslib/objects/cordsubscriber.py
+++ b/xos/core/xoslib/objects/cordsubscriber.py
@@ -33,6 +33,7 @@
("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"),
@@ -48,6 +49,7 @@
("lan_ip", "vcpe.lan_ip"),
("private_ip", "vcpe.private_ip"),
("wan_ip", "vcpe.wan_ip"),
+ ("wan_mac", "vcpe.wan_mac"),
)
def __getattr__(self, key):
diff --git a/xos/core/xoslib/static/js/xosCord.js b/xos/core/xoslib/static/js/xosCord.js
index 99c6e94..f666b77 100644
--- a/xos/core/xoslib/static/js/xosCord.js
+++ b/xos/core/xoslib/static/js/xosCord.js
@@ -1,4 +1,4 @@
-OBJS = ['cordSubscriber', ]
+OBJS = ['cordSubscriber', 'cordUser']
CordAdminApp = new XOSApplication({
logTableId: "#logTable",
diff --git a/xos/core/xoslib/static/js/xoslib/xos-backbone.js b/xos/core/xoslib/static/js/xoslib/xos-backbone.js
index e7ee9cc..c4f1126 100644
--- a/xos/core/xoslib/static/js/xoslib/xos-backbone.js
+++ b/xos/core/xoslib/static/js/xoslib/xos-backbone.js
@@ -37,6 +37,7 @@
HPCVIEW_API = XOSLIB_BASE + "/hpcview/";
CORDSUBSCRIBER_API = XOSLIB_BASE + "/cordsubscriber/";
+ CORDUSER_API = XOSLIB_BASE + "/corduser/";
XOSModel = Backbone.Model.extend({
relatedCollections: [],
@@ -729,7 +730,8 @@
define_model(this, {urlRoot: CORDSUBSCRIBER_API,
modelName: "cordSubscriber",
- listFields: ["id", "vlan_id", "routeable_subnet"],
+ relatedCollections: {"cordUsers": "subscriber"},
+ listFields: ["id", "service_specific_id", "vlan_id", "routeable_subnet"],
detailFields: ["id", "service_specific_id", "vcpe_id", "image_name", "sliver_name",
"firewall_enable", "firewall_rules", "url_filter_enable", "url_filter_rules", "cdn_enable",
"nat_ip", "lan_ip", "wan_ip", "private_ip",
@@ -740,6 +742,13 @@
disableAdd: true,
});
+ define_model(this, {urlRoot: CORDUSER_API,
+ modelName: "cordUser",
+ listFields: ["id", "subscriber", "name", "level", "mac"],
+ detailFields: ["subscriber", "name", "level", "mac"],
+ disableAdd: true,
+ });
+
/* by default, have slicePlus only fetch the slices the user can see */
this.slicesPlus.currentUserCanSee = true;
diff --git a/xos/core/xoslib/templates/xosCordSubscriber.html b/xos/core/xoslib/templates/xosCordSubscriber.html
index dadf14f..67568ec 100644
--- a/xos/core/xoslib/templates/xosCordSubscriber.html
+++ b/xos/core/xoslib/templates/xosCordSubscriber.html
@@ -30,6 +30,7 @@
<%= model.attributes.lan_ip %> (lan)<br>
<%= model.attributes.nat_ip %> (nat)<br>
<%= model.attributes.private_ip %> (private) </td></tr>
+ <tr><td class="xos-label-cell">SSH Command:</td><td><%= model.attributes.ssh_command %></td></tr>
</table>
</div>
diff --git a/xos/observers/vbng/steps/sync_vbngtenant.py b/xos/observers/vbng/steps/sync_vbngtenant.py
index 64494f8..b0dd345 100644
--- a/xos/observers/vbng/steps/sync_vbngtenant.py
+++ b/xos/observers/vbng/steps/sync_vbngtenant.py
@@ -12,7 +12,7 @@
from hpc.models import HpcService, CDNPrefix
from util.logger import Logger, logging
-VBNG_API = "http://<vnbg-addr>/onos/virtualbng/privateip/"
+VBNG_API = "http://10.0.3.136:8181/onos/virtualbng/privateip/"
# hpclibrary will be in steps/..
parentdir = os.path.join(os.path.dirname(__file__),"..")
@@ -37,14 +37,10 @@
return objs
def defer_sync(self, o, reason):
- o.backend_register="{}"
- o.backend_status = "2 - " + reason
- o.save(update_fields=['enacted','backend_status','backend_register'])
logger.info("defer object %s due to %s" % (str(o), reason))
+ raise Exception("defer object %s due to %s" % (str(o), reason))
- def sync_record(self, o):
- logger.info("sync'ing VBNGTenant %s" % str(o))
-
+ def get_private_ip(self, o):
vcpes = VCPETenant.get_tenant_objects().all()
vcpes = [x for x in vcpes if (x.vbng is not None) and (x.vbng.id == o.id)]
if not vcpes:
@@ -60,26 +56,38 @@
external_ns = None
for ns in sliver.networkslivers.all():
- if (ns.ip) and (ns.network.template.visibility=="private") and (ns.network.template.translation=="none"):
- # need some logic here to find the right network
+ if (ns.ip) and ("WAN" in ns.network.template.name):
external_ns = ns
if not external_ns:
- self.defer_sync(o, "private network is not filled in yet")
+ self.defer_sync(o, "WAN network is not filled in yet")
return
- private_ip = external_ns.ip
+ return external_ns.ip
+
+ def sync_record(self, o):
+ logger.info("sync'ing VBNGTenant %s" % str(o))
if not o.routeable_subnet:
- print "This is where we would call Pingping's API"
- o.routeable_subnet = "placeholder-from-observer"
+ private_ip = self.get_private_ip(o)
+ logger.info("contacting vBNG service to request mapping for private ip %s" % private_ip)
- # r = requests.post(VBNG_API + "%s" % private_ip, )
- # public_ip = r.json()
- # o.routeable_subnet = public_ip
+ r = requests.post(VBNG_API + "%s" % private_ip, )
+ if (r.status_code != 200):
+ raise Exception("Received error from bng service (%d)" % r.status_code)
+ logger.info("received public IP %s from private IP %s" % (r.text, private_ip))
+ o.routeable_subnet = r.text
+ o.mapped_ip = private_ip
o.save()
- def delete_record(self, m):
- pass
+ def delete_record(self, o):
+ logger.info("deleting VBNGTenant %s" % str(o))
+
+ if o.mapped_ip:
+ private_ip = o.mapped_ip
+ logger.info("contacting vBNG service to delete private ip %s" % private_ip)
+ r = requests.delete(VBNG_API + "%s" % private_ip, )
+ if (r.status_code != 200):
+ raise Exception("Received error from bng service (%d)" % r.status_code)
diff --git a/xos/observers/vbng/vbng_observer_config b/xos/observers/vbng/vbng_observer_config
index 9094bb1..b75d498 100644
--- a/xos/observers/vbng/vbng_observer_config
+++ b/xos/observers/vbng/vbng_observer_config
@@ -31,6 +31,7 @@
#/var/log/hpc.log
driver=None
pretend=False
+backoff_disabled=True
[feefie]
client_id='vicci_dev_central'
diff --git a/xos/observers/vcpe/broadbandshield.py b/xos/observers/vcpe/broadbandshield.py
new file mode 100644
index 0000000..96f9944
--- /dev/null
+++ b/xos/observers/vcpe/broadbandshield.py
@@ -0,0 +1,353 @@
+import requests
+import logging
+import json
+import sys
+from rest_framework.exceptions import APIException
+
+""" format of settings
+
+ ["settings"]
+ ["watershed"]
+ ["rating"]
+ ["categories"]
+ ["blocklist"]
+ ["allowlist"]
+
+ ["users"]
+ array
+ ["account_id"] - 58
+ ["reporting"] - False
+ ["name"] - Scott1
+ ["devices"]
+ ["settings"] -
+ ["watershed"]
+ ["rating"]
+ ["categories"]
+ ["blocklist"]
+ ["allowlist"]
+
+ ["devices"]
+ array
+ ["username"] - "Scott1" or "" if whole-house
+ ["uuid"] - empty
+ ["mac_address"] - mac address as hex digits in ascii
+ ["type"] - "laptop"
+ ["name"] - human readable name of device ("Scott's laptop")
+ ["settings"]
+ ["watershed"]
+ array
+ array
+ ["rating"]
+ ["category"]
+ ["rating"] - ["G" | "NONE"]
+ ["categories"] - list of categories set by rating
+ ["blocklist"] - []
+ ["allowlist"] - []
+"""
+
+class BBS_Failure(APIException):
+ status_code=400
+ def __init__(self, why="broadbandshield error", fields={}):
+ APIException.__init__(self, {"error": "BBS_Failure",
+ "specific_error": why,
+ "fields": fields})
+
+
+class BBS:
+ level_map = {"PG_13": "PG-13",
+ None: "NONE"}
+
+ def __init__(self, username, password):
+ self.username = username
+ self.password = password
+ self.api = "https://www.broadbandshield.com/api"
+ self.session = None
+ self.settings = None
+
+ def login(self):
+ self.session = requests.Session()
+ r = self.session.post(self.api + "/login", data = json.dumps({"email": self.username, "password": self.password}))
+ if (r.status_code != 200):
+ raise BBS_Failure("Failed to login (%d)" % r.status_code)
+
+ def get_account(self):
+ if not self.session:
+ self.login()
+
+ r = self.session.get(self.api + "/account")
+ if (r.status_code != 200):
+ raise BBS_Failure("Failed to get account settings (%d)" % r.status_code)
+ self.settings = r.json()
+
+ return self.settings
+
+ def post_account(self):
+ if not self.settings:
+ raise XOSProgrammingError("no settings to post")
+
+ r = self.session.post(self.api + "/account/settings", data= json.dumps(self.settings))
+ if (r.status_code != 200):
+ raise BBS_Failure("Failed to set account settings (%d)" % r.status_code)
+
+ def add_device(self, name, mac, type="tablet", username=""):
+ data = {"name": name, "mac_address": mac, "type": type, "username": username}
+ r = self.session.post(self.api + "/device", data = json.dumps(data))
+ if (r.status_code != 200):
+ raise BBS_Failure("Failed to add device (%d)" % r.status_code)
+
+ def delete_device(self, data):
+ r = self.session.delete(self.api + "/device", data = json.dumps(data))
+ if (r.status_code != 200):
+ raise BBS_Failure("Failed to delete device (%d)" % r.status_code)
+
+ def add_user(self, name, rating="NONE", categories=[]):
+ data = {"name": name, "settings": {"rating": rating, "categories": categories}}
+ r = self.session.post(self.api + "/users", data = json.dumps(data))
+ if (r.status_code != 200):
+ raise BBS_Failure("Failed to add user (%d)" % r.status_code)
+
+ def delete_user(self, data):
+ r = self.session.delete(self.api + "/users", data = json.dumps(data))
+ if (r.status_code != 200):
+ raise BBS_Failure("Failed to delete user (%d)" % r.status_code)
+
+ def clear_users_and_devices(self):
+ if not self.settings:
+ self.get_account()
+
+ for device in self.settings["devices"]:
+ self.delete_device(device)
+
+ for user in self.settings["users"]:
+ self.delete_user(user)
+
+ def get_whole_home_level(self):
+ if not self.settings:
+ self.get_account()
+
+ return self.settings["settings"]["rating"]
+
+ def sync(self, whole_home_level, users):
+ if not self.settings:
+ self.get_account()
+
+ vcpe_users = {}
+ for user in users:
+ user = user.copy()
+ user["level"] = self.level_map.get(user["level"], user["level"])
+ user["mac"] = user.get("mac", "")
+ vcpe_users[user["name"]] = user
+
+ whole_home_level = self.level_map.get(whole_home_level, whole_home_level)
+
+ if (whole_home_level != self.settings["settings"]["rating"]):
+ print "*** set whole_home", whole_home_level, "***"
+ self.settings["settings"]["rating"] = whole_home_level
+ self.post_account()
+
+ bbs_usernames = [bbs_user["name"] for bbs_user in self.settings["users"]]
+ bbs_devicenames = [bbs_device["name"] for bbs_device in self.settings["devices"]]
+
+ add_users = []
+ add_devices = []
+ delete_users = []
+ delete_devices = []
+
+ for bbs_user in self.settings["users"]:
+ bbs_username = bbs_user["name"]
+ if bbs_username in vcpe_users.keys():
+ vcpe_user = vcpe_users[bbs_username]
+ if bbs_user["settings"]["rating"] != vcpe_user["level"]:
+ print "set user", vcpe_user["name"], "rating", vcpe_user["level"]
+ #bbs_user["settings"]["rating"] = vcpe_user["level"]
+ # add can be used as an update
+ add_users.append(vcpe_user)
+ else:
+ delete_users.append(bbs_user)
+
+ for bbs_device in self.settings["devices"]:
+ bbs_devicename = bbs_device["name"]
+ if bbs_devicename in vcpe_users.keys():
+ vcpe_user = vcpe_users[bbs_devicename]
+ if bbs_device["mac_address"] != vcpe_user["mac"]:
+ print "set device", vcpe_user["name"], "mac", vcpe_user["mac"]
+ #bbs_device["mac_address"] = vcpe_user["mac"]
+ # add of a device can't be used as an update, as you'll end
+ # up with two of them.
+ delete_devices.append(bbs_device)
+ add_devices.append(vcpe_user)
+ else:
+ delete_devices.append(bbs_device)
+
+ for (username, user) in vcpe_users.iteritems():
+ if not username in bbs_usernames:
+ add_users.append(user)
+ if not username in bbs_devicenames:
+ add_devices.append(user)
+
+ for bbs_user in delete_users:
+ print "delete user", bbs_user["name"]
+ self.delete_user(bbs_user)
+
+ for bbs_device in delete_devices:
+ print "delete device", bbs_device["name"]
+ self.delete_device(bbs_device)
+
+ for vcpe_user in add_users:
+ print "add user", vcpe_user["name"], "level", vcpe_user["level"]
+ self.add_user(vcpe_user["name"], vcpe_user["level"])
+
+ for vcpe_user in add_devices:
+ print "add device", vcpe_user["name"], "mac", vcpe_user["mac"]
+ self.add_device(vcpe_user["name"], vcpe_user["mac"], "tablet", vcpe_user["name"])
+
+ def get_whole_home_rating(self):
+ return self.settings["settings"]["rating"]
+
+ def get_user(self, name):
+ for user in self.settings["users"]:
+ if user["name"]==name:
+ return user
+ return None
+
+ def get_device(self, name):
+ for device in self.settings["devices"]:
+ if device["name"]==name:
+ return device
+ return None
+
+ def dump(self):
+ if not self.settings:
+ self.get_account()
+
+ print "whole_home_rating:", self.settings["settings"]["rating"]
+ print "users:"
+ for user in self.settings["users"]:
+ print " user", user["name"], "rating", user["settings"]["rating"]
+
+ print "devices:"
+ for device in self.settings["devices"]:
+ print " device", device["name"], "user", device["username"], "rating", device["settings"]["rating"], "mac", device["mac_address"]
+
+def self_test():
+ if len(sys.argv)!=3:
+ print "syntax: broadbandshield.py <email> <password>"
+ sys.exit(-1)
+
+ bbs = BBS(sys.argv[1], sys.argv[2])
+
+ print "*** initial ***"
+ bbs.dump()
+
+ open("bbs.json","w").write(json.dumps(bbs.settings))
+
+ # a new BBS account will throw a 500 error if it has no rating
+ bbs.settings["settings"]["rating"] = "R"
+ #bbs.settings["settings"]["category"] = [u'PORNOGRAPHY', u'ADULT', u'ILLEGAL', u'WEAPONS', u'DRUGS', u'GAMBLING', u'CYBERBULLY', u'ANONYMIZERS', u'SUICIDE', u'MALWARE']
+ #bbs.settings["settings"]["blocklist"] = []
+ #bbs.settings["settings"]["allowlist"] = []
+ #for water in bbs.settings["settings"]["watershed"];
+ # water["categories"]=[]
+ # delete everything
+ bbs.post_account()
+ bbs.clear_users_and_devices()
+
+ print "*** cleared ***"
+ bbs.settings=None
+ bbs.dump()
+
+ users = [{"name": "Moms pc", "level": "R", "mac": "010203040506"},
+ {"name": "Dads pc", "level": "R", "mac": "010203040507"},
+ {"name": "Jacks ipad", "level": "PG", "mac": "010203040508"},
+ {"name": "Jills iphone", "level": "G", "mac": "010203040509"}]
+
+ print "*** syncing mom-R, Dad-R, jack-PG, Jill-G, wholehome-PG-13 ***"
+
+ bbs.settings = None
+ bbs.sync("PG-13", users)
+
+ print "*** after sync ***"
+ bbs.settings=None
+ bbs.dump()
+ assert(bbs.get_whole_home_rating() == "PG-13")
+ assert(bbs.get_user("Moms pc")["settings"]["rating"] == "R")
+ assert(bbs.get_user("Dads pc")["settings"]["rating"] == "R")
+ assert(bbs.get_user("Jacks ipad")["settings"]["rating"] == "PG")
+ assert(bbs.get_user("Jills iphone")["settings"]["rating"] == "G")
+ assert(bbs.get_device("Moms pc")["mac_address"] == "010203040506")
+ assert(bbs.get_device("Dads pc")["mac_address"] == "010203040507")
+ assert(bbs.get_device("Jacks ipad")["mac_address"] == "010203040508")
+ assert(bbs.get_device("Jills iphone")["mac_address"] == "010203040509")
+
+ print "*** update whole home level ***"
+ bbs.settings=None
+ bbs.get_account()
+ bbs.settings["settings"]["rating"] = "PG"
+ bbs.post_account()
+
+ print "*** after sync ***"
+ bbs.settings=None
+ bbs.dump()
+ assert(bbs.get_whole_home_rating() == "PG")
+ assert(bbs.get_user("Moms pc")["settings"]["rating"] == "R")
+ assert(bbs.get_user("Dads pc")["settings"]["rating"] == "R")
+ assert(bbs.get_user("Jacks ipad")["settings"]["rating"] == "PG")
+ assert(bbs.get_user("Jills iphone")["settings"]["rating"] == "G")
+ assert(bbs.get_device("Moms pc")["mac_address"] == "010203040506")
+ assert(bbs.get_device("Dads pc")["mac_address"] == "010203040507")
+ assert(bbs.get_device("Jacks ipad")["mac_address"] == "010203040508")
+ assert(bbs.get_device("Jills iphone")["mac_address"] == "010203040509")
+
+ print "*** delete dad, change moms IP, change jills level to PG, change whole home to PG-13 ***"
+ users = [{"name": "Moms pc", "level": "R", "mac": "010203040511"},
+ {"name": "Jacks ipad", "level": "PG", "mac": "010203040508"},
+ {"name": "Jills iphone", "level": "PG", "mac": "010203040509"}]
+
+ bbs.settings = None
+ bbs.sync("PG-13", users)
+
+ print "*** after sync ***"
+ bbs.settings=None
+ bbs.dump()
+ assert(bbs.get_whole_home_rating() == "PG-13")
+ assert(bbs.get_user("Moms pc")["settings"]["rating"] == "R")
+ assert(bbs.get_user("Dads pc") == None)
+ assert(bbs.get_user("Jacks ipad")["settings"]["rating"] == "PG")
+ assert(bbs.get_user("Jills iphone")["settings"]["rating"] == "PG")
+ assert(bbs.get_device("Moms pc")["mac_address"] == "010203040511")
+ assert(bbs.get_device("Dads pc") == None)
+ assert(bbs.get_device("Jacks ipad")["mac_address"] == "010203040508")
+
+ print "add dad's laptop"
+ users = [{"name": "Moms pc", "level": "R", "mac": "010203040511"},
+ {"name": "Dads laptop", "level": "PG-13", "mac": "010203040512"},
+ {"name": "Jacks ipad", "level": "PG", "mac": "010203040508"},
+ {"name": "Jills iphone", "level": "PG", "mac": "010203040509"}]
+
+ bbs.settings = None
+ bbs.sync("PG-13", users)
+
+ print "*** after sync ***"
+ bbs.settings=None
+ bbs.dump()
+ assert(bbs.get_whole_home_rating() == "PG-13")
+ assert(bbs.get_user("Moms pc")["settings"]["rating"] == "R")
+ assert(bbs.get_user("Dads pc") == None)
+ assert(bbs.get_user("Dads laptop")["settings"]["rating"] == "PG-13")
+ assert(bbs.get_user("Jacks ipad")["settings"]["rating"] == "PG")
+ assert(bbs.get_user("Jills iphone")["settings"]["rating"] == "PG")
+ assert(bbs.get_device("Moms pc")["mac_address"] == "010203040511")
+ assert(bbs.get_device("Dads pc") == None)
+ assert(bbs.get_device("Dads laptop")["mac_address"] == "010203040512")
+ assert(bbs.get_device("Jacks ipad")["mac_address"] == "010203040508")
+
+ #bbs.add_user("tom", "G", [u'PORNOGRAPHY', u'ADULT', u'ILLEGAL', u'WEAPONS', u'DRUGS', u'GAMBLING', u'SOCIAL', u'CYBERBULLY', u'GAMES', u'ANONYMIZERS', u'SUICIDE', u'MALWARE'])
+ #bbs.add_device(name="tom's iphone", mac="010203040506", type="tablet", username="tom")
+
+def main():
+ self_test()
+
+if __name__ == "__main__":
+ main()
+
+
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.py b/xos/observers/vcpe/steps/sync_vcpetenant.py
index fc6dfee..a29ba09 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.py
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.py
@@ -15,6 +15,8 @@
parentdir = os.path.join(os.path.dirname(__file__),"..")
sys.path.insert(0,parentdir)
+from broadbandshield import BBS
+
logger = Logger(level=logging.INFO)
class SyncVCPETenant(SyncStep):
@@ -28,10 +30,8 @@
SyncStep.__init__(self, **args)
def defer_sync(self, o, reason):
- o.backend_register="{}"
- o.backend_status = "2 - " + reason
- o.save(update_fields=['enacted','backend_status','backend_register'])
logger.info("defer object %s due to %s" % (str(o), reason))
+ raise Exception("defer object %s due to %s" % (str(o), reason))
def fetch_pending(self, deleted):
if (not deleted):
@@ -51,6 +51,11 @@
for slice in service.slices.all():
if "dnsdemux" in slice.name:
for sliver in slice.slivers.all():
+ # Connect to a dnsdemux that's on the hpc_client network
+ # if one is available.
+ for ns in sliver.networkslivers.all():
+ if ns.ip and ns.network.labels and ("hpc_client" in ns.network.labels):
+ dnsdemux_ip = ns.ip
if dnsdemux_ip=="none":
try:
dnsdemux_ip = socket.gethostbyname(sliver.node.name)
@@ -107,6 +112,10 @@
fields.update(self.get_extra_attributes(o))
run_template_ssh(self.template_name, fields)
+ if o.url_filter_enable:
+ bbs = BBS(o.bbs_account, "123")
+ bbs.sync(o.url_filter_level, o.users)
+
o.save()
def delete_record(self, m):
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.yaml b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
index 0dc9f48..3593020 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.yaml
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
@@ -24,6 +24,8 @@
lan_ip: {{ lan_ip }}
wan_ip: {{ wan_ip }}
private_ip: {{ private_ip }}
+ hpc_client_ip: {{ hpc_client_ip }}
+ wan_mac: {{ wan_mac }}
tasks:
- name: Docker repository
diff --git a/xos/observers/vcpe/templates/start-vcpe.sh.j2 b/xos/observers/vcpe/templates/start-vcpe.sh.j2
index b01681c..efa8473 100755
--- a/xos/observers/vcpe/templates/start-vcpe.sh.j2
+++ b/xos/observers/vcpe/templates/start-vcpe.sh.j2
@@ -12,8 +12,9 @@
fi
# Set up networking via pipework
-docker exec vcpe ifconfig eth0 >> /dev/null || pipework eth3 -i eth0 vcpe {{ wan_ip }}/17@192.168.128.1 {{ wan_mac }}
-docker exec vcpe ifconfig eth1 >> /dev/null || pipework eth2 -i eth1 vcpe 192.168.0.1/24 @{{ vlan_ids[0] }}
+docker exec vcpe ifconfig eth0 >> /dev/null || pipework eth4 -i eth0 vcpe {{ wan_ip }}/17@192.168.128.1 {{ wan_mac }}
+docker exec vcpe ifconfig eth1 >> /dev/null || pipework eth3 -i eth1 vcpe 192.168.0.1/24 @{{ vlan_ids[0] }}
+docker exec vcpe ifconfig eth2 >> /dev/null || pipework eth0 -i eth2 vcpe {{ hpc_client_ip }}/16
# Now can start up dnsmasq
docker exec vcpe service dnsmasq start