Merge pull request #152 from girishgc/vPGWc_Service
added pgwc service changes
diff --git a/views/ngXosLib/package.json b/views/ngXosLib/package.json
index da9d27b..ba25551 100644
--- a/views/ngXosLib/package.json
+++ b/views/ngXosLib/package.json
@@ -12,7 +12,8 @@
"doc": "gulp docs; cd ./docs",
"doc:ci": "gulp makeDocs;",
"build": "gulp vendor && gulp helpers",
- "dev": "gulp dev"
+ "dev": "gulp dev",
+ "lint": "eslint xosHelpers/"
},
"author": "Matteo Scandolo",
"license": "ISC",
diff --git a/views/ngXosLib/xosHelpers/spec/services/helpers/form.helpers.test.js b/views/ngXosLib/xosHelpers/spec/services/helpers/form.helpers.test.js
index f6bfbb2..21e2649 100644
--- a/views/ngXosLib/xosHelpers/spec/services/helpers/form.helpers.test.js
+++ b/views/ngXosLib/xosHelpers/spec/services/helpers/form.helpers.test.js
@@ -185,6 +185,14 @@
label: 'Custom Label',
type: 'number',
hint: 'Test Hint'
+ },
+ select: {
+ label: 'Select Label',
+ type: 'select',
+ hint: 'Select Hint',
+ options: [
+ {id: 1, label: 'something'}
+ ]
}
};
@@ -224,19 +232,29 @@
type: 'number',
validators: {},
hint: 'Test Hint'
+ },
+ select: {
+ label: 'Select Label:',
+ type: 'select',
+ hint: 'Select Hint',
+ validators: {},
+ options: [
+ {id: 1, label: 'something'}
+ ]
}
};
let empty_model = {5: 'Nan'}
it('should create a form object', () => {
- let res = service.buildFormStructure(empty_modelField, empty_customFields, empty_model)
+ let res = service.buildFormStructure(empty_modelField, empty_customFields, empty_model);
expect(res.id).toEqual(empty_formObject.id);
expect(res.name).toEqual(empty_formObject.name);
expect(res.mail).toEqual(empty_formObject.mail);
expect(res.active).toEqual(empty_formObject.active);
expect(res.created).toEqual(empty_formObject.created);
expect(res.custom).toEqual(empty_formObject.custom);
+ expect(res.select).toEqual(empty_formObject.select);
expect(res).toEqual(empty_formObject);
});
});
diff --git a/views/ngXosLib/xosHelpers/spec/ui/field.test.js b/views/ngXosLib/xosHelpers/spec/ui/field.test.js
index f933a01..b946f93 100644
--- a/views/ngXosLib/xosHelpers/spec/ui/field.test.js
+++ b/views/ngXosLib/xosHelpers/spec/ui/field.test.js
@@ -92,6 +92,42 @@
});
});
+
+
+
+ describe('when a option is selected in dropdown', () => {
+ beforeEach(() => {
+ scope = rootScope.$new();
+ scope.name = 'label';
+ scope.field = {
+ label: 'Label',
+ type: 'select',
+ validators: {},
+ options: [
+ {
+ id: 0,
+ label: '---Site---'
+ },
+ {
+ id: 1,
+ label: '---Site1---'
+ }
+ ]
+ };
+ scope.ngModel = 'label';
+ compileElement();
+ });
+
+ it('No of select elements', () => {
+ expect($(element).find('select').children('option').length).toEqual(3);
+ });
+
+ it('should show a selected value', () => {
+ var elem = angular.element($(element).find('select').children('option')[1]);
+ expect(elem.text()).toEqual('---Site---');
+ });
+ });
+
describe('when a number input is passed', () => {
beforeEach(() => {
scope = rootScope.$new();
diff --git a/views/ngXosLib/xosHelpers/spec/ui/form.test.js b/views/ngXosLib/xosHelpers/spec/ui/form.test.js
index cb9f9e8..eac10f5 100644
--- a/views/ngXosLib/xosHelpers/spec/ui/form.test.js
+++ b/views/ngXosLib/xosHelpers/spec/ui/form.test.js
@@ -33,21 +33,21 @@
compile = $compile;
}));
- it('should throw an error if no config is specified', inject(($compile, $rootScope) => {
+ it('should throw an error if no config is specified', () => {
function errorFunctionWrapper(){
compileElement();
}
expect(errorFunctionWrapper).toThrow(new Error('[xosForm] Please provide a configuration via the "config" attribute'));
- }));
+ });
- it('should throw an error if no actions is specified', inject(($compile, $rootScope) => {
+ it('should throw an error if no actions is specified', () => {
function errorFunctionWrapper(){
- scope = $rootScope.$new();
+ scope = rootScope.$new();
scope.config = 'green';
compileElement();
}
expect(errorFunctionWrapper).toThrow(new Error('[xosForm] Please provide an action list in the configuration'));
- }));
+ });
describe('when correctly configured', () => {
diff --git a/views/ngXosLib/xosHelpers/spec/ui/table.test.js b/views/ngXosLib/xosHelpers/spec/ui/table.test.js
index e94eb36..1535c6e 100644
--- a/views/ngXosLib/xosHelpers/spec/ui/table.test.js
+++ b/views/ngXosLib/xosHelpers/spec/ui/table.test.js
@@ -31,22 +31,22 @@
rootScope = $rootScope;
}));
- it('should throw an error if no config is specified', inject(($compile, $rootScope) => {
+ it('should throw an error if no config is specified', () => {
function errorFunctionWrapper(){
compileElement();
}
expect(errorFunctionWrapper).toThrow(new Error('[xosTable] Please provide a configuration via the "config" attribute'));
- }));
+ });
- it('should throw an error if no config columns are specified', inject(($compile, $rootScope) => {
+ it('should throw an error if no config columns are specified', () => {
function errorFunctionWrapper(){
// setup the parent scope
- scope = $rootScope.$new();
+ scope = rootScope.$new();
scope.config = 'green';
compileElement();
}
expect(errorFunctionWrapper).toThrow(new Error('[xosTable] Please provide a columns list in the configuration'));
- }));
+ });
describe('when basicly configured', function() {
diff --git a/views/ngXosLib/xosHelpers/spec/ui/validation.test.js b/views/ngXosLib/xosHelpers/spec/ui/validation.test.js
index bb663fa..f8350ed 100644
--- a/views/ngXosLib/xosHelpers/spec/ui/validation.test.js
+++ b/views/ngXosLib/xosHelpers/spec/ui/validation.test.js
@@ -7,7 +7,7 @@
(function () {
'use strict';
- let compile, element, scope, isolatedScope;
+ let compile, element, scope;
const compileElement = (el) => {
element = el;
@@ -20,7 +20,6 @@
}
compile(element)(scope);
scope.$digest();
- isolatedScope = element.isolateScope().vm;
}
describe('The xos.helper module', function(){
diff --git a/views/ngXosLib/xosHelpers/src/services/helpers/ui/form.helpers.js b/views/ngXosLib/xosHelpers/src/services/helpers/ui/form.helpers.js
index 04a383a..2dabfe1 100644
--- a/views/ngXosLib/xosHelpers/src/services/helpers/ui/form.helpers.js
+++ b/views/ngXosLib/xosHelpers/src/services/helpers/ui/form.helpers.js
@@ -100,9 +100,12 @@
label: (customField[f] && customField[f].label) ? `${customField[f].label}:` : LabelFormatter.format(f),
type: (customField[f] && customField[f].type) ? customField[f].type : this._getFieldFormat(model[f]),
validators: (customField[f] && customField[f].validators) ? customField[f].validators : {},
- hint: (customField[f] && customField[f].hint)? customField[f].hint : ''
+ hint: (customField[f] && customField[f].hint)? customField[f].hint : '',
};
+ if(customField[f] && customField[f].options){
+ form[f].options = customField[f].options;
+ }
if(form[f].type === 'date'){
model[f] = new Date(model[f]);
}
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.component.js b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.component.js
index 20b7707..ea9e0ab 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/field/field.component.js
@@ -130,8 +130,10 @@
},
template: `
<label ng-if="vm.field.type !== 'object'">{{vm.field.label}}</label>
+ <!--<pre>{{vm.field.options | json}}</pre>-->
+ <!--<pre>{{vm.ngModel | json}}</pre>-->
<input
- ng-if="vm.field.type !== 'boolean' && vm.field.type !== 'object'"
+ ng-if="vm.field.type !== 'boolean' && vm.field.type !== 'object' && vm.field.type !== 'select'"
type="{{vm.field.type}}"
name="{{vm.name}}"
class="form-control"
@@ -139,6 +141,12 @@
ng-minlength="vm.field.validators.minlength || 0"
ng-maxlength="vm.field.validators.maxlength || 2000"
ng-required="vm.field.validators.required || false" />
+ <select class="form-control" ng-if ="vm.field.type === 'select'"
+ name = "{{vm.name}}"
+ ng-options="item.id as item.label for item in vm.field.options track by item.id"
+ ng-model="vm.ngModel"
+ ng-required="vm.field.validators.required || false">
+ </select>
<span class="boolean-field" ng-if="vm.field.type === 'boolean'">
<button
class="btn btn-success"
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/form/form.component.js b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/form/form.component.js
index 92843c2..2a9f00c 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/form/form.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/form/form.component.js
@@ -164,7 +164,7 @@
<ng-form name="vm.{{vm.config.formName || 'form'}}">
<div class="form-group" ng-repeat="(name, field) in vm.formField">
<xos-field name="name" field="field" ng-model="vm.ngModel[name]"></xos-field>
- <xos-validation errors="vm[vm.config.formName || 'form'][name].$error"></xos-validation>
+ <xos-validation field="vm[vm.config.formName || 'form'][name]" form="vm[vm.config.formName || 'form']"></xos-validation>
</div>
<div class="form-group" ng-if="vm.config.actions">
<button role="button" href=""
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/validation/validation.component.js b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/validation/validation.component.js
index dbe4fbb..37cfbd5 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/validation/validation.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/dumbComponents/validation/validation.component.js
@@ -29,41 +29,44 @@
</div>
<div class="col-xs-2">
<a class="btn"
- ng-click="vm.errors.required = !vm.errors.required"
- ng-class="{'btn-default': !vm.errors.required, 'btn-success': vm.errors.required}">
+ ng-click="vm.field.$error.required = !vm.field.$error.required"
+ ng-class="{'btn-default': !vm.field.$error.required, 'btn-success': vm.field.$error.required}">
Required
</a>
</div>
<div class="col-xs-2">
<a class="btn"
- ng-click="vm.errors.email = !vm.errors.email"
- ng-class="{'btn-default': !vm.errors.email, 'btn-success': vm.errors.email}">
+ ng-click="vm.field.$error.email = !vm.field.$error.email"
+ ng-class="{'btn-default': !vm.field.$error.email, 'btn-success': vm.field.$error.email}">
Email
</a>
</div>
<div class="col-xs-2">
<a class="btn"
- ng-click="vm.errors.minlength = !vm.errors.minlength"
- ng-class="{'btn-default': !vm.errors.minlength, 'btn-success': vm.errors.minlength}">
+ ng-click="vm.field.$error.minlength = !vm.field.$error.minlength"
+ ng-class="{'btn-default': !vm.field.$error.minlength, 'btn-success': vm.field.$error.minlength}">
Min Length
</a>
</div>
<div class="col-xs-2">
<a class="btn"
- ng-click="vm.errors.maxlength = !vm.errors.maxlength"
- ng-class="{'btn-default': !vm.errors.maxlength, 'btn-success': vm.errors.maxlength}">
+ ng-click="vm.field.$error.maxlength = !vm.field.$error.maxlength"
+ ng-class="{'btn-default': !vm.field.$error.maxlength, 'btn-success': vm.field.$error.maxlength}">
Max Length
</a>
</div>
</div>
- <xos-validation errors="vm.errors"></xos-validation>
+ <xos-validation field ="vm.field" form = "vm.form"></xos-validation>
</div>
</file>
<file name="script.js">
angular.module('sampleValidation', ['xos.uiComponents'])
.controller('SampleCtrl', function(){
- this.errors = {
- email: false
+ this.field = {
+ $error: {}
+ };
+ this.form= {
+ $submitted:true
}
});
</file>
@@ -79,19 +82,22 @@
},
template: `
<div ng-cloak>
+ <pre>{{vm.field | json}}</pre>
+ <!--<pre>{{vm.form.$submitted | json}}</pre>-->
+ <!--<pre>{{vm.field.$error.required !== false}}</pre>-->
<xos-alert config="vm.config" show="vm.field.$error.required !== undefined && vm.field.$error.required !== false && (vm.field.$touched || vm.form.$submitted)">
Field required
</xos-alert>
- <xos-alert config="vm.config" show="vm.field.$error.email !== undefined && vm.field.$error.email !== false && (vm.field.$touched || vm.form.$submitted)">
+ <xos-alert config="vm.config" show="vm.field.$error.email !== undefined && vm.field.$error.email !== false && (vm.field.$touched || vm.form.$submitted)">
This is not a valid email
</xos-alert>
- <xos-alert config="vm.config" show="vm.field.$error.minlength !== undefined && vm.field.$error.minlength !== false && (vm.field.$touched || vm.form.$submitted)">
+ <xos-alert config="vm.config" show="vm.field.$error.minlength !== undefined && vm.field.$error.minlength !== false && (vm.field.$touched || vm.form.$submitted)">
Too short
</xos-alert>
- <xos-alert config="vm.config" show="vm.field.$error.maxlength !== undefined && vm.field.$error.maxlength !== false && (vm.field.$touched || vm.form.$submitted)">
+ <xos-alert config="vm.config" show="vm.field.$error.maxlength !== undefined && vm.field.$error.maxlength !== false && (vm.field.$touched || vm.form.$submitted)">
Too long
</xos-alert>
- <xos-alert config="vm.config" show="vm.field.$error.custom !== undefined && vm.field.$error.custom !== false && (vm.field.$touched || vm.form.$submitted)">
+ <xos-alert config="vm.config" show="vm.field.$error.custom !== undefined && vm.field.$error.custom !== false && (vm.field.$touched || vm.form.$submitted)">
Field invalid
</xos-alert>
</div>
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartPie/smartPie.component.js b/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartPie/smartPie.component.js
index c1f08fa..f8a3985 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartPie/smartPie.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartPie/smartPie.component.js
@@ -198,13 +198,11 @@
const formatLabels = (data) => angular.isFunction(this.config.labelFormatter) ? this.config.labelFormatter(Object.keys(data)) : Object.keys(data);
const prepareData = (data) => {
- // $timeout(() => {
- // group data
- let grouped = groupData(data);
- this.data = formatData(grouped);
- // create labels
- this.labels = formatLabels(grouped);
- // }, 10);
+ // group data
+ let grouped = groupData(data);
+ this.data = formatData(grouped);
+ // create labels
+ this.labels = formatLabels(grouped);
}
if(this.config.resource){
diff --git a/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js b/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js
index b3a2eec..27b1ef6 100644
--- a/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js
+++ b/views/ngXosLib/xosHelpers/src/ui_components/smartComponents/smartTable/smartTable.component.js
@@ -225,7 +225,7 @@
let props = Object.keys(item);
_.remove(props, p => {
- return p == 'id' || p == 'validators'
+ return p === 'id' || p === 'validators'
});
// TODO move out cb, non sense triggering a lot of times
diff --git a/views/ngXosViews/diagnostic/mocks/data/cordsubscriber.json b/views/ngXosViews/diagnostic/mocks/data/cordsubscriber.json
index e2409a7..9f5919a 100644
--- a/views/ngXosViews/diagnostic/mocks/data/cordsubscriber.json
+++ b/views/ngXosViews/diagnostic/mocks/data/cordsubscriber.json
@@ -3,7 +3,6 @@
"humanReadableName": "cordSubscriber-1",
"id": 1,
"service_specific_id": "123",
- "vlan_id": "432",
"s_tag": "222",
"c_tag": "432",
"vcpe_id": 4,
diff --git a/xos/api/tenant/ceilometer/__init__.py b/xos/api/tenant/ceilometer/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/xos/api/tenant/ceilometer/__init__.py
@@ -0,0 +1 @@
+
diff --git a/xos/api/tenant/ceilometer/monitoringchannel.py b/xos/api/tenant/ceilometer/monitoringchannel.py
new file mode 100644
index 0000000..43e1636
--- /dev/null
+++ b/xos/api/tenant/ceilometer/monitoringchannel.py
@@ -0,0 +1,94 @@
+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 xos.apibase import XOSListCreateAPIView, XOSRetrieveUpdateDestroyAPIView, XOSPermissionDenied
+from api.xosapi_helpers import PlusModelSerializer, XOSViewSet, ReadOnlyField
+
+from services.ceilometer.models import MonitoringChannel, CeilometerService
+
+def get_default_ceilometer_service():
+ ceilometer_services = CeilometerService.get_service_objects().all()
+ if ceilometer_services:
+ return ceilometer_services[0].id
+ return None
+
+class MonitoringChannelForAPI(MonitoringChannel):
+ class Meta:
+ proxy = True
+ app_label = "ceilometer"
+
+ @property
+ def related(self):
+ related = {}
+ if self.creator:
+ related["creator"] = self.creator.username
+ if self.instance:
+ related["instance_id"] = self.instance.id
+ related["instance_name"] = self.instance.name
+ if self.instance.node:
+ related["compute_node_name"] = self.instance.node.name
+ return related
+
+class MonitoringChannelSerializer(PlusModelSerializer):
+ 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")
+ related = serializers.DictField(required=False)
+
+ #computeNodeName = serializers.SerializerMethodField("getComputeNodeName")
+
+ class Meta:
+ model = MonitoringChannelForAPI
+ fields = ('humanReadableName', 'id', 'provider_service', 'service_specific_attribute', 'ceilometer_url', 'tenant_list_str', 'related' )
+
+ def getHumanReadableName(self, obj):
+ return obj.__unicode__()
+
+ #def getComputeNodeName(self, obj):
+ # instance = obj.instance
+ # if not instance:
+ # return None
+ # return instance.node.name
+
+class MonitoringChannelSet(XOSViewSet):
+ base_name = "monitoringchannel"
+ method_name = "monitoringchannel"
+ method_kind = "viewset"
+ queryset = MonitoringChannelForAPI.get_tenant_objects().all()
+ serializer_class = MonitoringChannelSerializer
+
+ def get_queryset(self):
+ queryset = MonitoringChannelForAPI.get_tenant_objects().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 create(self, request):
+ current_user = request.user.username
+ existing_obj = None
+ for obj in MonitoringChannelForAPI.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(MonitoringChannelSet, self).create(request)
diff --git a/xos/configurations/acord/Makefile b/xos/configurations/acord/Makefile
index cfa04f8..e9b234c 100644
--- a/xos/configurations/acord/Makefile
+++ b/xos/configurations/acord/Makefile
@@ -13,9 +13,13 @@
sudo MYIP=$(MYIP) docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/cloudlab-openstack.yaml
sudo MYIP=$(MYIP) docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/nodes.yaml
-acord: cord
+acord: cord exampleservice
sudo MYIP=$(MYIP) docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/acord/ceilometer.yaml
+exampleservice:
+ #Ensure exampleservice is enabled in xos/tools/xos-manage and xos/settings.py file before uncommenting below lines
+ #sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/acord/acord-exampleservice.yaml
+
containers:
cd ../../../containers/xos; make devel
cd ../../../containers/synchronizer; make
diff --git a/xos/configurations/acord/acord-exampleservice.yaml b/xos/configurations/acord/acord-exampleservice.yaml
new file mode 100644
index 0000000..b6b23dd
--- /dev/null
+++ b/xos/configurations/acord/acord-exampleservice.yaml
@@ -0,0 +1,56 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup the ExampleService on the ACORD setup
+
+imports:
+ - custom_types/xos.yaml
+ - custom_types/exampleservice.yaml
+
+topology_template:
+ node_templates:
+
+ mysite:
+ type: tosca.nodes.Site
+
+ trusty-server-multi-nic:
+ type: tosca.nodes.Image
+
+ m1.small:
+ type: tosca.nodes.Flavor
+
+ mysite_exampleservice:
+ description: This slice holds the ExampleService
+ type: tosca.nodes.Slice
+ requirements:
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+ - exmapleservice:
+ node: service_example
+ relationship: tosca.relationships.MemberOfService
+ - default_image:
+ node: trusty-server-multi-nic
+ relationship: tosca.relationships.DefaultImage
+ - m1.small:
+ node: m1.small
+ relationship: tosca.relationships.DefaultFlavor
+
+ service_example:
+ type: tosca.nodes.ExampleService
+ properties:
+ view_url: /admin/exampleservice/exampleservice/$id$/
+ kind: exampleservice
+ public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+ private_key_fn: /opt/xos/synchronizers/exampleservice/exampleservice_private_key
+ service_message: hello
+ artifacts:
+ pubkey: /opt/xos/synchronizers/exampleservice/exampleservice_public_key
+
+ exampletenant1:
+ type: tosca.nodes.ExampleTenant
+ properties:
+ tenant_message: world
+ requirements:
+ - tenant:
+ node: service_example
+ relationship: tosca.relationships.TenantOfService
diff --git a/xos/configurations/acord/ceilometer.yaml b/xos/configurations/acord/ceilometer.yaml
index 66d5d32..089837d 100644
--- a/xos/configurations/acord/ceilometer.yaml
+++ b/xos/configurations/acord/ceilometer.yaml
@@ -122,38 +122,38 @@
properties:
view_url: /admin/ceilometer/ceilometerservice/$id$/
kind: ceilometer
- ceilometer_pub_sub_url: http://10.11.10.1:4455/
+ ceilometer_pub_sub_url: http://130.127.133.58: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
+ 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
-
- 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
+#
+# 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
@@ -186,19 +186,24 @@
properties:
max_instances: 2
-# 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
-# properties:
-# default_flavor: m1.small
-# max_instances: 2
+ 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
+ - default_image:
+ node: trusty-server-multi-nic
+ relationship: tosca.relationships.DefaultImage
+ - m1.small:
+ node: m1.small
+ relationship: tosca.relationships.DefaultFlavor
+ properties:
+ max_instances: 2
my_ceilometer_tenant:
description: Ceilometer Service default Tenant
@@ -209,27 +214,27 @@
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
+ 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
diff --git a/xos/configurations/acord/ceilometer_pub_sub.tar.gz b/xos/configurations/acord/ceilometer_pub_sub.tar.gz
deleted file mode 100644
index eb88a2b..0000000
--- a/xos/configurations/acord/ceilometer_pub_sub.tar.gz
+++ /dev/null
Binary files differ
diff --git a/xos/configurations/acord/docker-compose.yml b/xos/configurations/acord/docker-compose.yml
index f40761a..da9562e 100644
--- a/xos/configurations/acord/docker-compose.yml
+++ b/xos/configurations/acord/docker-compose.yml
@@ -31,6 +31,19 @@
volumes:
- ../setup/id_rsa:/opt/xos/synchronizers/monitoring_channel/monitoring_channel_private_key:ro # private key
+#Ensure exampleservice is enabled in xos/tools/xos-manage and xos/settings.py file before uncommenting below lines
+#xos_synchronizer_exampleservice:
+# image: xosproject/xos-synchronizer-openstack
+# command: bash -c "sleep 120; python /opt/xos/synchronizers/exampleservice/exampleservice-synchronizer.py -C /opt/xos/synchronizers/exampleservice/exampleservice_config"
+# labels:
+# org.xosproject.kind: synchronizer
+# org.xosproject.target: exampleservice
+# links:
+# - xos_db
+# volumes:
+# - ../setup:/root/setup:ro
+# - ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
+# - ../setup/id_rsa:/opt/xos/synchronizers/exampleservice/exampleservice_private_key:ro
# FUTURE
#xos_swarm_synchronizer:
@@ -51,3 +64,4 @@
- ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
- ./xos_cord_config:/opt/xos/xos_configuration/xos_cord_config:ro
- ../setup/id_rsa.pub:/opt/xos/synchronizers/monitoring_channel/monitoring_channel_public_key:ro
+# - ../setup/id_rsa.pub:/opt/xos/synchronizers/exampleservice/exampleservice_public_key:ro
diff --git a/xos/configurations/acord/install_ceilometer_patch.sh b/xos/configurations/acord/install_ceilometer_patch.sh
deleted file mode 100755
index 77aa05b..0000000
--- a/xos/configurations/acord/install_ceilometer_patch.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-if [ -d /usr/lib/python2.7/dist-packages/ceilometer/network/ext_services ]; then
- echo "Seems VCPE notification listeners are already enabled in ceilometer... so exiting gracefully..."
- exit 0
-fi
-echo "Verifying if all the required files are present"
-if [ ! -f openstack_ceilometer_patch.tar.gz ] || [ ! -f ceilometer_pub_sub.tar.gz ];
-then
- echo "File openstack_ceilometer_patch.tar.gz or ceilometer_pub_sub.tar.gz not found"
- exit 1
-fi
-echo "Copying the ceilometer patch files to /usr/lib/python2.7/dist-packages/ceilometer"
-tar -xzf openstack_ceilometer_patch.tar.gz
-sudo mv ceilometer/network/ext_services /usr/lib/python2.7/dist-packages/ceilometer/network/
-sudo mv ceilometer/network/statistics/onos /usr/lib/python2.7/dist-packages/ceilometer/network/statistics/
-sudo mv /usr/lib/python2.7/dist-packages/ceilometer/network/statistics/__init__.py /usr/lib/python2.7/dist-packages/ceilometer/network/statistics/orig_init.orig_py
-sudo mv ceilometer/network/statistics/__init__.py /usr/lib/python2.7/dist-packages/ceilometer/network/statistics/
-sudo mv ceilometer-2015.1.1.egg-info/entry_points.txt /usr/lib/python2.7/dist-packages/ceilometer-*egg-info/
-sudo mv pipeline.yaml /etc/ceilometer/
-echo "Restarting ceilometer-agent-notification"
-sudo service ceilometer-agent-notification restart
-echo "Restarting ceilometer-agent-central"
-sudo service ceilometer-agent-central restart
-tar -xzf ceilometer_pub_sub.tar.gz
-echo "Starting Ceilometer PUB/SUB service"
-cd ceilometer_pub_sub
-python sub_main.py &
diff --git a/xos/configurations/acord/openstack_ceilometer_patch.tar.gz b/xos/configurations/acord/openstack_ceilometer_patch.tar.gz
deleted file mode 100644
index 2c4f02c..0000000
--- a/xos/configurations/acord/openstack_ceilometer_patch.tar.gz
+++ /dev/null
Binary files differ
diff --git a/xos/configurations/cord-pod/Makefile b/xos/configurations/cord-pod/Makefile
index 878797c..229d5e3 100644
--- a/xos/configurations/cord-pod/Makefile
+++ b/xos/configurations/cord-pod/Makefile
@@ -18,6 +18,7 @@
cord: vsg_custom_images
sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/mgmt-net.yaml
sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/cord-vtn-vsg.yaml
+ sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/cord-volt-devices.yaml
exampleservice:
sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/pod-exampleservice.yaml
@@ -72,7 +73,7 @@
.PHONY: local_containers
local_containers:
echo "" > ../../../containers/xos/local_certs.crt
- for CRT in /usr/local/share/ca-certificates/* ; do \
+ for CRT in $$(/usr/local/share/ca-certificates/*) ; do \
echo Adding Certificate: $$CRT ;\
cat $$CRT >> ../../../containers/xos/local_certs.crt ;\
echo "" >> ../../../containers/xos/local_certs.crt ;\
diff --git a/xos/configurations/cord-pod/cord-volt-devices.yaml b/xos/configurations/cord-pod/cord-volt-devices.yaml
new file mode 100644
index 0000000..8b41623
--- /dev/null
+++ b/xos/configurations/cord-pod/cord-volt-devices.yaml
@@ -0,0 +1,47 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Just enough Tosca to get the vSG slice running on the CORD POD
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ service#volt:
+ type: tosca.nodes.VOLTService
+ properties:
+ no-create: True
+ no-delete: True
+ no-update: True
+
+ voltdev-1:
+ type: tosca.nodes.VOLTDevice
+ properties:
+ driver: pmc-olt
+ openflow_id: of:1000000000000001
+ access_devices: >
+ 2 222,
+ 3 223,
+ 4 224
+ requirements:
+ - volt_service:
+ node: service#volt
+ relationship: tosca.relationships.MemberOfService
+ - access_agent:
+ node: agent-1
+ relationship: tosca.relationships.UsesAgent
+
+ agent-1:
+ type: tosca.nodes.AccessAgent
+ properties:
+ mac: AA:BB:CC:DD:EE:FF
+ port_mappings: >
+ of:0000000000000002/2 DE:AD:BE:EF:BA:11,
+ of:0000000000000002/3 BE:EF:DE:AD:BE:EF
+ requirements:
+ - volt_service:
+ node: service#volt
+ relationship: tosca.relationships.MemberOfService
+
+
+
diff --git a/xos/configurations/cord-pod/cord-vtn-vsg.yaml b/xos/configurations/cord-pod/cord-vtn-vsg.yaml
index bb603fe..8c73799 100644
--- a/xos/configurations/cord-pod/cord-vtn-vsg.yaml
+++ b/xos/configurations/cord-pod/cord-vtn-vsg.yaml
@@ -16,7 +16,7 @@
replaces: service_vtr
service#volt:
- type: tosca.nodes.Service
+ type: tosca.nodes.VOLTService
requirements:
- vsg_tenant:
node: service#vsg
@@ -75,6 +75,50 @@
view_url: /admin/fabric/fabricservice/$id$/
replaces: service_fabric
+ service#ONOS_Fabric:
+ type: tosca.nodes.ONOSService
+ requirements:
+ properties:
+ kind: onos
+ view_url: /admin/onos/onosservice/$id$/
+ no_container: true
+ rest_hostname: onos-fabric
+ replaces: service_ONOS_Fabric
+
+ service#ONOS_CORD:
+ type: tosca.nodes.ONOSService
+ properties:
+ no-delete: true
+ no-create: true
+ no-update: true
+
+ vOLT_ONOS_app:
+ type: tosca.nodes.ONOSvOLTApp
+ requirements:
+ - onos_tenant:
+ node: service#ONOS_CORD
+ relationship: tosca.relationships.TenantOfService
+ - volt_service:
+ node: service#volt
+ relationship: tosca.relationships.UsedByService
+ properties:
+ install_dependencies: onos-ext-notifier-1.0-SNAPSHOT.oar, onos-ext-volt-event-publisher-1.0-SNAPSHOT.oar
+ dependencies: org.onosproject.openflow-base, org.onosproject.olt, org.ciena.onos.ext_notifier, org.ciena.onos.volt_event_publisher
+ autogenerate: volt-network-cfg
+
+ vRouter_ONOS_app:
+ type: tosca.nodes.ONOSvRouterApp
+ requirements:
+ - onos_tenant:
+ node: service#ONOS_Fabric
+ relationship: tosca.relationships.TenantOfService
+ - vrouter_service:
+ node: service#vrouter
+ relationship: tosca.relationships.UsedByService
+ properties:
+ dependencies: org.onosproject.vrouter
+ autogenerate: vrouter-network-cfg
+
Private:
type: tosca.nodes.NetworkTemplate
diff --git a/xos/configurations/cord-pod/pod-cdn.yaml b/xos/configurations/cord-pod/pod-cdn.yaml
new file mode 100644
index 0000000..2229686
--- /dev/null
+++ b/xos/configurations/cord-pod/pod-cdn.yaml
@@ -0,0 +1,52 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup the CDN on the pod
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+
+ Private:
+ type: tosca.nodes.NetworkTemplate
+
+ management:
+ type: tosca.nodes.network.Network.XOS
+ properties:
+ no-create: true
+ no-delete: true
+ no-update: true
+
+ cdn-public:
+ type: tosca.nodes.network.Network
+ properties:
+ ip_version: 4
+ cidr: 207.141.192.128/28
+ requirements:
+ - network_template:
+ node: Private
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - owner:
+ node: mysite_cdn
+ relationship: tosca.relationships.MemberOfSlice
+ - connection:
+ node: mysite_cdn
+ relationship: tosca.relationships.ConnectsToSlice
+
+ mysite:
+ type: tosca.nodes.Site
+
+ mysite_cdn:
+ description: This slice holds the CDN
+ type: tosca.nodes.Slice
+ properties:
+ network: noauto
+ requirements:
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+ - management:
+ node: management
+ relationship: tosca.relationships.ConnectsToNetwork
+
diff --git a/xos/configurations/devel/Makefile b/xos/configurations/devel/Makefile
index cc29f53..5d2e96f 100644
--- a/xos/configurations/devel/Makefile
+++ b/xos/configurations/devel/Makefile
@@ -1,8 +1,8 @@
MYIP:=$(shell hostname -i)
-cloudlab: common_cloudlab containers xos
+cloudlab: common_cloudlab local_containers xos
-devstack: upgrade_pkgs common_devstack containers xos
+devstack: upgrade_pkgs common_devstack local_containers xos
xos:
sudo MYIP=$(MYIP) docker-compose up -d
@@ -12,16 +12,25 @@
sudo MYIP=$(MYIP) docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/common/cloudlab-openstack.yaml
sudo MYIP=$(MYIP) docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /root/setup/nodes.yaml
-containers:
- cd ../../../containers/xos; make devel
- cd ../../../containers/synchronizer; make
-
common_cloudlab:
make -C ../common -f Makefile.cloudlab
common_devstack:
make -C ../common -f Makefile.devstack
+base:
+ make -C ../../../containers/xos base
+
+local_containers:
+ echo "" > ../../../containers/xos/local_certs.crt
+ for CRT in $$(/usr/local/share/ca-certificates/*) ; do \
+ echo Adding Certificate: $$CRT ;\
+ cat $$CRT >> ../../../containers/xos/local_certs.crt ;\
+ echo "" >> ../../../containers/xos/local_certs.crt ;\
+ done
+ make -C ../../../containers/xos devel
+ make -C ../../../containers/synchronizer
+
stop:
sudo MYIP=$(MYIP) docker-compose stop
@@ -43,8 +52,3 @@
upgrade_pkgs:
sudo pip install httpie --upgrade
-rebuild_xos:
- make -C ../../../containers/xos devel
-
-rebuild_synchronizer:
- make -C ../../../containers/synchronizer
diff --git a/xos/configurations/frontend/Makefile b/xos/configurations/frontend/Makefile
index 9e18aec..562578e 100644
--- a/xos/configurations/frontend/Makefile
+++ b/xos/configurations/frontend/Makefile
@@ -36,6 +36,8 @@
mock-cord-pod:
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 /opt/xos/configurations/cord-pod/mgmt-net.yaml
+ sudo docker-compose run xos bash -c "echo somekey > /opt/xos/synchronizers/vcpe/vcpe_public_key; python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/cord-pod/cord-vtn-vsg.yaml"
+ sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/cord-pod/cord-volt-devices.yaml
sudo docker exec frontend_xos_1 cp /opt/xos/configurations/cord-pod/xos_cord_config /opt/xos/xos_configuration/
sudo docker exec frontend_xos_1 touch /opt/xos/xos/settings.py
diff --git a/xos/configurations/test-standalone/docker-compose.yml b/xos/configurations/test-standalone/docker-compose.yml
index 5039f08..a0b87ed 100644
--- a/xos/configurations/test-standalone/docker-compose.yml
+++ b/xos/configurations/test-standalone/docker-compose.yml
@@ -11,7 +11,7 @@
# org.xosproject.target: swarm
xos:
- image: xosproject/xos
+ image: xosproject/xos-test
command: python /opt/xos/manage.py runserver 0.0.0.0:8000 --insecure --makemigrations
#command: sleep 86400 # For interactive session
ports:
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index b341e83..5196336 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -548,7 +548,7 @@
if self.slice.default_image:
return self.slice.default_image
- raise XOPSProgrammingError("Please set a default image for %s" % self.slice.name)
+ raise XOSProgrammingError("Please set a default image for %s" % self.slice.name)
def make_new_instance(self):
from core.models import Instance, Flavor
@@ -683,7 +683,7 @@
if slice.default_image:
return slice.default_image
- raise XOPSProgrammingError("Please set a default image for %s" % self.slice.name)
+ raise XOSProgrammingError("Please set a default image for %s" % self.slice.name)
def save_instance(self, instance):
# Override this function to do custom pre-save or post-save processing,
diff --git a/xos/core/xoslib/methods/ceilometerview.py b/xos/core/xoslib/methods/ceilometerview.py
index 16b32d4..9c87e40 100644
--- a/xos/core/xoslib/methods/ceilometerview.py
+++ b/xos/core/xoslib/methods/ceilometerview.py
@@ -1151,8 +1151,8 @@
if (not tenant_ceilometer_url):
raise XOSMissingField("Tenant ceilometer URL is missing")
- tenant_id = request.QUERY_PARAMS.get('tenant', None)
- resource_id = request.QUERY_PARAMS.get('resource', None)
+ tenant_id = request.query_params.get('tenant', None)
+ resource_id = request.query_params.get('resource', None)
query = []
if tenant_id:
@@ -1187,9 +1187,9 @@
raise XOSMissingField("Tenant ceilometer URL is missing")
tenant_map = getTenantControllerTenantMap(request.user)
- date_options = request.QUERY_PARAMS.get('period', 1)
- date_from = request.QUERY_PARAMS.get('date_from', '')
- date_to = request.QUERY_PARAMS.get('date_to', '')
+ date_options = request.query_params.get('period', 1)
+ date_from = request.query_params.get('date_from', '')
+ date_to = request.query_params.get('date_to', '')
try:
date_from, date_to = calc_date_args(date_from,
@@ -1208,9 +1208,9 @@
'op': 'le',
'value': date_to})
- meter_name = request.QUERY_PARAMS.get('meter', None)
- tenant_id = request.QUERY_PARAMS.get('tenant', None)
- resource_id = request.QUERY_PARAMS.get('resource', None)
+ meter_name = request.query_params.get('meter', None)
+ tenant_id = request.query_params.get('tenant', None)
+ resource_id = request.query_params.get('resource', None)
query = []
if tenant_id:
@@ -1284,12 +1284,12 @@
tenant_ceilometer_url = getTenantCeilometerProxyURL(request.user)
if (not tenant_ceilometer_url):
raise XOSMissingField("Tenant ceilometer URL is missing")
- meter_name = request.QUERY_PARAMS.get('meter', None)
+ meter_name = request.query_params.get('meter', None)
if not meter_name:
raise XOSMissingField("Meter name in query params is missing")
- limit = request.QUERY_PARAMS.get('limit', 10)
- tenant_id = request.QUERY_PARAMS.get('tenant', None)
- resource_id = request.QUERY_PARAMS.get('resource', None)
+ limit = request.query_params.get('limit', 10)
+ tenant_id = request.query_params.get('tenant', None)
+ resource_id = request.query_params.get('resource', None)
query = []
if tenant_id:
query.extend(make_query(tenant_id=tenant_id))
@@ -1340,7 +1340,7 @@
tenant_ceilometer_url = getTenantCeilometerProxyURL(request.user)
if (not tenant_ceilometer_url):
raise XOSMissingField("Tenant ceilometer URL is missing")
- instance_uuid = request.QUERY_PARAMS.get('instance-uuid', None)
+ instance_uuid = request.query_params.get('instance-uuid', None)
if not instance_uuid:
raise XOSMissingField("Instance UUID in query params is missing")
if not Instance.objects.filter(instance_uuid=instance_uuid):
@@ -1354,9 +1354,9 @@
#neutron port resource id is represented in ceilometer as "nova instance-name"+"-"+"nova instance-id"+"-"+"tap"+first 11 characters of port-id
resource_ids.append(xos_instance.instance_id+"-"+instance_uuid+"-tap"+p.port_id[:11])
- date_options = request.QUERY_PARAMS.get('period', 1)
- date_from = request.QUERY_PARAMS.get('date_from', '')
- date_to = request.QUERY_PARAMS.get('date_to', '')
+ date_options = request.query_params.get('period', 1)
+ date_from = request.query_params.get('date_from', '')
+ date_to = request.query_params.get('date_to', '')
try:
date_from, date_to = calc_date_args(date_from,
@@ -1437,9 +1437,9 @@
def get(self, request, format=None):
if (not request.user.is_authenticated()) or (not request.user.is_admin):
raise PermissionDenied("You must be authenticated admin user in order to use this API")
- service = request.QUERY_PARAMS.get('service', None)
- slice_hint = request.QUERY_PARAMS.get('slice_hint', None)
- scale = request.QUERY_PARAMS.get('scale', None)
+ service = request.query_params.get('service', None)
+ slice_hint = request.query_params.get('slice_hint', None)
+ scale = request.query_params.get('scale', None)
if not service or not slice_hint or not scale:
raise XOSMissingField("Mandatory fields missing")
services = Service.select_by_user(request.user)
diff --git a/xos/core/xoslib/methods/cordsubscriber.py b/xos/core/xoslib/methods/cordsubscriber.py
index 0be9b33..b69b09d 100644
--- a/xos/core/xoslib/methods/cordsubscriber.py
+++ b/xos/core/xoslib/methods/cordsubscriber.py
@@ -28,7 +28,6 @@
class CordSubscriberIdSerializer(serializers.ModelSerializer, PlusSerializerMixin):
id = ReadOnlyField()
service_specific_id = ReadOnlyField()
- vlan_id = ReadOnlyField() # XXX remove this
c_tag = ReadOnlyField()
s_tag = ReadOnlyField()
vcpe_id = ReadOnlyField()
@@ -67,7 +66,7 @@
class Meta:
model = CordSubscriber
fields = ('humanReadableName', 'id',
- 'service_specific_id', 'vlan_id', 's_tag', 'c_tag',
+ 'service_specific_id', 's_tag', 'c_tag',
'vcpe_id', 'instance', 'instance_name', 'image', 'image_name',
'firewall_enable', 'firewall_rules',
'url_filter_enable', 'url_filter_rules', 'url_filter_level',
diff --git a/xos/core/xoslib/methods/volttenant.py b/xos/core/xoslib/methods/volttenant.py
index 229e105..25559a0 100644
--- a/xos/core/xoslib/methods/volttenant.py
+++ b/xos/core/xoslib/methods/volttenant.py
@@ -64,11 +64,6 @@
if service_specific_id is not None:
queryset = queryset.filter(service_specific_id=service_specific_id)
-# vlan_id = self.request.query_params.get('vlan_id', None)
-# if vlan_id is not None:
-# ids = [x.id for x in queryset if x.get_attribute("vlan_id", None)==vlan_id]
-# queryset = queryset.filter(id__in=ids)
-
c_tag = self.request.query_params.get('c_tag', None)
if c_tag is not None:
ids = [x.id for x in queryset if x.get_attribute("c_tag", None)==c_tag]
diff --git a/xos/core/xoslib/objects/cordsubscriber.py b/xos/core/xoslib/objects/cordsubscriber.py
index 27596b7..681b769 100644
--- a/xos/core/xoslib/objects/cordsubscriber.py
+++ b/xos/core/xoslib/objects/cordsubscriber.py
@@ -39,7 +39,6 @@
# ("cdn_enable", "vcpe.cdn_enable"),
# uplink_speed, downlink_speed, status, enable_uverse
- ("vlan_id", "volt.vlan_id"), # XXX remove this
("c_tag", "volt.c_tag"),
("s_tag", "volt.s_tag"),
diff --git a/xos/core/xoslib/static/js/vendor/ngXosHelpers.js b/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
index 4af4da5..2fa6ede 100644
--- a/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
+++ b/xos/core/xoslib/static/js/vendor/ngXosHelpers.js
@@ -1 +1,2710 @@
-"use strict";!function(){angular.module("xos.uiComponents",["chart.js","RecursionHelper"])}(),function(){function e(){var e=function(e){return e.split("_").join(" ").trim()},n=function(e){return e.split(/(?=[A-Z])/).map(function(e){return e.toLowerCase()}).join(" ")},o=function(e){return e.slice(0,1).toUpperCase()+e.slice(1)},t=function(t){return t=e(t),t=n(t),t=o(t).replace(/\s\s+/g," ")+":",t.replace("::",":")};return{_formatByUnderscore:e,_formatByUppercase:n,_capitalize:o,format:t}}angular.module("xos.uiComponents").factory("LabelFormatter",e)}();var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};!function(){angular.module("xos.uiComponents").service("XosFormHelpers",["_","LabelFormatter",function(e,n){var o=this;this._isEmail=function(e){var n=/(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/;return n.test(e)},this._getFieldFormat=function(n){return angular.isArray(n)?"array":e.isDate(n)||!Number.isNaN(Date.parse(n))&&new Date(n).getTime()>6311808e5?"date":"boolean"==typeof n?"boolean":o._isEmail(n)?"email":"string"==typeof n||null===n?"text":"undefined"==typeof n?"undefined":_typeof(n)},this.buildFormStructure=function(t,i,r){return t=Object.keys(t).length>0?t:i,i=i||{},e.reduce(Object.keys(t),function(e,t){return e[t]={label:i[t]&&i[t].label?i[t].label+":":n.format(t),type:i[t]&&i[t].type?i[t].type:o._getFieldFormat(r[t]),validators:i[t]&&i[t].validators?i[t].validators:{},hint:i[t]&&i[t].hint?i[t].hint:""},"date"===e[t].type&&(r[t]=new Date(r[t])),"number"===e[t].type&&(r[t]=parseInt(r[t],10)),e},{})},this.parseModelField=function(n){return e.reduce(n,function(e,n){return e[n]={},e},{})}}])}(),function(){angular.module("xos.uiComponents").directive("xosValidation",function(){return{restrict:"E",scope:{field:"=",form:"="},template:'\n <div ng-cloak>\n <xos-alert config="vm.config" show="vm.field.$error.required !== undefined && vm.field.$error.required !== false && (vm.field.$touched || vm.form.$submitted)">\n Field required\n </xos-alert>\n <xos-alert config="vm.config" show="vm.field.$error.email !== undefined && vm.field.$error.email !== false && (vm.field.$touched || vm.form.$submitted)">\n This is not a valid email\n </xos-alert>\n <xos-alert config="vm.config" show="vm.field.$error.minlength !== undefined && vm.field.$error.minlength !== false && (vm.field.$touched || vm.form.$submitted)">\n Too short\n </xos-alert>\n <xos-alert config="vm.config" show="vm.field.$error.maxlength !== undefined && vm.field.$error.maxlength !== false && (vm.field.$touched || vm.form.$submitted)">\n Too long\n </xos-alert>\n <xos-alert config="vm.config" show="vm.field.$error.custom !== undefined && vm.field.$error.custom !== false && (vm.field.$touched || vm.form.$submitted)">\n Field invalid\n </xos-alert>\n </div>\n ',transclude:!0,bindToController:!0,controllerAs:"vm",controller:function(){this.config={type:"danger"}}}})}(),function(){angular.module("xos.uiComponents").directive("xosForm",function(){return{restrict:"E",scope:{config:"=",ngModel:"="},template:'\n <ng-form name="vm.{{vm.config.formName || \'form\'}}">\n <div class="form-group" ng-repeat="(name, field) in vm.formField">\n <xos-field name="name" field="field" ng-model="vm.ngModel[name]"></xos-field>\n <xos-validation errors="vm[vm.config.formName || \'form\'][name].$error"></xos-validation>\n </div>\n <div class="form-group" ng-if="vm.config.actions">\n <button role="button" href=""\n ng-repeat="action in vm.config.actions"\n ng-click="action.cb(vm.ngModel)"\n class="btn btn-{{action.class}}"\n title="{{action.label}}">\n <i class="glyphicon glyphicon-{{action.icon}}"></i>\n {{action.label}}\n </button>\n </div>\n </ng-form>\n ',bindToController:!0,controllerAs:"vm",controller:["$scope","$log","_","XosFormHelpers",function(e,n,o,t){var i=this;if(!this.config)throw new Error('[xosForm] Please provide a configuration via the "config" attribute');if(!this.config.actions)throw new Error("[xosForm] Please provide an action list in the configuration");this.excludedField=["id","validators","created","updated","deleted","backend_status"],this.config&&this.config.exclude&&(this.excludedField=this.excludedField.concat(this.config.exclude)),this.formField=[],e.$watch(function(){return i.ngModel},function(e){if(i.formField={},e){var n=o.difference(Object.keys(e),i.excludedField),r=t.parseModelField(n);i.formField=t.buildFormStructure(r,i.config.fields,e)}})}]}})}(),function(){angular.module("xos.uiComponents").directive("xosTable",function(){return{restrict:"E",scope:{data:"=",config:"="},template:'\n <div ng-show="vm.data.length > 0">\n <div class="row" ng-if="vm.config.filter == \'fulltext\'">\n <div class="col-xs-12">\n <input\n class="form-control"\n placeholder="Type to search.."\n type="text"\n ng-model="vm.query"/>\n </div>\n </div>\n <table ng-class="vm.classes" ng-hide="vm.data.length == 0">\n <thead>\n <tr>\n <th ng-repeat="col in vm.columns">\n {{col.label}}\n <span ng-if="vm.config.order">\n <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = false">\n <i class="glyphicon glyphicon-chevron-up"></i>\n </a>\n <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = true">\n <i class="glyphicon glyphicon-chevron-down"></i>\n </a>\n </span>\n </th>\n <th ng-if="vm.config.actions">Actions:</th>\n </tr>\n </thead>\n <tbody ng-if="vm.config.filter == \'field\'">\n <tr>\n <td ng-repeat="col in vm.columns">\n <input\n ng-if="col.type !== \'boolean\'"\n class="form-control"\n placeholder="Type to search by {{col.label}}"\n type="text"\n ng-model="vm.query[col.prop]"/>\n <select\n ng-if="col.type === \'boolean\'"\n class="form-control"\n ng-model="vm.query[col.prop]">\n <option value="">-</option>\n <option value="true">True</option>\n <option value="false">False</option>\n </select>\n </td>\n <td ng-if="vm.config.actions"></td>\n </tr>\n </tbody>\n <tbody>\n <tr ng-repeat="item in vm.data | filter:vm.query | orderBy:vm.orderBy:vm.reverse | pagination:vm.currentPage * vm.config.pagination.pageSize | limitTo: (vm.config.pagination.pageSize || vm.data.length) track by $index">\n <td ng-repeat="col in vm.columns" link-wrapper>\n <span ng-if="!col.type">{{item[col.prop]}}</span>\n <span ng-if="col.type === \'boolean\'">\n <i class="glyphicon"\n ng-class="{\'glyphicon-ok\': item[col.prop], \'glyphicon-remove\': !item[col.prop]}">\n </i>\n </span>\n <span ng-if="col.type === \'date\'">\n {{item[col.prop] | date:\'H:mm MMM d, yyyy\'}}\n </span>\n <span ng-if="col.type === \'array\'">\n {{item[col.prop] | arrayToList}}\n </span>\n <span ng-if="col.type === \'object\'">\n <dl class="dl-horizontal">\n <span ng-repeat="(k,v) in item[col.prop]">\n <dt>{{k}}</dt>\n <dd>{{v}}</dd>\n </span>\n </dl>\n </span>\n <span ng-if="col.type === \'custom\'">\n {{col.formatter(item)}}\n </span>\n <span ng-if="col.type === \'icon\'">\n <i class="glyphicon glyphicon-{{col.formatter(item)}}">\n </i>\n </span>\n </td>\n <td ng-if="vm.config.actions">\n <a href=""\n ng-repeat="action in vm.config.actions"\n ng-click="action.cb(item)"\n title="{{action.label}}">\n <i\n class="glyphicon glyphicon-{{action.icon}}"\n style="color: {{action.color}};"></i>\n </a>\n </td>\n </tr>\n </tbody>\n </table>\n <xos-pagination\n ng-if="vm.config.pagination"\n page-size="vm.config.pagination.pageSize"\n total-elements="vm.data.length"\n change="vm.goToPage">\n </xos-pagination>\n </div>\n <div ng-show="vm.data.length == 0 || !vm.data">\n <xos-alert config="{type: \'info\'}">\n No data to show.\n </xos-alert>\n </div>\n ',bindToController:!0,controllerAs:"vm",controller:["_",function(e){var n=this;if(!this.config)throw new Error('[xosTable] Please provide a configuration via the "config" attribute');if(!this.config.columns)throw new Error("[xosTable] Please provide a columns list in the configuration");this.config.order&&angular.isObject(this.config.order)&&(this.reverse=this.config.order.reverse||!1,this.orderBy=this.config.order.field||"id");var o=e.filter(this.config.columns,{type:"custom"});angular.isArray(o)&&o.length>0&&e.forEach(o,function(e){if(!e.formatter||!angular.isFunction(e.formatter))throw new Error("[xosTable] You have provided a custom field type, a formatter function should provided too.")});var t=e.filter(this.config.columns,{type:"icon"});angular.isArray(t)&&t.length>0&&e.forEach(t,function(e){if(!e.formatter||!angular.isFunction(e.formatter))throw new Error("[xosTable] You have provided an icon field type, a formatter function should provided too.")});var i=e.filter(this.config.columns,function(e){return angular.isDefined(e.link)});angular.isArray(i)&&i.length>0&&e.forEach(i,function(e){if(!angular.isFunction(e.link))throw new Error("[xosTable] The link property should be a function.")}),this.columns=this.config.columns,this.classes=this.config.classes||"table table-striped table-bordered",this.config.actions,this.config.pagination&&(this.currentPage=0,this.goToPage=function(e){n.currentPage=e})}]}}).filter("arrayToList",function(){return function(e){return angular.isArray(e)?e.join(", "):e}}).directive("linkWrapper",function(){return{restrict:"A",transclude:!0,template:'\n <a ng-if="col.link" href="{{col.link(item)}}">\n <div ng-transclude></div>\n </a>\n <div ng-transclude ng-if="!col.link"></div>\n '}})}(),function(){angular.module("xos.uiComponents").directive("xosPagination",function(){return{restrict:"E",scope:{pageSize:"=",totalElements:"=",change:"="},template:'\n <div class="row" ng-if="vm.pageList.length > 1">\n <div class="col-xs-12 text-center">\n <ul class="pagination">\n <li\n ng-click="vm.goToPage(vm.currentPage - 1)"\n ng-class="{disabled: vm.currentPage == 0}">\n <a href="" aria-label="Previous">\n <span aria-hidden="true">«</span>\n </a>\n </li>\n <li ng-repeat="i in vm.pageList" ng-class="{active: i === vm.currentPage}">\n <a href="" ng-click="vm.goToPage(i)">{{i + 1}}</a>\n </li>\n <li\n ng-click="vm.goToPage(vm.currentPage + 1)"\n ng-class="{disabled: vm.currentPage == vm.pages - 1}">\n <a href="" aria-label="Next">\n <span aria-hidden="true">»</span>\n </a>\n </li>\n </ul>\n </div>\n </div>\n ',bindToController:!0,controllerAs:"vm",controller:["$scope",function(e){var n=this;this.currentPage=0,this.goToPage=function(e){0>e||e===n.pages||(n.currentPage=e,n.change(e))},this.createPages=function(e){for(var n=[],o=0;e>o;o++)n.push(o);return n},e.$watch(function(){return n.totalElements},function(){n.totalElements&&(n.pages=Math.ceil(n.totalElements/n.pageSize),n.pageList=n.createPages(n.pages))})}]}}).filter("pagination",function(){return function(e,n){return e&&angular.isArray(e)?(n=parseInt(n,10),e.slice(n)):e}})}(),function(){angular.module("xos.uiComponents").directive("xosAlert",function(){return{restrict:"E",scope:{config:"=",show:"=?"},template:'\n <div ng-cloak class="alert alert-{{vm.config.type}}" ng-hide="!vm.show">\n <button type="button" class="close" ng-if="vm.config.closeBtn" ng-click="vm.dismiss()">\n <span aria-hidden="true">×</span>\n </button>\n <p ng-transclude></p>\n </div>\n ',transclude:!0,bindToController:!0,controllerAs:"vm",controller:["$timeout",function(e){var n=this;if(!this.config)throw new Error('[xosAlert] Please provide a configuration via the "config" attribute');this.show=this.show!==!1,this.dismiss=function(){n.show=!1},this.config.autoHide&&!function(){var o=e(function(){n.dismiss(),e.cancel(o)},n.config.autoHide)}()}]}})}(),function(){angular.module("xos.uiComponents").directive("xosField",["RecursionHelper",function(e){return{restrict:"E",scope:{name:"=",field:"=",ngModel:"="},template:'\n <label ng-if="vm.field.type !== \'object\'">{{vm.field.label}}</label>\n <input\n ng-if="vm.field.type !== \'boolean\' && vm.field.type !== \'object\'"\n type="{{vm.field.type}}"\n name="{{vm.name}}"\n class="form-control"\n ng-model="vm.ngModel"\n ng-minlength="vm.field.validators.minlength || 0"\n ng-maxlength="vm.field.validators.maxlength || 2000"\n ng-required="vm.field.validators.required || false" />\n <span class="boolean-field" ng-if="vm.field.type === \'boolean\'">\n <button\n class="btn btn-success"\n ng-show="vm.ngModel"\n ng-click="vm.ngModel = false">\n <i class="glyphicon glyphicon-ok"></i>\n </button>\n <button\n class="btn btn-danger"\n ng-show="!vm.ngModel"\n ng-click="vm.ngModel = true">\n <i class="glyphicon glyphicon-remove"></i>\n </button>\n </span>\n <div\n class="panel panel-default object-field"\n ng-if="vm.field.type == \'object\' && !vm.isEmptyObject(vm.ngModel)"\n >\n <div class="panel-heading">{{vm.field.label}}</div>\n <div class="panel-body">\n <div ng-repeat="(k, v) in vm.ngModel">\n <xos-field\n name="k"\n field="{label: vm.formatLabel(k), type: vm.getType(v)}"\n ng-model="v">\n </xos-field>\n </div>\n </div>\n </div>\n ',bindToController:!0,controllerAs:"vm",compile:function(n){return e.compile(n)},controller:["$attrs","XosFormHelpers","LabelFormatter",function(e,n,o){if(!this.name)throw new Error("[xosField] Please provide a field name");if(!this.field)throw new Error("[xosField] Please provide a field definition");if(!e.ngModel)throw new Error("[xosField] Please provide an ng-model");this.getType=n._getFieldFormat,this.formatLabel=o.format,this.isEmptyObject=function(e){return 0===Object.keys(e).length}}]}}])}();var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e};!function(){angular.module("xos.uiComponents").directive("xosSmartTable",function(){return{restrict:"E",scope:{config:"="},template:'\n <div class="row" ng-show="vm.data.length > 0">\n <div class="col-xs-12 text-right">\n <a href="" class="btn btn-success" ng-click="vm.createItem()">\n Add\n </a>\n </div>\n </div>\n <div class="row">\n <div class="col-xs-12 table-responsive">\n <xos-table config="vm.tableConfig" data="vm.data"></xos-table>\n </div>\n </div>\n <div class="panel panel-default" ng-show="vm.detailedItem">\n <div class="panel-heading">\n <div class="row">\n <div class="col-xs-11">\n <h3 class="panel-title" ng-show="vm.detailedItem.id">Update {{vm.config.resource}} {{vm.detailedItem.id}}</h3>\n <h3 class="panel-title" ng-show="!vm.detailedItem.id">Create {{vm.config.resource}} item</h3>\n </div>\n <div class="col-xs-1">\n <a href="" ng-click="vm.cleanForm()">\n <i class="glyphicon glyphicon-remove pull-right"></i>\n </a>\n </div>\n </div>\n </div>\n <div class="panel-body">\n <xos-form config="vm.formConfig" ng-model="vm.detailedItem"></xos-form>\n </div>\n </div>\n <xos-alert config="{type: \'success\', closeBtn: true}" show="vm.responseMsg">{{vm.responseMsg}}</xos-alert>\n <xos-alert config="{type: \'danger\', closeBtn: true}" show="vm.responseErr">{{vm.responseErr}}</xos-alert>\n ',bindToController:!0,controllerAs:"vm",controller:["$injector","LabelFormatter","_","XosFormHelpers",function(e,n,o,t){var i=this;this.responseMsg=!1,this.responseErr=!1,this.tableConfig={columns:[],actions:[{label:"delete",icon:"remove",cb:function(e){i.Resource["delete"]({id:e.id}).$promise.then(function(){o.remove(i.data,function(n){return n.id===e.id}),i.responseMsg=i.config.resource+" with id "+e.id+" successfully deleted"})["catch"](function(n){i.responseErr=n.data.detail||"Error while deleting "+i.config.resource+" with id "+e.id})},color:"red"},{label:"details",icon:"search",cb:function(e){i.detailedItem=e}}],classes:"table table-striped table-bordered table-responsive",filter:"field",order:!0,pagination:{pageSize:10}},this.formConfig={exclude:this.config.hiddenFields,fields:{},formName:this.config.resource+"Form",actions:[{label:"Save",icon:"ok",cb:function(e){var n=void 0,o=!0;e.id?(n=e.$update(),o=!1):n=e.$save(),n.then(function(n){o&&i.data.push(angular.copy(n)),delete i.detailedItem,i.responseMsg=i.config.resource+" with id "+e.id+" successfully saved"})["catch"](function(n){i.responseErr=n.data.detail||"Error while saving "+i.config.resource+" with id "+e.id})},"class":"success"}]},this.cleanForm=function(){delete i.detailedItem},this.createItem=function(){i.detailedItem=new i.Resource},this.Resource=e.get(this.config.resource);var r=function(){i.Resource.query().$promise.then(function(e){if(e[0]){var r=e[0],s=Object.keys(r);o.remove(s,function(e){return"id"==e||"validators"==e}),angular.isArray(i.config.hiddenFields)&&(s=o.difference(s,i.config.hiddenFields));var a=s.map(function(e){return n.format(e)});s.forEach(function(e,n){var o={label:a[n],prop:e};"string"!=typeof r[e]&&"undefined"!=typeof r[e]&&(o.type=_typeof(r[e])),i.tableConfig.columns.push(o)}),s.forEach(function(e,o){i.formConfig.fields[e]={label:n.format(a[o]).replace(":",""),type:t._getFieldFormat(r[e])}}),i.data=e}})};r()}]}})}(),function(){angular.module("xos.uiComponents").directive("xosSmartPie",function(){return{restrict:"E",scope:{config:"="},template:'\n <canvas\n class="chart chart-pie {{vm.config.classes}}"\n chart-data="vm.data" chart-labels="vm.labels"\n chart-legend="{{vm.config.legend}}">\n </canvas>\n ',bindToController:!0,controllerAs:"vm",controller:["$injector","$interval","$scope","$timeout","_",function(e,n,o,t,i){var r=this;if(!this.config.resource&&!this.config.data)throw new Error("[xosSmartPie] Please provide a resource or an array of data in the configuration");var s=function(e){return i.groupBy(e,r.config.groupBy)},a=function(e){return i.reduce(Object.keys(e),function(n,o){return n.concat(e[o].length)},[])},c=function(e){return angular.isFunction(r.config.labelFormatter)?r.config.labelFormatter(Object.keys(e)):Object.keys(e)},l=function(e){var n=s(e);r.data=a(n),r.labels=c(n)};this.config.resource?!function(){r.Resource=e.get(r.config.resource);var o=function(){r.Resource.query().$promise.then(function(e){e[0]&&l(e)})};o(),r.config.poll&&n(function(){o()},1e3*r.config.poll)}():o.$watch(function(){return r.config.data},function(e){e&&l(r.config.data)},!0),o.$on("create",function(e,n){console.log("create: "+n.id)}),o.$on("destroy",function(e,n){console.log("destroy: "+n.id)})}]}})}(),function(){function e(e,n,o){e.interceptors.push("SetCSRFToken"),n.startSymbol("{$"),n.endSymbol("$}"),o.defaults.stripTrailingSlashes=!1}e.$inject=["$httpProvider","$interpolateProvider","$resourceProvider"],angular.module("bugSnag",[]).factory("$exceptionHandler",function(){return function(e,n){window.Bugsnag?Bugsnag.notifyException(e,{diagnostics:{cause:n}}):console.error(e,n,e.stack)}}),angular.module("xos.helpers",["ngCookies","ngResource","ngAnimate","bugSnag","xos.uiComponents"]).config(e).factory("_",["$window",function(e){return e._}])}(),function(){angular.module("xos.helpers").service("vSG-Collection",["$resource",function(e){return e("/api/service/vsg/")}])}(),function(){angular.module("xos.helpers").service("vOLT-Collection",["$resource",function(e){return e("/api/tenant/cord/volt/:volt_id/",{volt_id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Login",["$resource",function(e){return e("/api/utility/login/")}]).service("Logout",["$resource",function(e){return e("/api/utility/logout/")}])}(),function(){angular.module("xos.helpers").service("Users",["$resource",function(e){return e("/api/core/users/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Truckroll",["$resource",function(e){return e("/api/tenant/truckroll/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Tenants",["$resource",function(e){return e("/api/core/tenants/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Subscribers",["$resource",function(e){return e("/api/tenant/cord/subscriber/:id/",{id:"@id"},{update:{method:"PUT"},"View-a-Subscriber-Features-Detail":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/"},"Read-Subscriber-uplink_speed":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uplink_speed/"},"Update-Subscriber-uplink_speed":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uplink_speed/"},"Read-Subscriber-downlink_speed":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/downlink_speed/"},"Update-Subscriber-downlink_speed":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/downlink_speed/"},"Read-Subscriber-cdn":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/cdn/"},"Update-Subscriber-cdn":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/cdn/"},"Read-Subscriber-uverse":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uverse/"},"Update-Subscriber-uverse":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/uverse/"},"Read-Subscriber-status":{method:"GET",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/status/"},"Update-Subscriber-status":{method:"PUT",isArray:!1,url:"/api/tenant/cord/subscriber/:id/features/status/"}})}])}(),function(){angular.module("xos.helpers").service("SlicesPlus",["$http","$q",function(e,n){this.query=function(o){var t=n.defer();return e.get("/api/utility/slicesplus/",{params:o}).then(function(e){t.resolve(e.data)})["catch"](function(e){t.reject(e.data)}),{$promise:t.promise}},this.get=function(o,t){var i=n.defer();return e.get("/api/utility/slicesplus/"+o,{params:t}).then(function(e){i.resolve(e.data)})["catch"](function(e){i.reject(e.data)}),{$promise:i.promise}}}])}(),function(){angular.module("xos.helpers").service("Slices",["$resource",function(e){return e("/api/core/slices/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Sites",["$resource",function(e){return e("/api/core/sites/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Services",["$resource",function(e){return e("/api/core/services/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("ONOS-Services-Collection",["$resource",function(e){return e("/api/service/onos/")}])}(),function(){angular.module("xos.helpers").service("ONOS-App-Collection",["$resource",function(e){return e("/api/tenant/onos/app/")}])}(),function(){angular.module("xos.helpers").service("Nodes",["$resource",function(e){return e("/api/core/nodes/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Networks",["$resource",function(e){return e("/api/core/networks/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Instances",["$resource",function(e){return e("/api/core/instances/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Flavors",["$resource",function(e){return e("/api/core/flavors/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("Example-Services-Collection",["$resource",function(e){return e("/api/service/exampleservice/")}])}(),function(){angular.module("xos.helpers").service("Deployments",["$resource",function(e){return e("/api/core/deployments/:id/",{id:"@id"},{update:{method:"PUT"}})}])}(),function(){angular.module("xos.helpers").service("XosUserPrefs",["$cookies",function(e){var n=this,o=e.get("xosUserPrefs")?JSON.parse(e.get("xosUserPrefs")):{};this.getAll=function(){return o=e.get("xosUserPrefs")?JSON.parse(e.get("xosUserPrefs")):{}},this.setAll=function(n){e.put("xosUserPrefs",JSON.stringify(n))},this.getSynchronizerNotificationStatus=function(){var e=arguments.length<=0||void 0===arguments[0]?!1:arguments[0];return e?n.getAll().synchronizers.notification[e]:n.getAll().synchronizers.notification},this.setSynchronizerNotificationStatus=function(){var e=arguments.length<=0||void 0===arguments[0]?!1:arguments[0],o=arguments[1];if(!e)throw new Error("[XosUserPrefs] When updating a synchronizer is mandatory to provide a name.");var t=n.getAll();t.synchronizers||(t.synchronizers={notification:{}}),t.synchronizers.notification[e]=o,n.setAll(t)}}])}(),function(){angular.module("xos.helpers").service("GraphService",["$q","Tenants","Services",function(e,n,o){var t=this;this.loadCoarseData=function(){var t=void 0,i=e.defer();return o.query().$promise.then(function(e){return t=e,n.query({kind:"coarse"}).$promise}).then(function(e){i.resolve({tenants:e,services:t})}),i.promise},this.getCoarseGraph=function(){return t.loadCoarseData().then(function(e){console.log(e)}),"ciao"}}])}(),function(){angular.module("xos.helpers").factory("Notification",function(){return window.Notification}).service("xosNotification",["$q","$log","Notification",function(e,n,o){var t=this;this.checkPermission=function(){var n=e.defer();return o.requestPermission().then(function(e){"granted"===e?n.resolve(e):n.reject(e)}),n.promise},this.sendNotification=function(e,t){var i=new o(e,t);i.onerror=function(e){n.error(e)}},this.notify=function(e,i){"Notification"in window?"granted"!==o.permission?t.checkPermission().then(function(){return t.sendNotification(e,i)}):"granted"===o.permission&&t.sendNotification(e,i):n.info("This browser does not support desktop notification")}}])}(),function(){function e(){return{request:function(e){return-1===e.url.indexOf(".html")&&(e.url+="?no_hyperlinks=1"),e}}}angular.module("xos.helpers").factory("NoHyperlinks",e)}(),angular.module("xos.helpers").config(["$provide",function(e){e.decorator("$log",["$delegate",function(e){var n=function(){return window.location.href.indexOf("debug=true")>=0},o=e.log,t=e.info,i=e.warn,r=e.error,s=e.debug,a=function(o){return function(){if(n()){var t=[].slice.call(arguments),i=new Date;t[0]="["+i.getHours()+":"+i.getMinutes()+":"+i.getSeconds()+"] "+t[0],"function"!=typeof e.reset||e.debug.logs instanceof Array||e.reset(),o.apply(null,t)}}};return e.info=a(t),e.log=a(o),e.warn=a(i),e.error=a(r),e.debug=a(s),e}])}]),function(){function e(){var e=function(e){return e.split("_").join(" ").trim()},n=function(e){return e.split(/(?=[A-Z])/).map(function(e){return e.toLowerCase()}).join(" ")},o=function(e){return e.slice(0,1).toUpperCase()+e.slice(1)},t=function(t){return t=e(t),t=n(t),t=o(t).replace(/\s\s+/g," ")+":",t.replace("::",":")};return{_formatByUnderscore:e,_formatByUppercase:n,_capitalize:o,format:t}}angular.module("xos.uiComponents").factory("LabelFormatter",e)}(),function(){function e(e){return{request:function(n){return"GET"!==n.method&&(n.headers["X-CSRFToken"]=e.get("xoscsrftoken")),n}}}e.$inject=["$cookies"],angular.module("xos.helpers").factory("SetCSRFToken",e)}();
\ No newline at end of file
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 3/24/16.
+ */
+
+(function () {
+ 'use strict';
+
+ /**
+ * @ngdoc overview
+ * @name xos.uiComponents
+ * @description
+ * A collection of UI components useful for Dashboard development.
+ * Currently available components are:
+ * - [xosAlert](/#/module/xos.uiComponents.directive:xosAlert)
+ * - [xosForm](/#/module/xos.uiComponents.directive:xosForm)
+ * - [xosPagination](/#/module/xos.uiComponents.directive:xosPagination)
+ * - [xosSmartTable](/#/module/xos.uiComponents.directive:xosSmartTable)
+ * - [xosTable](/#/module/xos.uiComponents.directive:xosTable)
+ * - [xosValidation](/#/module/xos.uiComponents.directive:xosValidation)
+ **/
+
+ angular.module('xos.uiComponents', ['chart.js', 'RecursionHelper']);
+})();
+//# sourceMappingURL=../maps/ui_components/ui-components.module.js.map
+
+'use strict';
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 3/24/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosSmartTable
+ * @link xos.uiComponents.directive:xosTable xosTable
+ * @link xos.uiComponents.directive:xosForm xosForm
+ * @restrict E
+ * @description The xos-table directive
+ * @param {Object} config The configuration for the component,
+ * it is composed by the name of an angular [$resource](https://docs.angularjs.org/api/ngResource/service/$resource)
+ * and an array of fields that shouldn't be printed.
+ * ```
+ * {
+ resource: 'Users',
+ hiddenFields: []
+ }
+ * ```
+ * @scope
+ * @example
+ <example module="sampleSmartTable">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-smart-table config="vm.config"></xos-smart-table>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleSmartTable', ['xos.uiComponents', 'ngResource', 'ngMockE2E'])
+ // This is only for documentation purpose
+ .run(function($httpBackend, _){
+ let datas = [{id: 1, name: 'Jhon', surname: 'Doe'}];
+ let count = 1;
+ let paramsUrl = new RegExp(/\/test\/(.+)/);
+ $httpBackend.whenDELETE(paramsUrl, undefined, ['id']).respond((method, url, data, headers, params) => {
+ data = angular.fromJson(data);
+ let id = url.match(paramsUrl)[1];
+ _.remove(datas, (d) => {
+ return d.id === parseInt(id);
+ })
+ return [204];
+ });
+ $httpBackend.whenGET('/test').respond(200, datas)
+ $httpBackend.whenPOST('/test').respond((method, url, data) => {
+ data = angular.fromJson(data);
+ data.id = ++count;
+ datas.push(data);
+ return [201, data, {}];
+ });
+ })
+ .factory('_', function($window){
+ return $window._;
+ })
+ .service('SampleResource', function($resource){
+ return $resource('/test/:id', {id: '@id'});
+ })
+ // End of documentation purpose, example start
+ .controller('SampleCtrl', function(){
+ this.config = {
+ resource: 'SampleResource'
+ };
+ });
+ </file>
+ </example>
+ */
+
+ .directive('xosSmartTable', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ config: '='
+ },
+ template: '\n <div class="row" ng-show="vm.data.length > 0">\n <div class="col-xs-12 text-right">\n <a href="" class="btn btn-success" ng-click="vm.createItem()">\n Add\n </a>\n </div>\n </div>\n <div class="row">\n <div class="col-xs-12 table-responsive">\n <xos-table config="vm.tableConfig" data="vm.data"></xos-table>\n </div>\n </div>\n <div class="panel panel-default" ng-show="vm.detailedItem">\n <div class="panel-heading">\n <div class="row">\n <div class="col-xs-11">\n <h3 class="panel-title" ng-show="vm.detailedItem.id">Update {{vm.config.resource}} {{vm.detailedItem.id}}</h3>\n <h3 class="panel-title" ng-show="!vm.detailedItem.id">Create {{vm.config.resource}} item</h3>\n </div>\n <div class="col-xs-1">\n <a href="" ng-click="vm.cleanForm()">\n <i class="glyphicon glyphicon-remove pull-right"></i>\n </a>\n </div>\n </div>\n </div>\n <div class="panel-body">\n <xos-form config="vm.formConfig" ng-model="vm.detailedItem"></xos-form>\n </div>\n </div>\n <xos-alert config="{type: \'success\', closeBtn: true}" show="vm.responseMsg">{{vm.responseMsg}}</xos-alert>\n <xos-alert config="{type: \'danger\', closeBtn: true}" show="vm.responseErr">{{vm.responseErr}}</xos-alert>\n ',
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: ["$injector", "LabelFormatter", "_", "XosFormHelpers", function controller($injector, LabelFormatter, _, XosFormHelpers) {
+ var _this = this;
+
+ // TODO
+ // - Validate the config (what if resource does not exist?)
+
+ // NOTE
+ // Corner case
+ // - if response is empty, how can we generate a form ?
+
+ this.responseMsg = false;
+ this.responseErr = false;
+
+ this.tableConfig = {
+ columns: [],
+ actions: [{
+ label: 'delete',
+ icon: 'remove',
+ cb: function cb(item) {
+ _this.Resource.delete({ id: item.id }).$promise.then(function () {
+ _.remove(_this.data, function (d) {
+ return d.id === item.id;
+ });
+ _this.responseMsg = _this.config.resource + ' with id ' + item.id + ' successfully deleted';
+ }).catch(function (err) {
+ _this.responseErr = err.data.detail || 'Error while deleting ' + _this.config.resource + ' with id ' + item.id;
+ });
+ },
+ color: 'red'
+ }, {
+ label: 'details',
+ icon: 'search',
+ cb: function cb(item) {
+ _this.detailedItem = item;
+ }
+ }],
+ classes: 'table table-striped table-bordered table-responsive',
+ filter: 'field',
+ order: true,
+ pagination: {
+ pageSize: 10
+ }
+ };
+
+ this.formConfig = {
+ exclude: this.config.hiddenFields,
+ fields: {},
+ formName: this.config.resource + 'Form',
+ actions: [{
+ label: 'Save',
+ icon: 'ok',
+ cb: function cb(item) {
+ var p = void 0;
+ var isNew = true;
+
+ if (item.id) {
+ p = item.$update();
+ isNew = false;
+ } else {
+ p = item.$save();
+ }
+
+ p.then(function (res) {
+ if (isNew) {
+ _this.data.push(angular.copy(res));
+ }
+ delete _this.detailedItem;
+ _this.responseMsg = _this.config.resource + ' with id ' + item.id + ' successfully saved';
+ }).catch(function (err) {
+ _this.responseErr = err.data.detail || 'Error while saving ' + _this.config.resource + ' with id ' + item.id;
+ });
+ },
+ class: 'success'
+ }]
+ };
+
+ this.cleanForm = function () {
+ delete _this.detailedItem;
+ };
+
+ this.createItem = function () {
+ _this.detailedItem = new _this.Resource();
+ };
+
+ this.Resource = $injector.get(this.config.resource);
+
+ var getData = function getData() {
+ _this.Resource.query().$promise.then(function (res) {
+
+ if (!res[0]) {
+ return;
+ }
+
+ var item = res[0];
+ var props = Object.keys(item);
+
+ _.remove(props, function (p) {
+ return p == 'id' || p == 'validators';
+ });
+
+ // TODO move out cb, non sense triggering a lot of times
+ if (angular.isArray(_this.config.hiddenFields)) {
+ props = _.difference(props, _this.config.hiddenFields);
+ }
+
+ var labels = props.map(function (p) {
+ return LabelFormatter.format(p);
+ });
+
+ props.forEach(function (p, i) {
+ var fieldConfig = {
+ label: labels[i],
+ prop: p
+ };
+
+ if (typeof item[p] !== 'string' && typeof item[p] !== 'undefined') {
+ fieldConfig.type = _typeof(item[p]);
+ }
+
+ _this.tableConfig.columns.push(fieldConfig);
+ });
+
+ // build form structure
+ // TODO move in a pure function for testing purposes
+ props.forEach(function (p, i) {
+ _this.formConfig.fields[p] = {
+ label: LabelFormatter.format(labels[i]).replace(':', ''),
+ type: XosFormHelpers._getFieldFormat(item[p])
+ };
+ });
+
+ _this.data = res;
+ });
+ };
+
+ getData();
+ }]
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/smartComponents/smartTable/smartTable.component.js.map
+
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 3/24/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosSmartPie
+ * @restrict E
+ * @description The xos-table directive
+ * @param {Object} config The configuration for the component,
+ * it is composed by the name of an angular [$resource](https://docs.angularjs.org/api/ngResource/service/$resource)
+ * and a field name that is used to group the data.
+ * ```
+ * {
+ resource: 'Users',
+ groupBy: 'fieldName',
+ classes: 'my-custom-class',
+ labelFormatter: (labels) => {
+ // here you can format your label,
+ // you should return an array with the same order
+ return labels;
+ }
+ }
+ * ```
+ * @scope
+ * @example
+
+ Displaying Local data
+ <example module="sampleSmartPieLocal">
+ <file name="index.html">
+ <div ng-controller="SampleCtrlLocal as vm">
+ <xos-smart-pie config="vm.configLocal"></xos-smart-pie>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleSmartPieLocal', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrlLocal', function($timeout){
+
+ this.datas = [
+ {id: 1, first_name: 'Jon', last_name: 'aaa', category: 2},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 1},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 2}
+ ];
+ this.configLocal = {
+ data: [],
+ groupBy: 'category',
+ classes: 'local',
+ labelFormatter: (labels) => {
+ return labels.map(l => l === '1' ? 'North' : 'Dragon');
+ }
+ };
+
+ $timeout(() => {
+ // this need to be triggered in this way just because of ngDoc,
+ // otherwise you can assign data directly in the config
+ this.configLocal.data = this.datas;
+ }, 1)
+ });
+ </file>
+ </example>
+ Fetching data from API
+ <example module="sampleSmartPieResource">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-smart-pie config="vm.config"></xos-smart-pie>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleSmartPieResource', ['xos.uiComponents', 'ngResource', 'ngMockE2E'])
+ .controller('SampleCtrl', function(){
+ this.config = {
+ resource: 'SampleResource',
+ groupBy: 'category',
+ classes: 'resource',
+ labelFormatter: (labels) => {
+ return labels.map(l => l === '1' ? 'North' : 'Dragon');
+ }
+ };
+ });
+ </file>
+ <file name="backendPoll.js">
+ angular.module('sampleSmartPieResource')
+ .run(function($httpBackend, _){
+ let datas = [
+ {id: 1, first_name: 'Jon', last_name: 'Snow', category: 1},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 2},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 1}
+ ];
+ $httpBackend.whenGET('/test').respond(200, datas)
+ })
+ .factory('_', function($window){
+ return $window._;
+ })
+ .service('SampleResource', function($resource){
+ return $resource('/test/:id', {id: '@id'});
+ })
+ </file>
+ </example>
+ Polling data from API
+ <example module="sampleSmartPiePoll">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-smart-pie config="vm.config"></xos-smart-pie>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleSmartPiePoll', ['xos.uiComponents', 'ngResource', 'ngMockE2E'])
+ .controller('SampleCtrl', function(){
+ this.config = {
+ resource: 'SampleResource',
+ groupBy: 'category',
+ poll: 2,
+ labelFormatter: (labels) => {
+ return labels.map(l => l === '1' ? 'Active' : 'Banned');
+ }
+ };
+ });
+ </file>
+ <file name="backend.js">
+ angular.module('sampleSmartPiePoll')
+ .run(function($httpBackend, _){
+ let mock = [
+ [
+ {id: 1, first_name: 'Jon', last_name: 'Snow', category: 1},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 2},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 1},
+ {id: 3, first_name: 'Tyrion', last_name: 'Lannister', category: 1}
+ ],
+ [
+ {id: 1, first_name: 'Jon', last_name: 'Snow', category: 1},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 2},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 2},
+ {id: 3, first_name: 'Tyrion', last_name: 'Lannister', category: 2}
+ ],
+ [
+ {id: 1, first_name: 'Jon', last_name: 'Snow', category: 1},
+ {id: 2, first_name: 'Danaerys', last_name: 'Targaryen', category: 2},
+ {id: 3, first_name: 'Aria', last_name: 'Stark', category: 1},
+ {id: 3, first_name: 'Tyrion', last_name: 'Lannister', category: 2}
+ ]
+ ];
+ $httpBackend.whenGET('/test').respond(function(method, url, data, headers, params) {
+ return [200, mock[Math.round(Math.random() * 3)]];
+ });
+ })
+ .factory('_', function($window){
+ return $window._;
+ })
+ .service('SampleResource', function($resource){
+ return $resource('/test/:id', {id: '@id'});
+ })
+ </file>
+ </example>
+ */
+ .directive('xosSmartPie', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ config: '='
+ },
+ template: '\n <canvas\n class="chart chart-pie {{vm.config.classes}}"\n chart-data="vm.data" chart-labels="vm.labels"\n chart-legend="{{vm.config.legend}}">\n </canvas>\n ',
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: ["$injector", "$interval", "$scope", "$timeout", "_", function controller($injector, $interval, $scope, $timeout, _) {
+ var _this = this;
+
+ if (!this.config.resource && !this.config.data) {
+ throw new Error('[xosSmartPie] Please provide a resource or an array of data in the configuration');
+ }
+
+ var groupData = function groupData(data) {
+ return _.groupBy(data, _this.config.groupBy);
+ };
+ var formatData = function formatData(data) {
+ return _.reduce(Object.keys(data), function (list, group) {
+ return list.concat(data[group].length);
+ }, []);
+ };
+ var formatLabels = function formatLabels(data) {
+ return angular.isFunction(_this.config.labelFormatter) ? _this.config.labelFormatter(Object.keys(data)) : Object.keys(data);
+ };
+
+ var prepareData = function prepareData(data) {
+ // $timeout(() => {
+ // group data
+ var grouped = groupData(data);
+ _this.data = formatData(grouped);
+ // create labels
+ _this.labels = formatLabels(grouped);
+ // }, 10);
+ };
+
+ if (this.config.resource) {
+ (function () {
+
+ _this.Resource = $injector.get(_this.config.resource);
+ var getData = function getData() {
+ _this.Resource.query().$promise.then(function (res) {
+
+ if (!res[0]) {
+ return;
+ }
+
+ prepareData(res);
+ });
+ };
+
+ getData();
+
+ if (_this.config.poll) {
+ $interval(function () {
+ getData();
+ }, _this.config.poll * 1000);
+ }
+ })();
+ } else {
+ $scope.$watch(function () {
+ return _this.config.data;
+ }, function (data) {
+ if (data) {
+ prepareData(_this.config.data);
+ }
+ }, true);
+ }
+
+ $scope.$on('create', function (event, chart) {
+ console.log('create: ' + chart.id);
+ });
+
+ $scope.$on('destroy', function (event, chart) {
+ console.log('destroy: ' + chart.id);
+ });
+ }]
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/smartComponents/smartPie/smartPie.component.js.map
+
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 4/15/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosValidation
+ * @restrict E
+ * @description The xos-validation directive
+ * @param {Object} errors The error object
+ * @element ANY
+ * @scope
+ * @example
+ <example module="sampleValidation">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <div class="row">
+ <div class="col-xs-12">
+ <label>Set an error type:</label>
+ </div>
+ <div class="col-xs-2">
+ <a class="btn"
+ ng-click="vm.field.$error.required = !vm.field.$error.required"
+ ng-class="{'btn-default': !vm.field.$error.required, 'btn-success': vm.field.$error.required}">
+ Required
+ </a>
+ </div>
+ <div class="col-xs-2">
+ <a class="btn"
+ ng-click="vm.field.$error.email = !vm.field.$error.email"
+ ng-class="{'btn-default': !vm.field.$error.email, 'btn-success': vm.field.$error.email}">
+ Email
+ </a>
+ </div>
+ <div class="col-xs-2">
+ <a class="btn"
+ ng-click="vm.field.$error.minlength = !vm.field.$error.minlength"
+ ng-class="{'btn-default': !vm.field.$error.minlength, 'btn-success': vm.field.$error.minlength}">
+ Min Length
+ </a>
+ </div>
+ <div class="col-xs-2">
+ <a class="btn"
+ ng-click="vm.field.$error.maxlength = !vm.field.$error.maxlength"
+ ng-class="{'btn-default': !vm.field.$error.maxlength, 'btn-success': vm.field.$error.maxlength}">
+ Max Length
+ </a>
+ </div>
+ </div>
+ <xos-validation field ="vm.field" form = "vm.form"></xos-validation>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleValidation', ['xos.uiComponents'])
+ .controller('SampleCtrl', function(){
+ this.field = {
+ $error: {}
+ };
+ this.form= {
+ $submitted:true
+ }
+ });
+ </file>
+ </example>
+ */
+
+ .directive('xosValidation', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ field: '=',
+ form: '='
+ },
+ template: '\n <div ng-cloak>\n <pre>{{vm.field | json}}</pre>\n <!--<pre>{{vm.form.$submitted | json}}</pre>-->\n <!--<pre>{{vm.field.$error.required !== false}}</pre>-->\n <xos-alert config="vm.config" show="vm.field.$error.required !== undefined && vm.field.$error.required !== false && (vm.field.$touched || vm.form.$submitted)">\n Field required\n </xos-alert>\n <xos-alert config="vm.config" show="vm.field.$error.email !== undefined && vm.field.$error.email !== false && (vm.field.$touched || vm.form.$submitted)">\n This is not a valid email\n </xos-alert>\n <xos-alert config="vm.config" show="vm.field.$error.minlength !== undefined && vm.field.$error.minlength !== false && (vm.field.$touched || vm.form.$submitted)">\n Too short\n </xos-alert>\n <xos-alert config="vm.config" show="vm.field.$error.maxlength !== undefined && vm.field.$error.maxlength !== false && (vm.field.$touched || vm.form.$submitted)">\n Too long\n </xos-alert>\n <xos-alert config="vm.config" show="vm.field.$error.custom !== undefined && vm.field.$error.custom !== false && (vm.field.$touched || vm.form.$submitted)">\n Field invalid\n </xos-alert>\n </div>\n ',
+ transclude: true,
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: function controller() {
+ this.config = {
+ type: 'danger'
+ };
+ }
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/dumbComponents/validation/validation.component.js.map
+
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 3/24/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosTable
+ * @restrict E
+ * @description The xos-table directive
+ * @param {Object} config The configuration for the component.
+ * ```
+ * {
+ * columns: [
+ * {
+ * label: 'Human readable name',
+ * prop: 'Property to read in the model object',
+ * type: 'boolean'| 'array'| 'object'| 'custom'| 'date' | 'icon' // see examples for more details
+ formatter: fn(), // receive the whole item if tipe is custom and return a string
+ link: fn() // receive the whole item and return an url
+ * }
+ * ],
+ * classes: 'table table-striped table-bordered',
+ * actions: [ // if defined add an action column
+ {
+ label: 'delete',
+ icon: 'remove', // refers to bootstraps glyphicon
+ cb: (user) => { // receive the model
+ console.log(user);
+ },
+ color: 'red'
+ }
+ ],
+ filter: 'field', // can be by `field` or `fulltext`
+ order: true | {field: 'property name', reverse: true | false} // whether to show ordering arrows, or a configuration for a default ordering
+ * }
+ * ```
+ * @param {Array} data The data that should be rendered
+ * @element ANY
+ * @scope
+ * @example
+ # Basic usage
+ <example module="sampleTable1">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl1 as vm">
+ <xos-table data="vm.data" config="vm.config"></xos-table>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleTable1', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl1', function(){
+ this.config = {
+ columns: [
+ {
+ label: 'First Name', // column title
+ prop: 'name' // property to read in the data array
+ },
+ {
+ label: 'Last Name',
+ prop: 'lastname'
+ }
+ ]
+ };
+ this.data = [
+ {
+ name: 'John',
+ lastname: 'Doe'
+ },
+ {
+ name: 'Gili',
+ lastname: 'Fereydoun'
+ }
+ ]
+ });
+ </file>
+ </example>
+ # Filtering
+ <example module="sampleTable2" animations="true">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl2 as vm">
+ <xos-table data="vm.data" config="vm.config"></xos-table>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleTable2', ['xos.uiComponents', 'ngAnimate'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl2', function(){
+ this.config = {
+ columns: [
+ {
+ label: 'First Name', // column title
+ prop: 'name' // property to read in the data array
+ },
+ {
+ label: 'Last Name',
+ prop: 'lastname'
+ }
+ ],
+ classes: 'table table-striped table-condensed', // table classes, default to `table table-striped table-bordered`
+ actions: [ // if defined add an action column
+ {
+ label: 'delete', // label
+ icon: 'remove', // icons, refers to bootstraps glyphicon
+ cb: (user) => { // callback, get feeded with the full object
+ console.log(user);
+ },
+ color: 'red' // icon color
+ }
+ ],
+ filter: 'field', // can be by `field` or `fulltext`
+ order: true
+ };
+ this.data = [
+ {
+ name: 'John',
+ lastname: 'Doe'
+ },
+ {
+ name: 'Gili',
+ lastname: 'Fereydoun'
+ }
+ ]
+ });
+ </file>
+ </example>
+ # Pagination
+ <example module="sampleTable3">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl3 as vm">
+ <xos-table data="vm.data" config="vm.config"></xos-table>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleTable3', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl3', function(){
+ this.config = {
+ columns: [
+ {
+ label: 'First Name', // column title
+ prop: 'name' // property to read in the data array
+ },
+ {
+ label: 'Last Name',
+ prop: 'lastname'
+ }
+ ],
+ pagination: {
+ pageSize: 2
+ }
+ };
+ this.data = [
+ {
+ name: 'John',
+ lastname: 'Doe'
+ },
+ {
+ name: 'Gili',
+ lastname: 'Fereydoun'
+ },
+ {
+ name: 'Lucky',
+ lastname: 'Clarkson'
+ },
+ {
+ name: 'Tate',
+ lastname: 'Spalding'
+ }
+ ]
+ });
+ </file>
+ </example>
+ # Field formatter
+ <example module="sampleTable4">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-table data="vm.data" config="vm.config"></xos-table>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleTable4', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl', function(){
+ this.config = {
+ columns: [
+ {
+ label: 'First Name',
+ prop: 'name',
+ link: item => `https://www.google.it/#q=${item.name}`
+ },
+ {
+ label: 'Enabled',
+ prop: 'enabled',
+ type: 'boolean'
+ },
+ {
+ label: 'Services',
+ prop: 'services',
+ type: 'array'
+ },
+ {
+ label: 'Details',
+ prop: 'details',
+ type: 'object'
+ },
+ {
+ label: 'Created',
+ prop: 'created',
+ type: 'date'
+ },
+ {
+ label: 'Icon',
+ type: 'icon',
+ formatter: item => item.icon //note that this refer to [Bootstrap Glyphicon](http://getbootstrap.com/components/#glyphicons)
+ }
+ ]
+ };
+ this.data = [
+ {
+ name: 'John',
+ enabled: true,
+ services: ['Cdn', 'IpTv'],
+ details: {
+ c_tag: '243',
+ s_tag: '444'
+ },
+ created: new Date('December 17, 1995 03:24:00'),
+ icon: 'music'
+ },
+ {
+ name: 'Gili',
+ enabled: false,
+ services: ['Cdn', 'IpTv', 'Cache'],
+ details: {
+ c_tag: '675',
+ s_tag: '893'
+ },
+ created: new Date(),
+ icon: 'camera'
+ }
+ ]
+ });
+ </file>
+ </example>
+ # Custom formatter
+ <example module="sampleTable5">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-table data="vm.data" config="vm.config"></xos-table>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleTable5', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl', function(){
+ this.config = {
+ columns: [
+ {
+ label: 'Username',
+ prop: 'username'
+ },
+ {
+ label: 'Features',
+ type: 'custom',
+ formatter: (val) => {
+
+ let cdnEnabled = val.features.cdn ? 'enabled' : 'disabled';
+ return `
+ Cdn is ${cdnEnabled},
+ uplink speed is ${val.features.uplink_speed}
+ and downlink speed is ${val.features.downlink_speed}
+ `;
+ }
+ }
+ ]
+ };
+ this.data = [
+ {
+ username: 'John',
+ features: {
+ "cdn": false,
+ "uplink_speed": 1000000000,
+ "downlink_speed": 1000000000,
+ "uverse": true,
+ "status": "enabled"
+ }
+ },
+ {
+ username: 'Gili',
+ features: {
+ "cdn": true,
+ "uplink_speed": 3000000000,
+ "downlink_speed": 2000000000,
+ "uverse": true,
+ "status": "enabled"
+ }
+ }
+ ]
+ });
+ </file>
+ </example>
+ **/
+
+ .directive('xosTable', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ data: '=',
+ config: '='
+ },
+ template: '\n <div ng-show="vm.data.length > 0">\n <div class="row" ng-if="vm.config.filter == \'fulltext\'">\n <div class="col-xs-12">\n <input\n class="form-control"\n placeholder="Type to search.."\n type="text"\n ng-model="vm.query"/>\n </div>\n </div>\n <table ng-class="vm.classes" ng-hide="vm.data.length == 0">\n <thead>\n <tr>\n <th ng-repeat="col in vm.columns">\n {{col.label}}\n <span ng-if="vm.config.order">\n <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = false">\n <i class="glyphicon glyphicon-chevron-up"></i>\n </a>\n <a href="" ng-click="vm.orderBy = col.prop; vm.reverse = true">\n <i class="glyphicon glyphicon-chevron-down"></i>\n </a>\n </span>\n </th>\n <th ng-if="vm.config.actions">Actions:</th>\n </tr>\n </thead>\n <tbody ng-if="vm.config.filter == \'field\'">\n <tr>\n <td ng-repeat="col in vm.columns">\n <input\n ng-if="col.type !== \'boolean\'"\n class="form-control"\n placeholder="Type to search by {{col.label}}"\n type="text"\n ng-model="vm.query[col.prop]"/>\n <select\n ng-if="col.type === \'boolean\'"\n class="form-control"\n ng-model="vm.query[col.prop]">\n <option value="">-</option>\n <option value="true">True</option>\n <option value="false">False</option>\n </select>\n </td>\n <td ng-if="vm.config.actions"></td>\n </tr>\n </tbody>\n <tbody>\n <tr ng-repeat="item in vm.data | filter:vm.query | orderBy:vm.orderBy:vm.reverse | pagination:vm.currentPage * vm.config.pagination.pageSize | limitTo: (vm.config.pagination.pageSize || vm.data.length) track by $index">\n <td ng-repeat="col in vm.columns" link-wrapper>\n <span ng-if="!col.type">{{item[col.prop]}}</span>\n <span ng-if="col.type === \'boolean\'">\n <i class="glyphicon"\n ng-class="{\'glyphicon-ok\': item[col.prop], \'glyphicon-remove\': !item[col.prop]}">\n </i>\n </span>\n <span ng-if="col.type === \'date\'">\n {{item[col.prop] | date:\'H:mm MMM d, yyyy\'}}\n </span>\n <span ng-if="col.type === \'array\'">\n {{item[col.prop] | arrayToList}}\n </span>\n <span ng-if="col.type === \'object\'">\n <dl class="dl-horizontal">\n <span ng-repeat="(k,v) in item[col.prop]">\n <dt>{{k}}</dt>\n <dd>{{v}}</dd>\n </span>\n </dl>\n </span>\n <span ng-if="col.type === \'custom\'">\n {{col.formatter(item)}}\n </span>\n <span ng-if="col.type === \'icon\'">\n <i class="glyphicon glyphicon-{{col.formatter(item)}}">\n </i>\n </span>\n </td>\n <td ng-if="vm.config.actions">\n <a href=""\n ng-repeat="action in vm.config.actions"\n ng-click="action.cb(item)"\n title="{{action.label}}">\n <i\n class="glyphicon glyphicon-{{action.icon}}"\n style="color: {{action.color}};"></i>\n </a>\n </td>\n </tr>\n </tbody>\n </table>\n <xos-pagination\n ng-if="vm.config.pagination"\n page-size="vm.config.pagination.pageSize"\n total-elements="vm.data.length"\n change="vm.goToPage">\n </xos-pagination>\n </div>\n <div ng-show="vm.data.length == 0 || !vm.data">\n <xos-alert config="{type: \'info\'}">\n No data to show.\n </xos-alert>\n </div>\n ',
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: ["_", function controller(_) {
+ var _this = this;
+
+ if (!this.config) {
+ throw new Error('[xosTable] Please provide a configuration via the "config" attribute');
+ }
+
+ if (!this.config.columns) {
+ throw new Error('[xosTable] Please provide a columns list in the configuration');
+ }
+
+ // handle default ordering
+ if (this.config.order && angular.isObject(this.config.order)) {
+ this.reverse = this.config.order.reverse || false;
+ this.orderBy = this.config.order.field || 'id';
+ }
+
+ // if columns with type 'custom' are provided
+ // check that a custom formatte3 is provided too
+ var customCols = _.filter(this.config.columns, { type: 'custom' });
+ if (angular.isArray(customCols) && customCols.length > 0) {
+ _.forEach(customCols, function (col) {
+ if (!col.formatter || !angular.isFunction(col.formatter)) {
+ throw new Error('[xosTable] You have provided a custom field type, a formatter function should provided too.');
+ }
+ });
+ }
+
+ // if columns with type 'icon' are provided
+ // check that a custom formatte3 is provided too
+ var iconCols = _.filter(this.config.columns, { type: 'icon' });
+ if (angular.isArray(iconCols) && iconCols.length > 0) {
+ _.forEach(iconCols, function (col) {
+ if (!col.formatter || !angular.isFunction(col.formatter)) {
+ throw new Error('[xosTable] You have provided an icon field type, a formatter function should provided too.');
+ }
+ });
+ }
+
+ // if a link property is passed,
+ // it should be a function
+ var linkedColumns = _.filter(this.config.columns, function (col) {
+ return angular.isDefined(col.link);
+ });
+ if (angular.isArray(linkedColumns) && linkedColumns.length > 0) {
+ _.forEach(linkedColumns, function (col) {
+ if (!angular.isFunction(col.link)) {
+ throw new Error('[xosTable] The link property should be a function.');
+ }
+ });
+ }
+
+ this.columns = this.config.columns;
+ this.classes = this.config.classes || 'table table-striped table-bordered';
+
+ if (this.config.actions) {
+ // TODO validate action format
+ }
+ if (this.config.pagination) {
+ this.currentPage = 0;
+ this.goToPage = function (n) {
+ _this.currentPage = n;
+ };
+ }
+ }]
+ };
+ })
+ // TODO move in separate files
+ // TODO test
+ .filter('arrayToList', function () {
+ return function (input) {
+ if (!angular.isArray(input)) {
+ return input;
+ }
+ return input.join(', ');
+ };
+ })
+ // TODO test
+ .directive('linkWrapper', function () {
+ return {
+ restrict: 'A',
+ transclude: true,
+ template: '\n <a ng-if="col.link" href="{{col.link(item)}}">\n <div ng-transclude></div>\n </a>\n <div ng-transclude ng-if="!col.link"></div>\n '
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/dumbComponents/table/table.component.js.map
+
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 4/15/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosPagination
+ * @restrict E
+ * @description The xos-table directive
+ * @param {Number} pageSize Number of elements per page
+ * @param {Number} totalElements Number of total elements in the collection
+ * @param {Function} change The callback to be triggered on page change.
+ * * @element ANY
+ * @scope
+ * @example
+ <example module="samplePagination">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl1 as vm">
+ <xos-pagination
+ page-size="vm.pageSize"
+ total-elements="vm.totalElements"
+ change="vm.change">
+ </xos-pagination>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('samplePagination', ['xos.uiComponents'])
+ .controller('SampleCtrl1', function(){
+ this.pageSize = 10;
+ this.totalElements = 35;
+ this.change = (pageNumber) => {
+ console.log(pageNumber);
+ }
+ });
+ </file>
+ </example>
+ **/
+
+ .directive('xosPagination', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ pageSize: '=',
+ totalElements: '=',
+ change: '='
+ },
+ template: '\n <div class="row" ng-if="vm.pageList.length > 1">\n <div class="col-xs-12 text-center">\n <ul class="pagination">\n <li\n ng-click="vm.goToPage(vm.currentPage - 1)"\n ng-class="{disabled: vm.currentPage == 0}">\n <a href="" aria-label="Previous">\n <span aria-hidden="true">«</span>\n </a>\n </li>\n <li ng-repeat="i in vm.pageList" ng-class="{active: i === vm.currentPage}">\n <a href="" ng-click="vm.goToPage(i)">{{i + 1}}</a>\n </li>\n <li\n ng-click="vm.goToPage(vm.currentPage + 1)"\n ng-class="{disabled: vm.currentPage == vm.pages - 1}">\n <a href="" aria-label="Next">\n <span aria-hidden="true">»</span>\n </a>\n </li>\n </ul>\n </div>\n </div>\n ',
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: ["$scope", function controller($scope) {
+ var _this = this;
+
+ this.currentPage = 0;
+
+ this.goToPage = function (n) {
+ if (n < 0 || n === _this.pages) {
+ return;
+ }
+ _this.currentPage = n;
+ _this.change(n);
+ };
+
+ this.createPages = function (pages) {
+ var arr = [];
+ for (var i = 0; i < pages; i++) {
+ arr.push(i);
+ }
+ return arr;
+ };
+
+ // watch for data changes
+ $scope.$watch(function () {
+ return _this.totalElements;
+ }, function () {
+ if (_this.totalElements) {
+ _this.pages = Math.ceil(_this.totalElements / _this.pageSize);
+ _this.pageList = _this.createPages(_this.pages);
+ }
+ });
+ }]
+ };
+ }).filter('pagination', function () {
+ return function (input, start) {
+ if (!input || !angular.isArray(input)) {
+ return input;
+ }
+ start = parseInt(start, 10);
+ return input.slice(start);
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/dumbComponents/pagination/pagination.component.js.map
+
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 4/18/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosForm
+ * @restrict E
+ * @description The xos-form directive.
+ * This components have two usage, given a model it is able to autogenerate a form or it can be configured to create a custom form.
+ * @param {Object} config The configuration object
+ * ```
+ * {
+ * exclude: ['id', 'validators', 'created', 'updated', 'deleted'], //field to be skipped in the form, the provide values are concatenated
+ * actions: [ // define the form buttons with related callback
+ * {
+ label: 'save',
+ icon: 'ok', // refers to bootstraps glyphicon
+ cb: (user) => { // receive the model
+ console.log(user);
+ },
+ class: 'success'
+ }
+ * ],
+ * fields: {
+ * field_name: {
+ * label: 'Field Label',
+ * type: 'string' // options are: [date, boolean, number, email, string],
+ * validators: {
+ * minlength: number,
+ maxlength: number,
+ required: boolean,
+ min: number,
+ max: number
+ * }
+ * }
+ * }
+ * }
+ * ```
+ * @element ANY
+ * @scope
+ * @requires xos.uiComponents.directive:xosField
+ * @requires xos.uiComponents.XosFormHelpers
+ * @requires xos.helpers._
+ * @example
+
+ Autogenerated form
+ <example module="sampleForm">
+ <file name="script.js">
+ angular.module('sampleForm', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl', function(){
+ this.model = {
+ first_name: 'Jhon',
+ last_name: 'Doe',
+ email: 'jhon.doe@sample.com',
+ active: true,
+ birthDate: '2015-02-17T22:06:38.059000Z'
+ }
+ this.config = {
+ exclude: ['password', 'last_login'],
+ formName: 'sampleForm',
+ actions: [
+ {
+ label: 'Save',
+ icon: 'ok', // refers to bootstraps glyphicon
+ cb: (user) => { // receive the model
+ console.log(user);
+ },
+ class: 'success'
+ }
+ ]
+ };
+ });
+ </file>
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-form ng-model="vm.model" config="vm.config"></xos-form>
+ </div>
+ </file>
+ </example>
+ Configuration defined form
+ <example module="sampleForm1">
+ <file name="script.js">
+ angular.module('sampleForm1', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl1', function(){
+ this.model = {
+ };
+ this.config = {
+ exclude: ['password', 'last_login'],
+ formName: 'sampleForm1',
+ actions: [
+ {
+ label: 'Save',
+ icon: 'ok', // refers to bootstraps glyphicon
+ cb: (user) => { // receive the model
+ console.log(user);
+ },
+ class: 'success'
+ }
+ ],
+ fields: {
+ first_name: {
+ type: 'string',
+ validators: {
+ required: true
+ }
+ },
+ last_name: {
+ label: 'Surname',
+ type: 'string',
+ validators: {
+ required: true,
+ minlength: 10
+ }
+ },
+ age: {
+ type: 'number',
+ validators: {
+ required: true,
+ min: 21
+ }
+ },
+ }
+ };
+ });
+ </file>
+ <file name="index.html">
+ <div ng-controller="SampleCtrl1 as vm">
+ <xos-form ng-model="vm.model" config="vm.config"></xos-form>
+ </div>
+ </file>
+ </example>
+ **/
+
+ .directive('xosForm', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ config: '=',
+ ngModel: '='
+ },
+ template: '\n <ng-form name="vm.{{vm.config.formName || \'form\'}}">\n <div class="form-group" ng-repeat="(name, field) in vm.formField">\n <xos-field name="name" field="field" ng-model="vm.ngModel[name]"></xos-field>\n <xos-validation field="vm[vm.config.formName || \'form\'][name]" form="vm[vm.config.formName || \'form\']"></xos-validation>\n </div>\n <div class="form-group" ng-if="vm.config.actions">\n <button role="button" href=""\n ng-repeat="action in vm.config.actions"\n ng-click="action.cb(vm.ngModel)"\n class="btn btn-{{action.class}}"\n title="{{action.label}}">\n <i class="glyphicon glyphicon-{{action.icon}}"></i>\n {{action.label}}\n </button>\n </div>\n </ng-form>\n ',
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: ["$scope", "$log", "_", "XosFormHelpers", function controller($scope, $log, _, XosFormHelpers) {
+ var _this = this;
+
+ if (!this.config) {
+ throw new Error('[xosForm] Please provide a configuration via the "config" attribute');
+ }
+
+ if (!this.config.actions) {
+ throw new Error('[xosForm] Please provide an action list in the configuration');
+ }
+
+ this.excludedField = ['id', 'validators', 'created', 'updated', 'deleted', 'backend_status'];
+ if (this.config && this.config.exclude) {
+ this.excludedField = this.excludedField.concat(this.config.exclude);
+ }
+
+ this.formField = [];
+ $scope.$watch(function () {
+ return _this.ngModel;
+ }, function (model) {
+
+ // empty from old stuff
+ _this.formField = {};
+
+ if (!model) {
+ return;
+ }
+
+ var diff = _.difference(Object.keys(model), _this.excludedField);
+ var modelField = XosFormHelpers.parseModelField(diff);
+ _this.formField = XosFormHelpers.buildFormStructure(modelField, _this.config.fields, model);
+ });
+ }]
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/dumbComponents/form/form.component.js.map
+
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 5/25/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosField
+ * @restrict E
+ * @description The xos-field directive.
+ * This component decide, give a field wich kind of input it need to print.
+ * @param {string} name The field name
+ * @param {object} field The field configuration:
+ * ```
+ * {
+ * label: 'Label',
+ * type: 'number', //typeof field
+ * validators: {} // see xosForm for more details
+ * }
+ * ```
+ * @param {mixed} ngModel The field value
+ *
+ * @example
+
+ # Basic Example
+
+ <example module="sampleField1">
+ <file name="script.js">
+ angular.module('sampleField1', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl', function(){
+ this.name = 'input-name';
+ this.field = {label: 'My String Value:', type: 'string'};
+ this.model = 'my string';
+ });
+ </file>
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-field ng-model="vm.model" name="vm.name" field="vm.field"></xos-field>
+ </div>
+ </file>
+ </example>
+
+ # Possible Values
+ <example module="sampleField2">
+ <file name="script.js">
+ angular.module('sampleField2', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl', function(){
+ this.field1 = {
+ name: 'number-field',
+ field: {label: 'My Number Value:', type: 'number'},
+ model: 2
+ };
+ this.field2 = {
+ name: 'date-field',
+ field: {label: 'My Date Value:', type: 'date'},
+ model: new Date()
+ };
+ this.field3 = {
+ name: 'boolean-field',
+ field: {label: 'My Boolean Value:', type: 'boolean'},
+ model: true
+ };
+ this.field4 = {
+ name: 'email-field',
+ field: {label: 'My Email Value:', type: 'email'},
+ model: 'sample@domain.us'
+ };
+ });
+ </file>
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-field ng-model="vm.field1.model" name="vm.field1.name" field="vm.field1.field"></xos-field>
+ <xos-field ng-model="vm.field2.model" name="vm.field2.name" field="vm.field2.field"></xos-field>
+ <xos-field ng-model="vm.field3.model" name="vm.field3.name" field="vm.field3.field"></xos-field>
+ <xos-field ng-model="vm.field4.model" name="vm.field4.name" field="vm.field4.field"></xos-field>
+ </div>
+ </file>
+ </example>
+ # This element is recursive
+ <example module="sampleField3">
+ <file name="script.js">
+ angular.module('sampleField3', ['xos.uiComponents'])
+ .factory('_', function($window){
+ return $window._;
+ })
+ .controller('SampleCtrl', function(){
+ this.name = 'input-name';
+ this.field = {label: 'My Object Value:', type: 'object'};
+ this.model = {
+ name: 'Jhon',
+ age: '25',
+ email: 'jhon@thewall.ru',
+ active: true
+ };
+ });
+ </file>
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm">
+ <xos-field ng-model="vm.model" name="vm.name" field="vm.field"></xos-field>
+ </div>
+ </file>
+ </example>
+ */
+ .directive('xosField', ["RecursionHelper", function (RecursionHelper) {
+ return {
+ restrict: 'E',
+ scope: {
+ name: '=',
+ field: '=',
+ ngModel: '='
+ },
+ template: '\n <label ng-if="vm.field.type !== \'object\'">{{vm.field.label}}</label>\n <!--<pre>{{vm.field.options | json}}</pre>-->\n <!--<pre>{{vm.ngModel | json}}</pre>-->\n <input\n ng-if="vm.field.type !== \'boolean\' && vm.field.type !== \'object\' && vm.field.type !== \'select\'"\n type="{{vm.field.type}}"\n name="{{vm.name}}"\n class="form-control"\n ng-model="vm.ngModel"\n ng-minlength="vm.field.validators.minlength || 0"\n ng-maxlength="vm.field.validators.maxlength || 2000"\n ng-required="vm.field.validators.required || false" />\n <select class="form-control" ng-if ="vm.field.type === \'select\'"\n name = "{{vm.name}}"\n ng-options="item.id as item.label for item in vm.field.options track by item.id"\n ng-model="vm.ngModel"\n ng-required="vm.field.validators.required || false">\n </select>\n <span class="boolean-field" ng-if="vm.field.type === \'boolean\'">\n <button\n class="btn btn-success"\n ng-show="vm.ngModel"\n ng-click="vm.ngModel = false">\n <i class="glyphicon glyphicon-ok"></i>\n </button>\n <button\n class="btn btn-danger"\n ng-show="!vm.ngModel"\n ng-click="vm.ngModel = true">\n <i class="glyphicon glyphicon-remove"></i>\n </button>\n </span>\n <div\n class="panel panel-default object-field"\n ng-if="vm.field.type == \'object\' && !vm.isEmptyObject(vm.ngModel)"\n >\n <div class="panel-heading">{{vm.field.label}}</div>\n <div class="panel-body">\n <div ng-repeat="(k, v) in vm.ngModel">\n <xos-field\n name="k"\n field="{label: vm.formatLabel(k), type: vm.getType(v)}"\n ng-model="v">\n </xos-field>\n </div>\n </div>\n </div>\n ',
+ bindToController: true,
+ controllerAs: 'vm',
+ // the compile cicle is needed to support recursion
+ compile: function compile(element) {
+ return RecursionHelper.compile(element);
+ },
+ controller: ["$attrs", "XosFormHelpers", "LabelFormatter", function controller($attrs, XosFormHelpers, LabelFormatter) {
+ // console.log('Field: ', this.name, this.field, this.ngModel);
+ if (!this.name) {
+ throw new Error('[xosField] Please provide a field name');
+ }
+ if (!this.field) {
+ throw new Error('[xosField] Please provide a field definition');
+ }
+ if (!$attrs.ngModel) {
+ throw new Error('[xosField] Please provide an ng-model');
+ }
+
+ this.getType = XosFormHelpers._getFieldFormat;
+ this.formatLabel = LabelFormatter.format;
+
+ this.isEmptyObject = function (o) {
+ return Object.keys(o).length === 0;
+ };
+ }]
+ };
+ }]);
+})();
+//# sourceMappingURL=../../../maps/ui_components/dumbComponents/field/field.component.js.map
+
+'use strict';
+
+/**
+ * © OpenCORD
+ *
+ * Visit http://guide.xosproject.org/devguide/addview/ for more information
+ *
+ * Created by teone on 4/15/16.
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc directive
+ * @name xos.uiComponents.directive:xosAlert
+ * @restrict E
+ * @description The xos-alert directive
+ * @param {Object} config The configuration object
+ * ```
+ * {
+ * type: 'danger', //info, success, warning
+ * closeBtn: true, //default false
+ * autoHide: 3000 //delay to automatically hide the alert
+ * }
+ * ```
+ * @param {Boolean=} show Binding to show and hide the alert, default to true
+ * @element ANY
+ * @scope
+ * @example
+ <example module="sampleAlert1">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl1 as vm">
+ <xos-alert config="vm.config1">
+ A sample alert message
+ </xos-alert>
+ <xos-alert config="vm.config2">
+ A sample alert message (with close button)
+ </xos-alert>
+ <xos-alert config="vm.config3">
+ A sample info message
+ </xos-alert>
+ <xos-alert config="vm.config4">
+ A sample success message
+ </xos-alert>
+ <xos-alert config="vm.config5">
+ A sample warning message
+ </xos-alert>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleAlert1', ['xos.uiComponents'])
+ .controller('SampleCtrl1', function(){
+ this.config1 = {
+ type: 'danger'
+ };
+ this.config2 = {
+ type: 'danger',
+ closeBtn: true
+ };
+ this.config3 = {
+ type: 'info'
+ };
+ this.config4 = {
+ type: 'success'
+ };
+ this.config5 = {
+ type: 'warning'
+ };
+ });
+ </file>
+ </example>
+ <example module="sampleAlert2" animations="true">
+ <file name="index.html">
+ <div ng-controller="SampleCtrl as vm" class="row">
+ <div class="col-sm-4">
+ <a class="btn btn-default btn-block" ng-show="!vm.show" ng-click="vm.show = true">Show Alert</a>
+ <a class="btn btn-default btn-block" ng-show="vm.show" ng-click="vm.show = false">Hide Alert</a>
+ </div>
+ <div class="col-sm-8">
+ <xos-alert config="vm.config1" show="vm.show">
+ A sample alert message, not displayed by default.
+ </xos-alert>
+ </div>
+ </div>
+ </file>
+ <file name="script.js">
+ angular.module('sampleAlert2', ['xos.uiComponents', 'ngAnimate'])
+ .controller('SampleCtrl', function(){
+ this.config1 = {
+ type: 'success'
+ };
+ this.show = false;
+ });
+ </file>
+ </example>
+ **/
+
+ .directive('xosAlert', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ config: '=',
+ show: '=?'
+ },
+ template: '\n <div ng-cloak class="alert alert-{{vm.config.type}}" ng-hide="!vm.show">\n <button type="button" class="close" ng-if="vm.config.closeBtn" ng-click="vm.dismiss()">\n <span aria-hidden="true">×</span>\n </button>\n <p ng-transclude></p>\n </div>\n ',
+ transclude: true,
+ bindToController: true,
+ controllerAs: 'vm',
+ controller: ["$timeout", function controller($timeout) {
+ var _this = this;
+
+ if (!this.config) {
+ throw new Error('[xosAlert] Please provide a configuration via the "config" attribute');
+ }
+
+ // default the value to true
+ this.show = this.show !== false;
+
+ this.dismiss = function () {
+ _this.show = false;
+ };
+
+ if (this.config.autoHide) {
+ (function () {
+ var to = $timeout(function () {
+ _this.dismiss();
+ $timeout.cancel(to);
+ }, _this.config.autoHide);
+ })();
+ }
+ }]
+ };
+ });
+})();
+//# sourceMappingURL=../../../maps/ui_components/dumbComponents/alert/alert.component.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ /**
+ * @ngdoc service
+ * @name xos.uiComponents.LabelFormatter
+ * @description This factory define a set of helper function to format label started from an object property
+ **/
+
+ angular.module('xos.uiComponents').factory('LabelFormatter', labelFormatter);
+
+ function labelFormatter() {
+
+ var _formatByUnderscore = function _formatByUnderscore(string) {
+ return string.split('_').join(' ').trim();
+ };
+
+ var _formatByUppercase = function _formatByUppercase(string) {
+ return string.split(/(?=[A-Z])/).map(function (w) {
+ return w.toLowerCase();
+ }).join(' ');
+ };
+
+ var _capitalize = function _capitalize(string) {
+ return string.slice(0, 1).toUpperCase() + string.slice(1);
+ };
+
+ var format = function format(string) {
+ string = _formatByUnderscore(string);
+ string = _formatByUppercase(string);
+
+ string = _capitalize(string).replace(/\s\s+/g, ' ') + ':';
+ return string.replace('::', ':');
+ };
+
+ return {
+ // test export
+ _formatByUnderscore: _formatByUnderscore,
+ _formatByUppercase: _formatByUppercase,
+ _capitalize: _capitalize,
+ // export to use
+ format: format
+ };
+ }
+})();
+//# sourceMappingURL=../../../maps/services/helpers/ui/label_formatter.service.js.map
+
+'use strict';
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
+
+(function () {
+
+ angular.module('xos.uiComponents')
+
+ /**
+ * @ngdoc service
+ * @name xos.uiComponents.XosFormHelpers
+ * @requires xos.uiComponents.LabelFormatter
+ * @requires xos.helpers._
+ **/
+
+ .service('XosFormHelpers', ["_", "LabelFormatter", function (_, LabelFormatter) {
+ var _this = this;
+
+ /**
+ * @ngdoc method
+ * @name xos.uiComponents.XosFormHelpers#_isEmail
+ * @methodOf xos.uiComponents.XosFormHelpers
+ * @description
+ * Return true if the string is an email address
+ * @param {string} text The string to be evaluated
+ * @returns {boolean} If the string match an email format
+ **/
+
+ this._isEmail = function (text) {
+ var re = /(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/;
+ return re.test(text);
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.uiComponents.XosFormHelpers#_getFieldFormat
+ * @methodOf xos.uiComponents.XosFormHelpers
+ * @description
+ * Return the type of the input
+ * @param {mixed} value The data to be evaluated
+ * @returns {string} The type of the input
+ **/
+
+ this._getFieldFormat = function (value) {
+
+ if (angular.isArray(value)) {
+ return 'array';
+ }
+
+ // check if is date
+ if (_.isDate(value) || !Number.isNaN(Date.parse(value)) && new Date(value).getTime() > 631180800000) {
+ return 'date';
+ }
+
+ // check if is boolean
+ // isNaN(false) = false, false is a number (0), true is a number (1)
+ if (typeof value === 'boolean') {
+ return 'boolean';
+ }
+
+ // check if a string is an email
+ if (_this._isEmail(value)) {
+ return 'email';
+ }
+
+ // if null return string
+ if (typeof value === 'string' || value === null) {
+ return 'text';
+ }
+
+ return typeof value === 'undefined' ? 'undefined' : _typeof(value);
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.uiComponents.XosFormHelpers#buildFormStructure
+ * @methodOf xos.uiComponents.XosFormHelpers
+ * @description
+ * Return the type of the input
+ * @param {object} modelField An object containing one property for each field of the model
+ * @param {object} customField An object containing one property for each field custom field
+ * @param {object} model The actual model on wich build the form structure (it is used to determine the type of the input)
+ * @returns {object} An object describing the form structure in the form of:
+ * ```
+ * {
+ * 'field-name': {
+ * label: 'Label',
+ * type: 'number', //typeof field
+ * validators: {}, // see xosForm for more details
+ * hint: 'A Custom hint for the field'
+ * }
+ * }
+ * ```
+ **/
+
+ this.buildFormStructure = function (modelField, customField, model) {
+
+ modelField = Object.keys(modelField).length > 0 ? modelField : customField; //if no model field are provided, check custom
+ customField = customField || {};
+
+ return _.reduce(Object.keys(modelField), function (form, f) {
+
+ form[f] = {
+ label: customField[f] && customField[f].label ? customField[f].label + ':' : LabelFormatter.format(f),
+ type: customField[f] && customField[f].type ? customField[f].type : _this._getFieldFormat(model[f]),
+ validators: customField[f] && customField[f].validators ? customField[f].validators : {},
+ hint: customField[f] && customField[f].hint ? customField[f].hint : ''
+ };
+
+ if (customField[f] && customField[f].options) {
+ form[f].options = customField[f].options;
+ }
+ if (form[f].type === 'date') {
+ model[f] = new Date(model[f]);
+ }
+
+ if (form[f].type === 'number') {
+ model[f] = parseInt(model[f], 10);
+ }
+
+ return form;
+ }, {});
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.uiComponents.XosFormHelpers#parseModelField
+ * @methodOf xos.uiComponents.XosFormHelpers
+ * @description
+ * Helpers for buildFormStructure, convert a list of model properties in an object used to build the form structure, eg:
+ * ```
+ * // input:
+ * ['id', 'name'm 'mail']
+ *
+ * // output
+ * {
+ * id: {},
+ * name: {},
+ * mail: {}
+ * }
+ * ```
+ * @param {array} fields An array of fields representing the model properties
+ * @returns {object} An object containing one property for each field of the model
+ **/
+
+ this.parseModelField = function (fields) {
+ return _.reduce(fields, function (form, f) {
+ form[f] = {};
+ return form;
+ }, {});
+ };
+ }]);
+})();
+//# sourceMappingURL=../../../maps/services/helpers/ui/form.helpers.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ config.$inject = ["$httpProvider", "$interpolateProvider", "$resourceProvider"];
+ angular.module('bugSnag', []).factory('$exceptionHandler', function () {
+ return function (exception, cause) {
+ if (window.Bugsnag) {
+ Bugsnag.notifyException(exception, { diagnostics: { cause: cause } });
+ } else {
+ console.error(exception, cause, exception.stack);
+ }
+ };
+ });
+
+ /**
+ * @ngdoc overview
+ * @name xos.helpers
+ * @description this is the module that group all the helpers service and components for XOS
+ **/
+
+ angular.module('xos.helpers', ['ngCookies', 'ngResource', 'ngAnimate', 'bugSnag', 'xos.uiComponents']).config(config)
+
+ /**
+ * @ngdoc service
+ * @name xos.helpers._
+ * @description Wrap [lodash](https://lodash.com/docs) in an Angular Service
+ **/
+
+ .factory('_', ["$window", function ($window) {
+ return $window._;
+ }]);
+
+ function config($httpProvider, $interpolateProvider, $resourceProvider) {
+ $httpProvider.interceptors.push('SetCSRFToken');
+
+ $interpolateProvider.startSymbol('{$');
+ $interpolateProvider.endSymbol('$}');
+
+ // NOTE http://www.masnun.com/2013/09/18/django-rest-framework-angularjs-resource-trailing-slash-problem.html
+ $resourceProvider.defaults.stripTrailingSlashes = false;
+ }
+})();
+//# sourceMappingURL=maps/xosHelpers.module.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.vSG-Collection
+ * @description Angular resource to fetch /api/service/vsg/
+ **/
+ .service('vSG-Collection', ["$resource", function ($resource) {
+ return $resource('/api/service/vsg/');
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/vSG.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.vOLT-Collection
+ * @description Angular resource to fetch /api/tenant/cord/volt/:volt_id/
+ **/
+ .service('vOLT-Collection', ["$resource", function ($resource) {
+ return $resource('/api/tenant/cord/volt/:volt_id/', { volt_id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/vOLT.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Login
+ * @description Angular resource to fetch /api/utility/login/
+ **/
+ .service('Login', ["$resource", function ($resource) {
+ return $resource('/api/utility/login/');
+ }])
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Logout
+ * @description Angular resource to fetch /api/utility/logout/
+ **/
+ .service('Logout', ["$resource", function ($resource) {
+ return $resource('/api/utility/logout/');
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Utility.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Users
+ * @description Angular resource to fetch /api/core/users/:id/
+ **/
+ .service('Users', ["$resource", function ($resource) {
+ return $resource('/api/core/users/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Users.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Truckroll
+ * @description Angular resource to fetch /api/tenant/truckroll/:id/
+ **/
+ .service('Truckroll', ["$resource", function ($resource) {
+ return $resource('/api/tenant/truckroll/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Truckroll.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Tenant
+ * @description Angular resource to fetch /api/core/tenant/:id/
+ **/
+ .service('Tenants', ["$resource", function ($resource) {
+ return $resource('/api/core/tenants/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Tenant.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Subscribers
+ * @description Angular resource to fetch Subscribers
+ **/
+ .service('Subscribers', ["$resource", function ($resource) {
+ return $resource('/api/tenant/cord/subscriber/:id/', { id: '@id' }, {
+ update: { method: 'PUT' },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#View-a-Subscriber-Features-Detail
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * View-a-Subscriber-Features-Detail
+ **/
+ 'View-a-Subscriber-Features-Detail': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-uplink_speed
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-uplink_speed
+ **/
+ 'Read-Subscriber-uplink_speed': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/uplink_speed/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-uplink_speed
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-uplink_speed
+ **/
+ 'Update-Subscriber-uplink_speed': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/uplink_speed/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-downlink_speed
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-downlink_speed
+ **/
+ 'Read-Subscriber-downlink_speed': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/downlink_speed/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-downlink_speed
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-downlink_speed
+ **/
+ 'Update-Subscriber-downlink_speed': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/downlink_speed/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-cdn
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-cdn
+ **/
+ 'Read-Subscriber-cdn': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/cdn/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-cdn
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-cdn
+ **/
+ 'Update-Subscriber-cdn': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/cdn/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-uverse
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-uverse
+ **/
+ 'Read-Subscriber-uverse': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/uverse/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-uverse
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-uverse
+ **/
+ 'Update-Subscriber-uverse': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/uverse/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Read-Subscriber-status
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Read-Subscriber-status
+ **/
+ 'Read-Subscriber-status': {
+ method: 'GET',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/status/'
+ },
+ /**
+ * @ngdoc method
+ * @name xos.helpers.Subscribers#Update-Subscriber-status
+ * @methodOf xos.helpers.Subscribers
+ * @description
+ * Update-Subscriber-status
+ **/
+ 'Update-Subscriber-status': {
+ method: 'PUT',
+ isArray: false,
+ url: '/api/tenant/cord/subscriber/:id/features/status/'
+ }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Subscribers.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.SlicesPlus
+ * @description Angular resource to fetch /api/utility/slicesplus/
+ * This is a read-only API and only the `query` method is currently supported.
+ **/
+ .service('SlicesPlus', ["$http", "$q", function ($http, $q) {
+ this.query = function (params) {
+ var deferred = $q.defer();
+
+ $http.get('/api/utility/slicesplus/', { params: params }).then(function (res) {
+ deferred.resolve(res.data);
+ }).catch(function (res) {
+ deferred.reject(res.data);
+ });
+
+ return { $promise: deferred.promise };
+ };
+
+ this.get = function (id, params) {
+ var deferred = $q.defer();
+
+ $http.get('/api/utility/slicesplus/' + id, { params: params }).then(function (res) {
+ deferred.resolve(res.data);
+ }).catch(function (res) {
+ deferred.reject(res.data);
+ });
+ return { $promise: deferred.promise };
+ };
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Slices_plus.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Slices
+ * @description Angular resource to fetch /api/core/slices/:id/
+ **/
+ .service('Slices', ["$resource", function ($resource) {
+ return $resource('/api/core/slices/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Slices.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Sites
+ * @description Angular resource to fetch /api/core/sites/:id/
+ **/
+ .service('Sites', ["$resource", function ($resource) {
+ return $resource('/api/core/sites/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Sites.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Services
+ * @description Angular resource to fetch /api/core/services/:id/
+ **/
+ .service('Services', ["$resource", function ($resource) {
+ return $resource('/api/core/services/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Services.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.ONOS-Services-Collection
+ * @description Angular resource to fetch /api/service/onos/
+ **/
+ .service('ONOS-Services-Collection', ["$resource", function ($resource) {
+ return $resource('/api/service/onos/');
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/ONOS-Services.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.ONOS-App-Collection
+ * @description Angular resource to fetch /api/tenant/onos/app/
+ **/
+ .service('ONOS-App-Collection', ["$resource", function ($resource) {
+ return $resource('/api/tenant/onos/app/');
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/ONOS-Apps.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Nodes
+ * @description Angular resource to fetch /api/core/nodes/:id/
+ **/
+ .service('Nodes', ["$resource", function ($resource) {
+ return $resource('/api/core/nodes/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Nodes.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Networks
+ * @description Angular resource to fetch /api/core/networks/:id/
+ **/
+ .service('Networks', ["$resource", function ($resource) {
+ return $resource('/api/core/networks/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Networks.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Instances
+ * @description Angular resource to fetch /api/core/instances/:id/
+ **/
+ .service('Instances', ["$resource", function ($resource) {
+ return $resource('/api/core/instances/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Instances.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Flavors
+ * @description Angular resource to fetch /api/core/flavors/:id/
+ **/
+ .service('Flavors', ["$resource", function ($resource) {
+ return $resource('/api/core/flavors/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Flavors.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Example-Services-Collection
+ * @description Angular resource to fetch /api/service/exampleservice/
+ **/
+ .service('Example-Services-Collection', ["$resource", function ($resource) {
+ return $resource('/api/service/exampleservice/');
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Example.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers')
+ /**
+ * @ngdoc service
+ * @name xos.helpers.Deployments
+ * @description Angular resource to fetch /api/core/deployments/:id/
+ **/
+ .service('Deployments', ["$resource", function ($resource) {
+ return $resource('/api/core/deployments/:id/', { id: '@id' }, {
+ update: { method: 'PUT' }
+ });
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/rest/Deployments.js.map
+
+'use strict';
+
+(function () {
+
+ angular.module('xos.helpers')
+
+ /**
+ * @ngdoc service
+ * @name xos.helpers.XosUserPrefs
+ * @description
+ * This service is used to store the user preferences in cookies, so that they survive to page changes.
+ * The structure of the user preference is:
+ * ```
+ * {
+ * synchronizers: {
+ * notification: {
+ * 'volt': boolean,
+ * 'openstack': boolean,
+ * ...
+ * }
+ * }
+ * }
+ * ```
+ **/
+
+ .service('XosUserPrefs', ["$cookies", function ($cookies) {
+ var _this = this;
+
+ var userPrefs = $cookies.get('xosUserPrefs') ? JSON.parse($cookies.get('xosUserPrefs')) : {};
+
+ /**
+ * @ngdoc method
+ * @name xos.helpers.XosUserPrefs#getAll
+ * @methodOf xos.helpers.XosUserPrefs
+ * @description
+ * Return all the user preferences stored in cookies
+ * @returns {object} The user preferences
+ **/
+ this.getAll = function () {
+ userPrefs = $cookies.get('xosUserPrefs') ? JSON.parse($cookies.get('xosUserPrefs')) : {};
+ return userPrefs;
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.helpers.XosUserPrefs#setAll
+ * @methodOf xos.helpers.XosUserPrefs
+ * @description
+ * Override all user preferences
+ * @param {object} prefs The user preferences
+ **/
+ this.setAll = function (prefs) {
+ $cookies.put('xosUserPrefs', JSON.stringify(prefs));
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.helpers.XosUserPrefs#getSynchronizerNotificationStatus
+ * @methodOf xos.helpers.XosUserPrefs
+ * @description
+ * Return the synchronizer notification status, if name is not provided return the status for all synchronizers
+ * @param {string=} prefs The synchronizer name
+ * @returns {object | string} The synchronizer status
+ **/
+ this.getSynchronizerNotificationStatus = function () {
+ var name = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0];
+
+ if (name) {
+ return _this.getAll().synchronizers.notification[name];
+ }
+ return _this.getAll().synchronizers.notification;
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.helpers.XosUserPrefs#setSynchronizerNotificationStatus
+ * @methodOf xos.helpers.XosUserPrefs
+ * @description
+ * Update the notification status for a single synchronizer
+ * @param {string} name The synchronizer name
+ * @param {boolean} value The notification status (true means that it has been sent)
+ **/
+ this.setSynchronizerNotificationStatus = function () {
+ var name = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0];
+ var value = arguments[1];
+
+ if (!name) {
+ throw new Error('[XosUserPrefs] When updating a synchronizer is mandatory to provide a name.');
+ }
+
+ var cookies = _this.getAll();
+
+ if (!cookies.synchronizers) {
+ cookies.synchronizers = {
+ notification: {}
+ };
+ }
+
+ cookies.synchronizers.notification[name] = value;
+ _this.setAll(cookies);
+ };
+ }]);
+})();
+//# sourceMappingURL=../../maps/services/helpers/user-prefs.service.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ /**
+ * @ngdoc service
+ * @name xos.helpers.ServiceGraph
+ * @description This factory define a set of helper function to query the service tenancy graph
+ **/
+
+ angular.module('xos.helpers').service('GraphService', ["$q", "Tenants", "Services", function ($q, Tenants, Services) {
+ var _this = this;
+
+ this.loadCoarseData = function () {
+
+ var services = void 0;
+
+ var deferred = $q.defer();
+
+ Services.query().$promise.then(function (res) {
+ services = res;
+ return Tenants.query({ kind: 'coarse' }).$promise;
+ }).then(function (tenants) {
+ deferred.resolve({
+ tenants: tenants,
+ services: services
+ });
+ });
+
+ return deferred.promise;
+ };
+
+ this.getCoarseGraph = function () {
+ _this.loadCoarseData().then(function (res) {
+ console.log(res);
+ });
+ return 'ciao';
+ };
+ }]);
+})();
+//# sourceMappingURL=../maps/services/service_graph.service.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.helpers').factory('Notification', function () {
+ return window.Notification;
+ })
+ /**
+ * @ngdoc service
+ * @name xos.helpers.xosNotification
+ * @description This factory define a set of helper function to trigger desktop notification
+ **/
+ .service('xosNotification', ["$q", "$log", "Notification", function ($q, $log, Notification) {
+ var _this = this;
+
+ this.checkPermission = function () {
+ var deferred = $q.defer();
+ Notification.requestPermission().then(function (permission) {
+ if (permission === 'granted') {
+ deferred.resolve(permission);
+ } else {
+ deferred.reject(permission);
+ }
+ });
+ return deferred.promise;
+ };
+
+ this.sendNotification = function (title, options) {
+ var notification = new Notification(title, options);
+ notification.onerror = function (err) {
+ $log.error(err);
+ };
+ };
+
+ /**
+ * @ngdoc method
+ * @name xos.helpers.xosNotification#notify
+ * @methodOf xos.helpers.xosNotification
+ * @description
+ * This method will check for user permission and if granted will send a browser notification.
+ * @param {string} title The notification title
+ * @param {object} options The notification options: `{icon: 'url', body: 'Notification body'}`
+ **/
+
+ this.notify = function (title, options) {
+ if (!('Notification' in window)) {
+ $log.info('This browser does not support desktop notification');
+ } else if (Notification.permission !== 'granted') {
+ _this.checkPermission().then(function () {
+ return _this.sendNotification(title, options);
+ });
+ } else if (Notification.permission === 'granted') {
+ _this.sendNotification(title, options);
+ }
+ };
+ }]);
+})();
+//# sourceMappingURL=../maps/services/notification.service.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ /**
+ * @ngdoc service
+ * @name xos.helpers.NoHyperlinks
+ * @description This factory is automatically loaded trough xos.helpers and will add an $http interceptor that will add ?no_hyperlinks=1 to your api request, that is required by django
+ **/
+
+ angular.module('xos.helpers').factory('NoHyperlinks', noHyperlinks);
+
+ function noHyperlinks() {
+ return {
+ request: function request(_request) {
+ if (_request.url.indexOf('.html') === -1) {
+ _request.url += '?no_hyperlinks=1';
+ }
+ return _request;
+ }
+ };
+ }
+})();
+//# sourceMappingURL=../maps/services/noHyperlinks.interceptor.js.map
+
+'use strict';
+
+// TODO write tests for log
+
+angular.module('xos.helpers').config(['$provide', function ($provide) {
+ // Use the `decorator` solution to substitute or attach behaviors to
+ // original service instance; @see angular-mocks for more examples....
+
+ $provide.decorator('$log', ['$delegate', function ($delegate) {
+
+ var isLogEnabled = function isLogEnabled() {
+ return window.location.href.indexOf('debug=true') >= 0;
+ };
+ // Save the original $log.debug()
+ var logFn = $delegate.log;
+ var infoFn = $delegate.info;
+ var warnFn = $delegate.warn;
+ var errorFn = $delegate.error;
+ var debugFn = $delegate.debug;
+
+ // create the replacement function
+ var replacement = function replacement(fn) {
+ return function () {
+ // console.log(`Is Log Enabled: ${isLogEnabled()}`)
+ if (!isLogEnabled()) {
+ // console.log('logging is disabled');
+ return;
+ }
+ var args = [].slice.call(arguments);
+ var now = new Date();
+
+ // Prepend timestamp
+ args[0] = '[' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds() + '] ' + args[0];
+
+ // HACK awfull fix for angular mock implementation whithin jasmine test failing issue
+ if (typeof $delegate.reset === 'function' && !($delegate.debug.logs instanceof Array)) {
+ // if we are within the mock and did not reset yet, we call it to avoid issue
+ // console.log('mock log impl fix to avoid logs array not existing...');
+ $delegate.reset();
+ }
+
+ // Call the original with the output prepended with formatted timestamp
+ fn.apply(null, args);
+ };
+ };
+
+ $delegate.info = replacement(infoFn);
+ $delegate.log = replacement(logFn);
+ $delegate.warn = replacement(warnFn);
+ $delegate.error = replacement(errorFn);
+ $delegate.debug = replacement(debugFn);
+
+ return $delegate;
+ }]);
+}]);
+//# sourceMappingURL=../maps/services/log.decorator.js.map
+
+'use strict';
+
+(function () {
+ 'use strict';
+
+ /**
+ * @ngdoc service
+ * @name xos.helpers.SetCSRFToken
+ * @description This factory is automatically loaded trough xos.helpers and will add an $http interceptor that will the CSRF-Token to your request headers
+ **/
+
+ setCSRFToken.$inject = ["$cookies"];
+ angular.module('xos.helpers').factory('SetCSRFToken', setCSRFToken);
+
+ function setCSRFToken($cookies) {
+ return {
+ request: function request(_request) {
+ if (_request.method !== 'GET') {
+ _request.headers['X-CSRFToken'] = $cookies.get('xoscsrftoken');
+ }
+ return _request;
+ }
+ };
+ }
+})();
+//# sourceMappingURL=../maps/services/csrfToken.interceptor.js.map
diff --git a/xos/core/xoslib/static/js/xoslib/xos-backbone.js b/xos/core/xoslib/static/js/xoslib/xos-backbone.js
index 04d5fb7..d22d0ec 100644
--- a/xos/core/xoslib/static/js/xoslib/xos-backbone.js
+++ b/xos/core/xoslib/static/js/xoslib/xos-backbone.js
@@ -827,7 +827,7 @@
define_model(this, {urlRoot: CORDSUBSCRIBER_API,
modelName: "cordSubscriber",
relatedCollections: {"cordUsers": "subscriber"},
- listFields: ["id", "service_specific_id", "vlan_id", "routeable_subnet"],
+ listFields: ["id", "service_specific_id", "routeable_subnet"],
detailFields: ["id", "service_specific_id", "vcpe_id", "image_name", "instance_name",
"firewall_enable", "firewall_rules", "url_filter_enable", "url_filter_rules", "cdn_enable",
"nat_ip", "lan_ip", "wan_ip", "private_ip",
diff --git a/xos/services/cord/admin.py b/xos/services/cord/admin.py
index e7704d3..18f1e81 100644
--- a/xos/services/cord/admin.py
+++ b/xos/services/cord/admin.py
@@ -94,6 +94,59 @@
def queryset(self, request):
return VOLTTenant.get_tenant_objects_by_user(request.user)
+class AccessDeviceInline(XOSTabularInline):
+ model = AccessDevice
+ fields = ['volt_device','uplink','vlan']
+ readonly_fields = []
+ extra = 0
+# max_num = 0
+ suit_classes = 'suit-tab suit-tab-accessdevices'
+
+# @property
+# def selflink_reverse_path(self):
+# return "admin:cord_volttenant_change"
+
+class VOLTDeviceAdmin(ReadOnlyAwareAdmin):
+ list_display = ('backend_status_icon', 'name', 'openflow_id', 'driver' )
+ list_display_links = ('backend_status_icon', 'name', 'openflow_id')
+ fieldsets = [ (None, {'fields': ['backend_status_text','name','volt_service','openflow_id','driver','access_agent'],
+ 'classes':['suit-tab suit-tab-general']})]
+ readonly_fields = ('backend_status_text',)
+ inlines = [AccessDeviceInline]
+
+ suit_form_tabs = (('general','Details'), ('accessdevices','Access Devices'))
+
+class AccessDeviceAdmin(ReadOnlyAwareAdmin):
+ list_display = ('backend_status_icon', 'id', 'volt_device', 'uplink', 'vlan' )
+ list_display_links = ('backend_status_icon', 'id')
+ fieldsets = [ (None, {'fields': ['backend_status_text','volt_device','uplink','vlan'],
+ 'classes':['suit-tab suit-tab-general']})]
+ readonly_fields = ('backend_status_text',)
+
+ suit_form_tabs = (('general','Details'),)
+
+class AgentPortMappingInline(XOSTabularInline):
+ model = AgentPortMapping
+ fields = ['access_agent', 'mac', 'port']
+ readonly_fields = []
+ extra = 0
+# max_num = 0
+ suit_classes = 'suit-tab suit-tab-accessportmaps'
+
+# @property
+# def selflink_reverse_path(self):
+# return "admin:cord_volttenant_change"
+
+class AccessAgentAdmin(ReadOnlyAwareAdmin):
+ list_display = ('backend_status_icon', 'name', 'mac' )
+ list_display_links = ('backend_status_icon', 'name')
+ fieldsets = [ (None, {'fields': ['backend_status_text','name','volt_service','mac'],
+ 'classes':['suit-tab suit-tab-general']})]
+ readonly_fields = ('backend_status_text',)
+ inlines= [AgentPortMappingInline]
+
+ suit_form_tabs = (('general','Details'), ('accessportmaps', 'Port Mappings'))
+
#-----------------------------------------------------------------------------
# vCPE
#-----------------------------------------------------------------------------
@@ -398,6 +451,10 @@
admin.site.register(VOLTService, VOLTServiceAdmin)
admin.site.register(VOLTTenant, VOLTTenantAdmin)
+admin.site.register(VOLTDevice, VOLTDeviceAdmin)
+admin.site.register(AccessDevice, AccessDeviceAdmin)
+admin.site.register(AccessAgent, AccessAgentAdmin)
+
admin.site.register(VSGService, VSGServiceAdmin)
admin.site.register(VSGTenant, VSGTenantAdmin)
admin.site.register(VBNGService, VBNGServiceAdmin)
diff --git a/xos/services/cord/models.py b/xos/services/cord/models.py
index 48c9597..19b3ba6 100644
--- a/xos/services/cord/models.py
+++ b/xos/services/cord/models.py
@@ -1,5 +1,5 @@
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 import Service, PlCoreBase, Slice, Instance, Tenant, TenantWithContainer, Node, Image, User, Flavor, Subscriber, NetworkParameter, NetworkParameterType, Port, AddressPool, User
from core.models.plcorebase import StrippedCharField
import os
from django.db import models, transaction
@@ -188,15 +188,20 @@
class Meta:
app_label = "cord"
verbose_name = "vOLT Service"
- proxy = True
class VOLTTenant(Tenant):
- class Meta:
- proxy = True
-
KIND = VOLT_KIND
- default_attributes = {"vlan_id": None, "s_tag": None, "c_tag": None}
+ class Meta:
+ app_label = "cord"
+ verbose_name = "vOLT Tenant"
+
+ s_tag = models.IntegerField(null=True, blank=True, help_text="s-tag")
+ c_tag = models.IntegerField(null=True, blank=True, help_text="c-tag")
+
+ # at some point, this should probably end up part of Tenant.
+ creator = models.ForeignKey(User, related_name='created_volts', blank=True, null=True)
+
def __init__(self, *args, **kwargs):
volt_services = VOLTService.get_service_objects().all()
if volt_services:
@@ -205,32 +210,6 @@
self.cached_vcpe = None
@property
- def s_tag(self):
- return self.get_attribute("s_tag", self.default_attributes["s_tag"])
-
- @s_tag.setter
- def s_tag(self, value):
- self.set_attribute("s_tag", value)
-
- @property
- def c_tag(self):
- return self.get_attribute("c_tag", self.default_attributes["c_tag"])
-
- @c_tag.setter
- def c_tag(self, value):
- self.set_attribute("c_tag", value)
-
- # for now, vlan_id is a synonym for c_tag
-
- @property
- def vlan_id(self):
- return self.c_tag
-
- @vlan_id.setter
- def vlan_id(self, value):
- self.c_tag = value
-
- @property
def vcpe(self):
vcpe = self.get_newest_subscribed_tenant(VSGTenant)
if not vcpe:
@@ -257,28 +236,6 @@
return None
return subs[0]
- @property
- def creator(self):
- if getattr(self, "cached_creator", None):
- return self.cached_creator
- creator_id=self.get_attribute("creator_id")
- if not creator_id:
- return None
- users=User.objects.filter(id=creator_id)
- if not users:
- return None
- user=users[0]
- self.cached_creator = users[0]
- return user
-
- @creator.setter
- def creator(self, value):
- if value:
- value = value.id
- if (value != self.get_attribute("creator_id", None)):
- self.cached_creator=None
- self.set_attribute("creator_id", value)
-
def manage_vcpe(self):
# Each VOLT object owns exactly one VCPE object
@@ -347,9 +304,6 @@
super(VOLTTenant, self).save(*args, **kwargs)
model_policy_volt(self.pk)
- #self.manage_vcpe()
- #self.manage_subscriber()
- #self.cleanup_orphans()
def delete(self, *args, **kwargs):
self.cleanup_vcpe()
@@ -366,6 +320,49 @@
volt.manage_subscriber()
volt.cleanup_orphans()
+class VOLTDevice(PlCoreBase):
+ class Meta:
+ app_label = "cord"
+
+ name = models.CharField(max_length=254, help_text="name of device", null=False, blank=False)
+ volt_service = models.ForeignKey(VOLTService, related_name='volt_devices')
+ openflow_id = models.CharField(max_length=254, help_text="OpenFlow ID", null=True, blank=True)
+ driver = models.CharField(max_length=254, help_text="driver", null=True, blank=True)
+ access_agent = models.ForeignKey("AccessAgent", related_name='volt_devices', blank=True, null=True)
+
+ def __unicode__(self): return u'%s' % (self.name)
+
+class AccessDevice(PlCoreBase):
+ class Meta:
+ app_label = "cord"
+
+ volt_device = models.ForeignKey(VOLTDevice, related_name='access_devices')
+ uplink = models.IntegerField(null=True, blank=True)
+ vlan = models.IntegerField(null=True, blank=True)
+
+ def __unicode__(self): return u'%s-%d:%d' % (self.volt_device.name,self.uplink,self.vlan)
+
+class AccessAgent(PlCoreBase):
+ class Meta:
+ app_label = "cord"
+
+ name = models.CharField(max_length=254, help_text="name of agent", null=False, blank=False)
+ volt_service = models.ForeignKey(VOLTService, related_name='access_agents')
+ mac = models.CharField(max_length=32, help_text="MAC Address or Access Agent", null=True, blank=True)
+
+ def __unicode__(self): return u'%s' % (self.name)
+
+class AgentPortMapping(PlCoreBase):
+ class Meta:
+ app_label = "cord"
+
+ access_agent = models.ForeignKey(AccessAgent, related_name='port_mappings')
+ mac = models.CharField(max_length=32, help_text="MAC Address", null=True, blank=True)
+ port = models.CharField(max_length=32, help_text="Openflow port ID", null=True, blank=True)
+
+ def __unicode__(self): return u'%s-%s-%s' % (self.access_agent.name, self.port, self.mac)
+
+
# -------------------------------------------
# VCPE
# -------------------------------------------
diff --git a/xos/services/cord/templates/voltadmin.html b/xos/services/cord/templates/voltadmin.html
index e6887c5..807ab2c 100644
--- a/xos/services/cord/templates/voltadmin.html
+++ b/xos/services/cord/templates/voltadmin.html
@@ -1,6 +1,10 @@
<div class = "row text-center">
<div class="col-xs-12">
<a href="/admin/cord/volttenant/">vOLT Tenants</a>
+ </div><div class="col-xs-12">
+ <a href="/admin/cord/voltdevice/">vOLT Devices</a>
+ </div><div class="col-xs-12">
+ <a href="/admin/cord/accessagent/">vOLT Access Agents</a>
</div>
</div>
diff --git a/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.yaml b/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.yaml
index 1c4da12..ca72c5f 100644
--- a/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.yaml
+++ b/xos/synchronizers/monitoring_channel/steps/sync_monitoringchannel.yaml
@@ -66,13 +66,21 @@
# dest=/etc/resolv.conf
{% endif %}
+# FIXME: Temporary workaround to delete the monitoring-channel_ceilometer_proxy_config file always
+# to trigger ansible notify handlers in the following task.
+# Due to some issue, ansible "changed" flag is set to false even though there is a change of
+# ceilometer configuration file, because of which the configuration change is not reflecting in
+# ceilometer containers
+# - file: path=/usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config state=absent
+
- name: ceilometer proxy config
template: src=/opt/xos/synchronizers/monitoring_channel/templates/ceilometer_proxy_config.j2 dest=/usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config mode=0777
notify:
-# - restart monitoring-channel
- - stop monitoring-channel
- - remove container
- - start monitoring-channel
+ - copy ceilo-config-file
+ - restart monitoring-channel container
+# - stop monitoring-channel
+# - remove container
+# - start monitoring-channel
- name: Monitoring channel upstart
template: src=/opt/xos/synchronizers/monitoring_channel/templates/monitoring-channel.conf.j2 dest=/etc/init/monitoring-channel-{{ unique_id }}.conf
@@ -118,6 +126,12 @@
{% endif %}
handlers:
+ - name: copy ceilo-config-file
+ shell: docker cp /usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config monitoring-channel-{{ unique_id }}:/usr/local/share/ceilometer_proxy_config
+
+ - name: restart monitoring-channel container
+ shell: docker restart monitoring-channel-{{ unique_id }}
+
- name: restart monitoring-channel
shell: service monitoring-channel-{{ unique_id }} stop; sleep 1; service monitoring-channel-{{ unique_id }} start
diff --git a/xos/synchronizers/monitoring_channel/templates/start-monitoring-channel.sh.j2 b/xos/synchronizers/monitoring_channel/templates/start-monitoring-channel.sh.j2
index 1685e07..4486985 100755
--- a/xos/synchronizers/monitoring_channel/templates/start-monitoring-channel.sh.j2
+++ b/xos/synchronizers/monitoring_channel/templates/start-monitoring-channel.sh.j2
@@ -24,9 +24,11 @@
#sudo docker pull srikanthvavila/monitoring-channel
if [ -z "$HEADNODEFLATLANIP" ] || [ "$HEADNODEFLATLANIP" == "None" ]
then
- docker run -d --name=$MONITORING_CHANNEL --privileged=true -p $HOST_FORWARDING_PORT_FOR_CEILOMETER:8000 -v /usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config:/usr/local/share/ceilometer_proxy_config srikanthvavila/monitoring-channel
+# docker run -d --name=$MONITORING_CHANNEL --privileged=true -p $HOST_FORWARDING_PORT_FOR_CEILOMETER:8000 -v /usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config:/usr/local/share/ceilometer_proxy_config srikanthvavila/monitoring-channel
+ docker run -d --name=$MONITORING_CHANNEL --privileged=true -p $HOST_FORWARDING_PORT_FOR_CEILOMETER:8000 srikanthvavila/monitoring-channel
else
- docker run -d --name=$MONITORING_CHANNEL --add-host="ctl:$HEADNODEFLATLANIP" --privileged=true -p $HOST_FORWARDING_PORT_FOR_CEILOMETER:8000 -v /usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config:/usr/local/share/ceilometer_proxy_config srikanthvavila/monitoring-channel
+# docker run -d --name=$MONITORING_CHANNEL --add-host="ctl:$HEADNODEFLATLANIP" --privileged=true -p $HOST_FORWARDING_PORT_FOR_CEILOMETER:8000 -v /usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config:/usr/local/share/ceilometer_proxy_config srikanthvavila/monitoring-channel
+ docker run -d --name=$MONITORING_CHANNEL --add-host="ctl:$HEADNODEFLATLANIP" --privileged=true -p $HOST_FORWARDING_PORT_FOR_CEILOMETER:8000 srikanthvavila/monitoring-channel
fi
else
docker start $MONITORING_CHANNEL
@@ -41,6 +43,7 @@
# Now copy ceilometer proxy configuration to container
#cat /usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config | sudo docker exec -i $MONITORING_CHANNEL bash -c 'cat > /usr/local/share/ceilometer_proxy_config'
+docker cp /usr/local/share/monitoring-channel-{{ unique_id }}_ceilometer_proxy_config $MONITORING_CHANNEL:/usr/local/share/ceilometer_proxy_config
# Attach to container
-#docker start -a $MONITORING_CHANNEL
+docker start -a $MONITORING_CHANNEL
diff --git a/xos/synchronizers/onos/steps/sync_onosapp.py b/xos/synchronizers/onos/steps/sync_onosapp.py
index 64c9452..3a9abfc 100644
--- a/xos/synchronizers/onos/steps/sync_onosapp.py
+++ b/xos/synchronizers/onos/steps/sync_onosapp.py
@@ -18,6 +18,7 @@
from xos.logger import Logger, logging
from services.vrouter.models import VRouterService
from services.vtn.models import VTNService
+from services.cord.models import VOLTService, VOLTDevice, AccessDevice
# hpclibrary will be in steps/..
parentdir = os.path.join(os.path.dirname(__file__),"..")
@@ -243,18 +244,44 @@
return json.dumps(data, indent=4, sort_keys=True)
def get_volt_network_config(self, o, attrs):
- data = {
- "devices" : {
- "of:1000000000000001" : {
- "accessDevice" : {
- "uplink" : "2",
- "vlan" : "222",
- },
- "basic" : {
- "driver" : "pmc-olt"
- }
+ try:
+ volt = VOLTService.get_service_objects().all()[0]
+ except:
+ return None
+
+ devices = []
+ for voltdev in volt.volt_devices.all():
+ access_devices = []
+ for access in voltdev.access_devices.all():
+ access_device = {
+ "uplink" : access.uplink,
+ "vlan" : access.vlan
+ }
+ access_devices.append(access_device)
+
+ if voltdev.access_agent:
+ agent = voltdev.access_agent
+ olts = {}
+ for port_mapping in agent.port_mappings.all():
+ olts[port_mapping.port] = port_mapping.mac
+ agent_config = {
+ "olts" : olts,
+ "mac" : agent.mac
+ }
+
+ device = {
+ voltdev.openflow_id : {
+ "accessDevice" : access_devices,
+ "accessAgent" : agent_config
+ },
+ "basic" : {
+ "driver" : voltdev.driver
}
}
+ devices.append(device)
+
+ data = {
+ "devices" : devices
}
return json.dumps(data, indent=4, sort_keys=True)
diff --git a/xos/synchronizers/vcpe/steps/sync_vcpetenant.py b/xos/synchronizers/vcpe/steps/sync_vcpetenant.py
index 9e3dfac..d8bc525 100644
--- a/xos/synchronizers/vcpe/steps/sync_vcpetenant.py
+++ b/xos/synchronizers/vcpe/steps/sync_vcpetenant.py
@@ -130,11 +130,9 @@
else:
logger.info("neither bbs_slice nor bbs_server is configured in the vCPE",extra=o.tologdict())
- vlan_ids = []
s_tags = []
c_tags = []
if o.volt:
- vlan_ids.append(o.volt.vlan_id) # XXX remove this
s_tags.append(o.volt.s_tag)
c_tags.append(o.volt.c_tag)
@@ -146,15 +144,14 @@
safe_macs=[]
if vcpe_service.url_filter_kind == "safebrowsing":
if o.volt and o.volt.subscriber:
- for user in o.volt.subscriber.users:
+ for user in o.volt.subscriber.devices:
level = user.get("level",None)
mac = user.get("mac",None)
if level in ["G", "PG"]:
if mac:
safe_macs.append(mac)
- fields = {"vlan_ids": vlan_ids, # XXX remove this
- "s_tags": s_tags,
+ fields = {"s_tags": s_tags,
"c_tags": c_tags,
"dnsdemux_ip": dnsdemux_ip,
"cdn_prefixes": cdn_prefixes,
@@ -199,7 +196,7 @@
if o.volt and o.volt.subscriber:
url_filter_enable = o.volt.subscriber.url_filter_enable
url_filter_level = o.volt.subscriber.url_filter_level
- url_filter_users = o.volt.subscriber.users
+ url_filter_users = o.volt.subscriber.devices
if service.url_filter_kind == "broadbandshield":
# disable url_filter if there are no bbs_addrs
diff --git a/xos/synchronizers/vcpe/steps/sync_vcpetenant.yaml b/xos/synchronizers/vcpe/steps/sync_vcpetenant.yaml
index 3823328..9be0f98 100644
--- a/xos/synchronizers/vcpe/steps/sync_vcpetenant.yaml
+++ b/xos/synchronizers/vcpe/steps/sync_vcpetenant.yaml
@@ -9,10 +9,6 @@
dnsdemux_ip: {{ dnsdemux_ip }}
firewall_enable: {{ firewall_enable }}
url_filter_enable: {{ url_filter_enable }}
- vlan_ids:
- {% for vlan_id in vlan_ids %}
- - {{ vlan_id }}
- {% endfor %}
c_tags:
{% for c_tag in c_tags %}
- {{ c_tag }}
diff --git a/xos/synchronizers/vcpe/steps/sync_vcpetenant_new.yaml b/xos/synchronizers/vcpe/steps/sync_vcpetenant_new.yaml
index 324e274..435b721 100644
--- a/xos/synchronizers/vcpe/steps/sync_vcpetenant_new.yaml
+++ b/xos/synchronizers/vcpe/steps/sync_vcpetenant_new.yaml
@@ -10,10 +10,6 @@
dnsdemux_ip: {{ dnsdemux_ip }}
firewall_enable: {{ firewall_enable }}
url_filter_enable: {{ url_filter_enable }}
- vlan_ids:
- {% for vlan_id in vlan_ids %}
- - {{ vlan_id }}
- {% endfor %}
c_tags:
{% for c_tag in c_tags %}
- {{ c_tag }}
diff --git a/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml b/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml
index 09e4d23..b24d15a 100644
--- a/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml
+++ b/xos/synchronizers/vcpe/steps/sync_vcpetenant_vtn.yaml
@@ -10,10 +10,6 @@
dnsdemux_ip: {{ dnsdemux_ip }}
firewall_enable: {{ firewall_enable }}
url_filter_enable: {{ url_filter_enable }}
- vlan_ids:
- {% for vlan_id in vlan_ids %}
- - {{ vlan_id }}
- {% endfor %}
c_tags:
{% for c_tag in c_tags %}
- {{ c_tag }}
diff --git a/xos/synchronizers/vcpe/templates/vlan_sample.j2 b/xos/synchronizers/vcpe/templates/vlan_sample.j2
index a26c840..b73954b 100644
--- a/xos/synchronizers/vcpe/templates/vlan_sample.j2
+++ b/xos/synchronizers/vcpe/templates/vlan_sample.j2
@@ -1,5 +1,5 @@
# below is a list of all vlan_ids associated with this vcpe
-{% for vlan_id in vlan_ids %}
+{% for vlan_id in c_tags %}
{{ vlan_id }}
{% endfor %}
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 473e313..109fc1d 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -309,6 +309,16 @@
required: true
description: MAC address for this device.
+ tosca.nodes.VOLTService:
+ derived_from: tosca.nodes.Root
+ description: >
+ CORD: The vOLT Service
+ capabilities:
+ xos_base_service_caps
+ properties:
+ xos_base_props
+ xos_base_service_props
+
tosca.nodes.VOLTTenant:
derived_from: tosca.nodes.Root
description: >
@@ -325,6 +335,58 @@
required: false
description: c_tag, identifies which subscriber within s_tag
+ tosca.nodes.VOLTDevice:
+ derived_from: tosca.nodes.Root
+ description: >
+ CORD: A vOLT Device.
+ properties:
+ xos_base_props
+ openflow_id:
+ type: string
+ required: false
+ description: openflow id
+ driver:
+ type: string
+ required: false
+ description: driver name
+ access_devices:
+ type: string
+ required: false
+ description: list of access devices, in format "uplink vlan", multiple entries separated by commas
+
+# XXX - uncomment if we want access device to be specified as separate Tosca
+# objects, rather than encoding them into VOLTDevice.access_devices
+# tosca.nodes.AccessDevice:
+# derived_from: tosca.nodes.Root
+# description: >
+# CORD: A vOLT Access Device.
+# properties:
+# xos_base_props
+# uplink:
+# type: integer
+# required: false
+# description: uplink
+# vlan:
+# type: integer
+# required: false
+# description: vlan
+
+ tosca.nodes.AccessAgent:
+ derived_from: tosca.nodes.Root
+ description: >
+ CORD: A vOLT Access Agent.
+ properties:
+ xos_base_props
+ mac:
+ type: string
+ required: false
+ description: mac address
+ port_mappings:
+ type: string
+ required: false
+ description: list of port mappings, in format "port mac", multiple entries separated by commas
+
+
tosca.nodes.User:
derived_from: tosca.nodes.Root
@@ -966,6 +1028,12 @@
tosca.relationships.TagsObject:
derived_from: tosca.relationships.Root
+ tosca.relationships.MemberOfDevice:
+ derived_from: tosca.relationships.Root
+
+ tosca.relationships.UsesAgent:
+ 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 67d5367..8b4c669 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -766,6 +766,64 @@
required: true
description: MAC address for this device.
+ tosca.nodes.VOLTService:
+ derived_from: tosca.nodes.Root
+ description: >
+ CORD: The vOLT Service
+ capabilities:
+ scalable:
+ type: tosca.capabilities.Scalable
+ service:
+ type: tosca.capabilities.xos.Service
+ properties:
+ no-delete:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to delete this object
+ no-create:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to create this object
+ no-update:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
+ 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.
+
tosca.nodes.VOLTTenant:
derived_from: tosca.nodes.Root
description: >
@@ -789,6 +847,86 @@
required: false
description: c_tag, identifies which subscriber within s_tag
+ tosca.nodes.VOLTDevice:
+ derived_from: tosca.nodes.Root
+ description: >
+ CORD: A vOLT Device.
+ properties:
+ no-delete:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to delete this object
+ no-create:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to create this object
+ no-update:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
+ openflow_id:
+ type: string
+ required: false
+ description: openflow id
+ driver:
+ type: string
+ required: false
+ description: driver name
+ access_devices:
+ type: string
+ required: false
+ description: list of access devices, in format "uplink vlan", multiple entries separated by commas
+
+# tosca.nodes.AccessDevice:
+# derived_from: tosca.nodes.Root
+# description: >
+# CORD: A vOLT Access Device.
+# properties:
+# xos_base_props
+# uplink:
+# type: integer
+# required: false
+# description: uplink
+# vlan:
+# type: integer
+# required: false
+# description: vlan
+
+ tosca.nodes.AccessAgent:
+ derived_from: tosca.nodes.Root
+ description: >
+ CORD: A vOLT Access Agent.
+ properties:
+ no-delete:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to delete this object
+ no-create:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to create this object
+ no-update:
+ type: boolean
+ default: false
+ description: Do not allow Tosca to update this object
+ replaces:
+ type: string
+ required: false
+ descrption: Replaces/renames this object
+ mac:
+ type: string
+ required: false
+ description: mac address
+ port_mappings:
+ type: string
+ required: false
+ description: list of port mappings, in format "port mac", multiple entries separated by commas
+
+
tosca.nodes.User:
derived_from: tosca.nodes.Root
@@ -1715,6 +1853,9 @@
tosca.relationships.TagsObject:
derived_from: tosca.relationships.Root
+ tosca.relationships.MemberOfDevice:
+ derived_from: tosca.relationships.Root
+
tosca.capabilities.xos.Service:
derived_from: tosca.capabilities.Root
description: An XOS Service
diff --git a/xos/tosca/resources/accessagent.py b/xos/tosca/resources/accessagent.py
new file mode 100644
index 0000000..368ce55
--- /dev/null
+++ b/xos/tosca/resources/accessagent.py
@@ -0,0 +1,56 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from services.cord.models import AccessAgent, VOLTDevice, VOLTService, AgentPortMapping
+from xosresource import XOSResource
+
+class XOSAccessAgent(XOSResource):
+ provides = "tosca.nodes.AccessAgent"
+ xos_model = AccessAgent
+ copyin_props = ["mac"]
+
+ def get_xos_args(self, throw_exception=True):
+ args = super(XOSAccessAgent, self).get_xos_args()
+
+ volt_service_name = self.get_requirement("tosca.relationships.MemberOfService", throw_exception=throw_exception)
+ if volt_service_name:
+ args["volt_service"] = self.get_xos_object(VOLTService, throw_exception=throw_exception, name=volt_service_name)
+
+ return args
+
+ def postprocess(self, obj):
+ # For convenient, allow the port mappings to be specified by a Tosca
+ # string with commas between lines.
+ # <port> <mac>,
+ # <port> <mac>,
+ # ...
+ # <port> <mac>
+
+ port_mappings_str = self.get_property("port_mappings")
+ port_mappings = []
+ if port_mappings_str:
+ lines = [x.strip() for x in port_mappings_str.split(",")]
+ for line in lines:
+ if not (" " in line):
+ raise "Malformed port mapping `%s`", line
+ (port, mac) = line.split(" ")
+ port=port.strip()
+ mac=mac.strip()
+ port_mappings.append( (port, mac) )
+
+ for apm in list(AgentPortMapping.objects.filter(access_agent=obj)):
+ if (apm.port, apm.mac) not in port_mappings:
+ print "Deleting AgentPortMapping '%s'" % apm
+ apm.delete()
+
+ for port_mapping in port_mappings:
+ existing_objs = AgentPortMapping.objects.filter(access_agent=obj, port=port_mapping[0], mac=port_mapping[1])
+ if not existing_objs:
+ apm = AgentPortMapping(access_agent=obj, port=port_mapping[0], mac=port_mapping[1])
+ apm.save()
+ print "Created AgentPortMapping '%s'" % apm
+
diff --git a/xos/tosca/resources/accessdevice.py b/xos/tosca/resources/accessdevice.py
new file mode 100644
index 0000000..94deb86
--- /dev/null
+++ b/xos/tosca/resources/accessdevice.py
@@ -0,0 +1,40 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from services.cord.models import AccessDevice, VOLTDevice
+from xosresource import XOSResource
+
+class XOSAccessDevice(XOSResource):
+ provides = "tosca.nodes.AccessDevice"
+ xos_model = AccessDevice
+ copyin_props = ["uplink", "vlan"]
+ name_field = None
+
+ def get_xos_args(self, throw_exception=True):
+ args = super(XOSAccessDevice, self).get_xos_args()
+
+ volt_device_name = self.get_requirement("tosca.relationships.MemberOfDevice", throw_exception=throw_exception)
+ if volt_device_name:
+ args["volt_device"] = self.get_xos_object(VOLTDevice, throw_exception=throw_exception, name=volt_device_name)
+
+ return args
+
+ # AccessDevice has no name field, so we rely on matching the keys. We assume
+ # the for a given VOLTDevice, there is only one AccessDevice per (uplink, vlan)
+ # pair.
+
+ def get_existing_objs(self):
+ args = self.get_xos_args(throw_exception=False)
+ volt_device = args.get("volt_device", None)
+ uplink = args.get("uplink", None)
+ vlan = args.get("vlan", None)
+ if (volt_device is not None) and (uplink is not None) and (vlan is not None):
+ existing_obj = self.get_xos_object(AccessDevice, volt_device=volt_device, uplink=uplink, vlan=vlan, throw_exception=False)
+ if existing_obj:
+ return [ existing_obj ]
+ return []
+
diff --git a/xos/tosca/resources/voltdevice.py b/xos/tosca/resources/voltdevice.py
new file mode 100644
index 0000000..f1c6830
--- /dev/null
+++ b/xos/tosca/resources/voltdevice.py
@@ -0,0 +1,52 @@
+import os
+import pdb
+import sys
+import tempfile
+sys.path.append("/opt/tosca")
+from translator.toscalib.tosca_template import ToscaTemplate
+
+from services.cord.models import VOLTDevice, VOLTService, AccessDevice, AccessAgent
+from xosresource import XOSResource
+
+class XOSVOLTDevice(XOSResource):
+ provides = "tosca.nodes.VOLTDevice"
+ xos_model = VOLTDevice
+ copyin_props = ["openflow_id", "driver"]
+
+ def get_xos_args(self, throw_exception=True):
+ args = super(XOSVOLTDevice, self).get_xos_args()
+
+ volt_service_name = self.get_requirement("tosca.relationships.MemberOfService", throw_exception=throw_exception)
+ if volt_service_name:
+ args["volt_service"] = self.get_xos_object(VOLTService, throw_exception=throw_exception, name=volt_service_name)
+
+ agent_name = self.get_requirement("tosca.relationships.UsesAgent", throw_exception=throw_exception)
+ if agent_name:
+ args["access_agent"] = self.get_xos_object(AccessAgent, throw_exception=throw_exception, name=agent_name)
+
+ return args
+
+ def postprocess(self, obj):
+ access_devices_str = self.get_property("access_devices")
+ access_devices = []
+ if access_devices_str:
+ lines = [x.strip() for x in access_devices_str.split(",")]
+ for line in lines:
+ if not (" " in line):
+ raise "Malformed access device `%s`", line
+ (uplink, vlan) = line.split(" ")
+ uplink=int(uplink.strip())
+ vlan=int(vlan.strip())
+ access_devices.append( (uplink, vlan) )
+
+ for ad in list(AccessDevice.objects.filter(volt_device=obj)):
+ if (ad.uplink, ad.vlan) not in access_devices:
+ print "Deleting AccessDevice '%s'" % ad
+ ad.delete()
+
+ for access_device in access_devices:
+ existing_objs = AccessDevice.objects.filter(volt_device=obj, uplink=access_device[0], vlan=access_device[1])
+ if not existing_objs:
+ ad = AccessDevice(volt_device=obj, uplink=access_device[0], vlan=access_device[1])
+ ad.save()
+ print "Created AccessDevice '%s'" % ad
diff --git a/xos/tosca/resources/voltservice.py b/xos/tosca/resources/voltservice.py
new file mode 100644
index 0000000..57cf846
--- /dev/null
+++ b/xos/tosca/resources/voltservice.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.cord.models import VOLTService
+
+from service import XOSService
+
+class XOSVOLTService(XOSService):
+ provides = "tosca.nodes.VOLTService"
+ xos_model = VOLTService
+ copyin_props = ["view_url", "icon_url", "kind", "enabled", "published", "public_key", "private_key_fn", "versionNumber"]
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index 2f8fa27..7835689 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -243,6 +243,8 @@
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
+ },'django.db.backends': {
+ 'level': 'WARNING',
},
}
}