Merge branch 'master' of github.com:open-cloud/xos
diff --git a/views/ngXosViews/openVPNDashboard/src/js/main.js b/views/ngXosViews/openVPNDashboard/src/js/main.js
index 04d5d76..b16c2fb 100644
--- a/views/ngXosViews/openVPNDashboard/src/js/main.js
+++ b/views/ngXosViews/openVPNDashboard/src/js/main.js
@@ -23,7 +23,7 @@
   this.getOpenVpnTenants = () => {
     let deferred = $q.defer();
 
-    $http.get('/xoslib/openvpntenant/')
+    $http.get('/api/tenant/openvpn/list/')
     .then((res) => {
       deferred.resolve(res.data)
     })
diff --git a/xos/api/tenant/openvpn/__init__.py b/xos/api/tenant/openvpn/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/xos/api/tenant/openvpn/__init__.py
@@ -0,0 +1 @@
+
diff --git a/xos/core/xoslib/methods/openvpnview.py b/xos/api/tenant/openvpn/openvpn.py
similarity index 84%
rename from xos/core/xoslib/methods/openvpnview.py
rename to xos/api/tenant/openvpn/openvpn.py
index d8cf39e..9cc13f0 100644
--- a/xos/core/xoslib/methods/openvpnview.py
+++ b/xos/api/tenant/openvpn/openvpn.py
@@ -1,16 +1,9 @@
 import jinja2
+
+from api.xosapi_helpers import PlusModelSerializer, ReadOnlyField, XOSViewSet
 from core.models import TenantPrivilege
-from plus import PlusSerializerMixin
 from rest_framework import serializers
 from services.openvpn.models import OpenVPNService, OpenVPNTenant
-from xos.apibase import XOSListCreateAPIView
-
-if hasattr(serializers, "ReadOnlyField"):
-    # rest_framework 3.x
-    ReadOnlyField = serializers.ReadOnlyField
-else:
-    # rest_framework 2.x
-    ReadOnlyField = serializers.Field
 
 
 def get_default_openvpn_service():
@@ -20,7 +13,7 @@
     return None
 
 
-class OpenVPNTenantSerializer(serializers.ModelSerializer, PlusSerializerMixin):
+class OpenVPNTenantSerializer(PlusModelSerializer):
     """A Serializer for the OpenVPNTenant that has the minimum information required for clients.
 
     Attributes:
@@ -63,15 +56,16 @@
                   "ca_crt": obj.get_ca_crt(pki_dir),
                   "client_crt": obj.get_client_cert(client_name, pki_dir),
                   "client_key": obj.get_client_key(client_name, pki_dir)
-                 }
+                  }
         return template.render(fields)
 
 
-class OpenVPNTenantList(XOSListCreateAPIView):
+class OpenVPNTenantViewSet(XOSViewSet):
     """Class that provides a list of OpenVPNTenants that the user has permission to access."""
+    base_name = "openvpn"
+    method_kind = "viewset"
+    method_name = "list"
     serializer_class = OpenVPNTenantSerializer
-    method_kind = "list"
-    method_name = "openvpntenant"
 
     def get_queryset(self):
         # Get every privilege for this user
diff --git a/xos/configurations/cord-pod/make-vtn-external-yaml.sh b/xos/configurations/cord-pod/make-vtn-external-yaml.sh
index c623394..4c9e0ee 100644
--- a/xos/configurations/cord-pod/make-vtn-external-yaml.sh
+++ b/xos/configurations/cord-pod/make-vtn-external-yaml.sh
@@ -18,14 +18,14 @@
       requirements:
       properties:
           kind: onos
-          view_url: /admin/onos/onosservice/$id$/
+          view_url: /admin/onos/onosservice/\$id$/
           no_container: true
           rest_hostname: onos-cord
 
     service_vtn:
       type: tosca.nodes.VTNService
       properties:
-          view_url: /admin/vtn/vtnservice/$id$/
+          view_url: /admin/vtn/vtnservice/\$id$/
           privateGatewayMac: 00:00:00:00:00:01
           localManagementIp: 172.27.0.1/24
           ovsdbPort: 6641
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 5cc0ddd..a0fd3cb 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -2381,6 +2381,17 @@
 
         return tabs
 
+class AddressPoolInline(XOSTabularInline):
+    model = AddressPool
+    extra = 0
+    suit_classes = 'suit-tab suit-tab-addresspools'
+    fields = ['cidr', 'gateway_ip', 'gateway_mac']
+    readonly_fields = ['cidr',]
+
+    # disable the add link
+    def has_add_permission(self, request):
+        return False
+
 # Now register the new UserAdmin...
 admin.site.register(User, UserAdmin)
 # ... and, since we're not using Django's builtin permissions,
diff --git a/xos/core/xoslib/static/js/xosOpenVPNDashboard.js b/xos/core/xoslib/static/js/xosOpenVPNDashboard.js
index 8723888..b28322f 100644
--- a/xos/core/xoslib/static/js/xosOpenVPNDashboard.js
+++ b/xos/core/xoslib/static/js/xosOpenVPNDashboard.js
@@ -1 +1 @@
-"use strict";angular.module("xos.openVPNDashboard",["ngResource","ngCookies","ngLodash","ui.router","xos.helpers"]).config(["$stateProvider",function(n){n.state("openVPNList",{url:"/",template:"<vpn-list></vpn-list>"})}]).config(["$compileProvider",function(n){n.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|file|blob):/)}]).service("Vpn",["$http","$q",function(n,e){this.getOpenVpnTenants=function(){var t=e.defer();return n.get("/xoslib/openvpntenant/").then(function(n){t.resolve(n.data)})["catch"](function(n){t.reject(n)}),t.promise}}]).config(["$httpProvider",function(n){n.interceptors.push("NoHyperlinks")}]).directive("vpnList",function(){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/openvpn-list.tpl.html",controller:["Vpn",function(n){var e=this;n.getOpenVpnTenants().then(function(n){e.vpns=n;for(var t=0;t<e.vpns.length;t++){var i=new Blob([e.vpns[t].script_text],{type:"text/plain"});e.vpns[t].script_text=(window.URL||window.webkitURL).createObjectURL(i)}})["catch"](function(n){throw new Error(n)})}]}}),angular.module("xos.openVPNDashboard").run(["$templateCache",function(n){n.put("templates/openvpn-list.tpl.html",'<div style="display: table;">\n  <div class="vpn-row">\n    <h1 class="vpn-cell">VPN List</h1>\n  </div>\n  <div class="vpn-row">\n    <div class="vpn-cell vpn-header">ID</div>\n    <div class="vpn-cell vpn-header">VPN Network</div>\n    <div class="vpn-cell vpn-header">VPN Subnet</div>\n    <div class="vpn-cell vpn-header">Script Link</div>\n  </div>\n  <div class="vpn-row" ng-repeat="vpn in vm.vpns">\n    <div class="vpn-cell">{{ vpn.id }}</div>\n    <div class="vpn-cell">{{ vpn.server_network }}</div>\n    <div class="vpn-cell">{{ vpn.vpn_subnet }}</div>\n    <div class="vpn-cell">\n      <a download="connect-{{ vpn.id }}.vpn" ng-href="{{ vpn.script_text }}">Script</a>\n    </div>\n  </div>\n</div>\n')}]),angular.module("xos.openVPNDashboard").run(["$location",function(n){n.path("/")}]),angular.bootstrap(angular.element("#xosOpenVPNDashboard"),["xos.openVPNDashboard"]);
\ No newline at end of file
+"use strict";angular.module("xos.openVPNDashboard",["ngResource","ngCookies","ngLodash","ui.router","xos.helpers"]).config(["$stateProvider",function(n){n.state("openVPNList",{url:"/",template:"<vpn-list></vpn-list>"})}]).config(["$compileProvider",function(n){n.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|file|blob):/)}]).service("Vpn",["$http","$q",function(n,e){this.getOpenVpnTenants=function(){var t=e.defer();return n.get("/api/tenant/openvpn/list/").then(function(n){t.resolve(n.data)})["catch"](function(n){t.reject(n)}),t.promise}}]).config(["$httpProvider",function(n){n.interceptors.push("NoHyperlinks")}]).directive("vpnList",function(){return{restrict:"E",scope:{},bindToController:!0,controllerAs:"vm",templateUrl:"templates/openvpn-list.tpl.html",controller:["Vpn",function(n){var e=this;n.getOpenVpnTenants().then(function(n){e.vpns=n;for(var t=0;t<e.vpns.length;t++){var i=new Blob([e.vpns[t].script_text],{type:"text/plain"});e.vpns[t].script_text=(window.URL||window.webkitURL).createObjectURL(i)}})["catch"](function(n){throw new Error(n)})}]}}),angular.module("xos.openVPNDashboard").run(["$templateCache",function(n){n.put("templates/openvpn-list.tpl.html",'<div style="display: table;">\n  <div class="vpn-row">\n    <h1 class="vpn-cell">VPN List</h1>\n  </div>\n  <div class="vpn-row">\n    <div class="vpn-cell vpn-header">ID</div>\n    <div class="vpn-cell vpn-header">VPN Network</div>\n    <div class="vpn-cell vpn-header">VPN Subnet</div>\n    <div class="vpn-cell vpn-header">Script Link</div>\n  </div>\n  <div class="vpn-row" ng-repeat="vpn in vm.vpns">\n    <div class="vpn-cell">{{ vpn.id }}</div>\n    <div class="vpn-cell">{{ vpn.server_network }}</div>\n    <div class="vpn-cell">{{ vpn.vpn_subnet }}</div>\n    <div class="vpn-cell">\n      <a download="connect-{{ vpn.id }}.vpn" ng-href="{{ vpn.script_text }}">Script</a>\n    </div>\n  </div>\n</div>\n')}]),angular.module("xos.openVPNDashboard").run(["$location",function(n){n.path("/")}]),angular.bootstrap(angular.element("#xosOpenVPNDashboard"),["xos.openVPNDashboard"]);
\ No newline at end of file
diff --git a/xos/services/vrouter/admin.py b/xos/services/vrouter/admin.py
index 318b3dc..4bd99b6 100644
--- a/xos/services/vrouter/admin.py
+++ b/xos/services/vrouter/admin.py
@@ -11,7 +11,7 @@
 from django.contrib.contenttypes import generic
 from suit.widgets import LinkedSelect
 from core.models import AddressPool
-from core.admin import ServiceAppAdmin,SliceInline,ServiceAttrAsTabInline, ReadOnlyAwareAdmin, XOSTabularInline, ServicePrivilegeInline, TenantRootTenantInline, TenantRootPrivilegeInline
+from core.admin import ServiceAppAdmin,SliceInline,ServiceAttrAsTabInline, ReadOnlyAwareAdmin, XOSTabularInline, ServicePrivilegeInline, AddressPoolInline
 from core.middleware import get_request
 
 from functools import update_wrapper
@@ -38,7 +38,7 @@
     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]
+    inlines = [SliceInline,ServiceAttrAsTabInline,ServicePrivilegeInline,AddressPoolInline]
     form = VRouterServiceForm
 
     extracontext_registered_admins = True
@@ -47,6 +47,7 @@
 
     suit_form_tabs =(('general', 'vRouter Service Details'),
         ('administration', 'Administration'),
+        ('addresspools', 'Addresses'),
         #('tools', 'Tools'),
         ('slices','Slices'),
         ('serviceattrs','Additional Attributes'),