move over vtn services from XOS repo
diff --git a/xos/admin.py b/xos/admin.py
new file mode 100644
index 0000000..464f197
--- /dev/null
+++ b/xos/admin.py
@@ -0,0 +1,93 @@
+from django.contrib import admin
+
+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.vtn.models import *
+
+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 VTNServiceForm(forms.ModelForm):
+ privateGatewayMac = forms.CharField(required=False)
+ localManagementIp = forms.CharField(required=False)
+ ovsdbPort = forms.CharField(required=False)
+ sshPort = forms.CharField(required=False)
+ sshUser = forms.CharField(required=False)
+ sshKeyFile = forms.CharField(required=False)
+ mgmtSubnetBits = forms.CharField(required=False)
+ xosEndpoint = forms.CharField(required=False)
+ xosUser = forms.CharField(required=False)
+ xosPassword = forms.CharField(required=False)
+
+ def __init__(self,*args,**kwargs):
+ super (VTNServiceForm,self ).__init__(*args,**kwargs)
+ if self.instance:
+ self.fields['privateGatewayMac'].initial = self.instance.privateGatewayMac
+ self.fields['localManagementIp'].initial = self.instance.localManagementIp
+ self.fields['ovsdbPort'].initial = self.instance.ovsdbPort
+ self.fields['sshPort'].initial = self.instance.sshPort
+ self.fields['sshUser'].initial = self.instance.sshUser
+ self.fields['sshKeyFile'].initial = self.instance.sshKeyFile
+ self.fields['mgmtSubnetBits'].initial = self.instance.mgmtSubnetBits
+ self.fields['xosEndpoint'].initial = self.instance.xosEndpoint
+ self.fields['xosUser'].initial = self.instance.xosUser
+ self.fields['xosPassword'].initial = self.instance.xosPassword
+
+ def save(self, commit=True):
+ self.instance.privateGatewayMac = self.cleaned_data.get("privateGatewayMac")
+ self.instance.localManagementIp = self.cleaned_data.get("localManagementIp")
+ self.instance.ovsdbPort = self.cleaned_data.get("ovsdbPort")
+ self.instance.sshPort = self.cleaned_data.get("sshPort")
+ self.instance.sshUser = self.cleaned_data.get("sshUser")
+ self.instance.sshKeyFile = self.cleaned_data.get("sshKeyFile")
+ self.instance.mgmtSubnetBits = self.cleaned_data.get("mgmtSubnetBits")
+ self.instance.xosEndpoint = self.cleaned_data.get("xosEndpoint")
+ self.instance.xosUser = self.cleaned_data.get("xosUser")
+ self.instance.xosPassword = self.cleaned_data.get("xosPassword")
+ return super(VTNServiceForm, self).save(commit=commit)
+
+ class Meta:
+ model = VTNService
+
+class VTNServiceAdmin(ReadOnlyAwareAdmin):
+ model = VTNService
+ form = VTNServiceForm
+ verbose_name = "VTN Service"
+ verbose_name_plural = "VTN 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",
+ 'privateGatewayMac', 'localManagementIp', 'ovsdbPort', 'sshPort', 'sshUser', 'sshKeyFile', 'mgmtSubnetBits', 'xosEndpoint', 'xosUser', 'xosPassword' ], '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', 'VTN Service Details'),
+# ('administration', 'Administration'),
+ ('slices','Slices'),
+ ('serviceattrs','Additional Attributes'),
+ ('serviceprivileges','Privileges'),
+ )
+
+ suit_form_includes = ( # ('vtnadmin.html', 'top', 'administration'),
+ ) #('hpctools.html', 'top', 'tools') )
+
+ def queryset(self, request):
+ return VTNService.get_service_objects_by_user(request.user)
+
+admin.site.register(VTNService, VTNServiceAdmin)
diff --git a/xos/api/service/vtn.py b/xos/api/service/vtn.py
new file mode 100644
index 0000000..6b02616
--- /dev/null
+++ b/xos/api/service/vtn.py
@@ -0,0 +1,96 @@
+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 viewsets
+from rest_framework.decorators import detail_route, list_route
+from rest_framework.views import APIView
+from core.models import *
+from services.vtn.models import VTNService
+from django.forms import widgets
+from django.conf.urls import patterns, url
+from api.xosapi_helpers import PlusModelSerializer, XOSViewSet, ReadOnlyField
+from django.shortcuts import get_object_or_404
+from xos.apibase import XOSListCreateAPIView, XOSRetrieveUpdateDestroyAPIView, XOSPermissionDenied
+from xos.exceptions import *
+import json
+import subprocess
+
+class VTNServiceSerializer(PlusModelSerializer):
+ id = ReadOnlyField()
+
+ privateGatewayMac = serializers.CharField(required=False)
+ localManagementIp = serializers.CharField(required=False)
+ ovsdbPort = serializers.IntegerField(required=False)
+ sshPort = serializers.IntegerField(required=False)
+ sshUser = serializers.CharField(required=False)
+ sshKeyFile = serializers.CharField(required=False)
+ mgmtSubnetBits = serializers.IntegerField(required=False)
+ xosEndpoint = serializers.CharField(required=False)
+ xosUser = serializers.CharField(required=False)
+ xosPassword = serializers.CharField(required=False)
+
+
+ humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+ class Meta:
+ model = VTNService
+ fields = ('humanReadableName', 'id', 'privateGatewayMac', 'localManagementIp', 'ovsdbPort', 'sshPort', 'sshUser', 'sshKeyFile',
+ 'mgmtSubnetBits', 'xosEndpoint', 'xosUser', 'xosPassword')
+
+ def getHumanReadableName(self, obj):
+ return obj.__unicode__()
+
+class VTNViewSet(XOSViewSet):
+ base_name = "vtn"
+ method_name = "vtn"
+ method_kind = "viewset"
+
+ # these are just because ViewSet needs some queryset and model, even if we don't use the
+ # default endpoints
+ queryset = VTNService.get_service_objects().all()
+ model = VTNService
+ serializer_class = VTNServiceSerializer
+
+ @classmethod
+ def get_urlpatterns(self, api_path="^"):
+ patterns = []
+
+ patterns.append( self.detail_url("services/$", {"get": "get_services"}, "services") )
+ patterns.append( self.detail_url("services_names/$", {"get": "get_services_names"}, "services") )
+ patterns.append( self.detail_url("services/(?P<service>[a-zA-Z0-9\-_]+)/$", {"get": "get_service"}, "get_service") )
+
+ # Not as RESTful as it could be, but maintain these endpoints for compability
+ patterns.append( self.list_url("services/$", {"get": "get_services"}, "rootvtn_services") )
+ patterns.append( self.list_url("services_names/$", {"get": "get_services_names"}, "rootvtn_services") )
+ patterns.append( self.list_url("services/(?P<service>[a-zA-Z0-9\-_]+)/$", {"get": "get_service"}, "rootvtn_get_service") )
+
+ patterns = patterns + super(VTNViewSet,self).get_urlpatterns(api_path)
+
+ return patterns
+
+ def get_services_names(self, request, pk=None):
+ result = {}
+ for service in Service.objects.all():
+ for id in service.get_vtn_src_names():
+ dependencies = service.get_vtn_dependencies_names()
+ if dependencies:
+ result[id] = dependencies
+ return Response(result)
+
+ def get_services(self, request, pk=None):
+ result = {}
+ for service in Service.objects.all():
+ for id in service.get_vtn_src_ids():
+ dependencies = service.get_vtn_dependencies_ids()
+ if dependencies:
+ result[id] = dependencies
+ return Response(result)
+
+ def get_service(self, request, pk=None, service=None):
+ for xos_service in Service.objects.all():
+ if service in xos_service.get_vtn_src_ids():
+ return Response(xos_service.get_vtn_dependencies_ids())
+ return Response([])
+
+
diff --git a/xos/models.py b/xos/models.py
new file mode 100644
index 0000000..c805f24
--- /dev/null
+++ b/xos/models.py
@@ -0,0 +1,45 @@
+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
+import traceback
+from xos.exceptions import *
+from xos.config import Config
+
+class ConfigurationError(Exception):
+ pass
+
+VTN_KIND = "VTN"
+
+# -------------------------------------------
+# VTN
+# -------------------------------------------
+
+class VTNService(Service):
+ KIND = VTN_KIND
+
+ class Meta:
+ app_label = "vtn"
+ verbose_name = "VTN Service"
+ proxy = True
+
+ simple_attributes = ( ("privateGatewayMac", "00:00:00:00:00:01"),
+ ("localManagementIp", "172.27.0.1/24"),
+ ("ovsdbPort", "6641"),
+ ("sshPort", "22"),
+ ("sshUser", "root"),
+ ("sshKeyFile", "/root/node_key") ,
+ ("mgmtSubnetBits", "24"),
+ ("xosEndpoint", "http://xos/"),
+ ("xosUser", "padmin@vicci.org"),
+ ("xosPassword", "letmein"),
+
+ )
+
+VTNService.setup_simple_attributes()
diff --git a/xos/synchronizer/manifest b/xos/synchronizer/manifest
new file mode 100644
index 0000000..dccfcdc
--- /dev/null
+++ b/xos/synchronizer/manifest
@@ -0,0 +1,10 @@
+manifest
+vtn-synchronizer.py
+steps/sync_tenant.py
+steps/sync_port_addresses.py
+start.sh
+stop.sh
+model-deps
+supervisor/vtn-observer.conf
+run.sh
+vtn_synchronizer_config
diff --git a/xos/synchronizer/model-deps b/xos/synchronizer/model-deps
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/xos/synchronizer/model-deps
@@ -0,0 +1 @@
+{}
diff --git a/xos/synchronizer/run.sh b/xos/synchronizer/run.sh
new file mode 100755
index 0000000..000a563
--- /dev/null
+++ b/xos/synchronizer/run.sh
@@ -0,0 +1,2 @@
+export XOS_DIR=/opt/xos
+python vtn-synchronizer.py -C $XOS_DIR/synchronizers/vtn/vtn_synchronizer_config
diff --git a/xos/synchronizer/start.sh b/xos/synchronizer/start.sh
new file mode 100755
index 0000000..2c43440
--- /dev/null
+++ b/xos/synchronizer/start.sh
@@ -0,0 +1,2 @@
+export XOS_DIR=/opt/xos
+nohup python vtn-synchronizer.py -C $XOS_DIR/synchronizers/vtn/vtn_synchronizer_config > /dev/null 2>&1 &
diff --git a/xos/synchronizer/steps/sync_port_addresses.py b/xos/synchronizer/steps/sync_port_addresses.py
new file mode 100644
index 0000000..553df6f
--- /dev/null
+++ b/xos/synchronizer/steps/sync_port_addresses.py
@@ -0,0 +1,133 @@
+import os
+import requests
+import socket
+import sys
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep
+from core.models import Service, Port, Controller, Tag, Tenant
+from core.models.service import COARSE_KIND
+from services.vsg.models import VSGTenant
+from xos.logger import Logger, logging
+from requests.auth import HTTPBasicAuth
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+logger = Logger(level=logging.INFO)
+
+# XXX should save and load this
+glo_saved_vtn_maps = []
+
+class SyncPortAddresses(SyncStep):
+ requested_interval = 0 # 3600
+ provides=[Port]
+ observes=Port
+
+ def __init__(self, **args):
+ SyncStep.__init__(self, **args)
+
+ def call(self, **args):
+ global glo_saved_vtn_maps
+
+ logger.info("sync'ing vsg tenant to port addresses")
+
+ # build up a dictionary of port-->[wan_addrs] mappings
+ port_addrs = {}
+ for vsg in VSGTenant.get_tenant_objects().all():
+ if not vsg.instance:
+ logger.info("skipping vsg %s because it has no instance" % vsg)
+
+ wan_ip = vsg.wan_container_ip
+ if not wan_ip:
+ logger.info("skipping vsg %s because it has no wan_container_ip" % vsg)
+
+ wan_mac = vsg.wan_container_mac
+ if not wan_mac:
+ logger.info("skipping vsg %s because it has no wan_container_mac" % vsg)
+
+ lan_network = vsg.get_lan_network(vsg.instance)
+ if not lan_network:
+ logger.info("skipping vsg %s because it has no lan_network" % vsg)
+
+ lan_port = Port.objects.filter(instance = vsg.instance, network=lan_network)
+ if not lan_port:
+ logger.info("skipping vsg %s because it has no lan_port" % vsg)
+ lan_port = lan_port[0]
+
+ if not lan_port.port_id:
+ logger.info("skipping vsg %s because its lan_port has no port_id" % vsg)
+
+ if not (lan_port.pk in port_addrs):
+ port_addrs[lan_port.pk] = []
+ entry = {"mac_address": wan_mac, "ip_address": wan_ip}
+ addr_pairs = port_addrs[lan_port.pk]
+ if not entry in addr_pairs:
+ addr_pairs.append(entry)
+
+ # now do the VM_WAN_IP from the instance
+ if vsg.instance:
+ wan_vm_ip = vsg.wan_vm_ip
+ wan_vm_mac = vsg.wan_vm_mac
+ entry = {"mac_address": wan_vm_mac, "ip_address": wan_vm_ip}
+ if not entry in addr_pairs:
+ addr_pairs.append(entry)
+
+ # Get all ports in all controllers
+ ports_by_id = {}
+ for controller in Controller.objects.all():
+ if not controller.admin_tenant:
+ logger.info("controller %s has no admin_tenant" % controller)
+ continue
+ try:
+ driver = self.driver.admin_driver(controller = controller)
+ ports = driver.shell.quantum.list_ports()["ports"]
+ except:
+ logger.log_exc("failed to get ports from controller %s" % controller)
+ continue
+
+ for port in ports:
+ ports_by_id[port["id"]] = port
+
+ for port_pk in port_addrs.keys():
+ port = Port.objects.get(pk=port_pk)
+ addr_pairs = port_addrs[port_pk]
+ neutron_port = ports_by_id.get(port.port_id,None)
+ if not neutron_port:
+ logger.info("failed to get neutron port for port %s" % port)
+ continue
+
+ ips = [x["ip_address"] for x in addr_pairs]
+
+ changed = False
+
+ # delete addresses in neutron that don't exist in XOS
+ aaps = neutron_port.get("allowed_address_pairs", [])
+ for aap in aaps[:]:
+ if not aap["ip_address"] in ips:
+ logger.info("removing address %s from port %s" % (aap["ip_address"], port))
+ aaps.remove(aap)
+ changed = True
+
+ aaps_ips = [x["ip_address"] for x in aaps]
+
+ # add addresses in XOS that don't exist in neutron
+ for addr in addr_pairs:
+ if not addr["ip_address"] in aaps_ips:
+ logger.info("adding address %s to port %s" % (addr, port))
+ aaps.append( addr )
+ aaps_ips.append(addr["ip_address"])
+ changed = True
+
+ if changed:
+ logger.info("updating port %s" % port)
+ driver.shell.quantum.update_port(port.port_id, {"port": {"allowed_address_pairs": aaps}})
+
+
+
+
+
+
+
diff --git a/xos/synchronizer/steps/sync_tenant.py b/xos/synchronizer/steps/sync_tenant.py
new file mode 100644
index 0000000..a0e6cdb
--- /dev/null
+++ b/xos/synchronizer/steps/sync_tenant.py
@@ -0,0 +1,94 @@
+import os
+import requests
+import socket
+import sys
+import base64
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep
+from core.models import Service, Tenant
+from core.models.service import COARSE_KIND
+from xos.logger import Logger, logging
+from requests.auth import HTTPBasicAuth
+
+# hpclibrary will be in steps/..
+parentdir = os.path.join(os.path.dirname(__file__),"..")
+sys.path.insert(0,parentdir)
+
+logger = Logger(level=logging.INFO)
+
+# XXX should save and load this
+glo_saved_vtn_maps = []
+
+class SyncTenant(SyncStep):
+ provides=[Tenant]
+ observes=Tenant
+ requested_interval=0
+
+ def __init__(self, **args):
+ SyncStep.__init__(self, **args)
+
+ def get_vtn_onos_service(self):
+# vtn_tenant = Tenant.objects.filter(name="VTN_ONOS_app") # XXX fixme - hardcoded
+# if not vtn_tenant:
+# raise "No VTN Onos App found"
+# vtn_tenant = vtn_tenant[0]
+#
+# vtn_service = vtn_tenant.provider_service
+ vtn_service = Service.objects.filter(name="service_ONOS_VTN") # XXX fixme - harcoded
+ if not vtn_service:
+ raise "No VTN Onos Service"
+
+ return vtn_service[0]
+
+ def get_vtn_addr(self):
+ vtn_service = self.get_vtn_onos_service()
+
+ if not vtn_service.slices.exists():
+ raise "VTN Service has no slices"
+
+ vtn_slice = vtn_service.slices.all()[0]
+
+ if not vtn_slice.instances.exists():
+ raise "VTN Slice has no instances"
+
+ vtn_instance = vtn_slice.instances.all()[0]
+
+ return vtn_instance.node.name
+
+ def call(self, **args):
+ global glo_saved_vtn_maps
+
+ logger.info("sync'ing vtn services")
+
+ vtn_maps = []
+ for service in Service.objects.all():
+ for id in service.get_vtn_src_ids():
+ dependencies = service.get_vtn_dependencies_ids()
+ if dependencies:
+ for dependency in dependencies:
+ vtn_maps.append( (id, dependency) )
+
+ for vtn_map in vtn_maps:
+ if not (vtn_map in glo_saved_vtn_maps):
+ # call vtn rest api to add map
+ url = "http://" + self.get_vtn_addr() + ":8181/onos/cordvtn/service-dependency/%s/%s" % (vtn_map[0], vtn_map[1])
+
+ print "POST %s" % url
+ r = requests.post(url, auth=HTTPBasicAuth('karaf', 'karaf') ) # XXX fixme - hardcoded auth
+ if (r.status_code != 200):
+ raise Exception("Received error from vtn service (%d)" % r.status_code)
+
+ for vtn_map in glo_saved_vtn_maps:
+ if not vtn_map in vtn_maps:
+ # call vtn rest api to delete map
+ url = "http://" + self.get_vtn_addr() + ":8181/onos/cordvtn/service-dependency/%s/%s" % (vtn_map[0],vtn_map[1])
+
+ print "DELETE %s" % url
+ r = requests.delete(url, auth=HTTPBasicAuth('karaf', 'karaf') ) # XXX fixme - hardcoded auth
+ if (r.status_code != 200):
+ raise Exception("Received error from vtn service (%d)" % r.status_code)
+
+ glo_saved_vtn_maps = vtn_maps
+ # TODO: save this
+
diff --git a/xos/synchronizer/stop.sh b/xos/synchronizer/stop.sh
new file mode 100755
index 0000000..7ff2b06
--- /dev/null
+++ b/xos/synchronizer/stop.sh
@@ -0,0 +1 @@
+pkill -9 -f vtn-synchronizer.py
diff --git a/xos/synchronizer/supervisor/vtn-observer.conf b/xos/synchronizer/supervisor/vtn-observer.conf
new file mode 100644
index 0000000..714afa7
--- /dev/null
+++ b/xos/synchronizer/supervisor/vtn-observer.conf
@@ -0,0 +1,2 @@
+[program:vtn-observer]
+command=python /opt/xos/observers/vbng/vtn-observer.py -C /opt/xos/observers/vbng/vtn_observer_config
diff --git a/xos/synchronizer/vtn-synchronizer.py b/xos/synchronizer/vtn-synchronizer.py
new file mode 100755
index 0000000..84bec4f
--- /dev/null
+++ b/xos/synchronizer/vtn-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/synchronizer/vtn_synchronizer_config b/xos/synchronizer/vtn_synchronizer_config
new file mode 100644
index 0000000..d931839
--- /dev/null
+++ b/xos/synchronizer/vtn_synchronizer_config
@@ -0,0 +1,44 @@
+
+[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=vtn
+dependency_graph=/opt/xos/synchronizers/vtn/model-deps
+steps_dir=/opt/xos/synchronizers/vtn/steps
+sys_dir=/opt/xos/synchronizers/vtn/sys
+deleters_dir=/opt/xos/synchronizers/vtn/deleters
+log_file=console
+#/var/log/hpc.log
+driver=openstack
+pretend=False
+backoff_disabled=True
+
+[nova]
+ca_ssl_cert=/etc/ssl/certs/ca-certificates.crt
+
+[feefie]
+client_id='vicci_dev_central'
+user_id='pl'
+
+[networking]
+use_vtn=True
diff --git a/xos/templates/vtnadmin.html b/xos/templates/vtnadmin.html
new file mode 100644
index 0000000..a3a2a52
--- /dev/null
+++ b/xos/templates/vtnadmin.html
@@ -0,0 +1,5 @@
+<div class = "row text-center">
+ <div class="col-xs-12">
+ <a href="/admin/vtn/vnrtenant/">vTN Tenants</a>
+ </div>
+</div>
diff --git a/xos/tosca/resources/vtnservice.py b/xos/tosca/resources/vtnservice.py
new file mode 100644
index 0000000..2a5738f
--- /dev/null
+++ b/xos/tosca/resources/vtnservice.py
@@ -0,0 +1,15 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from services.vtn.models import VTNService
+
+from service import XOSService
+
+class XOSVTNService(XOSService):
+ provides = "tosca.nodes.VTNService"
+ xos_model = VTNService
+ copyin_props = ["view_url", "icon_url", "enabled", "published", "public_key", "versionNumber", 'privateGatewayMac', 'localManagementIp', 'ovsdbPort', 'sshPort', 'sshUser', 'sshKeyFile', 'mgmtSubnetBits', 'xosEndpoint', 'xosUser', 'xosPassword']
diff --git a/xos/vtn-onboard.yaml b/xos/vtn-onboard.yaml
new file mode 100644
index 0000000..9141dbd
--- /dev/null
+++ b/xos/vtn-onboard.yaml
@@ -0,0 +1,25 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Onboard the exampleservice
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ servicecontroller#vtn:
+ type: tosca.nodes.ServiceController
+ properties:
+ base_url: file:///opt/xos_services/vtn/xos/
+ # The following will concatenate with base_url automatically, if
+ # base_url is non-null.
+ models: models.py
+ admin: admin.py
+ admin_template: templates/vtnadmin.html
+ synchronizer: synchronizer/manifest
+ synchronizer_run: vtn-synchronizer.py
+ tosca_resource: tosca/resources/vtnservice.py
+ rest_service: api/service/vtn.py
+ #private_key: file:///opt/xos/key_import/vsg_rsa
+ #public_key: file:///opt/xos/key_import/vsg_rsa.pub
+