Merge "Add pagination to Network Ports table in Launch Instance wizard"
This commit is contained in:
commit
ef034dda00
|
@ -0,0 +1,18 @@
|
||||||
|
<div ng-controller="LaunchInstanceNetworkPortController as ctrl">
|
||||||
|
<dl class="port-details dl-horizontal">
|
||||||
|
<dt translate>ID</dt>
|
||||||
|
<dd>{$ item.id $}</dd>
|
||||||
|
<dt translate>Project ID</dt>
|
||||||
|
<dd>{$ item.tenant_id $}</dd>
|
||||||
|
<dt translate>Network ID</dt>
|
||||||
|
<dd>{$ item.network_id $}</dd>
|
||||||
|
<dt translate>Network</dt>
|
||||||
|
<dd>{$ item.network_name $}</dd>
|
||||||
|
<dt translate>VNIC type</dt>
|
||||||
|
<dd>{$ item['binding:vnic_type'] | decode:ctrl.vnicTypes $}</dd>
|
||||||
|
<div ng-if="item['binding:host_id']">
|
||||||
|
<dt translate>Host ID</dt>
|
||||||
|
<dd>{$ item['binding:host_id'] $}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
|
@ -27,11 +27,12 @@
|
||||||
.controller('LaunchInstanceNetworkPortController', LaunchInstanceNetworkPortController);
|
.controller('LaunchInstanceNetworkPortController', LaunchInstanceNetworkPortController);
|
||||||
|
|
||||||
LaunchInstanceNetworkPortController.$inject = [
|
LaunchInstanceNetworkPortController.$inject = [
|
||||||
'$scope',
|
'horizon.dashboard.project.workflow.launch-instance.basePath',
|
||||||
|
'launchInstanceModel',
|
||||||
'horizon.framework.widgets.action-list.button-tooltip.row-warning.service'
|
'horizon.framework.widgets.action-list.button-tooltip.row-warning.service'
|
||||||
];
|
];
|
||||||
|
|
||||||
function LaunchInstanceNetworkPortController($scope, tooltipService) {
|
function LaunchInstanceNetworkPortController(basePath, launchInstanceModel, tooltipService) {
|
||||||
var ctrl = this;
|
var ctrl = this;
|
||||||
|
|
||||||
ctrl.portStatuses = {
|
ctrl.portStatuses = {
|
||||||
|
@ -53,25 +54,96 @@
|
||||||
'virtio-forwarder': gettext('Virtio Forwarder')
|
'virtio-forwarder': gettext('Virtio Forwarder')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getPortStatus(status) {
|
||||||
|
return ctrl.portStatuses[status];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPortAdminState(state) {
|
||||||
|
return ctrl.portAdminStates[state];
|
||||||
|
}
|
||||||
|
|
||||||
|
var portsArr = launchInstanceModel.ports;
|
||||||
|
ctrl.portsObj = {};
|
||||||
|
ctrl.isPortsObjGenerated = false;
|
||||||
|
|
||||||
|
function getNameOrID(id) {
|
||||||
|
ctrl.portsObj = ctrl.getPortsObj(portsArr);
|
||||||
|
var port = ctrl.portsObj[id];
|
||||||
|
return ctrl.nameOrID(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPortFixedIPs(id) {
|
||||||
|
var port = ctrl.portsObj[id];
|
||||||
|
var fixedIPs = '';
|
||||||
|
for (var ip in port.subnet_names) {
|
||||||
|
fixedIPs += ip + ' on subnet ' + port.subnet_names[ip] + '\n';
|
||||||
|
}
|
||||||
|
return fixedIPs;
|
||||||
|
}
|
||||||
|
|
||||||
ctrl.tableDataMulti = {
|
ctrl.tableDataMulti = {
|
||||||
available: $scope.model.ports,
|
available: launchInstanceModel.ports,
|
||||||
allocated: $scope.model.newInstanceSpec.ports,
|
allocated: launchInstanceModel.newInstanceSpec.ports
|
||||||
displayedAvailable: [],
|
|
||||||
displayedAllocated: []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ctrl.availableTableConfig = {
|
||||||
|
selectAll: false,
|
||||||
|
trackId: 'id',
|
||||||
|
detailsTemplateUrl: basePath + 'networkports/port-details.html',
|
||||||
|
columns: [
|
||||||
|
{id: 'id', title: gettext('Name'), priority: 1, filters: [getNameOrID]},
|
||||||
|
{id: 'id', title: gettext('IP'), priority: 2, filters: [getPortFixedIPs]},
|
||||||
|
{id: 'admin_state', title: gettext('Admin State'), priority: 2,
|
||||||
|
filters: [getPortAdminState]},
|
||||||
|
{id: 'status', title: gettext('Status'), priority: 2, filters: [getPortStatus]}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
ctrl.allocatedTableConfig = angular.copy(ctrl.availableTableConfig);
|
||||||
|
|
||||||
|
ctrl.tableHelpText = {
|
||||||
|
allocHelpText: gettext('Select ports from those listed below.'),
|
||||||
|
availHelpText: gettext('Select one or more ports')
|
||||||
|
};
|
||||||
|
|
||||||
|
ctrl.filterFacets = [{
|
||||||
|
label: gettext('Name'),
|
||||||
|
name: 'name',
|
||||||
|
singleton: true
|
||||||
|
}, {
|
||||||
|
label: gettext('ID'),
|
||||||
|
name: 'id',
|
||||||
|
singleton: true
|
||||||
|
}, {
|
||||||
|
label: gettext('Admin State'),
|
||||||
|
name: 'admin_state',
|
||||||
|
singleton: true
|
||||||
|
}, {
|
||||||
|
label: gettext('Status'),
|
||||||
|
name: 'status',
|
||||||
|
singleton: true
|
||||||
|
}];
|
||||||
|
|
||||||
ctrl.tableLimits = {
|
ctrl.tableLimits = {
|
||||||
maxAllocation: -1
|
maxAllocation: -1
|
||||||
};
|
};
|
||||||
|
|
||||||
ctrl.tableHelpText = {
|
|
||||||
allocHelpText: gettext('Select ports from those listed below.')
|
|
||||||
};
|
|
||||||
|
|
||||||
ctrl.tooltipModel = tooltipService;
|
ctrl.tooltipModel = tooltipService;
|
||||||
|
|
||||||
ctrl.nameOrID = function nameOrId(data) {
|
ctrl.nameOrID = function nameOrId(data) {
|
||||||
return angular.isDefined(data.name) && data.name !== '' ? data.name : data.id;
|
return angular.isDefined(data.name) && data.name !== '' ? data.name : data.id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ctrl.getPortsObj = function (data) {
|
||||||
|
if (!ctrl.isPortsObjGenerated) {
|
||||||
|
var ports = data.reduce(function (acc, cur) {
|
||||||
|
acc[cur.id] = cur;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
ctrl.isPortsObjGenerated = true;
|
||||||
|
return ports;
|
||||||
|
}
|
||||||
|
else { return ctrl.portsObj; }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -3,160 +3,13 @@
|
||||||
Ports provide extra communication channels to your instances. You can select ports instead of networks or a mix of both.
|
Ports provide extra communication channels to your instances. You can select ports instead of networks or a mix of both.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<transfer-table tr-model="ctrl.tableDataMulti" help-text="ctrl.tableHelpText" limits="ctrl.tableLimits">
|
<transfer-table tr-model="ctrl.tableDataMulti" help-text="ctrl.tableHelpText" limits="ctrl.tableLimits" clone-content>
|
||||||
<allocated>
|
<hz-dynamic-table
|
||||||
<table st-table="ctrl.tableDataMulti.displayedAllocated" st-safe-src="ctrl.tableDataMulti.allocated"
|
config="$isAvailableTable ? ctrl.availableTableConfig : ctrl.allocatedTableConfig"
|
||||||
hz-table class="table table-striped table-rsp table-detail">
|
items="$isAvailableTable ? ($sourceItems | filterAvailable:trCtrl.allocatedIds) : $sourceItems"
|
||||||
<thead>
|
item-actions="trCtrl.itemActions"
|
||||||
<tr>
|
filter-facets="$isAvailableTable && ctrl.filterFacets"
|
||||||
<th class="reorder"></th>
|
table="ctrl">
|
||||||
<th class="expander"></th>
|
</hz-dynamic-table>
|
||||||
<th st-sort="name" st-sort-default class="rsp-p1" translate>Name</th>
|
</transfer-table> <!-- End Network Ports Transfer Table -->
|
||||||
<th class="rsp-p2" translate>IP</th>
|
|
||||||
<th st-sort="admin_state" class="rsp-p1" translate>Admin State</th>
|
|
||||||
<th st-sort="status" class="rsp-p1" translate>Status</th>
|
|
||||||
<th class="actions_column"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr ng-if="ctrl.tableDataMulti.allocated.length === 0">
|
|
||||||
<td colspan="7">
|
|
||||||
<div class="no-rows-help" translate>
|
|
||||||
Select an item from Available items below
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr ng-repeat-start="item in ctrl.tableDataMulti.displayedAllocated track by item.id"
|
|
||||||
lr-drag-data="ctrl.tableDataMulti.displayedAllocated" lr-drag-src="reorder"
|
|
||||||
lr-drop-target="reorder" lr-drop-success="trCtrl.updateAllocated(e, item, collection)">
|
|
||||||
<td class="reorder">
|
|
||||||
<span class="fa fa-sort" title="{$ 'Re-order items using drag and drop'|translate $}"></span>
|
|
||||||
{$ $index + 1 $}
|
|
||||||
</td>
|
|
||||||
<td class="expander">
|
|
||||||
<span class="fa fa-chevron-right" hz-expand-detail
|
|
||||||
title="{$ 'Click to see more details'|translate $}"></span>
|
|
||||||
</td>
|
|
||||||
<td class="rsp-p1 word-break">{$ ctrl.nameOrID(item) $}</td>
|
|
||||||
<td class="rsp-p2">
|
|
||||||
<div ng-repeat="ip in item.fixed_ips">
|
|
||||||
<span translate
|
|
||||||
translate-params-address="ip.ip_address"
|
|
||||||
translate-params-subnet="item.subnet_names[ip.ip_address]">
|
|
||||||
{$ address $} on subnet {$ subnet $}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="rsp-p1">{$ item.admin_state | decode:ctrl.portAdminStates $}</td>
|
|
||||||
<td class="rsp-p1">{$ item.status | decode:ctrl.portStatuses $}</td>
|
|
||||||
<td class="actions_column">
|
|
||||||
<action-list>
|
|
||||||
<action action-classes="'btn btn-default'"
|
|
||||||
callback="trCtrl.deallocate" item="item">
|
|
||||||
<span class="fa fa-arrow-down"></span>
|
|
||||||
</action>
|
|
||||||
</action-list>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr ng-repeat-end class="detail-row">
|
|
||||||
<td colspan="7" class="detail">
|
|
||||||
<dl class="dl-horizontal">
|
|
||||||
<dt translate>ID</dt>
|
|
||||||
<dd>{$ item.id $}</dd>
|
|
||||||
<dt translate>Project ID</dt>
|
|
||||||
<dd>{$ item.tenant_id $}</dd>
|
|
||||||
<dt translate>Network ID</dt>
|
|
||||||
<dd>{$ item.network_id $}</dd>
|
|
||||||
<dt translate>Network</dt>
|
|
||||||
<dd>{$ item.network_name $}</dd>
|
|
||||||
<dt translate>VNIC type</dt>
|
|
||||||
<dd>{$ item['binding:vnic_type'] | decode:ctrl.vnicTypes $}</dd>
|
|
||||||
<div ng-if="item['binding:host_id']">
|
|
||||||
<dt translate>Host ID</dt>
|
|
||||||
<dd>{$ item['binding:host_id'] $}</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</allocated>
|
|
||||||
|
|
||||||
<available>
|
|
||||||
<table st-table="ctrl.tableDataMulti.displayedAvailable" st-safe-src="ctrl.tableDataMulti.available"
|
|
||||||
hz-table class="table table-striped table-rsp table-detail">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="search-header" colspan="6">
|
|
||||||
<hz-search-bar icon-classes="fa-search"></hz-search-bar>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th class="expander"></th>
|
|
||||||
<th st-sort="name" st-sort-default class="rsp-p1" translate>Name</th>
|
|
||||||
<th class="rsp-p2" translate>IP</th>
|
|
||||||
<th st-sort="admin_state" class="rsp-p1" translate>Admin State</th>
|
|
||||||
<th st-sort="status" class="rsp-p1" translate>Status</th>
|
|
||||||
<th class="actions_column"></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr ng-if="trCtrl.numAvailable() === 0">
|
|
||||||
<td colspan="6">
|
|
||||||
<div class="no-rows-help" translate>
|
|
||||||
No available items
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr ng-repeat-start="item in ctrl.tableDataMulti.displayedAvailable track by item.id"
|
|
||||||
ng-if="!trCtrl.allocatedIds[item.id]">
|
|
||||||
<td class="expander">
|
|
||||||
<span class="fa fa-chevron-right" hz-expand-detail
|
|
||||||
title="{$ 'Click to see more details'|translate $}"></span>
|
|
||||||
</td>
|
|
||||||
<td class="rsp-p1 word-break">{$ ctrl.nameOrID(item) $}</td>
|
|
||||||
<td class="rsp-p2">
|
|
||||||
<div ng-repeat="ip in item.fixed_ips">
|
|
||||||
<span translate
|
|
||||||
translate-params-address="ip.ip_address"
|
|
||||||
translate-params-subnet="item.subnet_names[ip.ip_address]">
|
|
||||||
{$ address $} on subnet {$ subnet $}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="rsp-p1">{$ item.admin_state | decode:ctrl.portAdminStates $}</td>
|
|
||||||
<td class="rsp-p1">{$ item.status | decode:ctrl.portStatuses $}</td>
|
|
||||||
<td class="actions_column">
|
|
||||||
<action-list>
|
|
||||||
<action action-classes="'btn btn-default'"
|
|
||||||
callback="trCtrl.allocate" item="item">
|
|
||||||
<span class="fa fa-arrow-up"></span>
|
|
||||||
</action>
|
|
||||||
</action-list>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr ng-repeat-end class="detail-row">
|
|
||||||
<td colspan="6" class="detail">
|
|
||||||
<dl class="dl-horizontal">
|
|
||||||
<dt translate>ID</dt>
|
|
||||||
<dd>{$ item.id $}</dd>
|
|
||||||
<dt translate>Project ID</dt>
|
|
||||||
<dd>{$ item.tenant_id $}</dd>
|
|
||||||
<dt translate>Network ID</dt>
|
|
||||||
<dd>{$ item.network_id $}</dd>
|
|
||||||
<dt translate>Network</dt>
|
|
||||||
<dd>{$ item.network_name $}</dd>
|
|
||||||
<dt translate>VNIC type</dt>
|
|
||||||
<dd>{$ item['binding:vnic_type'] | decode:ctrl.vnicTypes $}</dd>
|
|
||||||
<div ng-if="item['binding:host_id']">
|
|
||||||
<dt translate>Host ID</dt>
|
|
||||||
<dd>{$ item['binding:host_id'] $}</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</available>
|
|
||||||
</transfer-table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,20 +23,19 @@
|
||||||
beforeEach(module('horizon.dashboard.project.workflow.launch-instance'));
|
beforeEach(module('horizon.dashboard.project.workflow.launch-instance'));
|
||||||
|
|
||||||
describe('LaunchInstanceNetworkPortController', function() {
|
describe('LaunchInstanceNetworkPortController', function() {
|
||||||
var scope, ctrl;
|
var ctrl;
|
||||||
|
var port = {name: 'test_name', id: 'test_id'};
|
||||||
|
|
||||||
beforeEach(inject(function($controller) {
|
beforeEach(inject(function($controller) {
|
||||||
scope = {
|
var model = {
|
||||||
model: {
|
newInstanceSpec: {
|
||||||
newInstanceSpec: {
|
ports: ['port-a']
|
||||||
ports: ['port-a']
|
},
|
||||||
},
|
ports: ['port-a', 'port-b']
|
||||||
ports: ['port-a', 'port-b']
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
ctrl = $controller('LaunchInstanceNetworkPortController', {
|
|
||||||
$scope: scope
|
ctrl = $controller('LaunchInstanceNetworkPortController',
|
||||||
});
|
{ launchInstanceModel: model });
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('has correct ports statuses', function() {
|
it('has correct ports statuses', function() {
|
||||||
|
@ -63,25 +62,35 @@
|
||||||
expect(ctrl.tableHelpText.allocHelpText).toBeDefined();
|
expect(ctrl.tableHelpText.allocHelpText).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('nameOrId return the name', function() {
|
it('nameOrID returns the name', function() {
|
||||||
var obj = {name: 'test_name', id: 'test_id'};
|
|
||||||
expect(ctrl.nameOrID).toBeDefined();
|
expect(ctrl.nameOrID).toBeDefined();
|
||||||
expect(ctrl.nameOrID(obj)).toBe('test_name');
|
expect(ctrl.nameOrID(port)).toBe('test_name');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('nameOrId return the id if the name is missing', function() {
|
it('nameOrID returns the id if the name is missing', function() {
|
||||||
expect(ctrl.nameOrID).toBeDefined();
|
expect(ctrl.nameOrID).toBeDefined();
|
||||||
expect(ctrl.nameOrID({'id': 'testid'})).toBe('testid');
|
expect(ctrl.nameOrID({'id': 'testid'})).toBe('testid');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getPortsObj returns generated ports object', function() {
|
||||||
|
expect(ctrl.getPortsObj).toBeDefined();
|
||||||
|
expect(ctrl.isPortsObjGenerated).toBe(false);
|
||||||
|
expect(ctrl.getPortsObj([port])).toEqual({'test_id': port});
|
||||||
|
expect(ctrl.isPortsObjGenerated).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getPortsObj returns existing ports object', function() {
|
||||||
|
ctrl.portsObj = {'test_id': port};
|
||||||
|
ctrl.isPortsObjGenerated = true;
|
||||||
|
expect(ctrl.getPortsObj).toBeDefined();
|
||||||
|
expect(ctrl.getPortsObj([port])).toEqual({'test_id': port});
|
||||||
|
});
|
||||||
|
|
||||||
it('uses scope to set table data', function() {
|
it('uses scope to set table data', function() {
|
||||||
expect(ctrl.tableDataMulti).toBeDefined();
|
expect(ctrl.tableDataMulti).toBeDefined();
|
||||||
expect(ctrl.tableDataMulti.available).toEqual(['port-a', 'port-b']);
|
expect(ctrl.tableDataMulti.available).toEqual(['port-a', 'port-b']);
|
||||||
expect(ctrl.tableDataMulti.allocated).toEqual(['port-a']);
|
expect(ctrl.tableDataMulti.allocated).toEqual(['port-a']);
|
||||||
expect(ctrl.tableDataMulti.displayedAllocated).toEqual([]);
|
|
||||||
expect(ctrl.tableDataMulti.displayedAvailable).toEqual([]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
Loading…
Reference in New Issue