Merge branch 'AddHelloWorldService' of https://github.com/jermowery/xos into AddHelloWorldService
diff --git a/Dockerfile.cord b/Dockerfile.cord
index 5a2074d..ee0879d 100644
--- a/Dockerfile.cord
+++ b/Dockerfile.cord
@@ -3,3 +3,5 @@
ADD xos/observers/vcpe/supervisor/vcpe-observer.conf /etc/supervisor/conf.d/
RUN sed -i 's/proxy_ssh=True/proxy_ssh=False/' /opt/xos/observers/vcpe/vcpe_observer_config
+ADD xos/observers/monitoring_channel/supervisor/monitoring_channel_observer.conf /etc/supervisor/conf.d/
+RUN sed -i 's/proxy_ssh=True/proxy_ssh=False/' /opt/xos/observers/monitoring_channel/monitoring_channel_observer_config
diff --git a/cloudlab-init.sh b/cloudlab-init.sh
index f275517..9a2c94a 100755
--- a/cloudlab-init.sh
+++ b/cloudlab-init.sh
@@ -22,6 +22,8 @@
then
cp ~/.ssh/id_rsa.pub xos/observers/vcpe/vcpe_public_key
cp ~/.ssh/id_rsa xos/observers/vcpe/vcpe_private_key
+ cp ~/.ssh/id_rsa.pub xos/observers/monitoring_channel/monitoring_channel_public_key
+ cp ~/.ssh/id_rsa xos/observers/monitoring_channel/monitoring_channel_private_key
fi
sudo docker build -t xos .
@@ -34,6 +36,8 @@
# OpenStack is using port 8000...
MYIP=$( hostname -i )
+MYFLATLANIF=$( sudo bash -c "netstat -i" |grep flat|awk '{print $1}' )
+MYFLATLANIP=$( ifconfig $MYFLATLANIF | grep "inet addr" | awk -F: '{print $2}' | awk '{print $1}' )
sudo docker run -d --add-host="ctl:$MYIP" -p 9999:8000 $IMAGE
echo "Waiting for XOS to come up"
@@ -52,7 +56,17 @@
sudo chmod a+r /tmp/admin-openrc.sh
#sudo sed -i 's/:5000/:35357/' /tmp/admin-openrc.sh
source /tmp/admin-openrc.sh
-http --auth $AUTH POST $XOS/xos/controllers/ name=CloudLab deployment=$XOS/xos/deployments/1/ backend_type=OpenStack version=Juno auth_url=$OS_AUTH_URL admin_user=$OS_USERNAME admin_tenant=$OS_TENANT_NAME admin_password=$OS_PASSWORD domain=Default
+
+if [ "$CORD" -ne 1 ]
+then
+ http --auth $AUTH POST $XOS/xos/controllers/ name=CloudLab deployment=$XOS/xos/deployments/1/ backend_type=OpenStack version=Kilo auth_url=$OS_AUTH_URL admin_user=$OS_USERNAME admin_tenant=$OS_TENANT_NAME admin_password=$OS_PASSWORD domain=Default
+else
+ sudo cp /root/setup/settings /tmp
+ sudo chmod a+r /tmp/settings
+ source /tmp/settings
+ source /tmp/admin-openrc.sh
+ http --auth $AUTH POST $XOS/xos/controllers/ name=CloudLab deployment=$XOS/xos/deployments/1/ backend_type=OpenStack version=Kilo auth_url=$OS_AUTH_URL admin_user=$OS_USERNAME admin_tenant=$OS_TENANT_NAME admin_password=$OS_PASSWORD domain=Default rabbit_host=$MYFLATLANIP rabbit_user=$RABBIT_USER rabbit_password=$RABBIT_PASS
+fi
# Add controller to site
http --auth $AUTH PATCH $XOS/xos/sitedeployments/1/ controller=$XOS/xos/controllers/1/
@@ -78,5 +92,5 @@
if [ "$CORD" -ne 0 ]
then
DOCKER=$( sudo docker ps|grep $IMAGE|awk '{print $NF}' )
- sudo docker exec $DOCKER bash -c "cd /opt/xos/tosca; python run.py padmin@vicci.org samples/cord-cloudlab.yaml"
+ sudo docker exec $DOCKER bash -c "cd /opt/xos/tosca; python run.py padmin@vicci.org samples/cord-cloudlab.yaml; python run.py padmin@vicci.org samples/ceilometer.yaml"
fi
diff --git a/synchronizers b/synchronizers
new file mode 120000
index 0000000..d587898
--- /dev/null
+++ b/synchronizers
@@ -0,0 +1 @@
+observers
\ No newline at end of file
diff --git a/xos/apigen/hpc-api.template.py b/xos/apigen/hpc-api.template.py
new file mode 100644
index 0000000..301346b
--- /dev/null
+++ b/xos/apigen/hpc-api.template.py
@@ -0,0 +1,211 @@
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from rest_framework.reverse import reverse
+from rest_framework import serializers
+from rest_framework import generics
+from rest_framework import status
+from rest_framework.generics import GenericAPIView
+from hpc.models import *
+from django.forms import widgets
+from rest_framework import filters
+from django.conf.urls import patterns, url
+from rest_framework.exceptions import PermissionDenied as RestFrameworkPermissionDenied
+from django.core.exceptions import PermissionDenied as DjangoPermissionDenied
+from apibase import XOSRetrieveUpdateDestroyAPIView, XOSListCreateAPIView, XOSNotAuthenticated
+
+if hasattr(serializers, "ReadOnlyField"):
+ # rest_framework 3.x
+ IdField = serializers.ReadOnlyField
+else:
+ # rest_framework 2.x
+ IdField = serializers.Field
+
+"""
+ Schema of the generator object:
+ all: Set of all Model objects
+ all_if(regex): Set of Model objects that match regex
+
+ Model object:
+ plural: English plural of object name
+ camel: CamelCase version of object name
+ refs: list of references to other Model objects
+ props: list of properties minus refs
+
+ TODO: Deal with subnets
+"""
+
+def get_hpc_REST_patterns():
+ return patterns('',
+ url(r'^hpcapi/$', hpc_api_root),
+ {% for object in generator.rest_models %}
+ url(r'hpcapi/{{ object.rest_name }}/$', {{ object.camel }}List.as_view(), name='{{ object.singular }}-list'),
+ url(r'hpcapi/{{ object.rest_name }}/(?P<pk>[a-zA-Z0-9\-]+)/$', {{ object.camel }}Detail.as_view(), name ='{{ object.singular }}-detail'),
+ {% endfor %}
+ )
+
+@api_view(['GET'])
+def hpc_api_root(request, format=None):
+ return Response({
+ {% for object in generator.rest_models %}'{{ object.plural }}': reverse('{{ object }}-list', request=request, format=format),
+ {% endfor %}
+ })
+
+# Based on serializers.py
+
+class XOSModelSerializer(serializers.ModelSerializer):
+ def save_object(self, obj, **kwargs):
+
+ """ rest_framework can't deal with ManyToMany relations that have a
+ through table. In xos, most of the through tables we have
+ use defaults or blank fields, so there's no reason why we shouldn't
+ be able to save these objects.
+
+ So, let's strip out these m2m relations, and deal with them ourself.
+ """
+ obj._complex_m2m_data={};
+ if getattr(obj, '_m2m_data', None):
+ for relatedObject in obj._meta.get_all_related_many_to_many_objects():
+ if (relatedObject.field.rel.through._meta.auto_created):
+ # These are non-trough ManyToMany relations and
+ # can be updated just fine
+ continue
+ fieldName = relatedObject.get_accessor_name()
+ if fieldName in obj._m2m_data.keys():
+ obj._complex_m2m_data[fieldName] = (relatedObject, obj._m2m_data[fieldName])
+ del obj._m2m_data[fieldName]
+
+ serializers.ModelSerializer.save_object(self, obj, **kwargs);
+
+ for (accessor, stuff) in obj._complex_m2m_data.items():
+ (relatedObject, data) = stuff
+ through = relatedObject.field.rel.through
+ local_fieldName = relatedObject.field.m2m_reverse_field_name()
+ remote_fieldName = relatedObject.field.m2m_field_name()
+
+ # get the current set of existing relations
+ existing = through.objects.filter(**{local_fieldName: obj});
+
+ data_ids = [item.id for item in data]
+ existing_ids = [getattr(item,remote_fieldName).id for item in existing]
+
+ #print "data_ids", data_ids
+ #print "existing_ids", existing_ids
+
+ # remove relations that are in 'existing' but not in 'data'
+ for item in list(existing):
+ if (getattr(item,remote_fieldName).id not in data_ids):
+ print "delete", getattr(item,remote_fieldName)
+ item.delete() #(purge=True)
+
+ # add relations that are in 'data' but not in 'existing'
+ for item in data:
+ if (item.id not in existing_ids):
+ #print "add", item
+ newModel = through(**{local_fieldName: obj, remote_fieldName: item})
+ newModel.save()
+
+{% for object in generator.rest_models %}
+
+class {{ object.camel }}Serializer(serializers.HyperlinkedModelSerializer):
+ id = IdField()
+ {% for ref in object.refs %}
+ {% if ref.multi %}
+ {{ ref.plural }} = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='{{ ref }}-detail')
+ {% else %}
+ {{ ref }} = serializers.HyperlinkedRelatedField(read_only=True, view_name='{{ ref }}-detail')
+ {% endif %}
+ {% endfor %}
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = {{ object.camel }}
+ fields = ('humanReadableName', 'validators', {% for prop in object.props %}'{{ prop }}',{% endfor %}{% for ref in object.refs %}{%if ref.multi %}'{{ ref.plural }}'{% else %}'{{ ref }}'{% endif %},{% endfor %})
+
+class {{ object.camel }}IdSerializer(XOSModelSerializer):
+ id = IdField()
+ {% for ref in object.refs %}
+ {% if ref.multi %}
+ {{ ref.plural }} = serializers.PrimaryKeyRelatedField(many=True, queryset = {{ ref.camel }}.objects.all())
+ {% else %}
+ {{ ref }} = serializers.PrimaryKeyRelatedField( queryset = {{ ref.camel }}.objects.all())
+ {% endif %}
+ {% endfor %}
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = {{ object.camel }}
+ fields = ('humanReadableName', 'validators', {% for prop in object.props %}'{{ prop }}',{% endfor %}{% for ref in object.refs %}{%if ref.multi %}'{{ ref.plural }}'{% else %}'{{ ref }}'{% endif %},{% endfor %})
+
+
+{% endfor %}
+
+serializerLookUp = {
+{% for object in generator.rest_models %}
+ {{ object.camel }}: {{ object.camel }}Serializer,
+{% endfor %}
+ None: None,
+ }
+
+# Based on core/views/*.py
+{% for object in generator.rest_models %}
+
+class {{ object.camel }}List(XOSListCreateAPIView):
+ queryset = {{ object.camel }}.objects.select_related().all()
+ serializer_class = {{ object.camel }}Serializer
+ id_serializer_class = {{ object.camel }}IdSerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filter_fields = ({% for prop in object.props %}'{{ prop }}',{% endfor %}{% for ref in object.refs %}{%if ref.multi %}'{{ ref.plural }}'{% else %}'{{ ref }}'{% endif %},{% endfor %})
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return {{ object.camel }}.select_by_user(self.request.user)
+
+
+class {{ object.camel }}Detail(XOSRetrieveUpdateDestroyAPIView):
+ queryset = {{ object.camel }}.objects.select_related().all()
+ serializer_class = {{ object.camel }}Serializer
+ id_serializer_class = {{ object.camel }}IdSerializer
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return {{ object.camel }}.select_by_user(self.request.user)
+
+ # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+ # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+{% endfor %}
diff --git a/xos/apigen/list.txt b/xos/apigen/list.txt
new file mode 100644
index 0000000..3e115e3
--- /dev/null
+++ b/xos/apigen/list.txt
@@ -0,0 +1,3 @@
+{% for o in generator.all %}
+{{ o.camel }}
+{% endfor %}
diff --git a/xos/apigen/modelgen b/xos/apigen/modelgen
index ec08c01..8ae0afb 100644
--- a/xos/apigen/modelgen
+++ b/xos/apigen/modelgen
@@ -7,15 +7,15 @@
import json
import re
from django.template import Context, Template
-
-blacklist = ['SingletonModel','PlCoreBase']
+from optparse import OptionParser
# Django set up
sys.path.append('.')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
from django.db.models.fields.related import ForeignKey, ManyToManyField
-from core.models import *
+
+options = None
def singular(foo, keys):
for k in keys:
@@ -27,13 +27,24 @@
g = globals()
-def enum_classes():
- model_classes = []
- for c in g.values():
- if type(c)==type(PlCoreBase) and c.__name__ not in blacklist:
- model_classes.append(c)
- return model_classes
+def enum_classes(apps):
+ model_classes = []
+ for app in apps:
+ app = app + ".models"
+ models_module = __import__(app)
+ for part in app.split(".")[1:]:
+ if hasattr(models_module, "PlCoreBase"):
+ break
+ models_module = getattr(models_module,part)
+ PlCoreBase = getattr(models_module,"PlCoreBase")
+
+ for classname in dir(models_module):
+ c = getattr(models_module, classname, None)
+ if type(c)==type(PlCoreBase) and c.__name__ not in options.blacklist:
+ model_classes.append(c)
+
+ return model_classes
class GenObj(object):
def __str__(self):
@@ -80,6 +91,10 @@
class Generator(dict):
def all(self):
return self.values()
+
+ def rest_models(self):
+ norest = [x.lower() for x in options.norest]
+ return [v for v in self.values() if not (str(v) in norest)]
def regex(self, r):
filtered = filter(lambda o:re.match(r,str(o)), self.values())
@@ -134,9 +149,9 @@
if (related_name.endswith("+")):
continue
- if (related_name!='+' and related_name.lower()!=str(obj).lower()):
- #print "XXX2", obj, f, related_name, related_model_name, refobj.plural_name
+ if (related_name!='+') and related_model_name in self: # and related_name.lower()!=str(obj).lower()):
refobj = self[related_model_name]
+ #print "XXX2", obj, f, related_name, related_model_name, refobj.plural_name
cobj = copy.deepcopy(refobj)
cobj.multi = True
@@ -144,17 +159,32 @@
-
+
def main():
- try:
- output = sys.argv[1]
- except:
- print 'Usage: modelgen <output template>'
- exit(1)
+ global options
+ parser = OptionParser(usage="modelgen [options] <template_fn>", )
+
+ parser.add_option("-a", "--app", dest="apps",
+ help="list of applications to parse", metavar="APP", default=[], action="append")
+ parser.add_option("-b", "--blacklist", dest="blacklist",
+ help="add model name to blacklist", metavar="MODEL", default=["SingletonModel", "PlCoreBase"], action="append")
+ parser.add_option("-n", "--no-rest", dest="norest",
+ help="do not generate rest api for model", metavar="MODEL", default=["SingletonModel", "PlCoreBase"], action="append")
+
+ (options, args) = parser.parse_args(sys.argv[1:])
+
+ if not options.apps:
+ options.apps = ["core"]
+
+ if len(args)!=1:
+ print 'Usage: modelgen [options] <template_fn>'
+ exit(1)
+
+ output = args[0]
generator = Generator()
- models = enum_classes()
+ models = enum_classes(options.apps)
for m in models:
generator.add_object(m)
diff --git a/xos/ceilometer/models.py b/xos/ceilometer/models.py
index 50f921b..fbfecd3 100644
--- a/xos/ceilometer/models.py
+++ b/xos/ceilometer/models.py
@@ -27,6 +27,10 @@
KIND = CEILOMETER_KIND
+ sync_attributes = ("private_ip", "private_mac",
+ "ceilometer_ip", "ceilometer_mac",
+ "nat_ip", "nat_mac",)
+
default_attributes = {}
def __init__(self, *args, **kwargs):
ceilometer_services = CeilometerService.get_service_objects().all()
@@ -66,14 +70,30 @@
return addresses
@property
+ def nat_ip(self):
+ return self.addresses.get("nat", (None, None))[0]
+
+ @property
+ def nat_mac(self):
+ return self.addresses.get("nat", (None, None))[1]
+
+ @property
def private_ip(self):
return self.addresses.get("nat", (None, None))[0]
@property
+ def private_mac(self):
+ return self.addresses.get("nat", (None, None))[1]
+
+ @property
def ceilometer_ip(self):
return self.addresses.get("ceilometer", (None, None))[0]
@property
+ def ceilometer_mac(self):
+ return self.addresses.get("ceilometer", (None, None))[1]
+
+ @property
def site_tenant_list(self):
tenant_ids = Set()
for sp in SitePrivilege.objects.filter(user=self.creator):
@@ -109,7 +129,7 @@
def ceilometer_url(self):
if not self.ceilometer_ip:
return None
- return "http://" + self.ceilometer_ip + "/uri/to/ceilometer/api/"
+ return "http://" + self.private_ip + ":8888/"
def model_policy_monitoring_channel(pk):
# TODO: this should be made in to a real model_policy
diff --git a/xos/configurations/common/Dockerfile.common b/xos/configurations/common/Dockerfile.common
index 5329142..03b479a 100644
--- a/xos/configurations/common/Dockerfile.common
+++ b/xos/configurations/common/Dockerfile.common
@@ -48,7 +48,7 @@
RUN pip install django-ipware
RUN pip install django-encrypted-fields
RUN pip install python-keyczar
-RUN pip install pygraphviz
+RUN pip install pygraphviz --install-option="--include-path=/usr/include/graphviz" --install-option="--library-path=/usr/lib/graphviz/"
RUN pip install dnslib
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-keystoneclient
diff --git a/xos/configurations/cord/Dockerfile.cord b/xos/configurations/cord/Dockerfile.cord
index dad9895..a21c7d0 100644
--- a/xos/configurations/cord/Dockerfile.cord
+++ b/xos/configurations/cord/Dockerfile.cord
@@ -5,7 +5,11 @@
ADD xos/configurations/common/id_rsa.pub /root/setup/padmin_public_key
ADD xos/configurations/common/id_rsa.pub /opt/xos/observers/vcpe/vcpe_public_key
ADD xos/configurations/common/id_rsa /opt/xos/observers/vcpe/vcpe_private_key
+ADD xos/configurations/common/id_rsa.pub /opt/xos/observers/onos/onos_key.pub
+ADD xos/configurations/common/id_rsa /opt/xos/observers/onos/onos_key
ADD xos/observers/vcpe/supervisor/vcpe-observer.conf /etc/supervisor/conf.d/
+ADD xos/observers/vbng/supervisor/vbng-observer.conf /etc/supervisor/conf.d/
+ADD xos/observers/onos/supervisor/onos-observer.conf /etc/supervisor/conf.d/
RUN sed -i 's/proxy_ssh=True/proxy_ssh=False/' /opt/xos/observers/vcpe/vcpe_observer_config
CMD /usr/bin/make -C /opt/xos/configurations/cord -f Makefile.inside; /bin/bash
diff --git a/xos/configurations/cord/README b/xos/configurations/cord/README
deleted file mode 100644
index 7c80c0f..0000000
--- a/xos/configurations/cord/README
+++ /dev/null
@@ -1,4 +0,0 @@
-This configuration configures XOS with the CORD services. It is intended to be
-run on CloudLab, on the "ctl" node set up by the OpenStack profile. It launches
-an XOS container on Cloudlab that runs the XOS develserver. The container is
-left running in the background.
diff --git a/xos/configurations/cord/README.md b/xos/configurations/cord/README.md
new file mode 100644
index 0000000..efabcb2
--- /dev/null
+++ b/xos/configurations/cord/README.md
@@ -0,0 +1,97 @@
+# CORD development environment
+
+This configuration can be used to set up a CORD development environment.
+It does the following:
+
+* Sets up a basic dataplane for testing end-to-end packet flow between a subscriber client and the Internet
+* Brings up ONOS apps for controlling the dataplane: virtualbng, olt
+* Configures XOS with the CORD services: vCPE, vBNG, vOLT
+
+**NOTE:** This configuration is under **active development** and is not yet finished! Some features are not
+fully working yet.
+
+## End-to-end dataplane
+
+The configuration uses XOS to set up an end-to-end dataplane for development of the XOS services and ONOS apps
+used in CORD. It abstracts away most of the complexity of the CORD hardware using virtual networks
+and Open vSwitch (OvS) switches. At a high level the dataplane looks like this:
+
+```
+ olt virtualbng
+ ---- ----
+ ONOS ONOS
+ | |
+client ----> OvS ----> vCPE ----> OvS ----> Internet
+ 1 2 3 4
+```
+
+On the datapath are two OvS switches, controlled by the `olt` and `virtualbng` ONOS applications. Once all the pieces are in
+place, the client at left should be able to obtain an IP address via DHCP from the vCPE and send packets out to the Internet.
+
+All of the components in the above diagram (i.e., client, OvS switches, ONOS, and vCPE) currently run in distinct VMs
+created by XOS. The numbers in the diagram correspond to networks set up by XOS:
+
+1. subscriber_network
+2. lan_network
+3. wan_network
+4. public_network
+
+## How to run it
+
+The configuration is intended to be run on [CloudLab](http://cloudlab.us).
+It launches an XOS container on Cloudlab that runs the XOS develserver. The container is left running in the background.
+
+To get started on CloudLab:
+* Create an experiment using the *OpenStack-CORD* profile. (You can also use the *OpenStack* profile, but choose *Kilo*
+and disable security groups.)
+* Wait until you get an email from CloudLab with title "OpenStack Instance Finished Setting Up".
+* Login to the *ctl* node of your experiment and run:
+```
+$ git clone https://github.com/open-cloud/xos.git
+$ cd xos/xos/configurations/cord/
+$ make
+```
+
+Running `make` in this directory creates the XOS Docker container and runs the TOSCA engine with `cord.yaml` to
+configure XOS with the CORD services. In addition, a number of VMs are created:
+
+1. *Slice mysite_onos*: runs the ONOS Docker container with `virtualbng` app loaded
+1. *Slice mysite_onos*: runs the ONOS Docker container with `olt` app loaded
+1. *Slice mysite_vbng*: for running OvS with the `virtualbng` app as controller
+1. *Slice mysite_volt*: for running OvS with the `olt` app as controller
+1. *Slice mysite_clients*: a subscriber client for end-to-end testing
+1. *Slice mysite_vcpe*: runs the vCPE Docker container
+
+Once all the VMs are up and the ONOS apps are configured, XOS should be able to get an address mapping from the `virtualbng`
+ONOS app for the vCPE. To verify that it has received an IP address mapping, look at the **Routeable subnet:** field in
+the appropriate *Vbng tenant* object in XOS. It should contain an IP address in the 10.254.0.0/24 subnet.
+
+## How to log into ONOS
+
+The ONOS Docker container runs in the VMs belonging to the *mysite_onos* slice. All ports exposed by the ONOS container are forwarded to the outside, and can be accessed from the *ctl* node using the `flat-lan-1-net` address of the hosting VM. For example, if the IP addresss of the VM is 10.11.10.30, then it is possible to SSH to ONOS as follows (password is *karaf*):
+
+```
+$ ssh -p 8101 karaf@10.11.10.30
+Password authentication
+Password:
+Welcome to Open Network Operating System (ONOS)!
+ ____ _ ______ ____
+ / __ \/ |/ / __ \/ __/
+ / /_/ / / /_/ /\ \
+ \____/_/|_/\____/___/
+
+
+Hit '<tab>' for a list of available commands
+and '[cmd] --help' for help on a specific command.
+Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown ONOS.
+
+onos>
+```
+
+For instance, to check the IP address mappings managed by the `virtualbng` app:
+
+```
+onos> vbngs
+ Private IP - Public IP
+ 10.0.1.3 - 10.254.0.129
+```
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
index 56ab11d..c66f7bc 100644
--- a/xos/configurations/cord/cord.yaml
+++ b/xos/configurations/cord/cord.yaml
@@ -7,6 +7,7 @@
topology_template:
node_templates:
+
# CORD Services
service_volt:
type: tosca.nodes.Service
@@ -18,55 +19,6 @@
view_url: /admin/cord/voltservice/$id$/
kind: vOLT
- Private:
- type: tosca.nodes.NetworkTemplate
-
- # networks required by vCPE
- lan_network:
- type: tosca.nodes.network.Network
- properties:
- ip_version: 4
- requirements:
- - network_template:
- node: Private
- relationship: tosca.relationships.UsesNetworkTemplate
- - owner:
- node: mysite_vcpe
- relationship: tosca.relationships.MemberOfSlice
- - connection:
- node: mysite_vcpe
- relationship: tosca.relationships.ConnectsToSlice
-
- wan_network:
- type: tosca.nodes.network.Network
- properties:
- ip_version: 4
- requirements:
- - network_template:
- node: Private
- relationship: tosca.relationships.UsesNetworkTemplate
- - owner:
- node: mysite_vcpe
- relationship: tosca.relationships.MemberOfSlice
- - connection:
- node: mysite_vcpe
- relationship: tosca.relationships.ConnectsToSlice
-
- hpc_client_network:
- type: tosca.nodes.network.Network
- properties:
- ip_version: 4
- requirements:
- - network_template:
- node: Private
- relationship: tosca.relationships.UsesNetworkTemplate
- - owner:
- node: mysite_vcpe
- relationship: tosca.relationships.MemberOfSlice
- - connection:
- node: mysite_vcpe
- relationship: tosca.relationships.ConnectsToSlice
-
service_vcpe:
type: tosca.nodes.VCPEService
requirements:
@@ -84,11 +36,172 @@
type: tosca.nodes.VBNGService
properties:
view_url: /admin/cord/vbngservice/$id$/
- vbng_url: http://10.0.3.136:8181/onos/virtualbng/
+# if unspecified, vbng observer will look for an ONOSApp Tenant and
+# generate a URL from its IP address
+# vbng_url: http://10.11.10.24:8181/onos/virtualbng/
+
+ service_ONOS_vBNG:
+ type: tosca.nodes.ONOSService
+ requirements:
+ properties:
+ kind: onos
+ view_url: /admin/onos/onosservice/$id$/
+ public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+ artifacts:
+ pubkey: /opt/xos/observers/onos/onos_key.pub
+
+ vBNG_ONOS_app:
+ type: tosca.nodes.ONOSvBNGApp
+ requirements:
+ - onos_tenant:
+ node: service_ONOS_vBNG
+ relationship: tosca.relationships.TenantOfService
+ - vbng_service:
+ node: service_vbng
+ relationship: tosca.relationships.UsedByService
+ properties:
+ dependencies: org.onosproject.proxyarp, org.onosproject.virtualbng, org.onosproject.openflow, org.onosproject.fwd
+ config_network-cfg.json: >
+ {
+ "ports" : {
+ "of:00000000000000a1/1" : {
+ "interfaces" : [
+ {
+ "ips" : [ "10.0.1.253/24" ],
+ "mac" : "00:00:00:00:00:99"
+ }
+ ]
+ },
+ "of:00000000000000a5/2" : {
+ "interfaces" : [
+ {
+ "ips" : [ "10.254.0.1/24" ],
+ "mac" : "00:00:00:00:00:98"
+ }
+ ]
+ }
+ }
+ }
+ config_virtualbng.json: >
+ {
+ "localPublicIpPrefixes" : [
+ "10.254.0.128/25"
+ ],
+ "nextHopIpAddress" : "10.254.0.1",
+ "publicFacingMac" : "00:00:00:00:00:66",
+ "xosIpAddress" : "10.11.10.1",
+ "xosRestPort" : "9999"
+ }
+
+ service_ONOS_vOLT:
+ type: tosca.nodes.ONOSService
+ requirements:
+ properties:
+ kind: onos
+ view_url: /admin/onos/onosservice/$id$/
+ public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+ artifacts:
+ pubkey: /opt/xos/observers/onos/onos_key.pub
+
+ vOLT_ONOS_app:
+ type: tosca.nodes.ONOSvOLTApp
+ requirements:
+ - onos_tenant:
+ node: service_ONOS_vOLT
+ relationship: tosca.relationships.TenantOfService
+ - volt_service:
+ node: service_volt
+ relationship: tosca.relationships.UsedByService
+ properties:
+ dependencies: org.onosproject.olt
+
+ # Network templates
+ Private:
+ type: tosca.nodes.NetworkTemplate
+
+ Public network hack:
+ type: tosca.nodes.NetworkTemplate
+ properties:
+ visibility: private
+ translation: NAT
+ shared_network_name: tun0-net
+
+
+ # Networks required by the CORD setup
+ lan_network:
+ type: tosca.nodes.network.Network
+ properties:
+ ip_version: 4
+ requirements:
+ - network_template:
+ node: Private
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - owner:
+ node: mysite_vcpe
+ relationship: tosca.relationships.MemberOfSlice
+ - connection:
+ node: mysite_vcpe
+ relationship: tosca.relationships.ConnectsToSlice
+ - connection:
+ node: mysite_volt
+ relationship: tosca.relationships.ConnectsToSlice
+
+ wan_network:
+ type: tosca.nodes.network.Network
+ properties:
+ ip_version: 4
+ requirements:
+ - network_template:
+ node: Private
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - owner:
+ node: mysite_vcpe
+ relationship: tosca.relationships.MemberOfSlice
+ - connection:
+ node: mysite_vcpe
+ relationship: tosca.relationships.ConnectsToSlice
+ - connection:
+ node: mysite_vbng
+ relationship: tosca.relationships.ConnectsToSlice
+
+ subscriber_network:
+ type: tosca.nodes.network.Network
+ properties:
+ ip_version: 4
+ requirements:
+ - network_template:
+ node: Private
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - owner:
+ node: mysite_volt
+ relationship: tosca.relationships.MemberOfSlice
+ - connection:
+ node: mysite_volt
+ relationship: tosca.relationships.ConnectsToSlice
+ - connection:
+ node: mysite_clients
+ relationship: tosca.relationships.ConnectsToSlice
+
+ public_network:
+ type: tosca.nodes.network.Network
+ properties:
+ requirements:
+ - network_template:
+ node: Public network hack
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - owner:
+ node: mysite_vbng
+ relationship: tosca.relationships.MemberOfSlice
+ - connection:
+ node: mysite_vbng
+ relationship: tosca.relationships.ConnectsToSlice
+
mysite:
type: tosca.nodes.Site
+
+ # CORD Slices
mysite_vcpe:
description: vCPE Controller Slice
type: tosca.nodes.Slice
@@ -100,12 +213,172 @@
node: mysite
relationship: tosca.relationships.MemberOfSite
- # Now let's add a subscriber
+ mysite_onos_vbng:
+ description: ONOS Controller Slice for vBNG
+ type: tosca.nodes.Slice
+ requirements:
+ - ONOS:
+ node: service_ONOS_vBNG
+ relationship: tosca.relationships.MemberOfService
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+ mysite_onos_volt:
+ description: ONOS Controller Slice for vOLT
+ type: tosca.nodes.Slice
+ requirements:
+ - ONOS:
+ node: service_ONOS_vOLT
+ relationship: tosca.relationships.MemberOfService
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+
+ mysite_vbng:
+ description: slice running OVS controlled by vBNG
+ type: tosca.nodes.Slice
+ requirements:
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+
+ mysite_volt:
+ description: OVS controlled by vOLT
+ type: tosca.nodes.Slice
+ requirements:
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+
+ mysite_clients:
+ description: slice for clients at the subscriber
+ type: tosca.nodes.Slice
+ requirements:
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+
+
+ # Virtual machines
+ onos_app_1:
+ type: tosca.nodes.Compute
+ capabilities:
+ # Host container properties
+ host:
+ properties:
+ num_cpus: 1
+ disk_size: 10 GB
+ mem_size: 4 MB
+ # Guest Operating System properties
+ os:
+ properties:
+ # host Operating System image properties
+ architecture: x86_64
+ type: linux
+ distribution: Ubuntu
+ version: 14.10
+ requirements:
+ - slice:
+ node: mysite_onos_vbng
+ relationship: tosca.relationships.MemberOfSlice
+
+ onos_app_2:
+ type: tosca.nodes.Compute
+ capabilities:
+ # Host container properties
+ host:
+ properties:
+ num_cpus: 1
+ disk_size: 10 GB
+ mem_size: 4 MB
+ # Guest Operating System properties
+ os:
+ properties:
+ # host Operating System image properties
+ architecture: x86_64
+ type: linux
+ distribution: Ubuntu
+ version: 14.10
+ requirements:
+ - slice:
+ node: mysite_onos_volt
+ relationship: tosca.relationships.MemberOfSlice
+
+ # VM for running the OVS controlled by vBNG
+ ovs_vbng:
+ type: tosca.nodes.Compute
+ capabilities:
+ # Host container properties
+ host:
+ properties:
+ num_cpus: 1
+ disk_size: 10 GB
+ mem_size: 4 MB
+ # Guest Operating System properties
+ os:
+ properties:
+ # host Operating System image properties
+ architecture: x86_64
+ type: linux
+ distribution: ubuntu
+ version: 14.04
+ requirements:
+ - slice:
+ node: mysite_vbng
+ relationship: tosca.relationships.MemberOfSlice
+
+ # VM for running the OVS controlled by vOLT
+ ovs_volt:
+ type: tosca.nodes.Compute
+ capabilities:
+ # Host container properties
+ host:
+ properties:
+ num_cpus: 1
+ disk_size: 10 GB
+ mem_size: 4 MB
+ # Guest Operating System properties
+ os:
+ properties:
+ # host Operating System image properties
+ architecture: x86_64
+ type: linux
+ distribution: ubuntu
+ version: 14.04
+ requirements:
+ - slice:
+ node: mysite_volt
+ relationship: tosca.relationships.MemberOfSlice
+
+ # A subscriber client VM
+ client1:
+ type: tosca.nodes.Compute
+ capabilities:
+ # Host container properties
+ host:
+ properties:
+ num_cpus: 1
+ disk_size: 10 GB
+ mem_size: 4 MB
+ # Guest Operating System properties
+ os:
+ properties:
+ # host Operating System image properties
+ architecture: x86_64
+ type: linux
+ distribution: ubuntu
+ version: 14.04
+ requirements:
+ - slice:
+ node: mysite_clients
+ relationship: tosca.relationships.MemberOfSlice
+
+ # A subscriber
My House:
type: tosca.nodes.CORDSubscriber
properties:
- service_specific_id: 1234
+ service_specific_id: 123
firewall_enable: false
cdn_enable: false
url_filter_enable: false
@@ -154,8 +427,8 @@
My Volt:
type: tosca.nodes.VOLTTenant
properties:
- service_specific_id: 1234
- vlan_id: 4321
+ service_specific_id: 123
+ vlan_id: 432
requirements:
- provider_service:
node: service_volt
diff --git a/xos/configurations/cord/dataplane/ansible.cfg b/xos/configurations/cord/dataplane/ansible.cfg
new file mode 100644
index 0000000..9100590
--- /dev/null
+++ b/xos/configurations/cord/dataplane/ansible.cfg
@@ -0,0 +1,4 @@
+[defaults]
+remote_user = ubuntu
+private_key_file = ~/.ssh/id_rsa
+host_key_checking = false
\ No newline at end of file
diff --git a/xos/configurations/cord/dataplane/dataplane.yaml b/xos/configurations/cord/dataplane/dataplane.yaml
new file mode 100644
index 0000000..ca6d9d5
--- /dev/null
+++ b/xos/configurations/cord/dataplane/dataplane.yaml
@@ -0,0 +1,92 @@
+---
+- hosts: switch_vbng
+ sudo: yes
+ vars:
+ controller:
+ - ip: "{{ hostvars['onos_vbng']['ansible_ssh_host'] }}"
+ - port: 6653
+ tasks:
+ - name: Fix /etc/hosts
+ lineinfile:
+ dest=/etc/hosts
+ regexp="127.0.0.1 localhost"
+ line="127.0.0.1 localhost {{ ansible_hostname }}"
+
+ - name: Install packages
+ apt: name={{ item }}
+ state=latest
+ update_cache=yes
+ with_items:
+ - openvswitch-switch
+ - python-netifaces
+
+ - name: Create br-vbng
+ openvswitch_bridge:
+ bridge=br-vbng
+ state=present
+
+ - name: Find wan_network interface
+ script: scripts/if_from_ip.py {{ wan_ip }}
+ register: wan_net
+
+ - name: Find public_network interface
+ script: scripts/if_from_ip.py {{ public_ip }}
+ register: public_net
+
+ - name: Hook up wan-network to br-vbng
+ openvswitch_port:
+ bridge=br-vbng
+ port={{ wan_net.stdout }}
+ state=present
+
+ - name: Hook up public-network to OvS
+ openvswitch_port:
+ bridge=br-vbng
+ port={{ public_net.stdout }}
+ state=present
+
+ - name: Add controller to switch
+ debug: msg="Not implemented yet"
+
+- hosts: switch_volt
+ sudo: yes
+ vars:
+ controller:
+ - ip: "{{ hostvars['onos_volt']['ansible_ssh_host'] }}"
+ - port: 6653
+ tasks:
+
+ - name: Fix /etc/hosts
+ lineinfile:
+ dest=/etc/hosts
+ regexp="127.0.0.1 localhost"
+ line="127.0.0.1 localhost {{ ansible_hostname }}"
+
+ - name: Install packages
+ apt: name={{ item }} state=present update_cache=yes
+ with_items:
+ - git
+ - python-netifaces
+
+ - name: Checkout the Mininet repo
+ git: repo=https://github.com/mininet/mininet.git
+ dest=/tmp/mininet
+
+ - name: Install the CPqD switch using Mininet install script
+ shell: /tmp/mininet/util/install.sh -3x
+ ignore_errors: true
+
+ - name: Find subscriber_network interface
+ script: scripts/if_from_ip.py {{ subscriber_ip }}
+ register: subscriber_net
+
+ - name: Find lan_network interface
+ script: scripts/if_from_ip.py {{ lan_ip }}
+ register: lan_net
+
+ - name: Run the datapath
+ shell: ofdatapath -i {{ subscriber_net.stdout }},{{ lan_net.stdout }} punix:/tmp/s1 -d 000000000001 --no-slicing
+
+ - name: Run the control program
+ shell: sudo ofprotocol unix:/tmp/s1 tcp:{{ controller.ip }}:{{ controller.port }} --fail=closed --listen=punix:/tmp/s1.listen
+
diff --git a/xos/configurations/cord/dataplane/generate.sh b/xos/configurations/cord/dataplane/generate.sh
new file mode 100755
index 0000000..360ed67
--- /dev/null
+++ b/xos/configurations/cord/dataplane/generate.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+# set -x
+
+source ~/admin-openrc.sh
+
+get_ip () {
+ LABEL=$1
+ NETWORK=$2
+ nova list --all-tenants|grep $LABEL|sed "s/^.*$NETWORK=//g"|sed 's/; .*$//g'|awk '{print $1}'
+}
+
+cat <<EOF
+onos_vbng ansible_ssh_host=$( get_ip mysite_onos_vbng flat-lan-1-net)
+switch_vbng ansible_ssh_host=$( get_ip mysite_vbng flat-lan-1-net) wan_ip=$( get_ip mysite_vbng wan_network) public_ip=$( get_ip mysite_vbng tun0-net )
+
+onos_volt ansible_ssh_host=$( get_ip mysite_onos_volt flat-lan-1-net)
+switch_volt ansible_ssh_host=$( get_ip mysite_volt flat-lan-1-net) subscriber_ip=$( get_ip mysite_volt subscriber_network) lan_ip=$( get_ip mysite_volt lan_network) vcpe_lan_ip=$( get_ip mysite_vcpe lan_network)
+EOF
diff --git a/xos/configurations/cord/dataplane/scripts/if_from_ip.py b/xos/configurations/cord/dataplane/scripts/if_from_ip.py
new file mode 100644
index 0000000..be1da48
--- /dev/null
+++ b/xos/configurations/cord/dataplane/scripts/if_from_ip.py
@@ -0,0 +1,14 @@
+#!/usr/bin/python
+
+import sys
+import netifaces
+
+def main (argv):
+ addr = argv[0]
+ for iface in netifaces.interfaces():
+ addrs = netifaces.ifaddresses(iface)
+ if 2 in addrs and addrs[2][0]['addr'] == addr:
+ print iface
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/xos/configurations/opencloud/Dockerfile b/xos/configurations/opencloud/Dockerfile
index 5b09507..cc8cd5a 100644
--- a/xos/configurations/opencloud/Dockerfile
+++ b/xos/configurations/opencloud/Dockerfile
@@ -3,7 +3,6 @@
# Set environment variables.
ENV HOME /root
-ENV TOSCA_CONFIG_PATH=/opt/xos/configurations/opencloud/opencloud.tosca
# XXX Workaround for docker bug:
# https://github.com/docker/docker/issues/6345
@@ -14,23 +13,21 @@
# Install.
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \
+ curl \
git \
- postgresql \
+ geoip-database \
graphviz \
graphviz-dev \
+ libgeoip1 \
libxslt1.1 \
libxslt1-dev \
- tar \
- gcc \
- geoip-database \
- libgeoip1 \
- openssh-client \
- wget \
- curl \
- python-dev \
libyaml-dev \
+ m4 \
+ nginx \
+ openssh-client \
+ python-dev \
pkg-config \
- supervisor \
+ postgresql \
python-crypto \
python-httplib2>=0.9.1 \
python-jinja2 \
@@ -44,7 +41,10 @@
python-novaclient \
python-neutronclient \
python-glanceclient \
- python-ceilometerclient
+ python-ceilometerclient \
+ supervisor \
+ tar \
+ wget
RUN pip install \
django==1.7 \
@@ -65,9 +65,11 @@
django-ipware \
django-encrypted-fields \
django_rest_swagger \
+ python-dateutil \
python-keyczar \
pygraphviz \
- dnslib
+ dnslib \
+ uwsgi
RUN easy_install --upgrade httplib2
@@ -84,11 +86,15 @@
RUN git clone -b release1.8.2 git://github.com/ansible/ansible-modules-extras.git /opt/ansible/v2/ansible/modules/extras
RUN git clone git://github.com/sb98052/ansible-modules-core.git /opt/ansible/lib/ansible/modules/core
RUN git clone git://github.com/sb98052/ansible-modules-core.git /opt/ansible/v2/ansible/modules/core
+# git clone uses cached copy, doesn't pick up latest
+RUN git -C /opt/ansible pull
+RUN git -C /opt/ansible/lib/ansible/modules/core pull
+RUN git -C /opt/ansible/v2/ansible/modules/core pull
ADD ansible-hosts /etc/ansible/hosts
-ADD http://code.jquery.com/jquery-1.9.1.min.js /usr/local/lib/python2.7/dist-packages/suit/static/suit/js/
# For Observer
+ADD http://code.jquery.com/jquery-1.9.1.min.js /usr/local/lib/python2.7/dist-packages/suit/static/suit/js/
RUN git clone git://git.planet-lab.org/fofum.git /tmp/fofum
RUN cd /tmp/fofum; python setup.py install
RUN rm -rf /tmp/fofum
@@ -100,16 +106,11 @@
RUN ln -s /usr/local/share/phantomjs-1.7.0-linux-x86_64 /usr/local/share/phantomjs
RUN ln -s /usr/local/share/phantomjs/bin/phantomjs /bin/phantomjs
-# Supervisor
-ADD observer.conf /etc/supervisor/conf.d/
-
# Get XOS
RUN git clone git://github.com/open-cloud/xos.git /tmp/xos && mv /tmp/xos/xos /opt/
-# Initscript is broken in Ubuntu
-#ADD observer-initscript /etc/init.d/xosobserver
-
RUN chmod +x /opt/xos/scripts/opencloud
+
RUN /opt/xos/scripts/opencloud genkeys
# Workaround for AUFS issue
@@ -117,7 +118,7 @@
RUN mkdir /etc/ssl/private-copy; mv /etc/ssl/private/* /etc/ssl/private-copy/; rm -r /etc/ssl/private; mv /etc/ssl/private-copy /etc/ssl/private; chmod -R 0700 /etc/ssl/private; chown -R postgres /etc/ssl/private
# Set postgres password to match default value in settings.py
-RUN service postgresql start; sudo -u postgres psql -c "alter user postgres with password 'password';"
+RUN service postgresql start && sudo -u postgres psql -c "alter user postgres with password 'password';"
# Turn DEBUG on so that devel server will serve static files
# (not necessary if --insecure is passed to 'manage.py runserver')
@@ -126,24 +127,17 @@
# Cruft to workaround problems with migrations, should go away...
RUN /opt/xos/scripts/opencloud remigrate
-# git clone uses cached copy, doesn't pick up latest
-RUN git -C /opt/ansible pull
-RUN git -C /opt/ansible/lib/ansible/modules/core pull
-RUN git -C /opt/ansible/v2/ansible/modules/core pull
# install Tosca engine
-RUN apt-get install -y m4
-RUN pip install python-dateutil
RUN bash /opt/xos/tosca/install_tosca.sh
+# configure nginx
+RUN cp /opt/xos/nginx/xos.conf /etc/nginx/sites-enabled/default
+
+# Supervisor configuration
+RUN cp /opt/xos/configurations/opencloud/supervisord.conf /etc/supervisor/conf.d/xos-all.conf
+
EXPOSE 8000
-# Set environment variables.
-ENV HOME /root
-
-# Define working directory.
-WORKDIR /root
-
# Define default command.
-#CMD ["/bin/bash"]
-CMD /opt/xos/scripts/docker_start_xos
+CMD /usr/bin/supervisord -c /etc/supervisor/conf.d/xos-all.conf
diff --git a/xos/configurations/opencloud/Makefile b/xos/configurations/opencloud/Makefile
index 863f2b7..ab88aea 100644
--- a/xos/configurations/opencloud/Makefile
+++ b/xos/configurations/opencloud/Makefile
@@ -1,16 +1,16 @@
-RUNNING_CONTAINER:=$(shell sudo docker ps|grep "opencloud_server"|awk '{print $$NF}')
+RUNNING_CONTAINER:=$(shell sudo docker ps|grep "opencloud-server"|awk '{print $$NF}')
.PHONY: build
build: ; docker build --rm -t opencloud .
.PHONY: run
-run: ; docker run --rm --name opencloud_server opencloud
+run: ; docker run --rm --name opencloud-server opencloud
.PHONY: runtosca
runtosca: ; docker exec -it $RUNNING_CONTAINER /usr/bin/python /opt/xos/tosca/run.py padmin@vicci.org $TOSCA_CONFIG_PATH
.PHONY: stop
-stop: ; docker stop opencloud_server
+stop: ; docker stop opencloud-server
.PHONY: rmcontainer
-rmcontainer: ; docker rm opencloud_server
+rmcontainer: ; docker rm opencloud-server
diff --git a/xos/configurations/opencloud/supervisord.conf b/xos/configurations/opencloud/supervisord.conf
new file mode 100644
index 0000000..1fe737d
--- /dev/null
+++ b/xos/configurations/opencloud/supervisord.conf
@@ -0,0 +1,21 @@
+[supervisord]
+nodaemon=true
+logfile=/var/log/supervisord.log
+
+[program:observer]
+autorestart=true
+command=python /opt/xos/xos-observer.py
+stderr_logfile=/var/log/supervisor/observer.err.log
+stdout_logfile=/var/log/supervisor/observer.out.log
+
+program:uwsgi]
+autorestart=true
+command=uwsgi --ini /opt/xos/uwsgi/xos.ini
+stderr_logfile=/var/log/supervisor/uwsgi.err.log
+stdout_logfile=/var/log/supervisor/uwsgi.out.log
+
+[program:nginx]
+command=/usr/sbin/nginx
+autorestart=true
+stderr_logfile=/var/log/supervisor/nginx.err.log
+stdout_logfile=/var/log/supervisor/nginx.out.log
diff --git a/xos/cord/models.py b/xos/cord/models.py
index d55ff2d..26d7d24 100644
--- a/xos/cord/models.py
+++ b/xos/cord/models.py
@@ -689,7 +689,7 @@
class VBNGService(Service):
KIND = VBNG_KIND
- simple_attributes = ( ("vbng_url", "http://10.0.3.136:8181/onos/virtualbng/"), )
+ simple_attributes = ( ("vbng_url", ""), ) # "http://10.0.3.136:8181/onos/virtualbng/"
class Meta:
app_label = "cord"
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 5d2996a..338f993 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -757,7 +757,7 @@
class ControllerAdmin(XOSBaseAdmin):
model = Controller
- fieldList = ['deployment', 'name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain']
+ fieldList = ['deployment', 'name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain', 'rabbit_host', 'rabbit_user', 'rabbit_password']
fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
inlines = [ControllerSiteInline] # ,ControllerImagesInline]
list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
@@ -1372,6 +1372,39 @@
# obj.os_manager = OpenStackManager(auth=auth, caller=request.user)
# obj.delete()
+class ContainerPortInline(XOSTabularInline):
+ fields = ['backend_status_icon', 'network', 'container', 'ip', 'mac', 'segmentation_id']
+ readonly_fields = ("backend_status_icon", "ip", "mac", "segmentation_id")
+ model = Port
+ selflink_fieldname = "network"
+ extra = 0
+ verbose_name_plural = "Ports"
+ verbose_name = "Port"
+ suit_classes = 'suit-tab suit-tab-ports'
+
+class ContainerAdmin(XOSBaseAdmin):
+ fieldsets = [
+ ('Instance Details', {'fields': ['backend_status_text', 'slice', 'node', 'docker_image', 'no_sync'], 'classes': ['suit-tab suit-tab-general'], })
+ ]
+ readonly_fields = ('backend_status_text', )
+ list_display = ['backend_status_icon', 'id']
+ list_display_links = ('backend_status_icon', 'id', )
+
+ suit_form_tabs =(('general', 'Instance Details'), ('ports', 'Ports'))
+
+ inlines = [TagInline, ContainerPortInline]
+
+ def formfield_for_foreignkey(self, db_field, request, **kwargs):
+ if db_field.name == 'slice':
+ kwargs['queryset'] = Slice.select_by_user(request.user)
+
+ return super(ContainerAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
+
+ def queryset(self, request):
+ # admins can see all instances. Users can only see instances of
+ # the slices they belong to.
+ return Container.select_by_user(request.user)
+
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
@@ -1727,7 +1760,7 @@
readonly_fields = ('backend_status_icon', )
class NetworkPortInline(XOSTabularInline):
- fields = ['backend_status_icon', 'network', 'instance', 'ip', 'mac']
+ fields = ['backend_status_icon', 'network', 'instance', 'container', 'ip', 'mac']
readonly_fields = ("backend_status_icon", "ip", "mac")
model = Port
selflink_fieldname = "instance"
@@ -2024,4 +2057,5 @@
admin.site.register(TenantRoot, TenantRootAdmin)
admin.site.register(TenantRootRole, TenantRootRoleAdmin)
admin.site.register(TenantAttribute, TenantAttributeAdmin)
+ admin.site.register(Container, ContainerAdmin)
diff --git a/xos/core/models/__init__.py b/xos/core/models/__init__.py
index c380e9c..bc97dab 100644
--- a/xos/core/models/__init__.py
+++ b/xos/core/models/__init__.py
@@ -24,6 +24,7 @@
from .node import Node
from .slicetag import SliceTag
from .instance import Instance
+from .container import Container
from .reservation import ReservedResource
from .reservation import Reservation
from .network import Network, NetworkParameterType, NetworkParameter, Port, NetworkTemplate, Router, NetworkSlice, ControllerNetwork
diff --git a/xos/core/models/container.py b/xos/core/models/container.py
new file mode 100644
index 0000000..151b576
--- /dev/null
+++ b/xos/core/models/container.py
@@ -0,0 +1,83 @@
+import os
+from django.db import models
+from django.db.models import Q
+from django.core import exceptions
+from core.models import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager
+from core.models.plcorebase import StrippedCharField
+from core.models import Image
+from core.models import Slice, SlicePrivilege
+from core.models import Node
+from core.models import Site
+from core.models import Deployment
+from core.models import Controller
+from core.models import User
+from core.models import Tag
+from core.models import Flavor
+from django.contrib.contenttypes import generic
+from xos.config import Config
+from monitor import driver as monitor
+from django.core.exceptions import PermissionDenied, ValidationError
+
+config = Config()
+
+
+# Create your models here.
+class Container(PlCoreBase):
+ name = StrippedCharField(max_length=200, help_text="Container name")
+ slice = models.ForeignKey(Slice, related_name='containers')
+ node = models.ForeignKey(Node, related_name='containers')
+ creator = models.ForeignKey(User, related_name='containers', blank=True, null=True)
+ docker_image = StrippedCharField(null=True, blank=True, max_length=200, help_text="name of docker container to instantiate")
+
+ def __unicode__(self):
+ return u'container-%s' % str(self.id)
+
+ def save(self, *args, **kwds):
+ if not self.name:
+ self.name = self.slice.name
+ if not self.creator and hasattr(self, 'caller'):
+ self.creator = self.caller
+ if not self.creator:
+ raise ValidationError('container has no creator')
+
+ if (self.slice.creator != self.creator):
+ # Check to make sure there's a slice_privilege for the user. If there
+ # isn't, then keystone will throw an exception inside the observer.
+ slice_privs = SlicePrivilege.objects.filter(slice=self.slice, user=self.creator)
+ if not slice_privs:
+ raise ValidationError('container creator has no privileges on slice')
+
+# XXX smbaker - disabled for now, was causing fault in tenant view create slice
+# if not self.controllerNetwork.test_acl(slice=self.slice):
+# raise exceptions.ValidationError("Deployment %s's ACL does not allow any of this slice %s's users" % (self.controllerNetwork.name, self.slice.name))
+
+ super(Container, self).save(*args, **kwds)
+
+ def can_update(self, user):
+ return True
+
+ @staticmethod
+ def select_by_user(user):
+ if user.is_admin:
+ qs = Container.objects.all()
+ else:
+ slices = Slice.select_by_user(user)
+ qs = Container.objects.filter(slice__in=slices)
+ return qs
+
+ def get_public_keys(self):
+ slice_memberships = SlicePrivilege.objects.filter(slice=self.slice)
+ pubkeys = set([sm.user.public_key for sm in slice_memberships if sm.user.public_key])
+
+ if self.creator.public_key:
+ pubkeys.add(self.creator.public_key)
+
+ if self.slice.creator.public_key:
+ pubkeys.add(self.slice.creator.public_key)
+
+ if self.slice.service and self.slice.service.public_key:
+ pubkeys.add(self.slice.service.public_key)
+
+ return pubkeys
+
+
diff --git a/xos/core/models/instance.py b/xos/core/models/instance.py
index 6240c34..75826f6 100644
--- a/xos/core/models/instance.py
+++ b/xos/core/models/instance.py
@@ -158,6 +158,17 @@
return ns.ip
return None
+ # return an address on nat-net
+ def get_network_ip(self, pattern):
+ for ns in self.ports.all():
+ if pattern in ns.network.name.lower():
+ return ns.ip
+ return None
+
+ # return an address that the synchronizer can use to SSH to the instance
+ def get_ssh_ip(self):
+ return self.get_network_ip("nat")
+
@staticmethod
def select_by_user(user):
if user.is_admin:
diff --git a/xos/core/models/network.py b/xos/core/models/network.py
index 2bdf17f..48af5a6 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -2,7 +2,7 @@
import socket
import sys
from django.db import models
-from core.models import PlCoreBase, Site, Slice, Instance, Controller
+from core.models import PlCoreBase, Site, Slice, Instance, Controller, Container
from core.models import ControllerLinkManager,ControllerLinkDeletionManager
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
@@ -158,7 +158,7 @@
router_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum router id")
subnet_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum subnet id")
subnet = models.CharField(max_length=32, blank=True)
-
+
class Meta:
unique_together = ('network', 'controller')
@@ -211,9 +211,12 @@
class Port(PlCoreBase):
network = models.ForeignKey(Network,related_name='links')
instance = models.ForeignKey(Instance, null=True, blank=True, related_name='ports')
+ container = models.ForeignKey(Container, null=True, blank=True, related_name='ports')
ip = models.GenericIPAddressField(help_text="Instance ip address", blank=True, null=True)
port_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum port id")
mac = models.CharField(null=True, blank=True, max_length=256, help_text="MAC address associated with this port")
+ #unattached = models.BooleanField(default=False, help_text="create this port even if no Instance is attached")
+ segmentation_id = models.CharField(null=True, blank=True, max_length=256, help_text="GRE segmentation id for port")
class Meta:
unique_together = ('network', 'instance')
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 8d2180c..950ce02 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -67,6 +67,10 @@
return cls.objects.filter(kind = cls.KIND)
@classmethod
+ def get_deleted_service_objects(cls):
+ return cls.deleted_objects.filter(kind = cls.KIND)
+
+ @classmethod
def get_service_objects_by_user(cls, user):
return cls.select_by_user(user).filter(kind = cls.KIND)
@@ -489,7 +493,7 @@
KIND = "Provider"
class TenantAttribute(PlCoreBase):
- name = models.SlugField(help_text="Attribute Name", max_length=128)
+ name = models.CharField(help_text="Attribute Name", max_length=128)
value = models.TextField(help_text="Attribute Value")
tenant = models.ForeignKey(Tenant, related_name='tenantattributes', help_text="The Tenant this attribute is associated with")
diff --git a/xos/core/models/site.py b/xos/core/models/site.py
index 26ff191..1bdef36 100644
--- a/xos/core/models/site.py
+++ b/xos/core/models/site.py
@@ -263,6 +263,9 @@
admin_password = StrippedCharField(max_length=200, null=True, blank=True, help_text="Password of theadmin user at this controller")
admin_tenant = StrippedCharField(max_length=200, null=True, blank=True, help_text="Name of the tenant the admin user belongs to")
domain = StrippedCharField(max_length=200, null=True, blank=True, help_text="Name of the domain this controller belongs to")
+ rabbit_host = StrippedCharField(max_length=200, null=True, blank=True, help_text="IP address of rabbitmq server at this controller")
+ rabbit_user = StrippedCharField(max_length=200, null=True, blank=True, help_text="Username of rabbitmq server at this controller")
+ rabbit_password = StrippedCharField(max_length=200, null=True, blank=True, help_text="Password of rabbitmq server at this controller")
deployment = models.ForeignKey(Deployment,related_name='controllerdeployments')
def __init__(self, *args, **kwargs):
diff --git a/xos/core/views/services.py b/xos/core/views/services.py
index 6d18c26..e11988b 100644
--- a/xos/core/views/services.py
+++ b/xos/core/views/services.py
@@ -121,14 +121,14 @@
g.graph_attr.update(graphdir="TB")
for service in Service.objects.all():
- provided_tenants = CoarseTenant.get_tenant_objects().filter(provider_service=service)
- subscribed_tenants = CoarseTenant.get_tenant_objects().filter(subscriber_service=service)
+ provided_tenants = Tenant.objects.filter(provider_service=service, subscriber_service__isnull=False)
+ subscribed_tenants = Tenant.objects.filter(subscriber_service=service, provider_service__isnull=False)
if not (provided_tenants or subscribed_tenants):
# nodes with no edges aren't interesting
continue
g.add_node(service.id, label=service.name)
- for tenant in CoarseTenant.get_tenant_objects().all():
+ for tenant in Tenant.objects.all():
if (not tenant.provider_service) or (not tenant.subscriber_service):
continue
g.add_edge(tenant.subscriber_service.id, tenant.provider_service.id)
diff --git a/xos/core/xoslib/methods/monitoringchannel.py b/xos/core/xoslib/methods/monitoringchannel.py
new file mode 100644
index 0000000..baab346
--- /dev/null
+++ b/xos/core/xoslib/methods/monitoringchannel.py
@@ -0,0 +1,89 @@
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from rest_framework.reverse import reverse
+from rest_framework import serializers
+from rest_framework import generics
+from rest_framework import status
+from core.models import *
+from django.forms import widgets
+from ceilometer.models import MonitoringChannel, CeilometerService
+from plus import PlusSerializerMixin
+from xos.apibase import XOSListCreateAPIView, XOSRetrieveUpdateDestroyAPIView, XOSPermissionDenied
+
+if hasattr(serializers, "ReadOnlyField"):
+ # rest_framework 3.x
+ ReadOnlyField = serializers.ReadOnlyField
+else:
+ # rest_framework 2.x
+ ReadOnlyField = serializers.Field
+
+def get_default_ceilometer_service():
+ ceilometer_services = CeilometerService.get_service_objects().all()
+ if ceilometer_services:
+ return ceilometer_services[0].id
+ return None
+
+class MonitoringChannelSerializer(serializers.ModelSerializer, PlusSerializerMixin):
+ id = ReadOnlyField()
+ service_specific_attribute = ReadOnlyField()
+ ceilometer_url = ReadOnlyField()
+ tenant_list_str = ReadOnlyField()
+ creator = ReadOnlyField()
+ instance = ReadOnlyField()
+ provider_service = serializers.PrimaryKeyRelatedField(queryset=CeilometerService.get_service_objects().all(), default=get_default_ceilometer_service)
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+
+ computeNodeName = serializers.SerializerMethodField("getComputeNodeName")
+
+ class Meta:
+ model = MonitoringChannel
+ fields = ('humanReadableName', 'id', 'provider_service', 'service_specific_attribute', 'ceilometer_url', 'tenant_list_str', 'creator', 'instance', 'computeNodeName' )
+
+ def getHumanReadableName(self, obj):
+ return obj.__unicode__()
+
+ def getComputeNodeName(self, obj):
+ instance = obj.instance
+ if not instance:
+ return None
+ return instance.node.name
+
+class MonitoringChannelList(XOSListCreateAPIView):
+ serializer_class = MonitoringChannelSerializer
+
+ method_kind = "list"
+ method_name = "monitoringchannel"
+
+ def get_queryset(self):
+ queryset = MonitoringChannel.get_tenant_objects().select_related().all()
+
+ current_user = self.request.user.username
+ if current_user is not None:
+ ids = [x.id for x in queryset if x.creator.username==current_user]
+ queryset = queryset.filter(id__in=ids)
+
+ return queryset
+
+ def post(self, request, format=None):
+ current_user = request.user.username
+ existing_obj = None
+ for obj in MonitoringChannel.get_tenant_objects().all():
+ if (obj.creator.username == current_user):
+ existing_obj = obj
+ break
+
+ if existing_obj:
+ serializer = MonitoringChannelSerializer(existing_obj)
+ headers = self.get_success_headers(serializer.data)
+ return Response( serializer.data, status=status.HTTP_200_OK )
+
+ return super(MonitoringChannelList, self).post(request, format)
+
+class MonitoringChannelDetail(XOSRetrieveUpdateDestroyAPIView):
+ serializer_class = MonitoringChannelSerializer
+ queryset = MonitoringChannel.get_tenant_objects().select_related().all()
+
+ method_kind = "detail"
+ method_name = "monitoringchannel"
+
diff --git a/xos/dmdot b/xos/dmdot
index 8570c4d..f438aad 100755
--- a/xos/dmdot
+++ b/xos/dmdot
@@ -12,7 +12,7 @@
from django.db.models.fields.related import ForeignKey
# defaults
-apps = ["core", "hpc", "cord", "requestrouter"]
+apps = ["core", "hpc", "cord", "requestrouter", "services.onos"]
output = "-json"
# syntax: dmdot [-json | -dot] [app_name]
@@ -22,7 +22,7 @@
if arg.startswith("-"):
output = arg
else:
- app = arg
+ apps+= [arg]
model_classes = []
class_names = []
diff --git a/xos/helloworld/models.py b/xos/helloworld/models.py
index 37f6751..a657f3a 100644
--- a/xos/helloworld/models.py
+++ b/xos/helloworld/models.py
@@ -11,7 +11,7 @@
class Hello(PlCoreBase):
name = models.CharField(max_length=254,help_text="Salutation e.g. Hello or Bonjour")
- sliver_backref = models.ForeignKey(Instance)
+ instance_backref = models.ForeignKey(Instance)
class World(PlCoreBase):
name = models.CharField(max_length=254,help_text="Name of planet")
diff --git a/xos/nginx/xos.conf b/xos/nginx/xos.conf
index 9b5d996..64d9d5e 100644
--- a/xos/nginx/xos.conf
+++ b/xos/nginx/xos.conf
@@ -6,8 +6,9 @@
server {
- listen 80;
- server_name 128.112.139.48;
+ listen 80;
+ listen [::]:80 default ipv6only=on; ## listen for ipv6
+ server_name 127.0.0.1;
location /static/ {
alias /opt/xos/core/static/;
diff --git a/xos/observers/base/SyncInstanceUsingAnsible.py b/xos/observers/base/SyncInstanceUsingAnsible.py
index 9455780..901bc97 100644
--- a/xos/observers/base/SyncInstanceUsingAnsible.py
+++ b/xos/observers/base/SyncInstanceUsingAnsible.py
@@ -8,7 +8,7 @@
from xos.config import Config
from observer.syncstep import SyncStep
from observer.ansible import run_template_ssh
-from core.models import Service, Slice
+from core.models import Service, Slice, ControllerSlice, ControllerUser
from util.logger import Logger, logging
logger = Logger(level=logging.INFO)
@@ -58,25 +58,59 @@
def sync_fields(self, o, fields):
self.run_playbook(o, fields)
+ def prepare_record(self, o):
+ pass
+
def sync_record(self, o):
logger.info("sync'ing object %s" % str(o))
- instance = self.get_instance(o)
- if not instance:
- self.defer_sync(o, "waiting on instance")
- return
-
if not os.path.exists(self.service_key_name):
raise Exception("Service key %s does not exist" % self.service_key_name)
service_key = file(self.service_key_name).read()
- fields = { "instance_name": instance.name,
- "hostname": instance.node.name,
- "instance_id": instance.instance_id,
- "private_key": service_key,
- "ansible_tag": "vcpe_tenant_" + str(o.id)
- }
+ self.prepare_record(o)
+
+ instance = self.get_instance(o)
+
+ if isinstance(instance, basestring):
+ # sync to some external host
+
+ # XXX - this probably needs more work...
+
+ fields = { "hostname": instance,
+ "instance_id": "ubuntu", # this is the username to log into
+ "private_key": service.key,
+ }
+ else:
+ # sync to an XOS instance
+ if not instance:
+ self.defer_sync(o, "waiting on instance")
+ return
+
+ if not instance.instance_name:
+ self.defer_sync(o, "waiting on instance.instance_name")
+ return
+
+ cslice = ControllerSlice.objects.get(slice=instance.slice)
+ if not cslice:
+ raise Exception("Controller slice object for %s does not exist" % instance.slice.name)
+
+ cuser = ControllerUser.objects.get(user=instance.creator)
+ if not cuser:
+ raise Exception("Controller user object for %s does not exist" % instance.creator)
+
+ fields = { "instance_name": instance.name,
+ "hostname": instance.node.name,
+ "instance_id": instance.instance_id,
+ "private_key": service_key,
+ "keystone_tenant_id": cslice.tenant_id,
+ "keystone_user_id": cuser.kuser_id,
+ "rabbit_user": instance.controller.rabbit_user,
+ "rabbit_password": instance.controller.rabbit_password,
+ "rabbit_host": instance.controller.rabbit_host,
+ "ansible_tag": o.__class__.__name__ + "_" + str(o.id)
+ }
# If 'o' defines a 'sync_attributes' list, then we'll copy those
# attributes into the Ansible recipe's field list automatically.
diff --git a/xos/observers/monitoring_channel/files/docker.list b/xos/observers/monitoring_channel/files/docker.list
new file mode 100644
index 0000000..0ee9ae0
--- /dev/null
+++ b/xos/observers/monitoring_channel/files/docker.list
@@ -0,0 +1 @@
+deb https://get.docker.com/ubuntu docker main
diff --git a/xos/observers/monitoring_channel/files/vm-resolv.conf b/xos/observers/monitoring_channel/files/vm-resolv.conf
new file mode 100644
index 0000000..cae093a
--- /dev/null
+++ b/xos/observers/monitoring_channel/files/vm-resolv.conf
@@ -0,0 +1 @@
+nameserver 8.8.8.8
diff --git a/xos/observers/monitoring_channel/model-deps b/xos/observers/monitoring_channel/model-deps
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/xos/observers/monitoring_channel/model-deps
@@ -0,0 +1 @@
+{}
diff --git a/xos/observers/monitoring_channel/monitoring_channel_observer.py b/xos/observers/monitoring_channel/monitoring_channel_observer.py
new file mode 100755
index 0000000..d6a71ff
--- /dev/null
+++ b/xos/observers/monitoring_channel/monitoring_channel_observer.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# This imports and runs ../../xos-observer.py
+
+import importlib
+import os
+import sys
+observer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"../..")
+sys.path.append(observer_path)
+mod = importlib.import_module("xos-observer")
+mod.main()
diff --git a/xos/observers/monitoring_channel/monitoring_channel_observer_config b/xos/observers/monitoring_channel/monitoring_channel_observer_config
new file mode 100644
index 0000000..922a019
--- /dev/null
+++ b/xos/observers/monitoring_channel/monitoring_channel_observer_config
@@ -0,0 +1,40 @@
+
+[plc]
+name=plc
+deployment=VICCI
+
+[db]
+name=xos
+user=postgres
+password=password
+host=localhost
+port=5432
+
+[api]
+host=128.112.171.237
+port=8000
+ssl_key=None
+ssl_cert=None
+ca_ssl_cert=None
+ratelimit_enabled=0
+omf_enabled=0
+mail_support_address=support@localhost
+nova_enabled=True
+
+[observer]
+name=monitoring_channel
+dependency_graph=/opt/xos/observers/monitoring_channel/model-deps
+steps_dir=/opt/xos/observers/monitoring_channel/steps
+sys_dir=/opt/xos/observers/monitoring_channel/sys
+deleters_dir=/opt/xos/observers/monitoring_channel/deleters
+log_file=console
+driver=None
+pretend=False
+backoff_disabled=True
+save_ansible_output=True
+proxy_ssh=True
+full_setup=True
+
+[feefie]
+client_id='vicci_dev_central'
+user_id='pl'
diff --git a/xos/observers/monitoring_channel/steps/sync_monitoringchannel.py b/xos/observers/monitoring_channel/steps/sync_monitoringchannel.py
new file mode 100644
index 0000000..919b4ea
--- /dev/null
+++ b/xos/observers/monitoring_channel/steps/sync_monitoringchannel.py
@@ -0,0 +1,74 @@
+import hashlib
+import os
+import socket
+import sys
+import base64
+import time
+from django.db.models import F, Q
+from xos.config import Config
+from observer.syncstep import SyncStep
+from observer.ansible import run_template_ssh
+from observers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
+from core.models import Service, Slice
+from ceilometer.models import MonitoringChannel
+from util.logger import Logger, logging
+
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+logger = Logger(level=logging.INFO)
+
+class SyncMonitoringChannel(SyncInstanceUsingAnsible):
+ provides=[MonitoringChannel]
+ observes=MonitoringChannel
+ requested_interval=0
+ template_name = "sync_monitoringchannel.yaml"
+ service_key_name = "/opt/xos/observers/monitoring_channel/monitoring_channel_private_key"
+
+ def __init__(self, *args, **kwargs):
+ super(SyncMonitoringChannel, self).__init__(*args, **kwargs)
+
+ def fetch_pending(self, deleted):
+ if (not deleted):
+ objs = MonitoringChannel.get_tenant_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
+ else:
+ objs = MonitoringChannel.get_deleted_tenant_objects()
+
+ return objs
+
+ def get_extra_attributes(self, o):
+ # This is a place to include extra attributes. In the case of Monitoring Channel, we need to know
+ # 1) Allowed tenant ids
+ # 2) Ceilometer API service endpoint URL if running externally
+ # 3) Credentials to access Ceilometer API service
+
+ instance = self.get_instance(o)
+
+ try:
+ full_setup = Config().observer_full_setup
+ except:
+ full_setup = True
+
+ fields = {"unique_id": o.id,
+ "allowed_tenant_ids": o.tenant_list,
+ "auth_url":instance.controller.auth_url,
+ "admin_user":instance.controller.admin_user,
+ "admin_password":instance.controller.admin_password,
+ "admin_tenant":instance.controller.admin_tenant,
+ "full_setup": full_setup}
+
+ return fields
+
+ def run_playbook(self, o, fields):
+ #ansible_hash = hashlib.md5(repr(sorted(fields.items()))).hexdigest()
+ #quick_update = (o.last_ansible_hash == ansible_hash)
+
+ #if quick_update:
+ # logger.info("quick_update triggered; skipping ansible recipe")
+ #else:
+ super(SyncMonitoringChannel, self).run_playbook(o, fields)
+
+ #o.last_ansible_hash = ansible_hash
+
+ def delete_record(self, m):
+ pass
diff --git a/xos/observers/monitoring_channel/steps/sync_monitoringchannel.yaml b/xos/observers/monitoring_channel/steps/sync_monitoringchannel.yaml
new file mode 100644
index 0000000..bbe284f
--- /dev/null
+++ b/xos/observers/monitoring_channel/steps/sync_monitoringchannel.yaml
@@ -0,0 +1,89 @@
+---
+- hosts: {{ instance_name }}
+ gather_facts: False
+ connection: ssh
+ user: ubuntu
+ sudo: yes
+ vars:
+ unique_id: {{ unique_id }}
+ auth_url: {{ auth_url }}
+ admin_user: {{ admin_user }}
+ admin_password: {{ admin_password }}
+ admin_tenant: {{ admin_tenant }}
+ shared_lan_ip: {{ private_ip }}
+ shared_lan_mac: {{ private_mac }}
+ headnode_flat_lan_ip: {{ rabbit_host }}
+ ceilometer_client_acess_ip: {{ ceilometer_ip }}
+ ceilometer_client_acess_mac: {{ ceilometer_mac }}
+ allowed_tenant_ids:
+ {% for allowed_tenant_id in allowed_tenant_ids %}
+ - {{ allowed_tenant_id }}
+ {% endfor %}
+
+ tasks:
+{% if full_setup %}
+ - name: Docker repository
+ copy: src=/opt/xos/observers/monitoring_channel/files/docker.list
+ dest=/etc/apt/sources.list.d/docker.list
+
+ - name: Import the repository key
+ apt_key: keyserver=keyserver.ubuntu.com id=36A1D7869245C8950F966E92D8576A8BA88D21E9
+
+ - name: install Docker
+ apt: name=lxc-docker-1.5.0 state=present update_cache=yes
+
+ - name: install python-setuptools
+ apt: name=python-setuptools state=present
+
+ - name: install pip
+ easy_install: name=pip
+
+ - name: install docker-py
+ pip: name=docker-py version=0.5.3
+
+ - name: install Pipework
+ get_url: url=https://raw.githubusercontent.com/jpetazzo/pipework/master/pipework
+ dest=/usr/local/bin/pipework
+ mode=0755
+
+ - name: Disable resolvconf service
+ shell: service resolvconf stop
+ shell: echo manual > /etc/init/resolvconf.override
+ shell: rm -f /etc/resolv.conf
+
+ - name: Install resolv.conf
+ copy: src=/opt/xos/observers/monitoring_channel/files/vm-resolv.conf
+ dest=/etc/resolv.conf
+{% endif %}
+
+ - name: ceilometer proxy config
+ template: src=/opt/xos/observers/monitoring_channel/templates/ceilometer_proxy_config.j2 dest=/usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config mode=0777
+
+ - name: Monitoring channel upstart
+ template: src=/opt/xos/observers/monitoring_channel/templates/monitoring-channel.conf.j2 dest=/etc/init/monitoring-channel-{{ unique_id }}.conf
+
+ - name: Monitoring channel startup script
+ template: src=/opt/xos/observers/monitoring_channel/templates/start-monitoring-channel.sh.j2 dest=/usr/local/sbin/start-monitoring-channel-{{ unique_id }}.sh mode=0755
+ notify:
+# - restart monitoring-channel
+ - stop monitoring-channel
+ - remove container
+ - start monitoring-channel
+
+# These are samples, not necessary for correct function of demo
+
+ - name: Make sure Monitoring channel service is running
+ service: name=monitoring-channel-{{ unique_id }} state=started
+
+ handlers:
+ - name: restart monitoring-channel
+ shell: service monitoring-channel-{{ unique_id }} stop; sleep 1; service vcpe-{{ unique_id }} start
+
+ - name: stop monitoring-channel
+ service: name=monitoring-channel-{{ unique_id }} state=stopped
+
+ - name: remove container
+ docker: name=monitoring-channel-{{ unique_id }} state=absent image=docker-vcpe
+
+ - name: start monitoring-channel
+ service: name=monitoring-channel-{{ unique_id }} state=started
diff --git a/xos/observers/monitoring_channel/supervisor/monitoring_channel_observer.conf b/xos/observers/monitoring_channel/supervisor/monitoring_channel_observer.conf
new file mode 100644
index 0000000..1b78703
--- /dev/null
+++ b/xos/observers/monitoring_channel/supervisor/monitoring_channel_observer.conf
@@ -0,0 +1,2 @@
+[program:monitoring_channel_observer]
+command=python /opt/xos/observers/monitoring_channel/monitoring_channel_observer.py -C /opt/xos/observers/monitoring_channel/monitoring_channel_observer_config
diff --git a/xos/observers/monitoring_channel/templates/Dockerfile.monitoring_channel b/xos/observers/monitoring_channel/templates/Dockerfile.monitoring_channel
new file mode 100644
index 0000000..45defb8
--- /dev/null
+++ b/xos/observers/monitoring_channel/templates/Dockerfile.monitoring_channel
@@ -0,0 +1,26 @@
+FROM ubuntu:14.04.2
+MAINTAINER Andy Bavier <acb@cs.princeton.edu>
+
+# XXX Workaround for docker bug:
+# https://github.com/docker/docker/issues/6345
+# Kernel 3.15 breaks docker, uss the line below as a workaround
+# until there is a fix
+RUN ln -s -f /bin/true /usr/bin/chfn
+# XXX End workaround
+
+# Install.
+RUN apt-get update && apt-get install -y \
+ python-pip \
+ python-dev
+
+RUN pip install web.py
+RUN pip install wsgilog
+RUN pip install python-ceilometerclient
+RUN mkdir -p /usr/local/share
+ADD ceilometer_proxy_server.py /usr/local/share/
+RUN chmod +x /usr/local/share/ceilometer_proxy_server.py
+ADD start_ceilometer_proxy /usr/local/sbin/
+RUN chmod +x /usr/local/sbin/start_ceilometer_proxy
+EXPOSE 8000
+WORKDIR /usr/local/share
+CMD /usr/local/sbin/start_ceilometer_proxy
diff --git a/xos/observers/monitoring_channel/templates/ceilometer_proxy_config.j2 b/xos/observers/monitoring_channel/templates/ceilometer_proxy_config.j2
new file mode 100644
index 0000000..cba6f2a
--- /dev/null
+++ b/xos/observers/monitoring_channel/templates/ceilometer_proxy_config.j2
@@ -0,0 +1,14 @@
+# This file autogenerated by monitoring-channel observer
+# It contains a list of attributes to be used by ceilometer proxy web server
+# syntax: key=value
+
+[default]
+auth_url={{ auth_url }}
+admin_user={{ admin_user }}
+admin_tenant={{ admin_tenant }}
+admin_password={{ admin_password }}
+
+[allowed_tenants]
+{% for tenant_id in allowed_tenant_ids %}
+{{ tenant_id }}
+{% endfor %}
diff --git a/xos/observers/monitoring_channel/templates/ceilometer_proxy_server.py b/xos/observers/monitoring_channel/templates/ceilometer_proxy_server.py
new file mode 100644
index 0000000..c6be286
--- /dev/null
+++ b/xos/observers/monitoring_channel/templates/ceilometer_proxy_server.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python
+import web
+import ConfigParser
+import io
+import json
+from ceilometerclient import client
+import logging
+from wsgilog import WsgiLog
+
+web.config.debug=False
+
+logfile = "ceilometer_proxy_server.log"
+level=logging.INFO
+logger=logging.getLogger('ceilometer_proxy_server')
+logger.setLevel(level)
+handler=logging.handlers.RotatingFileHandler(logfile,maxBytes=1000000, backupCount=1)
+logger.addHandler(handler)
+
+class FileLog(WsgiLog):
+ def __init__(self, application):
+ WsgiLog.__init__(
+ self,
+ application,
+ logformat = '%(message)s',
+ tofile = True,
+ toprint = True,
+ prnlevel = level,
+ file = logfile,
+ backups =1
+ )
+ def __call__(self, environ, start_response):
+ def hstart_response(status, response_headers, *args):
+ out = start_response(status, response_headers, *args)
+ try:
+ logline=environ["SERVER_PROTOCOL"]+" "+environ["REQUEST_METHOD"]+" "+environ["REQUEST_URI"]+" - "+status
+ except err:
+ logline="Could not log <%s> due to err <%s>" % (str(environ), err)
+ logger.info(logline)
+
+ return out
+
+ return super(FileLog, self).__call__(environ, hstart_response)
+
+#TODOs:
+#-See if we can avoid using python-ceilometerclient and instead use the REST calls directly with AuthToken
+#
+urls = (
+ r'^/v2/meters$', 'meter_list',
+ r'^/v2/meters/(?P<meter_name>[A-Za-z0-9_:.\-]+)/statistics$', 'statistics_list',
+ r'^/v2/samples$', 'sample_list',
+ r'^/v2/resources$', 'resource_list',
+)
+
+app = web.application(urls, globals())
+
+config = None
+ceilometer_client = None
+
+
+def parse_ceilometer_proxy_config():
+ global config
+ config = ConfigParser.RawConfigParser(allow_no_value=True)
+ config.read('ceilometer_proxy_config')
+
+def ceilometerclient():
+ global config, ceilometer_client
+ if ceilometer_client:
+ return ceilometer_client
+
+ if not config:
+ parse_ceilometer_proxy_config()
+
+ keystone = {}
+ keystone['os_username']=config.get('default','admin_user')
+ keystone['os_password']=config.get('default','admin_password')
+ keystone['os_auth_url']=config.get('default','auth_url')
+ keystone['os_tenant_name']=config.get('default','admin_tenant')
+ ceilometer_client = client.get_client(2,**keystone)
+ logger.info('ceilometer get_client is successful')
+ return ceilometer_client
+
+def make_query(user_id=None, tenant_id=None, resource_id=None,
+ user_ids=None, tenant_ids=None, resource_ids=None):
+ """Returns query built from given parameters.
+
+ This query can be then used for querying resources, meters and
+ statistics.
+
+ :Parameters:
+ - `user_id`: user_id, has a priority over list of ids
+ - `tenant_id`: tenant_id, has a priority over list of ids
+ - `resource_id`: resource_id, has a priority over list of ids
+ - `user_ids`: list of user_ids
+ - `tenant_ids`: list of tenant_ids
+ - `resource_ids`: list of resource_ids
+ """
+ user_ids = user_ids or []
+ tenant_ids = tenant_ids or []
+ resource_ids = resource_ids or []
+
+ query = []
+ if user_id:
+ user_ids = [user_id]
+ for u_id in user_ids:
+ query.append({"field": "user_id", "op": "eq", "value": u_id})
+
+ if tenant_id:
+ tenant_ids = [tenant_id]
+ for t_id in tenant_ids:
+ query.append({"field": "project_id", "op": "eq", "value": t_id})
+
+ if resource_id:
+ resource_ids = [resource_id]
+ for r_id in resource_ids:
+ query.append({"field": "resource_id", "op": "eq", "value": r_id})
+
+ return query
+
+def filter_query_params(query_params):
+ new_query=[]
+ i=0
+ user_specified_tenants=[]
+ for field in query_params['q.field']:
+ if field != 'project_id':
+ query = {}
+ query['field']=field
+ if query_params['q.op'][i] != '':
+ query['op']=query_params['q.op'][i]
+ query['value']=query_params['q.value'][i]
+ new_query.append(query)
+ else:
+ user_specified_tenants.append(query_params['q.value'][i])
+ i=i+1
+ return new_query,user_specified_tenants
+
+class meter_list:
+ def GET(self):
+ global config
+ keyword_args = {
+ "q.field": [],
+ "q.op": [],
+ "q.type": [],
+ "q.value": [],
+ }
+ query_params = web.input(**keyword_args)
+ new_query, user_specified_tenants = filter_query_params(query_params)
+
+ client = ceilometerclient()
+ meters=[]
+ for (k,v) in config.items('allowed_tenants'):
+ if user_specified_tenants and (k not in user_specified_tenants):
+ continue
+ final_query=[]
+ final_query.extend(new_query)
+ query = make_query(tenant_id=k)
+ final_query.extend(query)
+ logger.debug('final query=%s',final_query)
+ results = client.meters.list(q=final_query)
+ meters.extend(results)
+ return json.dumps([ob._info for ob in meters])
+
+class statistics_list:
+ def GET(self, meter_name):
+ global config
+ keyword_args = {
+ "q.field": [],
+ "q.op": [],
+ "q.type": [],
+ "q.value": [],
+ "period": None
+ }
+ query_params = web.input(**keyword_args)
+ new_query, user_specified_tenants = filter_query_params(query_params)
+
+ client = ceilometerclient()
+ period = query_params.period
+ statistics = []
+ for (k,v) in config.items('allowed_tenants'):
+ if user_specified_tenants and (k not in user_specified_tenants):
+ continue
+ final_query=[]
+ final_query.extend(new_query)
+ query = make_query(tenant_id=k)
+ final_query.extend(query)
+ logger.debug('final query=%s',final_query)
+ results = client.statistics.list(meter_name=meter_name, q=final_query, period=period)
+ statistics.extend(results)
+ return json.dumps([ob._info for ob in statistics])
+
+class sample_list:
+ def GET(self):
+ global config
+ keyword_args = {
+ "q.field": [],
+ "q.op": [],
+ "q.type": [],
+ "q.value": [],
+ }
+ query_params = web.input(**keyword_args)
+ new_query, user_specified_tenants = filter_query_params(query_params)
+
+ client = ceilometerclient()
+ samples=[]
+ for (k,v) in config.items('allowed_tenants'):
+ if user_specified_tenants and (k not in user_specified_tenants):
+ continue
+ final_query=[]
+ final_query.extend(new_query)
+ query = make_query(tenant_id=k)
+ final_query.extend(query)
+ logger.debug('final query=%s',final_query)
+ results = client.samples.list(q=final_query)
+ samples.extend(results)
+ return json.dumps([ob._info for ob in samples])
+
+class resource_list:
+ def GET(self):
+ global config
+ keyword_args = {
+ "q.field": [],
+ "q.op": [],
+ "q.type": [],
+ "q.value": [],
+ }
+ query_params = web.input(**keyword_args)
+ new_query, user_specified_tenants = filter_query_params(query_params)
+
+ client = ceilometerclient()
+ resources=[]
+ for (k,v) in config.items('allowed_tenants'):
+ if user_specified_tenants and (k not in user_specified_tenants):
+ continue
+ final_query=[]
+ final_query.extend(new_query)
+ query = make_query(tenant_id=k)
+ final_query.extend(query)
+ logger.debug('final query=%s',final_query)
+ results = client.resources.list(q=final_query, links=1)
+ resources.extend(results)
+ return json.dumps([ob._info for ob in resources])
+
+if __name__ == "__main__":
+ app.run(FileLog)
diff --git a/xos/observers/monitoring_channel/templates/monitoring-channel.conf.j2 b/xos/observers/monitoring_channel/templates/monitoring-channel.conf.j2
new file mode 100644
index 0000000..eb937ac
--- /dev/null
+++ b/xos/observers/monitoring_channel/templates/monitoring-channel.conf.j2
@@ -0,0 +1,10 @@
+# Upstart script for Monitoring channel
+description "Upstart script for Monitoring channel container"
+author "andy@onlab.us"
+start on filesystem and started docker
+stop on runlevel [!2345]
+respawn
+
+script
+ /usr/local/sbin/start-monitoring-channel-{{ unique_id }}.sh
+end script
diff --git a/xos/observers/monitoring_channel/templates/start-monitoring-channel.sh.j2 b/xos/observers/monitoring_channel/templates/start-monitoring-channel.sh.j2
new file mode 100755
index 0000000..10d9ef5
--- /dev/null
+++ b/xos/observers/monitoring_channel/templates/start-monitoring-channel.sh.j2
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+function mac_to_iface {
+ MAC=$1
+ ifconfig|grep $MAC| awk '{print $1}'|grep -v '\.'
+}
+
+function generate_mac_from_ip {
+ IP=$1
+ printf "02:42:%02x:%02x:%02x:%02x\n" `echo $IP|awk -F '.' '{print $1, $2, $3, $4}'`
+}
+
+iptables -L > /dev/null
+ip6tables -L > /dev/null
+
+MONITORING_CHANNEL=monitoring-channel-{{ unique_id }}
+HEADNODEFLATLANIP={{ headnode_flat_lan_ip }}
+
+docker inspect $MONITORING_CHANNEL > /dev/null 2>&1
+if [ "$?" == 1 ]
+then
+ #sudo docker build -t monitoring-channel -f Dockerfile.monitoring_channel .
+ sudo docker pull srikanthvavila/monitoring-channel
+ docker run -d --name=$MONITORING_CHANNEL --add-host="ctl:$HEADNODEFLATLANIP" --privileged=true -p 8888:8000 srikanthvavila/monitoring-channel
+else
+ docker start $MONITORING_CHANNEL
+fi
+
+# Set up networking via pipework
+#SHARED_LAN_IFACE=$( mac_to_iface {{ shared_lan_mac }} )
+#docker exec $MONITORING_CHANNEL ifconfig eth0 >> /dev/null || pipework $SHARED_LAN_IFACE -i eth0 $MONITORING_CHANNEL 192.168.0.1/24
+
+# Make sure VM's eth0 (hpc_client) has no IP address
+#ifconfig $HPC_IFACE 0.0.0.0
+
+# Now copy ceilometer proxy configuration to container
+cat /usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config | docker exec -i $MONITORING_CHANNEL bash -c 'cat > /usr/local/share/ceilometer_proxy_config'
+
+# Attach to container
+docker start -a $MONITORING_CHANNEL
diff --git a/xos/observers/monitoring_channel/templates/start_ceilometer_proxy b/xos/observers/monitoring_channel/templates/start_ceilometer_proxy
new file mode 100644
index 0000000..ddaa9c8
--- /dev/null
+++ b/xos/observers/monitoring_channel/templates/start_ceilometer_proxy
@@ -0,0 +1 @@
+/usr/local/share/ceilometer_proxy_server.py 8000
diff --git a/xos/observers/onos/model-deps b/xos/observers/onos/model-deps
new file mode 100644
index 0000000..2da80e0
--- /dev/null
+++ b/xos/observers/onos/model-deps
@@ -0,0 +1,5 @@
+{
+ "ONOSApp": [
+ "ONOSService"
+ ]
+}
diff --git a/xos/observers/onos/onos-observer.py b/xos/observers/onos/onos-observer.py
new file mode 100755
index 0000000..d6a71ff
--- /dev/null
+++ b/xos/observers/onos/onos-observer.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+# This imports and runs ../../xos-observer.py
+
+import importlib
+import os
+import sys
+observer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"../..")
+sys.path.append(observer_path)
+mod = importlib.import_module("xos-observer")
+mod.main()
diff --git a/xos/observers/onos/onos_observer_config b/xos/observers/onos/onos_observer_config
new file mode 100644
index 0000000..3c6d63d
--- /dev/null
+++ b/xos/observers/onos/onos_observer_config
@@ -0,0 +1,41 @@
+
+[plc]
+name=plc
+deployment=VICCI
+
+[db]
+name=xos
+user=postgres
+password=password
+host=localhost
+port=5432
+
+[api]
+host=128.112.171.237
+port=8000
+ssl_key=None
+ssl_cert=None
+ca_ssl_cert=None
+ratelimit_enabled=0
+omf_enabled=0
+mail_support_address=support@localhost
+nova_enabled=True
+
+[observer]
+name=onos
+dependency_graph=/opt/xos/observers/onos/model-deps
+steps_dir=/opt/xos/observers/onos/steps
+sys_dir=/opt/xos/observers/onos/sys
+deleters_dir=/opt/xos/observers/onos/deleters
+log_file=console
+driver=None
+pretend=False
+backoff_disabled=True
+save_ansible_output=True
+# set proxy_ssh to false on cloudlab
+proxy_ssh=False
+full_setup=True
+
+[feefie]
+client_id='vicci_dev_central'
+user_id='pl'
diff --git a/xos/observers/onos/run.sh b/xos/observers/onos/run.sh
new file mode 100755
index 0000000..ea4c511
--- /dev/null
+++ b/xos/observers/onos/run.sh
@@ -0,0 +1,6 @@
+#if [[ ! -e ./vcpe-observer.py ]]; then
+# ln -s ../../xos-observer.py vcpe-observer.py
+#fi
+
+export XOS_DIR=/opt/xos
+python onos-observer.py -C $XOS_DIR/observers/onos/onos_observer_config
diff --git a/xos/observers/onos/scripts/dockerip.sh b/xos/observers/onos/scripts/dockerip.sh
new file mode 100644
index 0000000..7684f3e
--- /dev/null
+++ b/xos/observers/onos/scripts/dockerip.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+docker inspect --format '{{ .NetworkSettings.IPAddress }}' $1
diff --git a/xos/observers/onos/start.sh b/xos/observers/onos/start.sh
new file mode 100755
index 0000000..c13ffbe
--- /dev/null
+++ b/xos/observers/onos/start.sh
@@ -0,0 +1,6 @@
+#if [[ ! -e ./vcpe-observer.py ]]; then
+# ln -s ../../xos-observer.py vcpe-observer.py
+#fi
+
+export XOS_DIR=/opt/xos
+nohup python onos-observer.py -C $XOS_DIR/observers/onos/onos_observer_config > /dev/null 2>&1 &
diff --git a/xos/observers/onos/steps/sync_onosapp.py b/xos/observers/onos/steps/sync_onosapp.py
new file mode 100644
index 0000000..8c97391
--- /dev/null
+++ b/xos/observers/onos/steps/sync_onosapp.py
@@ -0,0 +1,110 @@
+import hashlib
+import os
+import socket
+import sys
+import base64
+import time
+from django.db.models import F, Q
+from xos.config import Config
+from observer.syncstep import SyncStep
+from observer.ansible import run_template_ssh
+from observers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
+from core.models import Service, Slice
+from services.onos.models import ONOSService, ONOSApp
+from util.logger import Logger, logging
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+logger = Logger(level=logging.INFO)
+
+class SyncONOSApp(SyncInstanceUsingAnsible):
+ provides=[ONOSApp]
+ observes=ONOSApp
+ requested_interval=0
+ template_name = "sync_onosapp.yaml"
+ service_key_name = "/opt/xos/observers/onos/onos_key"
+
+ def __init__(self, *args, **kwargs):
+ super(SyncONOSApp, self).__init__(*args, **kwargs)
+
+ def fetch_pending(self, deleted):
+ if (not deleted):
+ objs = ONOSApp.get_tenant_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
+ else:
+ objs = ONOSApp.get_deleted_tenant_objects()
+
+ return objs
+
+ def get_instance(self, o):
+ # We assume the ONOS service owns a slice, so pick one of the instances
+ # inside that slice to sync to.
+
+ serv = self.get_onos_service(o)
+
+ if serv.use_external_host:
+ return serv.use_external_host
+
+ if serv.slices.exists():
+ slice = serv.slices.all()[0]
+ if slice.instances.exists():
+ return slice.instances.all()[0]
+
+ return None
+
+ def get_onos_service(self, o):
+ if not o.provider_service:
+ return None
+
+ onoses = ONOSService.get_service_objects().filter(id=o.provider_service.id)
+ if not onoses:
+ return None
+
+ return onoses[0]
+
+ def get_files_dir(self, o):
+ if not hasattr(Config(), "observer_steps_dir"):
+ # make steps_dir mandatory; there's no valid reason for it to not
+ # be defined.
+ raise Exception("observer_steps_dir is not defined in config file")
+
+ step_dir = Config().observer_steps_dir
+
+ return os.path.join(step_dir, "..", "files", str(self.get_onos_service(o).id), o.name)
+
+ def write_configs(self, o):
+ o.config_fns = []
+ o.files_dir = self.get_files_dir(o)
+
+ if not os.path.exists(o.files_dir):
+ os.makedirs(o.files_dir)
+
+ for attr in o.tenantattributes.all():
+ if attr.name.startswith("config_"):
+ fn = attr.name[7:] # .replace("_json",".json")
+ o.config_fns.append(fn)
+ file(os.path.join(o.files_dir, fn),"w").write(attr.value)
+
+ def prepare_record(self, o):
+ self.write_configs(o)
+
+ def get_extra_attributes(self, o):
+ fields={}
+ fields["files_dir"] = o.files_dir
+ fields["appname"] = o.name
+ fields["nat_ip"] = self.get_instance(o).get_ssh_ip()
+ fields["config_fns"] = o.config_fns
+ fields["dependencies"] = [x.strip() for x in o.dependencies.split(",")]
+ fields["ONOS_container"] = "ONOS"
+ return fields
+
+ def sync_fields(self, o, fields):
+ # the super causes the playbook to be run
+ super(SyncONOSApp, self).sync_fields(o, fields)
+
+ def run_playbook(self, o, fields):
+ super(SyncONOSApp, self).run_playbook(o, fields)
+
+ def delete_record(self, m):
+ pass
diff --git a/xos/observers/onos/steps/sync_onosapp.yaml b/xos/observers/onos/steps/sync_onosapp.yaml
new file mode 100644
index 0000000..ad3718c
--- /dev/null
+++ b/xos/observers/onos/steps/sync_onosapp.yaml
@@ -0,0 +1,54 @@
+---
+- hosts: {{ instance_name }}
+ gather_facts: False
+ connection: ssh
+ user: ubuntu
+ sudo: yes
+ vars:
+ appname: {{ appname }}
+ dependencies: {{ dependencies }}
+
+ tasks:
+
+ - name: Config file directory
+ file:
+ path=/home/ubuntu/{{ appname }}/
+ state=directory
+
+{% if config_fns %}
+ - name: Copy over configuration files
+ copy:
+ src={{ files_dir }}/{{ '{{' }} item {{ '}}' }}
+ dest=/home/ubuntu/{{ appname }}/{{ '{{' }} item {{ '}}' }}
+ with_items:
+ {% for config_fn in config_fns %}
+ - {{ config_fn }}
+ {% endfor %}
+
+ - name: Make sure config directory exists
+ shell: docker exec {{ ONOS_container }} mkdir -p /root/onos/config/
+ sudo: yes
+
+ - name: Copy config files into container
+ shell: docker cp {{ appname }}/{{ '{{' }} item {{ '}}' }} {{ ONOS_container }}:/root/onos/config/
+ sudo: yes
+ with_items:
+ {% for config_fn in config_fns %}
+ - {{ config_fn }}
+ {% endfor %}
+{% endif %}
+
+ # Don't know how to check for this condition, just wait
+ - name: Wait for ONOS to install the apps
+ wait_for: timeout=15
+
+ - name: Add dependencies to ONOS
+ uri:
+ url: http://localhost:8181/onos/v1/applications/{{ '{{' }} item {{ '}}' }}/active
+ method: POST
+ user: karaf
+ password: karaf
+ with_items:
+ {% for dependency in dependencies %}
+ - {{ dependency }}
+ {% endfor %}
diff --git a/xos/observers/onos/steps/sync_onosservice.py b/xos/observers/onos/steps/sync_onosservice.py
new file mode 100644
index 0000000..65fa44e
--- /dev/null
+++ b/xos/observers/onos/steps/sync_onosservice.py
@@ -0,0 +1,73 @@
+import hashlib
+import os
+import socket
+import sys
+import base64
+import time
+from django.db.models import F, Q
+from xos.config import Config
+from observer.syncstep import SyncStep
+from observer.ansible import run_template_ssh
+from observers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
+from core.models import Service, Slice
+from services.onos.models import ONOSService, ONOSApp
+from util.logger import Logger, logging
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+logger = Logger(level=logging.INFO)
+
+class SyncONOSService(SyncInstanceUsingAnsible):
+ provides=[ONOSService]
+ observes=ONOSService
+ requested_interval=0
+ template_name = "sync_onosservice.yaml"
+ service_key_name = "/opt/xos/observers/onos/onos_key"
+
+ def __init__(self, *args, **kwargs):
+ super(SyncONOSService, self).__init__(*args, **kwargs)
+
+ def fetch_pending(self, deleted):
+ if (not deleted):
+ objs = ONOSService.get_service_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
+ else:
+ objs = ONOSService.get_deleted_service_objects()
+
+ return objs
+
+ def get_instance(self, o):
+ # We assume the ONOS service owns a slice, so pick one of the instances
+ # inside that slice to sync to.
+
+ serv = o
+
+ if serv.use_external_host:
+ return serv.use_external_host
+
+ if serv.slices.exists():
+ slice = serv.slices.all()[0]
+ if slice.instances.exists():
+ return slice.instances.all()[0]
+
+ return None
+
+ def get_extra_attributes(self, o):
+ fields={}
+ fields["instance_hostname"] = self.get_instance(o).instance_name.replace("_","-")
+ fields["appname"] = o.name
+ fields["nat_ip"] = self.get_instance(o).get_ssh_ip()
+ fields["ONOS_container"] = "ONOS"
+ return fields
+
+ def sync_fields(self, o, fields):
+ # the super causes the playbook to be run
+
+ super(SyncONOSService, self).sync_fields(o, fields)
+
+ def run_playbook(self, o, fields):
+ super(SyncONOSService, self).run_playbook(o, fields)
+
+ def delete_record(self, m):
+ pass
diff --git a/xos/observers/onos/steps/sync_onosservice.yaml b/xos/observers/onos/steps/sync_onosservice.yaml
new file mode 100644
index 0000000..fd9c3db
--- /dev/null
+++ b/xos/observers/onos/steps/sync_onosservice.yaml
@@ -0,0 +1,66 @@
+---
+- hosts: {{ instance_name }}
+ gather_facts: False
+ connection: ssh
+ user: ubuntu
+ sudo: yes
+
+ tasks:
+
+ - name: Fix /etc/hosts
+ lineinfile:
+ dest=/etc/hosts
+ regexp="127.0.0.1 localhost"
+ line="127.0.0.1 localhost {{ instance_hostname }}"
+
+ - name: Add repo key
+ apt_key:
+ keyserver=hkp://pgp.mit.edu:80
+ id=58118E89F3A912897C070ADBF76221572C52609D
+
+ - name: Install Docker repo
+ apt_repository:
+ repo="deb https://apt.dockerproject.org/repo ubuntu-trusty main"
+ state=present
+
+ - name: Install Docker
+ apt:
+ name={{ '{{' }} item {{ '}}' }}
+ state=latest
+ update_cache=yes
+ with_items:
+ - docker-engine
+ - python-pip
+ - python-httplib2
+
+ - name: Install docker-py
+ pip:
+ name=docker-py
+ state=latest
+
+ - name: Start ONOS container
+ docker:
+ docker_api_version: "1.18"
+ name: {{ ONOS_container }}
+ # was: reloaded
+ state: running
+ image: onosproject/onos
+ ports:
+ - "6653:6653"
+ - "8101:8101"
+ - "8181:8181"
+ - "9876:9876"
+
+ - name: Get Docker IP
+ script: /opt/xos/observers/onos/scripts/dockerip.sh {{ ONOS_container }}
+ register: dockerip
+
+ - name: Wait for ONOS to come up
+ wait_for:
+ host={{ '{{' }} dockerip.stdout {{ '}}' }}
+ port={{ '{{' }} item {{ '}}' }}
+ state=present
+ with_items:
+ - 8101
+ - 8181
+ - 9876
diff --git a/xos/observers/onos/stop.sh b/xos/observers/onos/stop.sh
new file mode 100755
index 0000000..17d6eb7
--- /dev/null
+++ b/xos/observers/onos/stop.sh
@@ -0,0 +1 @@
+pkill -9 -f onos-observer.py
diff --git a/xos/observers/onos/supervisor/onos-observer.conf b/xos/observers/onos/supervisor/onos-observer.conf
new file mode 100644
index 0000000..16afa8c
--- /dev/null
+++ b/xos/observers/onos/supervisor/onos-observer.conf
@@ -0,0 +1,2 @@
+[program:onos-observer]
+command=python /opt/xos/observers/onos/onos-observer.py -C /opt/xos/observers/onos/onos_observer_config
diff --git a/xos/observers/vbng/steps/sync_vbngtenant.py b/xos/observers/vbng/steps/sync_vbngtenant.py
index 8868d6f..b603ed6 100644
--- a/xos/observers/vbng/steps/sync_vbngtenant.py
+++ b/xos/observers/vbng/steps/sync_vbngtenant.py
@@ -50,9 +50,38 @@
def get_vbng_url(self, o):
service = self.get_vbng_service(o)
- if not service.vbng_url:
- raise Exception("vBNG service does not have vbng_url set")
- return service.vbng_url
+
+ # if the service object specifies a vbng_url, then use it
+ if service.vbng_url:
+ return service.vbng_url
+
+ # otherwise, see if the service has tenancy in ONOS
+ for tenant in service.subscribed_tenants.all():
+ if tenant.provider_service and tenant.provider_service.kind == "onos":
+ onos_service = tenant.provider_service
+ if not onos_service.slices.exists():
+ raise Exception("vBNG service is linked to an ONOSApp, but the App's Service has no slices")
+ onos_slice = onos_service.slices.all()[0]
+ if not onos_slice.instances.exists():
+ raise Exception("vBNG service is linked to an ONOSApp, but the App's Service's Slice has no instances")
+ instance = onos_slice.instances.all()[0]
+
+ #onos_app = ONOSApp.objects.filter(id = tenant.id)
+ #instance = onos_app.instance
+ #if not instance:
+ # raise Exception("ONOSApp has no instance")
+
+ if not instance.instance_name:
+ raise Exception("vBNG service is linked to an ONOSApp, but the App's Service's Slice's first instance is not instantiated")
+ ip = instance.get_network_ip("nat")
+ if not ip:
+ raise Exception("vBNG service is linked to an ONOSApp, but the App's Service's Slice's first instance does not have an ip")
+
+ logger.info("Using ip %s from ONOS Instance %s" % (ip, instance))
+
+ return "http://%s:8181/onos/virtualbng/" % ip
+
+ raise Exception("vBNG service does not have vbng_url set, and is not linked to an ONOSApp")
def get_private_interface(self, o):
vcpes = VCPETenant.get_tenant_objects().all()
diff --git a/xos/observers/vcpe/steps/sync_vcpetenant.yaml b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
index 1a30656..b485c0f 100644
--- a/xos/observers/vcpe/steps/sync_vcpetenant.yaml
+++ b/xos/observers/vcpe/steps/sync_vcpetenant.yaml
@@ -35,6 +35,11 @@
private_mac: {{ private_mac }}
hpc_client_ip: {{ hpc_client_ip }}
hpc_client_mac: {{ hpc_client_mac }}
+ keystone_tenant_id: {{ keystone_tenant_id }}
+ keystone_user_id: {{ keystone_user_id }}
+ rabbit_user: {{ rabbit_user }}
+ rabbit_password: {{ rabbit_password }}
+ rabbit_host: {{ rabbit_host }}
tasks:
{% if full_setup %}
@@ -73,6 +78,32 @@
- name: Install resolv.conf
copy: src=/opt/xos/observers/vcpe/files/vm-resolv.conf
dest=/etc/resolv.conf
+
+ - name: Verify if vcpe_stats_notifier ([] is to avoid capturing the shell process) cron job is already running
+ shell: pgrep -f [v]cpe_stats_notifier | wc -l
+ register: cron_job_pids_count
+
+# - name: DEBUG
+# debug: var=cron_job_pids_count.stdout
+
+ - name: make sure ~/bin exists
+ file: path=~/bin state=directory owner=root group=root
+ when: cron_job_pids_count.stdout == "0"
+
+ - name: Copy cron job to destination
+ copy: src=/opt/xos/observers/vcpe/vcpe_stats_notifier.py
+ dest=~/bin/vcpe_stats_notifier.py
+ when: cron_job_pids_count.stdout == "0"
+
+ - name: install python-kombu
+ apt: name=python-kombu state=present
+ when: cron_job_pids_count.stdout == "0"
+
+ - name: Initiate vcpe_stats_notifier cron job
+ command: python ~/bin/vcpe_stats_notifier.py --keystone_tenant_id={{ keystone_tenant_id }} --keystone_user_id={{ keystone_user_id }} --rabbit_user={{ rabbit_user }} --rabbit_password={{ rabbit_password }} --rabbit_host={{ rabbit_host }} --vcpeservice_rabbit_exchange='vcpeservice'
+ async: 9999999999999999
+ poll: 0
+ when: cron_job_pids_count.stdout == "0"
{% endif %}
- name: vCPE upstart
diff --git a/xos/observers/vcpe/vcpe_stats_notifier.py b/xos/observers/vcpe/vcpe_stats_notifier.py
new file mode 100644
index 0000000..d726e3c
--- /dev/null
+++ b/xos/observers/vcpe/vcpe_stats_notifier.py
@@ -0,0 +1,268 @@
+import six
+import uuid
+import datetime
+from kombu.connection import BrokerConnection
+from kombu.messaging import Exchange, Queue, Consumer, Producer
+import subprocess
+import re
+import time, threading
+import sys, getopt
+import logging
+
+
+logfile = "vcpe_stats_notifier.log"
+level=logging.INFO
+logger=logging.getLogger('vcpe_stats_notifier')
+logger.setLevel(level)
+# create formatter
+formatter = logging.Formatter("%(asctime)s;%(levelname)s;%(message)s")
+handler=logging.handlers.RotatingFileHandler(logfile,maxBytes=1000000, backupCount=1)
+# add formatter to handler
+handler.setFormatter(formatter)
+logger.addHandler(handler)
+
+def extract_dns_stats_from_all_vcpes():
+ p = subprocess.Popen('docker ps', shell=True, stdout=subprocess.PIPE)
+ firstline = True
+ dockercontainers = {}
+ while True:
+ out = p.stdout.readline()
+ if out == '' and p.poll() != None:
+ break
+ if out != '':
+ if firstline is True:
+ firstline = False
+ else:
+ fields = out.split()
+ container_fields = {}
+ container_fields['id'] = fields[0]
+ dockercontainers[fields[-1]] = container_fields
+ for k,v in dockercontainers.iteritems():
+ cmd = 'docker exec ' + v['id'] + ' killall -10 dnsmasq'
+ p = subprocess.Popen (cmd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
+ (output, error) = p.communicate()
+ if error:
+ logger.error("killall dnsmasq command failed with error = %s",error)
+ continue
+ cmd = 'docker exec ' + v['id'] + ' tail -7 /var/log/syslog'
+ p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
+ (output, error) = p.communicate()
+ if error:
+ logger.error("tail on dnsmasq log command failed with error = %s",error)
+ continue
+ log_list = output.splitlines()
+ i = 0
+ while i < len(log_list):
+ m = re.search('(?<=:\scache size\s)(\S*)(?=,\s),\s(\S*)(?=/)/(\S*)(?=\scache insertions re-used unexpired cache entries)', log_list[i])
+ if m == None:
+ i = i+1
+ continue;
+ v['cache_size'] = m.group(1)
+ v['replaced_unexpired_entries'] = m.group(2)
+ v['total_inserted_entries'] = m.group(3)
+ i = i+1
+ m = re.search('(?<=:\squeries forwarded\s)(\S*)(?=,),\squeries answered locally\s(\S*)(?=$)', log_list[i])
+ v['queries_forwarded'] = m.group(1)
+ v['queries_answered_locally'] = m.group(2)
+ break;
+ i = i+2
+ v['server_stats'] = []
+ while i < len(log_list):
+ m = re.search('(?<=:\sserver\s)(\S*)(?=#)#\d*:\squeries sent\s(\S*)(?=,),\sretried or failed\s(\S*)(?=$)', log_list[i])
+ if m == None:
+ i = i+1
+ continue
+ dns_server = {}
+ dns_server['id'] = m.group(1)
+ dns_server['queries_sent'] = m.group(2)
+ dns_server['queries_failed'] = m.group(3)
+ v['server_stats'].append(dns_server)
+ i = i+1
+ return dockercontainers
+
+
+keystone_tenant_id='3a397e70f64e4e40b69b6266c634d9d0'
+keystone_user_id='1e3ce043029547f1a61c1996d1a531a2'
+rabbit_user='openstack'
+rabbit_password='80608318c273f348a7c3'
+rabbit_host='10.11.10.1'
+vcpeservice_rabbit_exchange='vcpeservice'
+cpe_publisher_id='vcpe_publisher'
+
+producer = None
+
+def setup_rabbit_mq_channel():
+ global producer
+ global rabbit_user, rabbit_password, rabbit_host, vcpeservice_rabbit_exchange,cpe_publisher_id
+ vcpeservice_exchange = Exchange(vcpeservice_rabbit_exchange, "topic", durable=False)
+ # connections/channels
+ connection = BrokerConnection(rabbit_host, rabbit_user, rabbit_password)
+ logger.info('Connection to RabbitMQ server successful')
+ channel = connection.channel()
+ # produce
+ producer = Producer(channel, exchange=vcpeservice_exchange, routing_key='notifications.info')
+ p = subprocess.Popen('hostname', shell=True, stdout=subprocess.PIPE)
+ (hostname, error) = p.communicate()
+ cpe_publisher_id = cpe_publisher_id + '_on_' + hostname
+ logger.info('cpe_publisher_id=%s',cpe_publisher_id)
+
+def publish_cpe_stats():
+ global producer
+ global keystone_tenant_id, keystone_user_id, cpe_publisher_id
+
+ logger.debug('publish_cpe_stats invoked')
+
+ cpe_container_stats = extract_dns_stats_from_all_vcpes()
+
+ for k,v in cpe_container_stats.iteritems():
+ msg = {'event_type': 'vcpe',
+ 'message_id':six.text_type(uuid.uuid4()),
+ 'publisher_id': cpe_publisher_id,
+ 'timestamp':datetime.datetime.now().isoformat(),
+ 'priority':'INFO',
+ 'payload': {'vcpe_id':k,
+ 'user_id':keystone_user_id,
+ 'tenant_id':keystone_tenant_id
+ }
+ }
+ producer.publish(msg)
+ logger.debug('Publishing vcpe event: %s', msg)
+
+ if 'cache_size' in v:
+ msg = {'event_type': 'vcpe.dns.cache.size',
+ 'message_id':six.text_type(uuid.uuid4()),
+ 'publisher_id': cpe_publisher_id,
+ 'timestamp':datetime.datetime.now().isoformat(),
+ 'priority':'INFO',
+ 'payload': {'vcpe_id':k,
+ 'user_id':keystone_user_id,
+ 'tenant_id':keystone_tenant_id,
+ 'cache_size':v['cache_size']
+ }
+ }
+ producer.publish(msg)
+ logger.debug('Publishing vcpe.dns.cache.size event: %s', msg)
+
+ if 'total_inserted_entries' in v:
+ msg = {'event_type': 'vcpe.dns.total_inserted_entries',
+ 'message_id':six.text_type(uuid.uuid4()),
+ 'publisher_id': cpe_publisher_id,
+ 'timestamp':datetime.datetime.now().isoformat(),
+ 'priority':'INFO',
+ 'payload': {'vcpe_id':k,
+ 'user_id':keystone_user_id,
+ 'tenant_id':keystone_tenant_id,
+ 'total_inserted_entries':v['total_inserted_entries']
+ }
+ }
+ producer.publish(msg)
+ logger.debug('Publishing vcpe.dns.total_inserted_entries event: %s', msg)
+
+ if 'replaced_unexpired_entries' in v:
+ msg = {'event_type': 'vcpe.dns.replaced_unexpired_entries',
+ 'message_id':six.text_type(uuid.uuid4()),
+ 'publisher_id': cpe_publisher_id,
+ 'timestamp':datetime.datetime.now().isoformat(),
+ 'priority':'INFO',
+ 'payload': {'vcpe_id':k,
+ 'user_id':keystone_user_id,
+ 'tenant_id':keystone_tenant_id,
+ 'replaced_unexpired_entries':v['replaced_unexpired_entries']
+ }
+ }
+ producer.publish(msg)
+ logger.debug('Publishing vcpe.dns.replaced_unexpired_entries event: %s', msg)
+
+ if 'queries_forwarded' in v:
+ msg = {'event_type': 'vcpe.dns.queries_forwarded',
+ 'message_id':six.text_type(uuid.uuid4()),
+ 'publisher_id': cpe_publisher_id,
+ 'timestamp':datetime.datetime.now().isoformat(),
+ 'priority':'INFO',
+ 'payload': {'vcpe_id':k,
+ 'user_id':keystone_user_id,
+ 'tenant_id':keystone_tenant_id,
+ 'queries_forwarded':v['queries_forwarded']
+ }
+ }
+ producer.publish(msg)
+ logger.debug('Publishing vcpe.dns.queries_forwarded event: %s', msg)
+
+ if 'queries_answered_locally' in v:
+ msg = {'event_type': 'vcpe.dns.queries_answered_locally',
+ 'message_id':six.text_type(uuid.uuid4()),
+ 'publisher_id': cpe_publisher_id,
+ 'timestamp':datetime.datetime.now().isoformat(),
+ 'priority':'INFO',
+ 'payload': {'vcpe_id':k,
+ 'user_id':keystone_user_id,
+ 'tenant_id':keystone_tenant_id,
+ 'queries_answered_locally':v['queries_answered_locally']
+ }
+ }
+ producer.publish(msg)
+ logger.debug('Publishing vcpe.dns.queries_answered_locally event: %s', msg)
+
+ if 'server_stats' in v:
+ for server in v['server_stats']:
+ msg = {'event_type': 'vcpe.dns.server.queries_sent',
+ 'message_id':six.text_type(uuid.uuid4()),
+ 'publisher_id': cpe_publisher_id,
+ 'timestamp':datetime.datetime.now().isoformat(),
+ 'priority':'INFO',
+ 'payload': {'vcpe_id':k,
+ 'user_id':keystone_user_id,
+ 'tenant_id':keystone_tenant_id,
+ 'upstream_server':server['id'],
+ 'queries_sent':server['queries_sent']
+ }
+ }
+ producer.publish(msg)
+ logger.debug('Publishing vcpe.dns.server.queries_sent event: %s', msg)
+
+ msg = {'event_type': 'vcpe.dns.server.queries_failed',
+ 'message_id':six.text_type(uuid.uuid4()),
+ 'publisher_id': cpe_publisher_id,
+ 'timestamp':datetime.datetime.now().isoformat(),
+ 'priority':'INFO',
+ 'payload': {'vcpe_id':k,
+ 'user_id':keystone_user_id,
+ 'tenant_id':keystone_tenant_id,
+ 'upstream_server':server['id'],
+ 'queries_failed':server['queries_failed']
+ }
+ }
+ producer.publish(msg)
+ logger.debug('Publishing vcpe.dns.server.queries_failed event: %s', msg)
+
+def periodic_publish():
+ publish_cpe_stats()
+ #Publish every 5minutes
+ threading.Timer(300, periodic_publish).start()
+
+def main(argv):
+ global keystone_tenant_id, keystone_user_id, rabbit_user, rabbit_password, rabbit_host, vcpeservice_rabbit_exchange
+ try:
+ opts, args = getopt.getopt(argv,"",["keystone_tenant_id=","keystone_user_id=","rabbit_host=","rabbit_user=","rabbit_password=","vcpeservice_rabbit_exchange="])
+ except getopt.GetoptError:
+ print 'vcpe_stats_notifier.py keystone_tenant_id=<keystone_tenant_id> keystone_user_id=<keystone_user_id> rabbit_host=<IP addr> rabbit_user=<user> rabbit_password=<password> vcpeservice_rabbit_exchange=<exchange name>'
+ sys.exit(2)
+ for opt, arg in opts:
+ if opt in ("--keystone_tenant_id"):
+ keystone_tenant_id = arg
+ elif opt in ("--keystone_user_id"):
+ keystone_user_id = arg
+ elif opt in ("--rabbit_user"):
+ rabbit_user = arg
+ elif opt in ("--rabbit_password"):
+ rabbit_password = arg
+ elif opt in ("--rabbit_host"):
+ rabbit_host = arg
+ elif opt in ("--vcpeservice_rabbit_exchange"):
+ vcpeservice_rabbit_exchange = arg
+ logger.info("vcpe_stats_notifier args:keystone_tenant_id=%s keystone_user_id=%s rabbit_user=%s rabbit_host=%s vcpeservice_rabbit_exchange=%s",keystone_tenant_id,keystone_user_id,rabbit_user,rabbit_host,vcpeservice_rabbit_exchange)
+ setup_rabbit_mq_channel()
+ periodic_publish()
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/xos/openstack_observer/ansible.py b/xos/openstack_observer/ansible.py
index ee110c9..94b09bd 100644
--- a/xos/openstack_observer/ansible.py
+++ b/xos/openstack_observer/ansible.py
@@ -144,16 +144,21 @@
return ok_results
def run_template_ssh(name, opts, path='', expected_num=None):
- instance_id = opts["instance_id"]
instance_name = opts["instance_name"]
hostname = opts["hostname"]
private_key = opts["private_key"]
- nat_ip = opts["nat_ip"]
-
- try:
- proxy_ssh = Config().observer_proxy_ssh
- except:
- proxy_ssh = True
+ baremetal_ssh = opts.get("baremetal_ssh",False)
+ if baremetal_ssh:
+ # no instance_id or nat_ip for baremetal
+ # we never proxy to baremetal
+ proxy_ssh = False
+ else:
+ instance_id = opts["instance_id"]
+ nat_ip = opts["nat_ip"]
+ try:
+ proxy_ssh = Config().observer_proxy_ssh
+ except:
+ proxy_ssh = True
(opts, fqp) = get_playbook_fn(opts, path)
private_key_pathname = fqp + ".key"
@@ -177,7 +182,7 @@
f = open(hosts_pathname, "w")
f.write("[%s]\n" % instance_name)
- if proxy_ssh:
+ if proxy_ssh or baremetal_ssh:
f.write("%s ansible_ssh_private_key_file=%s\n" % (hostname, private_key_pathname))
else:
# acb: Login user is hardcoded, this is not great
diff --git a/xos/openstack_observer/steps/sync_container.py b/xos/openstack_observer/steps/sync_container.py
new file mode 100644
index 0000000..c6ac936
--- /dev/null
+++ b/xos/openstack_observer/steps/sync_container.py
@@ -0,0 +1,90 @@
+import hashlib
+import os
+import socket
+import sys
+import base64
+import time
+from django.db.models import F, Q
+from xos.config import Config
+from observer.syncstep import SyncStep
+from observer.ansible import run_template_ssh
+from core.models import Service, Slice, Container
+from services.onos.models import ONOSService, ONOSApp
+from util.logger import Logger, logging
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+logger = Logger(level=logging.INFO)
+
+class SyncContainer(SyncStep):
+ provides=[Container]
+ observes=Container
+ requested_interval=0
+ template_name = "sync_container.yaml"
+
+ def __init__(self, *args, **kwargs):
+ super(SyncContainer, self).__init__(*args, **kwargs)
+
+# def fetch_pending(self, deleted):
+# if (not deleted):
+# objs = ONOSService.get_service_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
+# else:
+# objs = ONOSService.get_deleted_service_objects()
+#
+# return objs
+
+ def get_node(self,o):
+ return o.node
+
+ def get_node_key(self, node):
+ return "/opt/xos/node-key"
+
+ def get_extra_attributes(self, o):
+ fields={}
+ fields["ansible_tag"] = "container-%s" % str(o.id)
+ fields["baremetal_ssh"] = True
+ fields["instance_name"] = "rootcontext"
+ fields["container_name"] = o.name
+ fields["docker_image"] = o.docker_image
+ fields["username"] = "xos"
+ return fields
+
+ def sync_fields(self, o, fields):
+ self.run_playbook(o, fields)
+
+ def sync_record(self, o):
+ logger.info("sync'ing object %s" % str(o))
+
+ node = self.get_node(o)
+ node_key_name = self.get_node_key(node)
+
+ if not os.path.exists(node_key_name):
+ raise Exception("Node key %s does not exist" % node_key_name)
+
+ node_key = file(node_key_name).read()
+
+ fields = { "hostname": node.name,
+ "private_key": node_key,
+ }
+
+ # If 'o' defines a 'sync_attributes' list, then we'll copy those
+ # attributes into the Ansible recipe's field list automatically.
+ if hasattr(o, "sync_attributes"):
+ for attribute_name in o.sync_attributes:
+ fields[attribute_name] = getattr(o, attribute_name)
+
+ fields.update(self.get_extra_attributes(o))
+
+ self.sync_fields(o, fields)
+
+ o.save()
+
+ def run_playbook(self, o, fields):
+ tStart = time.time()
+ run_template_ssh(self.template_name, fields, path="container")
+ logger.info("playbook execution time %d" % int(time.time()-tStart))
+
+ def delete_record(self, m):
+ pass
diff --git a/xos/openstack_observer/steps/sync_container.yaml b/xos/openstack_observer/steps/sync_container.yaml
new file mode 100644
index 0000000..d97f0df
--- /dev/null
+++ b/xos/openstack_observer/steps/sync_container.yaml
@@ -0,0 +1,48 @@
+---
+- hosts: {{ instance_name }}
+ gather_facts: False
+ connection: ssh
+ user: {{ username }}
+ sudo: yes
+
+ tasks:
+
+# - name: Fix /etc/hosts
+# lineinfile:
+# dest=/etc/hosts
+# regexp="127.0.0.1 localhost"
+# line="127.0.0.1 localhost {{ instance_hostname }}"
+
+ - name: Add repo key
+ apt_key:
+ keyserver=hkp://pgp.mit.edu:80
+ id=58118E89F3A912897C070ADBF76221572C52609D
+
+ - name: Install Docker repo
+ apt_repository:
+ repo="deb https://apt.dockerproject.org/repo ubuntu-trusty main"
+ state=present
+
+ - name: Install Docker
+ apt:
+ name={{ '{{' }} item {{ '}}' }}
+ state=latest
+ update_cache=yes
+ with_items:
+ - docker-engine
+ - python-pip
+ - python-httplib2
+
+ - name: Install docker-py
+ pip:
+ name=docker-py
+ state=latest
+
+ - name: Start Container
+ docker:
+ docker_api_version: "1.18"
+ name: {{ container_name }}
+ # was: reloaded
+ state: running
+ image: {{ docker_image }}
+
diff --git a/xos/openstack_observer/steps/sync_ports.py b/xos/openstack_observer/steps/sync_ports.py
index 967bef9..7b20d29 100644
--- a/xos/openstack_observer/steps/sync_ports.py
+++ b/xos/openstack_observer/steps/sync_ports.py
@@ -144,9 +144,14 @@
# For ports that were created by the user, find that ones
# that don't have neutron ports, and create them.
- for port in Port.objects.filter(port_id__isnull=True, instance__isnull=False):
- #logger.info("XXX working on port %s" % port)
- controller = port.instance.node.site_deployment.controller
+ for port in Port.objects.filter(Q(port_id__isnull=True), Q(instance__isnull=False) | Q(container__isnull=False)):
+ logger.info("XXX working on port %s" % port)
+ if port.instance:
+ controller = port.instance.node.site_deployment.controller
+ slice = port.instance.slice
+ else:
+ controller = port.container.node.site_deployment.controller
+ slice = port.container.slice
if controller:
cn=port.network.controllernetworks.filter(controller=controller)
if not cn:
@@ -174,7 +179,7 @@
caller = port.network.owner.creator
auth = {'username': caller.email,
'password': caller.remote_password,
- 'tenant': port.instance.slice.name} # port.network.owner.name}
+ 'tenant': slice.name}
client = OpenStackClient(controller=controller, **auth) # cacert=self.config.nova_ca_ssl_cert,
driver = OpenStackDriver(client=client)
@@ -183,6 +188,10 @@
if neutron_port["fixed_ips"]:
port.ip = neutron_port["fixed_ips"][0]["ip_address"]
port.mac = neutron_port["mac_address"]
+
+ neutron_network = driver.shell.quantum.list_networks(cn.net_id)["networks"][0]
+ if "provider:segmentation_id" in neutron_network:
+ port.segmentation_id = neutron_network["provider:segmentation_id"]
except:
logger.log_exc("failed to create neutron port for %s" % port)
continue
diff --git a/xos/openstack_synchronizer b/xos/openstack_synchronizer
new file mode 120000
index 0000000..ae75af5
--- /dev/null
+++ b/xos/openstack_synchronizer
@@ -0,0 +1 @@
+openstack_observer
\ No newline at end of file
diff --git a/xos/services/onos/admin.py b/xos/services/onos/admin.py
index b2e5349..d13a991 100644
--- a/xos/services/onos/admin.py
+++ b/xos/services/onos/admin.py
@@ -18,15 +18,32 @@
from django.core.urlresolvers import reverse
from django.contrib.admin.utils import quote
+class ONOSServiceForm(forms.ModelForm):
+ use_external_host = forms.CharField(required=False)
+
+ def __init__(self,*args,**kwargs):
+ super (ONOSServiceForm,self ).__init__(*args,**kwargs)
+ if self.instance:
+ # fields for the attributes
+ self.fields['use_external_host'].initial = self.instance.use_external_host
+
+ def save(self, commit=True):
+ self.instance.use_external_host = self.cleaned_data.get("use_external_host")
+ return super(ONOSServiceForm, self).save(commit=commit)
+
+ class Meta:
+ model = ONOSService
+
class ONOSServiceAdmin(ReadOnlyAwareAdmin):
model = ONOSService
verbose_name = "ONOS Service"
verbose_name_plural = "ONOS Services"
list_display = ("backend_status_icon", "name", "enabled")
list_display_links = ('backend_status_icon', 'name', )
- fieldsets = [(None, {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description',"view_url","icon_url" ], 'classes':['suit-tab suit-tab-general']})]
+ fieldsets = [(None, {'fields': ['backend_status_text', 'name','enabled','versionNumber', 'description',"view_url","icon_url", "use_external_host" ], 'classes':['suit-tab suit-tab-general']})]
readonly_fields = ('backend_status_text', )
inlines = [SliceInline,ServiceAttrAsTabInline,ServicePrivilegeInline]
+ form = ONOSServiceForm
extracontext_registered_admins = True
@@ -48,6 +65,7 @@
class ONOSAppForm(forms.ModelForm):
creator = forms.ModelChoiceField(queryset=User.objects.all())
name = forms.CharField()
+ dependencies = forms.CharField(required=False)
def __init__(self,*args,**kwargs):
super (ONOSAppForm,self ).__init__(*args,**kwargs)
@@ -57,6 +75,7 @@
# fields for the attributes
self.fields['creator'].initial = self.instance.creator
self.fields['name'].initial = self.instance.name
+ self.fields['dependencies'].initial = self.instance.dependencies
if (not self.instance) or (not self.instance.pk):
# default fields for an 'add' form
self.fields['kind'].initial = ONOS_KIND
@@ -64,10 +83,10 @@
if ONOSService.get_service_objects().exists():
self.fields["provider_service"].initial = ONOSService.get_service_objects().all()[0]
-
def save(self, commit=True):
self.instance.creator = self.cleaned_data.get("creator")
self.instance.name = self.cleaned_data.get("name")
+ self.instance.dependencies = self.cleaned_data.get("dependencies")
return super(ONOSAppForm, self).save(commit=commit)
class Meta:
@@ -76,7 +95,7 @@
class ONOSAppAdmin(ReadOnlyAwareAdmin):
list_display = ('backend_status_icon', 'name', )
list_display_links = ('backend_status_icon', 'name')
- fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'name', 'provider_service', 'service_specific_attribute',
+ fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'name', 'provider_service', 'subscriber_service', 'service_specific_attribute', "dependencies",
'creator'],
'classes':['suit-tab suit-tab-general']})]
readonly_fields = ('backend_status_text', 'instance', 'service_specific_attribute')
diff --git a/xos/services/onos/models.py b/xos/services/onos/models.py
index 018bd25..80e903e 100644
--- a/xos/services/onos/models.py
+++ b/xos/services/onos/models.py
@@ -21,13 +21,24 @@
verbose_name = "ONOS Service"
proxy = True
+ default_attributes = {"use_external_host": ""}
+
+ @property
+ def use_external_host(self):
+ return self.get_attribute("use_external_host", self.default_attributes["use_external_host"])
+
+ @use_external_host.setter
+ def use_external_host(self, value):
+ self.set_attribute("use_external_host", value)
+
class ONOSApp(Tenant): # aka 'ONOSTenant'
class Meta:
proxy = True
KIND = ONOS_KIND
- default_attributes = {"name": ""}
+ default_attributes = {"name": "",
+ "dependencies": ""}
def __init__(self, *args, **kwargs):
onos_services = ONOSService.get_service_objects().all()
if onos_services:
@@ -65,6 +76,27 @@
def name(self, value):
self.set_attribute("name", value)
+ @property
+ def dependencies(self):
+ return self.get_attribute("dependencies", self.default_attributes["dependencies"])
+
+ @dependencies.setter
+ def dependencies(self, value):
+ self.set_attribute("dependencies", value)
+
+ #@property
+ #def instance(self):
+ # instance_id = self.get_attribute("instance_id", self.default_attributes["instance_id"])
+ # if instance_id:
+ # instances = Instance.objects.filter(id=instance_id)
+ # if instances:
+ # return instances[0]
+ # return None
+
+ #@instance.setter
+ #def instance(self, value):
+ # self.set_attribute("instance_id", value.id)
+
def save(self, *args, **kwargs):
if not self.creator:
if not getattr(self, "caller", None):
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 3d766f1..e125150 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -104,17 +104,36 @@
An ONOS Application.
properties:
xos_base_tenant_props
+ dependencies:
+ type: string
+ required: false
tosca.nodes.ONOSvBNGApp:
derived_from: tosca.nodes.Root
description: >
- An ONOS Application.
+ An ONOS vBNG Application.
properties:
xos_base_tenant_props
- config_addresses_json:
+ dependencies:
type: string
required: false
- config_virtualbng_json:
+ config_addresses.json:
+ type: string
+ required: false
+ config_network-cfg.json:
+ type: string
+ required: false
+ config_virtualbng.json:
+ type: string
+ required: false
+
+ tosca.nodes.ONOSvOLTApp:
+ derived_from: tosca.nodes.Root
+ description: >
+ An ONOS vOLT Application.
+ properties:
+ xos_base_tenant_props
+ dependencies:
type: string
required: false
@@ -472,6 +491,18 @@
type: string
required: false
description: OpenStack domain (or "Default")
+ rabbit_host:
+ type: string
+ required: false
+ description: Rabbit host
+ rabbit_user:
+ type: string
+ required: false
+ description: Rabbit user
+ rabbit_password:
+ type: string
+ required: false
+ description: Rabbit password
tosca.nodes.Site:
derived_from: tosca.nodes.Root
@@ -563,6 +594,10 @@
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.Service ]
+ tosca.relationships.UsedByService:
+ derived_from: tosca.relationships.Root
+ valid_target_types: [ tosca.capabilities.xos.Service ]
+
tosca.relationships.ControllerDeployment:
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.Deployment ]
@@ -666,4 +701,3 @@
tosca.capabilities.xos.Image:
derived_from: tosca.capabilities.Root
description: An XOS Image
-
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 97031a6..24be6af 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -108,11 +108,14 @@
type: string
required: false
description: Service specific ID opaque to XOS but meaningful to service
+ dependencies:
+ type: string
+ required: false
tosca.nodes.ONOSvBNGApp:
derived_from: tosca.nodes.Root
description: >
- An ONOS Application.
+ An ONOS vBNG Application.
properties:
kind:
type: string
@@ -122,10 +125,33 @@
type: string
required: false
description: Service specific ID opaque to XOS but meaningful to service
- config_addresses_json:
+ dependencies:
type: string
required: false
- config_virtualbng_json:
+ config_addresses.json:
+ type: string
+ required: false
+ config_network-cfg.json:
+ type: string
+ required: false
+ config_virtualbng.json:
+ type: string
+ required: false
+
+ tosca.nodes.ONOSvOLTApp:
+ derived_from: tosca.nodes.Root
+ description: >
+ An ONOS vOLT Application.
+ properties:
+ kind:
+ type: string
+ default: generic
+ description: Kind of tenant
+ service_specific_id:
+ type: string
+ required: false
+ description: Service specific ID opaque to XOS but meaningful to service
+ dependencies:
type: string
required: false
@@ -615,6 +641,18 @@
type: string
required: false
description: OpenStack domain (or "Default")
+ rabbit_host:
+ type: string
+ required: false
+ description: Rabbit host
+ rabbit_user:
+ type: string
+ required: false
+ description: Rabbit user
+ rabbit_password:
+ type: string
+ required: false
+ description: Rabbit password
tosca.nodes.Site:
derived_from: tosca.nodes.Root
@@ -739,6 +777,10 @@
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.Service ]
+ tosca.relationships.UsedByService:
+ derived_from: tosca.relationships.Root
+ valid_target_types: [ tosca.capabilities.xos.Service ]
+
tosca.relationships.ControllerDeployment:
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.Deployment ]
@@ -842,4 +884,3 @@
tosca.capabilities.xos.Image:
derived_from: tosca.capabilities.Root
description: An XOS Image
-
diff --git a/xos/tosca/resources/controller.py b/xos/tosca/resources/controller.py
index 9a20ea5..2aa208c 100644
--- a/xos/tosca/resources/controller.py
+++ b/xos/tosca/resources/controller.py
@@ -15,7 +15,7 @@
class XOSController(XOSResource):
provides = "tosca.nodes.Controller"
xos_model = Controller
- copyin_props = ["backend_type", "version", "auth_url", "admin_user", "admin_password", "admin_tenant", "domain"]
+ copyin_props = ["backend_type", "version", "auth_url", "admin_user", "admin_password", "admin_tenant", "domain", "rabbit_host", "rabbit_user", "rabbit_password"]
def get_xos_args(self):
args = super(XOSController, self).get_xos_args()
diff --git a/xos/tosca/resources/onosapp.py b/xos/tosca/resources/onosapp.py
index 50af543..111cf9a 100644
--- a/xos/tosca/resources/onosapp.py
+++ b/xos/tosca/resources/onosapp.py
@@ -6,23 +6,29 @@
from translator.toscalib.tosca_template import ToscaTemplate
import pdb
-from core.models import User, TenantAttribute
+from core.models import User, TenantAttribute, Service
from services.onos.models import ONOSApp, ONOSService
from xosresource import XOSResource
class XOSONOSApp(XOSResource):
- provides = ["tosca.nodes.ONOSApp", "tosca.nodes.ONOSvBNGApp"]
+ provides = ["tosca.nodes.ONOSApp", "tosca.nodes.ONOSvBNGApp", "tosca.nodes.ONOSvOLTApp"]
xos_model = ONOSApp
- copyin_props = ["service_specific_id"]
+ copyin_props = ["service_specific_id", "dependencies"]
def get_xos_args(self, throw_exception=True):
args = super(XOSONOSApp, self).get_xos_args()
+ # provider_service is mandatory and must be the ONOS Service
provider_name = self.get_requirement("tosca.relationships.TenantOfService", throw_exception=throw_exception)
if provider_name:
args["provider_service"] = self.get_xos_object(ONOSService, throw_exception=throw_exception, name=provider_name)
+ # subscriber_service is optional and can be any service
+ subscriber_name = self.get_requirement("tosca.relationships.UsedByService", throw_exception=False)
+ if subscriber_name:
+ args["subscriber_service"] = self.get_xos_object(Service, throw_exception=throw_exception, name=subscriber_name)
+
return args
def get_existing_objs(self):
diff --git a/xos/tosca/samples/new_site_deploy_slice.yaml b/xos/tosca/samples/new_site_deploy_slice.yaml
index 6ccadc1..5e36bcb 100644
--- a/xos/tosca/samples/new_site_deploy_slice.yaml
+++ b/xos/tosca/samples/new_site_deploy_slice.yaml
@@ -27,6 +27,9 @@
admin_password: letmeout
admin_tenant: 12345678
domain: mydomain
+ rabbit_host: rabhost
+ rabbit_user: rabuser
+ rabbit_password: rabpw
newsite:
type: tosca.nodes.Site
diff --git a/xos/tosca/samples/onos.yaml b/xos/tosca/samples/onos.yaml
index 311819e..a549515 100644
--- a/xos/tosca/samples/onos.yaml
+++ b/xos/tosca/samples/onos.yaml
@@ -13,6 +13,9 @@
properties:
kind: onos
view_url: /admin/onos/onosservice/$id$/
+ public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+ artifacts:
+ pubkey: /opt/xos/observers/onos/onos_key.pub
vBNG:
type: tosca.nodes.ONOSvBNGApp
@@ -21,7 +24,8 @@
node: ONOS
relationship: tosca.relationships.TenantOfService
properties:
- config_addresses_json: >
+ dependencies: org.onosproject.proxyarp, org.onosproject.virtualbng, org.onosproject.openflow, org.onosproject.fwd
+ config_addresses.json: >
{
"addresses" : [
{
@@ -39,7 +43,7 @@
}
]
}
- config_virtualbng_json: >
+ config_virtualbng.json: >
{
"localPublicIpPrefixes" : [
"200.0.0.0/32",
@@ -52,3 +56,48 @@
"xosRestPort" : "9999"
}
+ vOLT:
+ type: tosca.nodes.ONOSvOLTApp
+ requirements:
+ - onos_tenant:
+ node: ONOS
+ relationship: tosca.relationships.TenantOfService
+ properties:
+ dependencies: org.onosproject.olt
+
+ mysite:
+ type: tosca.nodes.Site
+
+ mysite_onos:
+ description: ONOS Controller Slice
+ type: tosca.nodes.Slice
+ requirements:
+ - ONOS:
+ node: ONOS
+ relationship: tosca.relationships.MemberOfService
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+
+ my_server:
+ type: tosca.nodes.Compute
+ capabilities:
+ # Host container properties
+ host:
+ properties:
+ num_cpus: 1
+ disk_size: 10 GB
+ mem_size: 4 MB
+ # Guest Operating System properties
+ os:
+ properties:
+ # host Operating System image properties
+ architecture: x86_64
+ type: linux
+ distribution: Ubuntu
+ version: 14.10
+ requirements:
+ - slice:
+ node: mysite_onos
+ relationship: tosca.relationships.MemberOfSlice
+
diff --git a/xos/uwsgi/xos.ini b/xos/uwsgi/xos.ini
index da7f7dd..63eb6b2 100644
--- a/xos/uwsgi/xos.ini
+++ b/xos/uwsgi/xos.ini
@@ -2,17 +2,17 @@
chdir = /opt/xos
module = xos.wsgi:application
env = DJANGO_SETTINGS_MODULE=xos.settings
-socket = /var/run/uwsgi/xos.sock
+socket = /var/run/uwsgi.xos.sock
socket = 127.0.0.1:9001
-http = 128.112.139.48:9002
+http = 127.0.0.1:9002
stats = 127.0.0.1:9003
workers = 3
master = true
processes = 8
-uid = uwsgi
-gid = webserver
+uid = root
+gid = root
harakiri = 20
-daemonize=/var/log/uwsgi/xos.log
+daemonize=/var/log/uwsgi.xos.log
static-map = /static=/var/www/xos/static
-pidfile = /var/run/uwsgi/uwsgi.pid
+pidfile = /var/run/uwsgi.xos.pid
buffer-size = 8192
diff --git a/xos/xos/hpcapi.py b/xos/xos/hpcapi.py
new file mode 100644
index 0000000..f0e3a6c
--- /dev/null
+++ b/xos/xos/hpcapi.py
@@ -0,0 +1,824 @@
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from rest_framework.reverse import reverse
+from rest_framework import serializers
+from rest_framework import generics
+from rest_framework import status
+from rest_framework.generics import GenericAPIView
+from hpc.models import *
+from django.forms import widgets
+from rest_framework import filters
+from django.conf.urls import patterns, url
+from rest_framework.exceptions import PermissionDenied as RestFrameworkPermissionDenied
+from django.core.exceptions import PermissionDenied as DjangoPermissionDenied
+from apibase import XOSRetrieveUpdateDestroyAPIView, XOSListCreateAPIView, XOSNotAuthenticated
+
+if hasattr(serializers, "ReadOnlyField"):
+ # rest_framework 3.x
+ IdField = serializers.ReadOnlyField
+else:
+ # rest_framework 2.x
+ IdField = serializers.Field
+
+"""
+ Schema of the generator object:
+ all: Set of all Model objects
+ all_if(regex): Set of Model objects that match regex
+
+ Model object:
+ plural: English plural of object name
+ camel: CamelCase version of object name
+ refs: list of references to other Model objects
+ props: list of properties minus refs
+
+ TODO: Deal with subnets
+"""
+
+def get_hpc_REST_patterns():
+ return patterns('',
+ url(r'^hpcapi/$', hpc_api_root),
+
+ url(r'hpcapi/hpchealthchecks/$', HpcHealthCheckList.as_view(), name='hpchealthcheck-list'),
+ url(r'hpcapi/hpchealthchecks/(?P<pk>[a-zA-Z0-9\-]+)/$', HpcHealthCheckDetail.as_view(), name ='hpchealthcheck-detail'),
+
+ url(r'hpcapi/hpcservices/$', HpcServiceList.as_view(), name='hpcservice-list'),
+ url(r'hpcapi/hpcservices/(?P<pk>[a-zA-Z0-9\-]+)/$', HpcServiceDetail.as_view(), name ='hpcservice-detail'),
+
+ url(r'hpcapi/originservers/$', OriginServerList.as_view(), name='originserver-list'),
+ url(r'hpcapi/originservers/(?P<pk>[a-zA-Z0-9\-]+)/$', OriginServerDetail.as_view(), name ='originserver-detail'),
+
+ url(r'hpcapi/cdnprefixs/$', CDNPrefixList.as_view(), name='cdnprefix-list'),
+ url(r'hpcapi/cdnprefixs/(?P<pk>[a-zA-Z0-9\-]+)/$', CDNPrefixDetail.as_view(), name ='cdnprefix-detail'),
+
+ url(r'hpcapi/serviceproviders/$', ServiceProviderList.as_view(), name='serviceprovider-list'),
+ url(r'hpcapi/serviceproviders/(?P<pk>[a-zA-Z0-9\-]+)/$', ServiceProviderDetail.as_view(), name ='serviceprovider-detail'),
+
+ url(r'hpcapi/contentproviders/$', ContentProviderList.as_view(), name='contentprovider-list'),
+ url(r'hpcapi/contentproviders/(?P<pk>[a-zA-Z0-9\-]+)/$', ContentProviderDetail.as_view(), name ='contentprovider-detail'),
+
+ url(r'hpcapi/accessmaps/$', AccessMapList.as_view(), name='accessmap-list'),
+ url(r'hpcapi/accessmaps/(?P<pk>[a-zA-Z0-9\-]+)/$', AccessMapDetail.as_view(), name ='accessmap-detail'),
+
+ url(r'hpcapi/sitemaps/$', SiteMapList.as_view(), name='sitemap-list'),
+ url(r'hpcapi/sitemaps/(?P<pk>[a-zA-Z0-9\-]+)/$', SiteMapDetail.as_view(), name ='sitemap-detail'),
+
+ )
+
+@api_view(['GET'])
+def hpc_api_root(request, format=None):
+ return Response({
+ 'hpchealthchecks': reverse('hpchealthcheck-list', request=request, format=format),
+ 'hpcservices': reverse('hpcservice-list', request=request, format=format),
+ 'originservers': reverse('originserver-list', request=request, format=format),
+ 'cdnprefixs': reverse('cdnprefix-list', request=request, format=format),
+ 'serviceproviders': reverse('serviceprovider-list', request=request, format=format),
+ 'contentproviders': reverse('contentprovider-list', request=request, format=format),
+ 'accessmaps': reverse('accessmap-list', request=request, format=format),
+ 'sitemaps': reverse('sitemap-list', request=request, format=format),
+
+ })
+
+# Based on serializers.py
+
+class XOSModelSerializer(serializers.ModelSerializer):
+ def save_object(self, obj, **kwargs):
+
+ """ rest_framework can't deal with ManyToMany relations that have a
+ through table. In xos, most of the through tables we have
+ use defaults or blank fields, so there's no reason why we shouldn't
+ be able to save these objects.
+
+ So, let's strip out these m2m relations, and deal with them ourself.
+ """
+ obj._complex_m2m_data={};
+ if getattr(obj, '_m2m_data', None):
+ for relatedObject in obj._meta.get_all_related_many_to_many_objects():
+ if (relatedObject.field.rel.through._meta.auto_created):
+ # These are non-trough ManyToMany relations and
+ # can be updated just fine
+ continue
+ fieldName = relatedObject.get_accessor_name()
+ if fieldName in obj._m2m_data.keys():
+ obj._complex_m2m_data[fieldName] = (relatedObject, obj._m2m_data[fieldName])
+ del obj._m2m_data[fieldName]
+
+ serializers.ModelSerializer.save_object(self, obj, **kwargs);
+
+ for (accessor, stuff) in obj._complex_m2m_data.items():
+ (relatedObject, data) = stuff
+ through = relatedObject.field.rel.through
+ local_fieldName = relatedObject.field.m2m_reverse_field_name()
+ remote_fieldName = relatedObject.field.m2m_field_name()
+
+ # get the current set of existing relations
+ existing = through.objects.filter(**{local_fieldName: obj});
+
+ data_ids = [item.id for item in data]
+ existing_ids = [getattr(item,remote_fieldName).id for item in existing]
+
+ #print "data_ids", data_ids
+ #print "existing_ids", existing_ids
+
+ # remove relations that are in 'existing' but not in 'data'
+ for item in list(existing):
+ if (getattr(item,remote_fieldName).id not in data_ids):
+ print "delete", getattr(item,remote_fieldName)
+ item.delete() #(purge=True)
+
+ # add relations that are in 'data' but not in 'existing'
+ for item in data:
+ if (item.id not in existing_ids):
+ #print "add", item
+ newModel = through(**{local_fieldName: obj, remote_fieldName: item})
+ newModel.save()
+
+
+
+class HpcHealthCheckSerializer(serializers.HyperlinkedModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = HpcHealthCheck
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','kind','resource_name','result_contains','result_min_size','result_max_size',)
+
+class HpcHealthCheckIdSerializer(XOSModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = HpcHealthCheck
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','kind','resource_name','result_contains','result_min_size','result_max_size',)
+
+
+
+
+class HpcServiceSerializer(serializers.HyperlinkedModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = HpcService
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','service_specific_id','service_specific_attribute','service_ptr','cmi_hostname','hpc_port80','watcher_hpc_network','watcher_dnsdemux_network','watcher_dnsredir_network',)
+
+class HpcServiceIdSerializer(XOSModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = HpcService
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','service_specific_id','service_specific_attribute','service_ptr','cmi_hostname','hpc_port80','watcher_hpc_network','watcher_dnsdemux_network','watcher_dnsredir_network',)
+
+
+
+
+class OriginServerSerializer(serializers.HyperlinkedModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = OriginServer
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','origin_server_id','url','contentProvider','authenticated','enabled','protocol','redirects','description',)
+
+class OriginServerIdSerializer(XOSModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = OriginServer
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','origin_server_id','url','contentProvider','authenticated','enabled','protocol','redirects','description',)
+
+
+
+
+class CDNPrefixSerializer(serializers.HyperlinkedModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = CDNPrefix
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','cdn_prefix_id','prefix','contentProvider','description','defaultOriginServer','enabled',)
+
+class CDNPrefixIdSerializer(XOSModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = CDNPrefix
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','cdn_prefix_id','prefix','contentProvider','description','defaultOriginServer','enabled',)
+
+
+
+
+class ServiceProviderSerializer(serializers.HyperlinkedModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = ServiceProvider
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','service_provider_id','name','description','enabled',)
+
+class ServiceProviderIdSerializer(XOSModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = ServiceProvider
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','service_provider_id','name','description','enabled',)
+
+
+
+
+class ContentProviderSerializer(serializers.HyperlinkedModelSerializer):
+ id = IdField()
+
+
+ users = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='user-detail')
+
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = ContentProvider
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','content_provider_id','name','enabled','description','serviceProvider','users',)
+
+class ContentProviderIdSerializer(XOSModelSerializer):
+ id = IdField()
+
+
+ users = serializers.PrimaryKeyRelatedField(many=True, queryset = User.objects.all())
+
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = ContentProvider
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','content_provider_id','name','enabled','description','serviceProvider','users',)
+
+
+
+
+class AccessMapSerializer(serializers.HyperlinkedModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = AccessMap
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','name','description','map',)
+
+class AccessMapIdSerializer(XOSModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = AccessMap
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','name','description','map',)
+
+
+
+
+class SiteMapSerializer(serializers.HyperlinkedModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = SiteMap
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','serviceProvider','cdnPrefix','hpcService','name','description','map','map_id',)
+
+class SiteMapIdSerializer(XOSModelSerializer):
+ id = IdField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ validators = serializers.SerializerMethodField("getValidators")
+ def getHumanReadableName(self, obj):
+ return str(obj)
+ def getValidators(self, obj):
+ try:
+ return obj.getValidators()
+ except:
+ return None
+ class Meta:
+ model = SiteMap
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','serviceProvider','cdnPrefix','hpcService','name','description','map','map_id',)
+
+
+
+
+serializerLookUp = {
+
+ HpcHealthCheck: HpcHealthCheckSerializer,
+
+ HpcService: HpcServiceSerializer,
+
+ OriginServer: OriginServerSerializer,
+
+ CDNPrefix: CDNPrefixSerializer,
+
+ ServiceProvider: ServiceProviderSerializer,
+
+ ContentProvider: ContentProviderSerializer,
+
+ AccessMap: AccessMapSerializer,
+
+ SiteMap: SiteMapSerializer,
+
+ None: None,
+ }
+
+# Based on core/views/*.py
+
+
+class HpcHealthCheckList(XOSListCreateAPIView):
+ queryset = HpcHealthCheck.objects.select_related().all()
+ serializer_class = HpcHealthCheckSerializer
+ id_serializer_class = HpcHealthCheckIdSerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','kind','resource_name','result_contains','result_min_size','result_max_size',)
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return HpcHealthCheck.select_by_user(self.request.user)
+
+
+class HpcHealthCheckDetail(XOSRetrieveUpdateDestroyAPIView):
+ queryset = HpcHealthCheck.objects.select_related().all()
+ serializer_class = HpcHealthCheckSerializer
+ id_serializer_class = HpcHealthCheckIdSerializer
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return HpcHealthCheck.select_by_user(self.request.user)
+
+ # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+ # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class HpcServiceList(XOSListCreateAPIView):
+ queryset = HpcService.objects.select_related().all()
+ serializer_class = HpcServiceSerializer
+ id_serializer_class = HpcServiceIdSerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','description','enabled','kind','name','versionNumber','published','view_url','icon_url','public_key','service_specific_id','service_specific_attribute','service_ptr','cmi_hostname','hpc_port80','watcher_hpc_network','watcher_dnsdemux_network','watcher_dnsredir_network',)
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return HpcService.select_by_user(self.request.user)
+
+
+class HpcServiceDetail(XOSRetrieveUpdateDestroyAPIView):
+ queryset = HpcService.objects.select_related().all()
+ serializer_class = HpcServiceSerializer
+ id_serializer_class = HpcServiceIdSerializer
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return HpcService.select_by_user(self.request.user)
+
+ # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+ # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class OriginServerList(XOSListCreateAPIView):
+ queryset = OriginServer.objects.select_related().all()
+ serializer_class = OriginServerSerializer
+ id_serializer_class = OriginServerIdSerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','origin_server_id','url','contentProvider','authenticated','enabled','protocol','redirects','description',)
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return OriginServer.select_by_user(self.request.user)
+
+
+class OriginServerDetail(XOSRetrieveUpdateDestroyAPIView):
+ queryset = OriginServer.objects.select_related().all()
+ serializer_class = OriginServerSerializer
+ id_serializer_class = OriginServerIdSerializer
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return OriginServer.select_by_user(self.request.user)
+
+ # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+ # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class CDNPrefixList(XOSListCreateAPIView):
+ queryset = CDNPrefix.objects.select_related().all()
+ serializer_class = CDNPrefixSerializer
+ id_serializer_class = CDNPrefixIdSerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','cdn_prefix_id','prefix','contentProvider','description','defaultOriginServer','enabled',)
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return CDNPrefix.select_by_user(self.request.user)
+
+
+class CDNPrefixDetail(XOSRetrieveUpdateDestroyAPIView):
+ queryset = CDNPrefix.objects.select_related().all()
+ serializer_class = CDNPrefixSerializer
+ id_serializer_class = CDNPrefixIdSerializer
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return CDNPrefix.select_by_user(self.request.user)
+
+ # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+ # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class ServiceProviderList(XOSListCreateAPIView):
+ queryset = ServiceProvider.objects.select_related().all()
+ serializer_class = ServiceProviderSerializer
+ id_serializer_class = ServiceProviderIdSerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','hpcService','service_provider_id','name','description','enabled',)
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return ServiceProvider.select_by_user(self.request.user)
+
+
+class ServiceProviderDetail(XOSRetrieveUpdateDestroyAPIView):
+ queryset = ServiceProvider.objects.select_related().all()
+ serializer_class = ServiceProviderSerializer
+ id_serializer_class = ServiceProviderIdSerializer
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return ServiceProvider.select_by_user(self.request.user)
+
+ # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+ # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class ContentProviderList(XOSListCreateAPIView):
+ queryset = ContentProvider.objects.select_related().all()
+ serializer_class = ContentProviderSerializer
+ id_serializer_class = ContentProviderIdSerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','content_provider_id','name','enabled','description','serviceProvider','users',)
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return ContentProvider.select_by_user(self.request.user)
+
+
+class ContentProviderDetail(XOSRetrieveUpdateDestroyAPIView):
+ queryset = ContentProvider.objects.select_related().all()
+ serializer_class = ContentProviderSerializer
+ id_serializer_class = ContentProviderIdSerializer
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return ContentProvider.select_by_user(self.request.user)
+
+ # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+ # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class AccessMapList(XOSListCreateAPIView):
+ queryset = AccessMap.objects.select_related().all()
+ serializer_class = AccessMapSerializer
+ id_serializer_class = AccessMapIdSerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','name','description','map',)
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return AccessMap.select_by_user(self.request.user)
+
+
+class AccessMapDetail(XOSRetrieveUpdateDestroyAPIView):
+ queryset = AccessMap.objects.select_related().all()
+ serializer_class = AccessMapSerializer
+ id_serializer_class = AccessMapIdSerializer
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return AccessMap.select_by_user(self.request.user)
+
+ # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+ # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
+class SiteMapList(XOSListCreateAPIView):
+ queryset = SiteMap.objects.select_related().all()
+ serializer_class = SiteMapSerializer
+ id_serializer_class = SiteMapIdSerializer
+ filter_backends = (filters.DjangoFilterBackend,)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','contentProvider','serviceProvider','cdnPrefix','hpcService','name','description','map','map_id',)
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return SiteMap.select_by_user(self.request.user)
+
+
+class SiteMapDetail(XOSRetrieveUpdateDestroyAPIView):
+ queryset = SiteMap.objects.select_related().all()
+ serializer_class = SiteMapSerializer
+ id_serializer_class = SiteMapIdSerializer
+
+ def get_serializer_class(self):
+ no_hyperlinks=False
+ if hasattr(self.request,"QUERY_PARAMS"):
+ no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+ if (no_hyperlinks):
+ return self.id_serializer_class
+ else:
+ return self.serializer_class
+
+ def get_queryset(self):
+ if (not self.request.user.is_authenticated()):
+ raise XOSNotAuthenticated()
+ return SiteMap.select_by_user(self.request.user)
+
+ # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+ # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+
+
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index bf2e1c2..d7ec5e7 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -120,6 +120,12 @@
# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = 'xos.wsgi.application'
+# Default: 'csrftoken'
+CSRF_COOKIE_NAME = 'xoscsrftoken'
+# Default: 'django_language'
+LANGUAGE_COOKIE_NAME = 'xos_django_language'
+# Default: 'sessionid'
+SESSION_COOKIE_NAME = 'xossessionid'
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
diff --git a/xos/xos/urls.py b/xos/xos/urls.py
index 6ba98d8..3660282 100644
--- a/xos/xos/urls.py
+++ b/xos/xos/urls.py
@@ -5,6 +5,7 @@
# This is the generated API
from xosapi import *
+from hpcapi import *
from core.views.legacyapi import LegacyXMLRPC
from core.views.services import ServiceGridView, ServiceGraphView
@@ -56,5 +57,5 @@
# XOSLib rest methods
url(r'^xoslib/', include('core.xoslib.methods', namespace='xoslib')),
- ) + get_REST_patterns()
+ ) + get_REST_patterns() + get_hpc_REST_patterns()
diff --git a/xos/xos/xosapi.py b/xos/xos/xosapi.py
index bec3ea2..6532c72 100644
--- a/xos/xos/xosapi.py
+++ b/xos/xos/xosapi.py
@@ -1806,7 +1806,7 @@
return None
class Meta:
model = Controller
- fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','deployment','dashboardviews',)
+ fields = ('humanReadableName', 'validators', 'id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','rabbit_host','rabbit_user','rabbit_password','deployment','dashboardviews',)
class ControllerIdSerializer(XOSModelSerializer):
id = IdField()
@@ -4644,7 +4644,7 @@
serializer_class = ControllerSerializer
id_serializer_class = ControllerIdSerializer
filter_backends = (filters.DjangoFilterBackend,)
- filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','deployment','dashboardviews',)
+ filter_fields = ('id','created','updated','enacted','policed','backend_register','backend_status','deleted','write_protect','lazy_blocked','no_sync','name','backend_type','version','auth_url','admin_user','admin_password','admin_tenant','domain','rabbit_host','rabbit_user','rabbit_password','deployment','dashboardviews',)
def get_serializer_class(self):
no_hyperlinks=False