Show the member status properties
This change adds the 'Operating Status' and 'Provisioning Status' properties to the member attributes that are visible in the members table and detail pages. The member statuses are taken from the Load Balancer status tree API, which returns all child resources with their corresponding statuses for a particular Load Balancer. Closes-Bug: #1563444 Change-Id: Icd812bdc8774c3dd4522ff923229bfecaeb1b9dd
This commit is contained in:
parent
20fde209c2
commit
d57d7a4ebd
|
@ -428,6 +428,23 @@ class LoadBalancers(generic.View):
|
|||
return create_loadbalancer(request)
|
||||
|
||||
|
||||
@urls.register
|
||||
class LoadBalancerStatusTree(generic.View):
|
||||
"""API for retrieving the resource status tree for a single load balancer.
|
||||
|
||||
"""
|
||||
url_regex = r'lbaas/loadbalancers/(?P<loadbalancer_id>[^/]+)/statuses/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, loadbalancer_id):
|
||||
"""Get the status tree for a specific load balancer.
|
||||
|
||||
http://localhost/api/lbaas/loadbalancers/cc758c90-3d98-4ea1-af44-aab405c9c915/statuses
|
||||
"""
|
||||
return neutronclient(request).retrieve_loadbalancer_status(
|
||||
loadbalancer_id)
|
||||
|
||||
|
||||
@urls.register
|
||||
class LoadBalancer(generic.View):
|
||||
"""API for retrieving, updating, and deleting a single load balancer.
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
deleteLoadBalancer: deleteLoadBalancer,
|
||||
createLoadBalancer: createLoadBalancer,
|
||||
editLoadBalancer: editLoadBalancer,
|
||||
getLoadBalancerStatusTree: getLoadBalancerStatusTree,
|
||||
getListeners: getListeners,
|
||||
getListener: getListener,
|
||||
createListener: createListener,
|
||||
|
@ -147,6 +148,21 @@
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.app.core.openstack-service-api.lbaasv2.getLoadBalancerStatusTree
|
||||
* @description
|
||||
* Get the status tree for a load balancer
|
||||
* @param {string} id
|
||||
* Specifies the id of the load balancer to request the status tree for.
|
||||
*/
|
||||
|
||||
function getLoadBalancerStatusTree(id) {
|
||||
return apiService.get('/api/lbaas/loadbalancers/' + id + '/statuses/')
|
||||
.error(function () {
|
||||
toastService.add('error', gettext('Unable to retrieve load balancer status tree.'));
|
||||
});
|
||||
}
|
||||
|
||||
// Listeners
|
||||
|
||||
/**
|
||||
|
|
|
@ -59,6 +59,13 @@
|
|||
error: 'Unable to delete load balancer.',
|
||||
testInput: [ '1234' ]
|
||||
},
|
||||
{
|
||||
func: 'getLoadBalancerStatusTree',
|
||||
method: 'get',
|
||||
path: '/api/lbaas/loadbalancers/1234/statuses/',
|
||||
error: 'Unable to retrieve load balancer status tree.',
|
||||
testInput: [ '1234' ]
|
||||
},
|
||||
{
|
||||
func: 'getListeners',
|
||||
method: 'get',
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
|
||||
var provisioningStatus = {
|
||||
ACTIVE: gettext('Active'),
|
||||
INACTIVE: gettext('Inactive'),
|
||||
PENDING_CREATE: gettext('Pending Create'),
|
||||
PENDING_UPDATE: gettext('Pending Update'),
|
||||
PENDING_DELETE: gettext('Pending Delete'),
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
MemberDetailController.$inject = [
|
||||
'horizon.app.core.openstack-service-api.lbaasv2',
|
||||
'horizon.dashboard.project.lbaasv2.members.actions.rowActions',
|
||||
'$routeParams'
|
||||
'$routeParams',
|
||||
'horizon.dashboard.project.lbaasv2.loadbalancers.service',
|
||||
'horizon.dashboard.project.lbaasv2.members.service'
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -36,20 +38,29 @@
|
|||
* @param api The LBaaS v2 API service.
|
||||
* @param rowActions The pool members row actions service.
|
||||
* @param $routeParams The angular $routeParams service.
|
||||
* @param loadBalancersService The LBaaS v2 load balancers service.
|
||||
* @param membersService The LBaaS v2 members service.
|
||||
* @returns undefined
|
||||
*/
|
||||
|
||||
function MemberDetailController(api, rowActions, $routeParams) {
|
||||
function MemberDetailController(
|
||||
api, rowActions, $routeParams, loadBalancersService, membersService
|
||||
) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.actions = rowActions.init($routeParams.loadbalancerId, $routeParams.poolId).actions;
|
||||
ctrl.loadbalancerId = $routeParams.loadbalancerId;
|
||||
ctrl.listenerId = $routeParams.listenerId;
|
||||
ctrl.poolId = $routeParams.poolId;
|
||||
ctrl.operatingStatus = loadBalancersService.operatingStatus;
|
||||
ctrl.provisioningStatus = loadBalancersService.provisioningStatus;
|
||||
|
||||
init();
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
function init() {
|
||||
api.getMember($routeParams.poolId, $routeParams.memberId).success(set('member'));
|
||||
api.getMember($routeParams.poolId, $routeParams.memberId).success(memberSuccess);
|
||||
api.getPool($routeParams.poolId).success(set('pool'));
|
||||
api.getListener($routeParams.listenerId).success(set('listener'));
|
||||
api.getLoadBalancer($routeParams.loadbalancerId).success(set('loadbalancer'));
|
||||
|
@ -61,6 +72,15 @@
|
|||
}, property);
|
||||
}
|
||||
|
||||
function memberSuccess(response) {
|
||||
ctrl.member = response;
|
||||
membersService.associateMemberStatuses(
|
||||
ctrl.loadbalancerId,
|
||||
ctrl.listenerId,
|
||||
ctrl.poolId,
|
||||
[ctrl.member]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})();
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
'use strict';
|
||||
|
||||
describe('LBaaS v2 Member Detail Controller', function() {
|
||||
var lbaasv2API, ctrl, actions;
|
||||
var controller, lbaasv2API, membersService, ctrl, actions;
|
||||
|
||||
function fakeAPI() {
|
||||
return {
|
||||
|
@ -60,13 +60,15 @@
|
|||
|
||||
beforeEach(inject(function($injector) {
|
||||
lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2');
|
||||
actions = $injector.get('horizon.dashboard.project.lbaasv2.members.actions.rowActions');
|
||||
membersService = $injector.get('horizon.dashboard.project.lbaasv2.members.service');
|
||||
spyOn(lbaasv2API, 'getMember').and.callFake(fakeAPI);
|
||||
spyOn(lbaasv2API, 'getPool').and.callFake(fakeAPI);
|
||||
spyOn(lbaasv2API, 'getListener').and.callFake(fakeAPI);
|
||||
spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI);
|
||||
actions = $injector.get('horizon.dashboard.project.lbaasv2.members.actions.rowActions');
|
||||
spyOn(actions, 'init').and.callThrough();
|
||||
var controller = $injector.get('$controller');
|
||||
spyOn(membersService, 'associateMemberStatuses');
|
||||
controller = $injector.get('$controller');
|
||||
ctrl = controller('MemberDetailController', {
|
||||
$routeParams: {
|
||||
loadbalancerId: 'loadbalancerId',
|
||||
|
@ -82,17 +84,23 @@
|
|||
expect(lbaasv2API.getPool).toHaveBeenCalledWith('poolId');
|
||||
expect(lbaasv2API.getListener).toHaveBeenCalledWith('listenerId');
|
||||
expect(lbaasv2API.getLoadBalancer).toHaveBeenCalledWith('loadbalancerId');
|
||||
expect(ctrl.loadbalancer).toEqual({ provisioning_status: 'ACTIVE' });
|
||||
expect(ctrl.listener).toBe('foo');
|
||||
expect(ctrl.pool).toBe('foo');
|
||||
expect(ctrl.member).toBe('foo');
|
||||
});
|
||||
|
||||
it('should have actions', function() {
|
||||
it('should initialize the controller properties correctly', function() {
|
||||
expect(ctrl.loadbalancerId).toBeDefined();
|
||||
expect(ctrl.listenerId).toBeDefined();
|
||||
expect(ctrl.poolId).toBeDefined();
|
||||
expect(ctrl.operatingStatus).toBeDefined();
|
||||
expect(ctrl.provisioningStatus).toBeDefined();
|
||||
expect(ctrl.actions).toBe('member-actions');
|
||||
expect(actions.init).toHaveBeenCalledWith('loadbalancerId', 'poolId');
|
||||
});
|
||||
|
||||
it('should invoke the "associateMemberStatuses" method', function() {
|
||||
expect(membersService.associateMemberStatuses).toHaveBeenCalledWith(
|
||||
ctrl.loadbalancerId, ctrl.listenerId, ctrl.poolId, [ctrl.member]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
<dd>{$ ::ctrl.member.protocol_port $}</dd>
|
||||
<dt translate>Weight</dt>
|
||||
<dd>{$ ctrl.member.weight $}</dd>
|
||||
<dt translate>Operating Status</dt>
|
||||
<dd>{$ ctrl.member.operating_status | decode:ctrl.operatingStatus $}</dd>
|
||||
<dt translate>Provisioning Status</dt>
|
||||
<dd>{$ ctrl.member.provisioning_status | decode:ctrl.provisioningStatus $}</dd>
|
||||
<dt translate>Admin State Up</dt>
|
||||
<dd>{$ ctrl.member.admin_state_up | yesno $}</dd>
|
||||
<dt translate>Member ID</dt>
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.project.lbaasv2.members')
|
||||
.factory('horizon.dashboard.project.lbaasv2.members.service', membersService);
|
||||
|
||||
membersService.$inject = [
|
||||
'horizon.app.core.openstack-service-api.lbaasv2'
|
||||
];
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name horizon.dashboard.project.lbaasv2.members.service
|
||||
* @description General service for LBaaS v2 members.
|
||||
* @param api The LBaaS V2 service API.
|
||||
* @returns The members service.
|
||||
*/
|
||||
|
||||
function membersService(api) {
|
||||
var service = {
|
||||
associateMemberStatuses: associateMemberStatuses
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
////////////
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name horizon.dashboard.project.lbaasv2.members.service.associateMemberStatuses
|
||||
* @description Associates the list of specified members with their corresponding statuses
|
||||
* that are retrieved from the load balancer status tree.
|
||||
* @param loadBalancerId The load balancer ID.
|
||||
* @param listenerId The listener ID that the members belong to.
|
||||
* @param poolId The pool ID that the members belong to.
|
||||
* @param members The list of members to associate with their corresponding health statuses.
|
||||
* @returns None
|
||||
*/
|
||||
|
||||
function associateMemberStatuses(loadBalancerId, listenerId, poolId, members) {
|
||||
api.getLoadBalancerStatusTree(loadBalancerId).then(function(response) {
|
||||
|
||||
// Collect the member status data for all specified members
|
||||
var memberStatusData = [];
|
||||
var listeners = response.data.statuses.loadbalancer.listeners;
|
||||
for (var listenerIndex = 0; listenerIndex < listeners.length; listenerIndex++) {
|
||||
var listener = listeners[listenerIndex];
|
||||
if (listener.id === listenerId) {
|
||||
var pools = listener.pools;
|
||||
for (var poolIndex = 0; poolIndex < pools.length; poolIndex++) {
|
||||
var pool = pools[poolIndex];
|
||||
if (pool.id === poolId) {
|
||||
memberStatusData = pool.members;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Attach the status properties to each member object
|
||||
members.forEach(mapStatusToMember);
|
||||
function mapStatusToMember(member) {
|
||||
for (var memberIndex = 0; memberIndex < memberStatusData.length; memberIndex++) {
|
||||
var memberWithStatuses = memberStatusData[memberIndex];
|
||||
if (memberWithStatuses.id === member.id) {
|
||||
member.operating_status = memberWithStatuses.operating_status;
|
||||
member.provisioning_status = memberWithStatuses.provisioning_status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}());
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright 2016 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
describe('LBaaS v2 Members Service', function() {
|
||||
var service, $q, scope;
|
||||
|
||||
beforeEach(module('horizon.framework.widgets.toast'));
|
||||
beforeEach(module('horizon.framework.conf'));
|
||||
beforeEach(module('horizon.framework.util'));
|
||||
beforeEach(module('horizon.app.core.openstack-service-api'));
|
||||
beforeEach(module('horizon.dashboard.project.lbaasv2'));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.app.core.openstack-service-api.lbaasv2', {
|
||||
getLoadBalancerStatusTree: function() {
|
||||
var deferred = $q.defer();
|
||||
var response = {
|
||||
data: {
|
||||
statuses: {
|
||||
loadbalancer: {
|
||||
id: 'loadbalancer1',
|
||||
listeners: [
|
||||
{
|
||||
id: 'listener0',
|
||||
pools: []
|
||||
},
|
||||
{
|
||||
id: 'listener1',
|
||||
pools: [
|
||||
{
|
||||
id: 'pool0',
|
||||
members: []
|
||||
},
|
||||
{
|
||||
id: 'pool1',
|
||||
members: [
|
||||
{
|
||||
id: 'member1',
|
||||
operating_status: 'ONLINE',
|
||||
provisioning_status: 'ACTIVE'
|
||||
},
|
||||
{
|
||||
id: 'member2',
|
||||
operating_status: 'OFFLINE',
|
||||
provisioning_status: 'INACTIVE'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
deferred.resolve(response);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(inject(function ($injector) {
|
||||
service = $injector.get('horizon.dashboard.project.lbaasv2.members.service');
|
||||
$q = $injector.get('$q');
|
||||
scope = $injector.get('$rootScope').$new();
|
||||
}));
|
||||
|
||||
it('should define service attributes', function() {
|
||||
expect(service.associateMemberStatuses).toBeDefined();
|
||||
});
|
||||
|
||||
it('should correctly associate member health statuses', function() {
|
||||
var members = [
|
||||
{ id: 'member1' },
|
||||
{ id: 'member2' }
|
||||
];
|
||||
|
||||
service.associateMemberStatuses('loadbalancer1', 'listener1', 'pool1', members);
|
||||
scope.$apply();
|
||||
|
||||
expect(members.length).toBe(2);
|
||||
expect(members[0].operating_status).toBe('ONLINE');
|
||||
expect(members[0].provisioning_status).toBe('ACTIVE');
|
||||
expect(members[1].operating_status).toBe('OFFLINE');
|
||||
expect(members[1].provisioning_status).toBe('INACTIVE');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})();
|
|
@ -24,7 +24,9 @@
|
|||
'horizon.app.core.openstack-service-api.lbaasv2',
|
||||
'horizon.dashboard.project.lbaasv2.members.actions.rowActions',
|
||||
'horizon.dashboard.project.lbaasv2.members.actions.batchActions',
|
||||
'$routeParams'
|
||||
'$routeParams',
|
||||
'horizon.dashboard.project.lbaasv2.loadbalancers.service',
|
||||
'horizon.dashboard.project.lbaasv2.members.service'
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -38,11 +40,14 @@
|
|||
* @param rowActions The pool members row actions service.
|
||||
* @param batchActions The members batch actions service.
|
||||
* @param $routeParams The angular $routeParams service.
|
||||
* @param loadBalancersService The LBaaS v2 load balancers service.
|
||||
* @param membersService The LBaaS v2 members service.
|
||||
* @returns undefined
|
||||
*/
|
||||
|
||||
function MembersTableController(api, rowActions, batchActions, $routeParams) {
|
||||
|
||||
function MembersTableController(
|
||||
api, rowActions, batchActions, $routeParams, loadBalancersService, membersService
|
||||
) {
|
||||
var ctrl = this;
|
||||
ctrl.items = [];
|
||||
ctrl.src = [];
|
||||
|
@ -54,6 +59,8 @@
|
|||
ctrl.poolId = $routeParams.poolId;
|
||||
ctrl.rowActions = rowActions.init(ctrl.loadbalancerId, ctrl.poolId);
|
||||
ctrl.batchActions = batchActions.init(ctrl.loadbalancerId);
|
||||
ctrl.operatingStatus = loadBalancersService.operatingStatus;
|
||||
ctrl.provisioningStatus = loadBalancersService.provisioningStatus;
|
||||
|
||||
init();
|
||||
|
||||
|
@ -69,6 +76,11 @@
|
|||
function success(response) {
|
||||
ctrl.src = response.data.items;
|
||||
ctrl.loading = false;
|
||||
membersService.associateMemberStatuses(
|
||||
ctrl.loadbalancerId,
|
||||
ctrl.listenerId,
|
||||
ctrl.poolId,
|
||||
ctrl.src);
|
||||
}
|
||||
|
||||
function fail(/*response*/) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2016 IBM Corp.
|
||||
* Copyright 2017 Walmart.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -17,7 +18,7 @@
|
|||
'use strict';
|
||||
|
||||
describe('LBaaS v2 Members Table Controller', function() {
|
||||
var controller, lbaasv2API, scope;
|
||||
var controller, lbaasv2API, membersService, scope;
|
||||
var items = [{ foo: 'bar' }];
|
||||
var apiFail = false;
|
||||
|
||||
|
@ -40,15 +41,16 @@
|
|||
beforeEach(module('horizon.framework.util'));
|
||||
beforeEach(module('horizon.app.core.openstack-service-api'));
|
||||
beforeEach(module('horizon.dashboard.project.lbaasv2'));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('$uibModal', {});
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2');
|
||||
membersService = $injector.get('horizon.dashboard.project.lbaasv2.members.service');
|
||||
controller = $injector.get('$controller');
|
||||
spyOn(lbaasv2API, 'getMembers').and.callFake(fakeAPI);
|
||||
spyOn(membersService, 'associateMemberStatuses');
|
||||
}));
|
||||
|
||||
function createController() {
|
||||
|
@ -61,7 +63,7 @@
|
|||
}});
|
||||
}
|
||||
|
||||
it('should initialize correctly', function() {
|
||||
it('should initialize the controller properties correctly', function() {
|
||||
var ctrl = createController();
|
||||
expect(ctrl.items).toEqual([]);
|
||||
expect(ctrl.src).toEqual(items);
|
||||
|
@ -71,7 +73,10 @@
|
|||
expect(ctrl.loadbalancerId).toBeDefined();
|
||||
expect(ctrl.listenerId).toBeDefined();
|
||||
expect(ctrl.poolId).toBeDefined();
|
||||
expect(ctrl.rowActions).toBeDefined();
|
||||
expect(ctrl.batchActions).toBeDefined();
|
||||
expect(ctrl.operatingStatus).toBeDefined();
|
||||
expect(ctrl.provisioningStatus).toBeDefined();
|
||||
});
|
||||
|
||||
it('should invoke lbaasv2 apis', function() {
|
||||
|
@ -80,6 +85,12 @@
|
|||
expect(ctrl.src.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should invoke the "associateMemberStatuses" method', function() {
|
||||
var ctrl = createController();
|
||||
expect(membersService.associateMemberStatuses).toHaveBeenCalledWith(
|
||||
ctrl.loadbalancerId, ctrl.listenerId, ctrl.poolId, ctrl.src);
|
||||
});
|
||||
|
||||
it('should show error if loading fails', function() {
|
||||
apiFail = true;
|
||||
var ctrl = createController();
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
<th class="rsp-p1" st-sort="id" st-sort-default="id" translate>ID</th>
|
||||
<th class="rsp-p1" st-sort="address" translate>IP Address</th>
|
||||
<th class="rsp-p1" st-sort="protocol_port" translate>Protocol Port</th>
|
||||
<th class="rsp-p1" st-sort="protocol_port" translate>Operating Status</th>
|
||||
<th class="rsp-p1" st-sort="protocol_port" translate>Provisioning Status</th>
|
||||
<th class="rsp-p1" st-sort="weight" translate>Weight</th>
|
||||
<th class="actions_column" translate>Actions</th>
|
||||
</tr>
|
||||
|
@ -63,6 +65,8 @@
|
|||
<td class="rsp-p1"><a ng-href="project/ngloadbalancersv2/{$ ::table.loadbalancerId $}/listeners/{$ ::table.listenerId $}/pools/{$ ::table.poolId $}/members/{$ ::item.id $}">{$ ::item.id $}</a></td>
|
||||
<td class="rsp-p1">{$ ::item.address $}</td>
|
||||
<td class="rsp-p1">{$ ::item.protocol_port $}</td>
|
||||
<td class="rsp-p1">{$ ::item.operating_status | decode:table.operatingStatus $}</td>
|
||||
<td class="rsp-p1">{$ ::item.provisioning_status | decode:table.provisioningStatus $}</td>
|
||||
<td class="rsp-p1">{$ item.weight $}</td>
|
||||
<td class="actions_column">
|
||||
<!--
|
||||
|
|
Loading…
Reference in New Issue