Merge branch 'acb-develop'
Add basic message page explaining why service has been turned off.
diff --git a/xos/configurations/cord-pod/Makefile b/xos/configurations/cord-pod/Makefile
index ff4f258..592f579 100644
--- a/xos/configurations/cord-pod/Makefile
+++ b/xos/configurations/cord-pod/Makefile
@@ -14,6 +14,9 @@
sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/fixtures.yaml
sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/cord-vtn-vsg.yaml
+cord-ceilometer: ceilometer_custom_images cord
+ sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/ceilometer.yaml
+
nodes.yaml:
export SETUPDIR=.; bash ../common/make-nodes-yaml.sh
@@ -26,6 +29,22 @@
vtn_network_cfg_json:
export SETUPDIR=.; bash ./make-vtn-networkconfig-json.sh
+stop:
+ sudo MYIP=$(MYIP) docker-compose stop
+
+rm:
+ sudo MYIP=$(MYIP) docker-compose rm
+
+showlogs:
+ sudo MYIP=$(MYIP) docker-compose logs
+
+cleanup: stop rm
+ ./cleanup.sh
+ bash -c "source ./admin-openrc.sh; nova list --all-tenants; neutron net-list"
+
+ceilometer_custom_images:
+ bash -c "source ./admin-openrc.sh; glance image-show ceilometer-trusty-server-multi-nic || ! mkdir -p ./images || ! wget http://www.vicci.org/cord/ceilometer-trusty-server-multi-nic.qcow2 -P ./images || glance image-create --name ceilometer-trusty-server-multi-nic --disk-format qcow2 --file ./images/ceilometer-trusty-server-multi-nic.qcow2 --container-format bare"
+
.PHONY: local_containers
local_containers:
cd ../../../containers/xos; make devel
diff --git a/xos/configurations/cord-pod/ceilometer.yaml b/xos/configurations/cord-pod/ceilometer.yaml
new file mode 100644
index 0000000..3b32345
--- /dev/null
+++ b/xos/configurations/cord-pod/ceilometer.yaml
@@ -0,0 +1,258 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup CORD-related services -- vOLT, vCPE, vBNG.
+
+imports:
+ - custom_types/xos.yaml
+
+node_types:
+ tosca.nodes.SFlowService:
+ derived_from: tosca.nodes.Root
+ description: >
+ XOS SFlow Collection Service
+ capabilities:
+ scalable:
+ type: tosca.capabilities.Scalable
+ service:
+ type: tosca.capabilities.xos.Service
+ properties:
+ kind:
+ type: string
+ default: generic
+ description: Type of service.
+ view_url:
+ type: string
+ required: false
+ description: URL to follow when icon is clicked in the Service Directory.
+ icon_url:
+ type: string
+ required: false
+ description: ICON to display in the Service Directory.
+ enabled:
+ type: boolean
+ default: true
+ published:
+ type: boolean
+ default: true
+ description: If True then display this Service in the Service Directory.
+ public_key:
+ type: string
+ required: false
+ description: Public key to install into Instances to allows Services to SSH into them.
+ private_key_fn:
+ type: string
+ required: false
+ description: Location of private key file
+ versionNumber:
+ type: string
+ required: false
+ description: Version number of Service.
+ sflow_port:
+ type: integer
+ required: false
+ default: 6343
+ description: sFlow listening port
+ sflow_api_port:
+ type: integer
+ required: false
+ default: 33333
+ description: sFlow publish subscribe api listening port
+
+ tosca.nodes.CeilometerService:
+ derived_from: tosca.nodes.Root
+ description: >
+ XOS Ceilometer Service
+ capabilities:
+ scalable:
+ type: tosca.capabilities.Scalable
+ service:
+ type: tosca.capabilities.xos.Service
+ properties:
+ kind:
+ type: string
+ default: generic
+ description: Type of service.
+ view_url:
+ type: string
+ required: false
+ description: URL to follow when icon is clicked in the Service Directory.
+ icon_url:
+ type: string
+ required: false
+ description: ICON to display in the Service Directory.
+ enabled:
+ type: boolean
+ default: true
+ published:
+ type: boolean
+ default: true
+ description: If True then display this Service in the Service Directory.
+ public_key:
+ type: string
+ required: false
+ description: Public key to install into Instances to allows Services to SSH into them.
+ private_key_fn:
+ type: string
+ required: false
+ description: Location of private key file
+ versionNumber:
+ type: string
+ required: false
+ description: Version number of Service.
+ ceilometer_pub_sub_url:
+ type: string
+ required: false
+ description: REST URL of ceilometer PUB/SUB component
+
+ tosca.nodes.CeilometerTenant:
+ derived_from: tosca.nodes.Root
+ description: >
+ CORD: A Tenant of the Ceilometer Service.
+ properties:
+ kind:
+ type: string
+ default: generic
+ description: Kind of tenant
+
+topology_template:
+ node_templates:
+ service_ceilometer:
+ type: tosca.nodes.CeilometerService
+ requirements:
+ properties:
+ view_url: /admin/ceilometer/ceilometerservice/$id$/
+ kind: ceilometer
+ ceilometer_pub_sub_url: http://10.11.10.1:4455/
+ public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+ artifacts:
+ pubkey: /opt/xos/synchronizers/monitoring_channel/monitoring_channel_public_key
+
+# service_sflow:
+# type: tosca.nodes.SFlowService
+# requirements:
+# properties:
+# view_url: /admin/ceilometer/sflowservice/$id$/
+# kind: sflow
+# sflow_port: 6343
+# sflow_api_port: 33333
+
+ Private:
+ type: tosca.nodes.NetworkTemplate
+
+ management:
+ type: tosca.nodes.network.Network.XOS
+ properties:
+ no-create: true
+ no-delete: true
+ no-update: true
+
+# ceilometer_network:
+# type: tosca.nodes.network.Network.XOS
+# properties:
+# ip_version: 4
+# labels: ceilometer_client_access
+# requirements:
+# - network_template:
+# node: Private
+# relationship: tosca.relationships.UsesNetworkTemplate
+# - owner:
+# node: mysite_ceilometer
+# relationship: tosca.relationships.MemberOfSlice
+# - connection:
+# node: mysite_ceilometer
+# relationship: tosca.relationships.ConnectsToSlice
+
+ mysite:
+ type: tosca.nodes.Site
+
+ trusty-server-multi-nic:
+ type: tosca.nodes.Image
+
+ ceilometer-trusty-server-multi-nic:
+ type: tosca.nodes.Image
+
+ mysite_ceilometer:
+ description: Ceilometer Proxy Slice
+ type: tosca.nodes.Slice
+ requirements:
+ - ceilometer_service:
+ node: service_ceilometer
+ relationship: tosca.relationships.MemberOfService
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+ - default_image:
+ node: ceilometer-trusty-server-multi-nic
+ relationship: tosca.relationships.DefaultImage
+ - management:
+ node: management
+ relationship: tosca.relationships.ConnectsToNetwork
+ properties:
+ default_flavor: m1.small
+
+# mysite_sflow:
+# description: Slice for sFlow service
+# type: tosca.nodes.Slice
+# requirements:
+# - sflow_service:
+# node: service_sflow
+# relationship: tosca.relationships.MemberOfService
+# - site:
+# node: mysite
+# relationship: tosca.relationships.MemberOfSite
+
+ my_ceilometer_tenant:
+ description: Ceilometer Service default Tenant
+ type: tosca.nodes.CeilometerTenant
+ requirements:
+ - provider_service:
+ node: service_ceilometer
+ relationship: tosca.relationships.MemberOfService
+
+ # Virtual machines
+# sflow_service_instance:
+# 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_sflow
+# relationship: tosca.relationships.MemberOfSlice
+
+ Ceilometer:
+ type: tosca.nodes.DashboardView
+ properties:
+ url: template:xosCeilometerDashboard
+ Tenant:
+ type: tosca.nodes.DashboardView
+ properties:
+ no-create: true
+ no-update: true
+ no-delete: true
+
+ padmin@vicci.org:
+ type: tosca.nodes.User
+ properties:
+ firstname: XOS
+ lastname: admin
+ is_admin: true
+ requirements:
+ - tenant_dashboard:
+ node: Tenant
+ relationship: tosca.relationships.UsesDashboard
+ - ceilometer_dashboard:
+ node: Ceilometer
+ relationship: tosca.relationships.UsesDashboard
diff --git a/xos/configurations/cord-pod/cleanup.sh b/xos/configurations/cord-pod/cleanup.sh
new file mode 100755
index 0000000..6ca58c2
--- /dev/null
+++ b/xos/configurations/cord-pod/cleanup.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+function cleanup_network {
+ NETWORK=$1
+ SUBNETS=`neutron net-show $NETWORK | grep -i subnets | awk '{print $4}'`
+ if [[ $SUBNETS != "" ]]; then
+ PORTS=`neutron port-list | grep -i $SUBNETS | awk '{print $2}'`
+ for PORT in $PORTS; do
+ echo "Deleting port $PORT"
+ neutron port-delete $PORT
+ done
+ fi
+ neutron net-delete $NETWORK
+}
+
+source ./admin-openrc.sh
+
+echo "Deleting VMs"
+# Delete all VMs
+VMS=$( nova list --all-tenants|grep mysite|awk '{print $2}' )
+for VM in $VMS
+do
+ nova delete $VM
+done
+
+echo "Waiting 5 seconds..."
+sleep 5
+
+cleanup_network lan_network
+cleanup_network wan_network
+cleanup_network mysite_vcpe-private
+cleanup_network mysite_vsg-access
+cleanup_network management
+
+echo "Deleting networks"
+# Delete all networks beginning with mysite_
+NETS=$( neutron net-list --all-tenants|grep mysite|awk '{print $2}' )
+for NET in $NETS
+do
+ neutron net-delete $NET
+done
+
+neutron net-delete lan_network || true
+neutron net-delete subscriber_network || true
+neutron net-delete public_network || true
+neutron net-delete hpc_client_network || true
+neutron net-delete ceilometer_network || true
+neutron net-delete management || true
+neutron net-delete mysite_vsg-access || true
diff --git a/xos/configurations/cord-pod/docker-compose.yml b/xos/configurations/cord-pod/docker-compose.yml
index 6f442af..a0fec91 100644
--- a/xos/configurations/cord-pod/docker-compose.yml
+++ b/xos/configurations/cord-pod/docker-compose.yml
@@ -62,16 +62,17 @@
# links:
# - xos_db
-#xos_synchronizer_monitoring_channel:
-# image: xosproject/xos-synchronizer-openstack
-# command: bash -c "sleep 120; python /opt/xos/synchronizers/monitoring_channel/monitoring_channel_synchronizer.py -C /opt/xos/synchronizers/monitoring_channel/monitoring_channel_synchronizer_config"
-# labels:
-# org.xosproject.kind: synchronizer
-# org.xosproject.target: monitoring_channel
-# links:
-# - xos_db
-# volumes:
-# - ./id_rsa:/opt/xos/synchronizers/monitoring_channel/monitoring_channel_private_key:ro # private key
+xos_synchronizer_monitoring_channel:
+ image: xosproject/xos-synchronizer-openstack
+ command: bash -c "sleep 120; python /opt/xos/synchronizers/monitoring_channel/monitoring_channel_synchronizer.py -C /root/setup/files/monitoring_channel_synchronizer_config"
+ labels:
+ org.xosproject.kind: synchronizer
+ org.xosproject.target: monitoring_channel
+ links:
+ - xos_db
+ volumes:
+ - .:/root/setup:ro
+ - ./id_rsa:/opt/xos/synchronizers/monitoring_channel/monitoring_channel_private_key:ro # private key
xos:
command: python /opt/xos/manage.py runserver 0.0.0.0:80 --insecure --makemigrations
@@ -87,3 +88,4 @@
- ../vtn/files/xos_vtn_config:/opt/xos/xos_configuration/xos_vtn_config:ro
- ./id_rsa.pub:/opt/xos/synchronizers/onos/onos_key.pub:ro
- ./id_rsa.pub:/opt/xos/synchronizers/vcpe/vcpe_public_key:ro
+ - ./id_rsa.pub:/opt/xos/synchronizers/monitoring_channel/monitoring_channel_public_key:ro
diff --git a/xos/configurations/cord-pod/files/monitoring_channel_synchronizer_config b/xos/configurations/cord-pod/files/monitoring_channel_synchronizer_config
new file mode 100644
index 0000000..fb3f22a
--- /dev/null
+++ b/xos/configurations/cord-pod/files/monitoring_channel_synchronizer_config
@@ -0,0 +1,43 @@
+
+[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/synchronizers/monitoring_channel/model-deps
+steps_dir=/opt/xos/synchronizers/monitoring_channel/steps
+sys_dir=/opt/xos/synchronizers/monitoring_channel/sys
+deleters_dir=/opt/xos/synchronizers/monitoring_channel/deleters
+log_file=console
+driver=None
+pretend=False
+backoff_disabled=True
+save_ansible_output=True
+full_setup=True
+# For CORD_POD config, set proxy_ssh to True even on cloudlab
+proxy_ssh=True
+proxy_ssh_key=/root/setup/node_key
+proxy_ssh_user=root
+
+[feefie]
+client_id='vicci_dev_central'
+user_id='pl'
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
index 4dea365..c708d8e 100644
--- a/xos/configurations/cord/cord.yaml
+++ b/xos/configurations/cord/cord.yaml
@@ -8,6 +8,12 @@
topology_template:
node_templates:
# CORD Services
+ service_vtr:
+ type: tosca.nodes.Service
+ properties:
+ view_url: /admin/vtr/vtrservice/$id$/
+ kind: vTR
+
service_volt:
type: tosca.nodes.Service
requirements:
diff --git a/xos/configurations/cord/docker-compose.yml b/xos/configurations/cord/docker-compose.yml
index bfb04b8..28eeeb4 100644
--- a/xos/configurations/cord/docker-compose.yml
+++ b/xos/configurations/cord/docker-compose.yml
@@ -69,6 +69,19 @@
volumes:
- ../setup/id_rsa:/opt/xos/synchronizers/monitoring_channel/monitoring_channel_private_key:ro # private key
+xos_synchronizer_vtr:
+ image: xosproject/xos-synchronizer-openstack
+ command: bash -c "sleep 120; python /opt/xos/synchronizers/vtr/vtr-synchronizer.py -C /opt/xos/synchronizers/vtr/vtr_synchronizer_config"
+ labels:
+ org.xosproject.kind: synchronizer
+ org.xosproject.target: vtr
+ links:
+ - xos_db
+ extra_hosts:
+ - ctl:${MYIP}
+ volumes:
+ - ../setup/id_rsa:/opt/xos/synchronizers/vtr/vcpe_private_key:ro # private key
+ - ../setup:/root/setup:ro
# FUTURE
#xos_swarm_synchronizer:
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 910ee97..44fa06c 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -1554,7 +1554,6 @@
extra = 0
suit_classes = 'suit-tab suit-tab-admin-only'
fields = ['controller', 'user', 'kuser_id']
- readonly_fields=['controller']
class UserAdmin(XOSAdminMixin, UserAdmin):
diff --git a/xos/core/xoslib/methods/truckroll.py b/xos/core/xoslib/methods/truckroll.py
new file mode 100644
index 0000000..ca59866
--- /dev/null
+++ b/xos/core/xoslib/methods/truckroll.py
@@ -0,0 +1,90 @@
+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 services.cord.models import CordSubscriberRoot
+from services.vtr.models import VTRTenant, VTRService
+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_vtr_service():
+ vtr_services = VTRService.get_service_objects().all()
+ if vtr_services:
+ return vtr_services[0].id
+ return None
+
+class VTRTenantIdSerializer(serializers.ModelSerializer, PlusSerializerMixin):
+ id = ReadOnlyField()
+ target_id = serializers.IntegerField()
+ test = serializers.CharField()
+ argument = serializers.CharField(required=False)
+ provider_service = serializers.PrimaryKeyRelatedField(queryset=VTRService.get_service_objects().all(), default=get_default_vtr_service)
+ result = serializers.CharField(required=False)
+ backend_status = ReadOnlyField()
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ is_synced = serializers.SerializerMethodField("isSynced")
+
+ class Meta:
+ model = VTRTenant
+ fields = ('humanReadableName', 'id', 'provider_service', 'target_id', 'test', 'argument', 'result', 'is_synced', 'backend_status' )
+
+ def getHumanReadableName(self, obj):
+ return obj.__unicode__()
+
+ def isSynced(self, obj):
+ return (obj.enacted is not None) and (obj.enacted >= obj.updated)
+
+class VTRTenantList(XOSListCreateAPIView):
+ serializer_class = VTRTenantIdSerializer
+
+ method_kind = "list"
+ method_name = "truckroll"
+
+ def get_queryset(self):
+ queryset = VTRTenant.get_tenant_objects().select_related().all()
+
+ test = self.request.QUERY_PARAMS.get('test', None)
+ if test is not None:
+ ids = [x.id for x in queryset if x.get_attribute("test", None)==test]
+ queryset = queryset.filter(id__in=ids)
+
+ return queryset
+
+ def post(self, request, format=None):
+ data = request.DATA
+
+ existing_obj = None
+# for obj in VTRTenant.get_tenant_objects().all():
+# if (obj.tesst == data.get("test", None)) and (obj.target == data.get("target", None))):
+# existing_obj = obj
+
+ if existing_obj:
+ serializer = VTRTenantIdSerializer(existing_obj)
+ headers = self.get_success_headers(serializer.data)
+ return Response( serializer.data, status=status.HTTP_200_OK )
+
+ return super(VTRTenantList, self).post(request, format)
+
+class VTRTenantDetail(XOSRetrieveUpdateDestroyAPIView):
+ serializer_class = VTRTenantIdSerializer
+ queryset = VTRTenant.get_tenant_objects().select_related().all()
+
+ method_kind = "detail"
+ method_name = "truckroll"
+
+
+
+
+
diff --git a/xos/services/ceilometer/models.py b/xos/services/ceilometer/models.py
index d8fb0fa..5285bd7 100644
--- a/xos/services/ceilometer/models.py
+++ b/xos/services/ceilometer/models.py
@@ -88,10 +88,11 @@
for ns in self.instance.ports.all():
if "private" in ns.network.name.lower():
addresses["private"] = (ns.ip, ns.mac)
- elif "nat" in ns.network.name.lower():
+ elif ("nat" in ns.network.name.lower()) or ("management" in ns.network.name.lower()):
addresses["nat"] = (ns.ip, ns.mac)
- elif "ceilometer_client_access" in ns.network.labels.lower():
- addresses["ceilometer"] = (ns.ip, ns.mac)
+ #TODO: Do we need this client_access_network. Revisit in VTN context
+ #elif "ceilometer_client_access" in ns.network.labels.lower():
+ # addresses["ceilometer"] = (ns.ip, ns.mac)
return addresses
@property
@@ -165,7 +166,7 @@
@property
def ceilometer_url(self):
- if not self.ceilometer_ip:
+ if not self.private_ip:
return None
return "http://" + self.private_ip + ":" + str(self.ceilometer_port) + "/"
diff --git a/xos/services/cord/rest_examples/add_truckroll.sh b/xos/services/cord/rest_examples/add_truckroll.sh
new file mode 100755
index 0000000..ac092fa
--- /dev/null
+++ b/xos/services/cord/rest_examples/add_truckroll.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+source ./config.sh
+
+TARGET_ID=1
+TEST=ping
+ARGUMENT=128.112.139.30
+
+echo curl "-H \"Accept: application/json; indent=4\" -H \"Content-Type: application/json\" -u $AUTH -X POST -d \"{\\\"target_id\\\": \\\"$TARGET_ID\\\", \\\"test\\\": \\\"$TEST\\\", \\\"argument\\\": \\\"$ARGUMENT\\\"}\" $HOST/xoslib/truckroll/"
+
+curl -H "Accept: application/json; indent=4" -H "Content-Type: application/json" -u $AUTH -X POST -d "{\"target_id\": \"$TARGET_ID\", \"test\": \"$TEST\", \"argument\": \"$ARGUMENT\"}" $HOST/xoslib/truckroll/
diff --git a/xos/services/cord/rest_examples/list_truckrolls.sh b/xos/services/cord/rest_examples/list_truckrolls.sh
new file mode 100755
index 0000000..7aeaa4c
--- /dev/null
+++ b/xos/services/cord/rest_examples/list_truckrolls.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+source ./config.sh
+
+curl -H "Accept: application/json; indent=4" -H "Content-Type: application/json" -u $AUTH $HOST/xoslib/truckroll/
diff --git a/xos/services/vtr/__init__.py b/xos/services/vtr/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/xos/services/vtr/__init__.py
@@ -0,0 +1 @@
+
diff --git a/xos/services/vtr/admin.py b/xos/services/vtr/admin.py
new file mode 100644
index 0000000..12b48af
--- /dev/null
+++ b/xos/services/vtr/admin.py
@@ -0,0 +1,102 @@
+from django.contrib import admin
+
+from services.cord.models import *
+from django import forms
+from django.utils.safestring import mark_safe
+from django.contrib.auth.admin import UserAdmin
+from django.contrib.admin.widgets import FilteredSelectMultiple
+from django.contrib.auth.forms import ReadOnlyPasswordHashField
+from django.contrib.auth.signals import user_logged_in
+from django.utils import timezone
+from django.contrib.contenttypes import generic
+from suit.widgets import LinkedSelect
+from core.admin import ServiceAppAdmin,SliceInline,ServiceAttrAsTabInline, ReadOnlyAwareAdmin, XOSTabularInline, ServicePrivilegeInline, TenantRootTenantInline, TenantRootPrivilegeInline
+from core.middleware import get_request
+
+from services.vtr.models import *
+from services.cord.models import CordSubscriberRoot
+
+from functools import update_wrapper
+from django.contrib.admin.views.main import ChangeList
+from django.core.urlresolvers import reverse
+from django.contrib.admin.utils import quote
+
+class VTRServiceAdmin(ReadOnlyAwareAdmin):
+ model = VTRService
+ verbose_name = "vTR Service"
+ verbose_name_plural = "vTR Service"
+ 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']})]
+ readonly_fields = ('backend_status_text', )
+ inlines = [SliceInline,ServiceAttrAsTabInline,ServicePrivilegeInline]
+
+ extracontext_registered_admins = True
+
+ user_readonly_fields = ["name", "enabled", "versionNumber", "description"]
+
+ suit_form_tabs =(('general', 'vTR Service Details'),
+ ('administration', 'Administration'),
+ ('slices','Slices'),
+ ('serviceattrs','Additional Attributes'),
+ ('serviceprivileges','Privileges'),
+ )
+
+ suit_form_includes = (('vtradmin.html', 'top', 'administration'),
+ ) #('hpctools.html', 'top', 'tools') )
+
+ def queryset(self, request):
+ return VTRService.get_service_objects_by_user(request.user)
+
+class VTRTenantForm(forms.ModelForm):
+ simple_attributes = {"test": None,
+ "argument": None,
+ "result": None,
+ "target": None}
+ test = forms.ChoiceField(choices=VTRTenant.TEST_CHOICES, required=True)
+ argument = forms.CharField(required=False)
+ result = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 10, 'cols': 80, 'class': 'input-xxlarge'}))
+ target = forms.ModelChoiceField(queryset=CordSubscriberRoot.objects.all())
+
+ def __init__(self,*args,**kwargs):
+ super (VTRTenantForm,self ).__init__(*args,**kwargs)
+ self.fields['provider_service'].queryset = VTRService.get_service_objects().all()
+ if self.instance:
+ # fields for the attributes
+ self.fields['test'].initial = self.instance.test
+ self.fields['argument'].initial = self.instance.argument
+ self.fields['target'].initial = self.instance.target
+ self.fields['result'].initial = self.instance.result
+ if (not self.instance) or (not self.instance.pk):
+ # default fields for an 'add' form
+ self.fields['kind'].initial = VTR_KIND
+ if VTRService.get_service_objects().exists():
+ self.fields["provider_service"].initial = VTRService.get_service_objects().all()[0]
+
+ def save(self, commit=True):
+ self.instance.test = self.cleaned_data.get("test")
+ self.instance.argument = self.cleaned_data.get("argument")
+ self.instance.target = self.cleaned_data.get("target")
+ self.instance.result = self.cleaned_data.get("result")
+ return super(VTRTenantForm, self).save(commit=commit)
+
+ class Meta:
+ model = VTRTenant
+
+class VTRTenantAdmin(ReadOnlyAwareAdmin):
+ list_display = ('backend_status_icon', 'id', 'target', 'test', 'argument' )
+ list_display_links = ('backend_status_icon', 'id')
+ fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'provider_service', # 'subscriber_root', 'service_specific_id', 'service_specific_attribute',
+ 'target', 'test', 'argument', 'result'],
+ 'classes':['suit-tab suit-tab-general']})]
+ readonly_fields = ('backend_status_text', 'service_specific_attribute')
+ form = VTRTenantForm
+
+ suit_form_tabs = (('general','Details'),)
+
+ def queryset(self, request):
+ return VTRTenant.get_tenant_objects_by_user(request.user)
+
+admin.site.register(VTRService, VTRServiceAdmin)
+admin.site.register(VTRTenant, VTRTenantAdmin)
+
diff --git a/xos/services/vtr/models.py b/xos/services/vtr/models.py
new file mode 100644
index 0000000..1181faf
--- /dev/null
+++ b/xos/services/vtr/models.py
@@ -0,0 +1,86 @@
+from django.db import models
+from core.models import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, Port, AddressPool
+from core.models.plcorebase import StrippedCharField
+import os
+from django.db import models, transaction
+from django.forms.models import model_to_dict
+from django.db.models import Q
+from operator import itemgetter, attrgetter, methodcaller
+from core.models import Tag
+from core.models.service import LeastLoadedNodeScheduler
+from services.cord.models import CordSubscriberRoot
+import traceback
+from xos.exceptions import *
+from xos.config import Config
+
+class ConfigurationError(Exception):
+ pass
+
+VTR_KIND = "vTR"
+
+CORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
+
+# -------------------------------------------
+# VOLT
+# -------------------------------------------
+
+class VTRService(Service):
+ KIND = VTR_KIND
+
+ class Meta:
+ app_label = "vtr"
+ verbose_name = "vTR Service"
+ proxy = True
+
+class VTRTenant(Tenant):
+ class Meta:
+ proxy = True
+
+ KIND = VTR_KIND
+
+ TEST_CHOICES = ( ("ping", "Ping"), ("traceroute", "Trace Route"), ("tcpdump", "Tcp Dump") )
+
+ simple_attributes = ( ("test", None),
+ ("argument", None),
+ ("result", None),
+ ("target_id", None) )
+
+ sync_attributes = ( 'test', 'argument' )
+
+ def __init__(self, *args, **kwargs):
+ vtr_services = VTRService.get_service_objects().all()
+ if vtr_services:
+ self._meta.get_field("provider_service").default = vtr_services[0].id
+ super(VTRTenant, self).__init__(*args, **kwargs)
+
+ @property
+ def target(self):
+ if getattr(self, "cached_target", None):
+ return self.cached_target
+ target_id=self.target_id
+ if not target_id:
+ return None
+ users=CordSubscriberRoot.objects.filter(id=target_id)
+ if not users:
+ return None
+ user=users[0]
+ self.cached_target = users[0]
+ return user
+
+ @target.setter
+ def target(self, value):
+ if value:
+ value = value.id
+ if (value != self.get_attribute("target_id", None)):
+ self.cached_target=None
+ self.target_id = value
+
+ def save(self, *args, **kwargs):
+ super(VTRTenant, self).save(*args, **kwargs)
+
+ def delete(self, *args, **kwargs):
+ super(VTRTenant, self).delete(*args, **kwargs)
+
+
+VTRTenant.setup_simple_attributes()
+
diff --git a/xos/services/vtr/templates/vtradmin.html b/xos/services/vtr/templates/vtradmin.html
new file mode 100644
index 0000000..e8a33bc
--- /dev/null
+++ b/xos/services/vtr/templates/vtradmin.html
@@ -0,0 +1,6 @@
+<div class = "row text-center">
+ <div class="col-xs-12">
+ <a href="/admin/vtr/vtrtenant/">vTR Tenants</a>
+ </div>
+</div>
+
diff --git a/xos/synchronizers/vtr/files/run_tcpdump.sh b/xos/synchronizers/vtr/files/run_tcpdump.sh
new file mode 100644
index 0000000..ed75bf0
--- /dev/null
+++ b/xos/synchronizers/vtr/files/run_tcpdump.sh
@@ -0,0 +1,9 @@
+#! /bin/bash
+INTERFACE=$1
+tcpdump -n -e -i $INTERFACE -c 100 &
+PID_TCPDUMP=$!
+curl http://www.xosproject.org/ &> /dev/null &
+PID_CURL=$!
+sleep 30s
+kill $PID_TCPDUMP
+kill $PIUD_CURL
diff --git a/xos/synchronizers/vtr/model-deps b/xos/synchronizers/vtr/model-deps
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/xos/synchronizers/vtr/model-deps
@@ -0,0 +1 @@
+{}
diff --git a/xos/synchronizers/vtr/run-vtn.sh b/xos/synchronizers/vtr/run-vtn.sh
new file mode 100755
index 0000000..b2f9518
--- /dev/null
+++ b/xos/synchronizers/vtr/run-vtn.sh
@@ -0,0 +1,4 @@
+export XOS_DIR=/opt/xos
+cp /root/setup/node_key $XOS_DIR/synchronizers/vtr/node_key
+chmod 0600 $XOS_DIR/synchronizers/vtr/node_key
+python vtr-synchronizer.py -C $XOS_DIR/synchronizers/vtr/vtn_vtr_synchronizer_config
diff --git a/xos/synchronizers/vtr/run.sh b/xos/synchronizers/vtr/run.sh
new file mode 100755
index 0000000..388fdf9
--- /dev/null
+++ b/xos/synchronizers/vtr/run.sh
@@ -0,0 +1,2 @@
+export XOS_DIR=/opt/xos
+python vtr-synchronizer.py -C $XOS_DIR/synchronizers/vtr/vtr_synchronizer_config
diff --git a/xos/synchronizers/vtr/steps/sync_vtrtenant.py b/xos/synchronizers/vtr/steps/sync_vtrtenant.py
new file mode 100644
index 0000000..bf07d4a
--- /dev/null
+++ b/xos/synchronizers/vtr/steps/sync_vtrtenant.py
@@ -0,0 +1,150 @@
+import os
+import socket
+import sys
+import base64
+import time
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep
+from synchronizers.base.ansible import run_template_ssh
+from synchronizers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
+from core.models import Service, Slice, Tag
+from services.cord.models import VSGService, VSGTenant, VOLTTenant, CordSubscriberRoot
+from services.vtr.models import VTRService, VTRTenant
+from services.hpc.models import HpcService, CDNPrefix
+from xos.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)
+
+CORD_USE_VTN = getattr(Config(), "networking_use_vtn", False)
+
+class SyncVTRTenant(SyncInstanceUsingAnsible):
+ provides=[VTRTenant]
+ observes=VTRTenant
+ requested_interval=0
+ template_name = "sync_vtrtenant.yaml"
+ service_key_name = "/opt/xos/synchronizers/vtr/vcpe_private_key"
+
+ def __init__(self, *args, **kwargs):
+ super(SyncVTRTenant, self).__init__(*args, **kwargs)
+
+ def fetch_pending(self, deleted):
+ if (not deleted):
+ objs = VTRTenant.get_tenant_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
+ else:
+ objs = VTRTenant.get_deleted_tenant_objects()
+
+ return objs
+
+ def get_vtr_service(self, o):
+ if not o.provider_service:
+ return None
+
+ vtrs = VTRService.get_service_objects().filter(id=o.provider_service.id)
+ if not vtrs:
+ return None
+
+ return vtrs[0]
+
+ def get_vcpe_service(self, o):
+ if o.target:
+ # o.target is a CordSubscriberRoot
+ if o.target.volt and o.target.volt.vcpe:
+ vcpes = VSGService.get_service_objects().filter(id=o.target.volt.vcpe.provider_service.id)
+ if not vcpes:
+ return None
+ return vcpes[0]
+ return None
+
+ def get_instance(self, o):
+ if o.target and o.target.volt and o.target.volt.vcpe:
+ return o.target.volt.vcpe.instance
+ else:
+ return None
+
+ def get_extra_attributes(self, o):
+ vtr_service = self.get_vtr_service(o)
+ vcpe_service = self.get_vcpe_service(o)
+
+ if not vcpe_service:
+ raise Exception("No vcpeservice")
+
+ instance = self.get_instance(o)
+
+ if not instance:
+ raise Exception("No instance")
+
+ s_tags = []
+ c_tags = []
+ if o.target and o.target.volt:
+ s_tags.append(o.target.volt.s_tag)
+ c_tags.append(o.target.volt.c_tag)
+
+ wan_vm_ip=""
+ wan_vm_mac=""
+ tags = Tag.select_by_content_object(instance).filter(name="vm_wan_addr")
+ if tags:
+ parts=tags[0].value.split(",")
+ if len(parts)!=3:
+ raise Exception("vm_wan_addr tag is malformed: %s" % value)
+ wan_vm_ip = parts[1]
+ wan_vm_mac = parts[2]
+ else:
+ if CORD_USE_VTN:
+ raise Exception("no vm_wan_addr tag for instance %s" % instance)
+
+ fields = {"s_tags": s_tags,
+ "c_tags": c_tags,
+ "isolation": instance.isolation,
+ "wan_container_gateway_mac": vcpe_service.wan_container_gateway_mac,
+ "wan_container_gateway_ip": vcpe_service.wan_container_gateway_ip,
+ "wan_container_netbits": vcpe_service.wan_container_netbits,
+ "wan_vm_mac": wan_vm_mac,
+ "wan_vm_ip": wan_vm_ip,
+ "container_name": "vcpe-%s-%s" % (s_tags[0], c_tags[0]),
+ "dns_servers": [x.strip() for x in vcpe_service.dns_servers.split(",")],
+
+ "result_fn": "%s-vcpe-%s-%s" % (o.test, s_tags[0], c_tags[0]) }
+
+ # add in the sync_attributes that come from the SubscriberRoot object
+
+ if o.target and hasattr(o.target, "sync_attributes"):
+ for attribute_name in o.target.sync_attributes:
+ fields[attribute_name] = getattr(o.target, attribute_name)
+
+ for attribute_name in o.sync_attributes:
+ fields[attribute_name] = getattr(o,attribute_name)
+
+ return fields
+
+ def sync_fields(self, o, fields):
+ # the super causes the playbook to be run
+
+ super(SyncVTRTenant, self).sync_fields(o, fields)
+
+ def run_playbook(self, o, fields):
+ o.result = ""
+
+ result_fn = os.path.join("/opt/xos/synchronizers/vtr/result", fields["result_fn"])
+ if os.path.exists(result_fn):
+ os.remove(result_fn)
+
+ instance = self.get_instance(o)
+ if instance.isolation in ["container", "container_vm"]:
+ super(SyncVTRTenant, self).run_playbook(o, fields, "sync_vtrtenant_new.yaml")
+ else:
+ if CORD_USE_VTN:
+ super(SyncVTRTenant, self).run_playbook(o, fields, template_name="sync_vtrtenant_vtn.yaml")
+ else:
+ super(SyncVTRTenant, self).run_playbook(o, fields)
+
+ if os.path.exists(result_fn):
+ o.result = open(result_fn).read()
+
+
+ def delete_record(self, m):
+ pass
diff --git a/xos/synchronizers/vtr/steps/sync_vtrtenant.yaml b/xos/synchronizers/vtr/steps/sync_vtrtenant.yaml
new file mode 100644
index 0000000..ddcfa57
--- /dev/null
+++ b/xos/synchronizers/vtr/steps/sync_vtrtenant.yaml
@@ -0,0 +1,90 @@
+---
+- hosts: {{ instance_name }}
+ #gather_facts: False
+ connection: ssh
+ user: ubuntu
+ sudo: yes
+ vars:
+ container_name: {{ container_name }}
+ wan_container_ip: {{ wan_container_ip }}
+ wan_container_netbits: {{ wan_container_netbits }}
+ wan_container_mac: {{ wan_container_mac }}
+ wan_container_gateway_ip: {{ wan_container_gateway_ip }}
+ wan_vm_ip: {{ wan_vm_ip }}
+ wan_vm_mac: {{ wan_vm_mac }}
+
+ scope: vm
+ test: {{ test }}
+ argument: {{ argument }}
+ result_file: {{ result_fn }}
+
+
+ tasks:
+ - name: Remove any old result file
+ shell: rm -f /tmp/{{ result_fn }}
+
+ - name: Copy run_tcpdump.sh to VM
+ copy: src=/opt/xos/synchronizers/vtr/files/run_tcpdump.sh dest=/root/run_tcpdump.sh mode=0755
+ when: (test=="tcpdump")
+
+
+# -----------------
+# scope == VM
+# -----------------
+
+ - name: Send the pings from VM
+ shell: ping -c 10 {{ argument }} 2>&1 > /tmp/{{ result_fn }}
+ ignore_errors: yes
+ when: (scope=="vm") and (test=="ping")
+
+ - name: Install traceroute
+ apt: name=traceroute state=present
+ when: (scope=="vm") and (test=="traceroute")
+
+ - name: Send traceroute from VM
+ shell: traceroute {{ argument }} 2>&1 > /tmp/{{ result_fn }}
+ ignore_errors: yes
+ when: (scope=="vm") and (test=="traceroute")
+
+ - name: Run tcpdump for 30 seconds on VM
+ shell: /root/run_tcpdump.sh {{ argument }} 2>&1 > /tmp/{{ result_fn }}
+ ignore_errors: yes
+ when: (scope=="vm") and (test=="tcpdump")
+
+# ------------------
+# scope == container
+# ------------------
+
+ - name: Send the pings from Container
+ shell: docker exec {{ container_name }} ping -c 10 {{ argument }} 2>&1 > /tmp/{{ result_fn }}
+ ignore_errors: yes
+ when: (scope=="container") and (test=="ping")
+
+ - name: Install traceroute into Container
+ shell: docker exec {{ container_name }} apt-get -y install traceroute
+ when: (scope=="container") and (test=="traceroute")
+
+ - name: Send traceroute from Container
+ shell: docker exec {{ container_name }} traceroute {{ argument }} 2>&1 > /tmp/{{ result_fn }}
+ ignore_errors: yes
+ when: (scope=="container") and (test=="traceroute")
+
+ - name: Copy run_tcpdump.sh to container
+ command: docker cp /root/run_tcpdump.sh {{ container_name }}:/root/run_tcpdump.sh
+ when: (scope=="container") and (test=="tcpdump")
+
+ - name: Run tcpdump for 30 seconds from Container
+ shell: docker exec {{ container_name }} /root/run_tcpdump.sh {{ argument }} 2>&1 > /tmp/{{ result_fn }}
+ ignore_errors: yes
+ when: (scope=="container") and (test=="tcpdump")
+
+# ------------------
+# scope == *
+# ------------------
+
+ - name: Fetch the result
+ fetch: src=/tmp/{{ result_fn }} dest=/opt/xos/synchronizers/vtr/result/{{ result_fn }} flat=yes
+
+
+
+
diff --git a/xos/synchronizers/vtr/steps/sync_vtrtenant_vtn.yaml b/xos/synchronizers/vtr/steps/sync_vtrtenant_vtn.yaml
new file mode 100644
index 0000000..d2e6ef7
--- /dev/null
+++ b/xos/synchronizers/vtr/steps/sync_vtrtenant_vtn.yaml
@@ -0,0 +1,30 @@
+---
+- hosts: {{ instance_name }}
+ #gather_facts: False
+ connection: ssh
+ user: ubuntu
+ sudo: yes
+ vars:
+ container_name: {{ container_name }}
+ wan_container_ip: {{ wan_container_ip }}
+ wan_container_netbits: {{ wan_container_netbits }}
+ wan_container_mac: {{ wan_container_mac }}
+ wan_container_gateway_ip: {{ wan_container_gateway_ip }}
+ wan_vm_ip: {{ wan_vm_ip }}
+ wan_vm_mac: {{ wan_vm_mac }}
+
+ test: {{ test }}
+ argument: {{ argument }}
+ result_file: {{ result_fn }}
+
+
+ tasks:
+{% if test=="ping" %}
+ - name: Send the pings
+ shell: rm -f /tmp/{{ result_fn }}
+ shell: ping -c 10 {{ argument }} > /tmp/{{ result_fn }}
+
+ - name: Fetch the ping result
+ fetch: src=/tmp/{{ result_fn }} dest=/opt/xos/synchronizers/vtr/result/{{ result_fn }} flat=yes
+{% endif %}
+
diff --git a/xos/synchronizers/vtr/vtn_vtr_synchronizer_config b/xos/synchronizers/vtr/vtn_vtr_synchronizer_config
new file mode 100644
index 0000000..2c9140a
--- /dev/null
+++ b/xos/synchronizers/vtr/vtn_vtr_synchronizer_config
@@ -0,0 +1,47 @@
+
+[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=vtr
+dependency_graph=/opt/xos/synchronizers/vtr/model-deps
+steps_dir=/opt/xos/synchronizers/vtr/steps
+sys_dir=/opt/xos/synchronizers/vtr/sys
+deleters_dir=/opt/xos/synchronizers/vtr/deleters
+log_file=console
+#/var/log/hpc.log
+driver=None
+pretend=False
+backoff_disabled=True
+save_ansible_output=True
+# set proxy_ssh to false on cloudlab
+full_setup=True
+proxy_ssh=True
+proxy_ssh_key=/opt/xos/synchronizers/vtr/node_key
+proxy_ssh_user=root
+
+[networking]
+use_vtn=True
+
+[feefie]
+client_id='vicci_dev_central'
+user_id='pl'
diff --git a/xos/synchronizers/vtr/vtr-synchronizer.py b/xos/synchronizers/vtr/vtr-synchronizer.py
new file mode 100755
index 0000000..84bec4f
--- /dev/null
+++ b/xos/synchronizers/vtr/vtr-synchronizer.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__)),"../../synchronizers/base")
+sys.path.append(observer_path)
+mod = importlib.import_module("xos-synchronizer")
+mod.main()
diff --git a/xos/synchronizers/vtr/vtr_synchronizer_config b/xos/synchronizers/vtr/vtr_synchronizer_config
new file mode 100644
index 0000000..51bf25a
--- /dev/null
+++ b/xos/synchronizers/vtr/vtr_synchronizer_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=vtr
+dependency_graph=/opt/xos/synchronizers/vtr/model-deps
+steps_dir=/opt/xos/synchronizers/vtr/steps
+sys_dir=/opt/xos/synchronizers/vtr/sys
+deleters_dir=/opt/xos/synchronizers/vtr/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/tools/xos-manage b/xos/tools/xos-manage
index 4783bf5..5410f37 100755
--- a/xos/tools/xos-manage
+++ b/xos/tools/xos-manage
@@ -146,6 +146,7 @@
python ./manage.py makemigrations ceilometer
python ./manage.py makemigrations helloworldservice_complete
python ./manage.py makemigrations onos
+ python ./manage.py makemigrations vtr
#python ./manage.py makemigrations servcomp
}
diff --git a/xos/tosca/README.md b/xos/tosca/README.md
index 98f0aaf..b0a654a 100644
--- a/xos/tosca/README.md
+++ b/xos/tosca/README.md
@@ -6,8 +6,8 @@
as follows:
* custom_types -- Defines schema for XOS-specific models.
- * `.m4` files are source.
- * `.yaml` files are generated.
+ * `.m4` source files
+ * `.yaml` generated files
* definitions -- Defines schema for TOSCA's base models.
- * resources -- Translates TOSCA specification to Django API.
- * sample -- Example TOSCA models.
+ * resources -- Translates TOSCA to Django API.
+ * sample -- Example TOSCA specifications.
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 9773822..bb919e2 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -866,6 +866,9 @@
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.NodeLabel ]
+ tosca.relationships.DependsOn:
+ derived_from: tosca.relationships.Root
+
tosca.capabilities.xos.Service:
derived_from: tosca.capabilities.Root
description: An XOS Service
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 21e8b8b..530e534 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -1178,6 +1178,9 @@
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.xos.NodeLabel ]
+ tosca.relationships.DependsOn:
+ derived_from: tosca.relationships.Root
+
tosca.capabilities.xos.Service:
derived_from: tosca.capabilities.Root
description: An XOS Service
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index 8764b80..5c6c0cb 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -180,6 +180,7 @@
'services.ceilometer',
'services.requestrouter',
'services.syndicate_storage',
+ 'services.vtr',
'geoposition',
'rest_framework_swagger',
)