diff options
Diffstat (limited to 'openstack_dashboard/dashboards/project/static')
8 files changed, 212 insertions, 186 deletions
diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.controller.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.controller.js index 824cb9d31..f1c23a83e 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.controller.js +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.controller.js @@ -60,6 +60,7 @@ ctrl.createKeyPair = createKeyPair; ctrl.importKeyPair = importKeyPair; ctrl.setKeypairRequired = setKeypairRequired; + ctrl.setAdminPasswordChange = setAdminPasswordChange; ctrl.tableData = { available: launchInstanceModel.keypairs, @@ -105,6 +106,10 @@ 'OPENSTACK_HYPERVISOR_FEATURES.requires_keypair' ).then(setKeypairRequired); + ctrl.setAdminPassword = false; + + ctrl.adminPassConfError = gettext('Passwords do not match.'); + ////////// /** @@ -200,6 +205,11 @@ function setKeypairRequired(setting) { ctrl.isKeypairRequired = setting ? 1 : 0; } + + function setAdminPasswordChange() { + ctrl.confirmedAdminPassword = null; + launchInstanceModel.newInstanceSpec.admin_pass = null; + } } })(); diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.controller.spec.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.controller.spec.js index 6e2e06244..c277f1470 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.controller.spec.js +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.controller.spec.js @@ -19,7 +19,7 @@ describe('Launch Instance Keypair Step', function() { describe('LaunchInstanceKeypairController', function() { - var ctrl, q, settings; + var ctrl, q, settings, model; var $uibModal = { open: angular.noop }; var toastServiceMock = {add: angular.noop}; @@ -28,6 +28,7 @@ beforeEach(function() { settings = { OPENSTACK_HYPERVISOR_FEATURES: { + can_set_password: true, requires_keypair: false } }; @@ -48,7 +49,7 @@ beforeEach(inject(function($controller, $q) { q = $q; - var model = { + model = { newInstanceSpec: { key_pair: ['key1'] }, @@ -189,6 +190,17 @@ ctrl.setKeypairRequired(false); expect(ctrl.isKeypairRequired).toBe(0); }); + + it('defines setAdminPassword', function() { + expect(ctrl.setAdminPassword).toBeDefined(); + expect(ctrl.setAdminPassword).toBe(false); + }); + + it('clears password fields when setAdminPassword is changed', function() { + ctrl.setAdminPasswordChange(); + expect(ctrl.confirmedAdminPassword).toBe(null); + expect(model.newInstanceSpec.admin_pass).toBe(null); + }); }); }); diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.html b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.html index 4271bcaac..040fa408e 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.html +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/keypair/keypair.html @@ -25,4 +25,56 @@ table="ctrl"> </hz-dynamic-table> </transfer-table> <!-- End Key Pairs Table --> + + <settings-service required-settings='["OPENSTACK_HYPERVISOR_FEATURES.can_set_password"]'> + <div class="admin-password"> + <div class="themable-checkbox"> + <input ng-change="ctrl.setAdminPasswordChange()" + ng-model="ctrl.setAdminPassword" + type="checkbox" + id="setPassword"> + <label for="setPassword"> + <span translate>Set admin password</span> + </label> + </div> + + <div class="container"> + <div class="row"> + + <div ng-if="ctrl.setAdminPassword" ng-form="adminPasswordForm"> + <div class="col-sm-3"> + <div class="form-group"> + <label class="control-label" translate for="adminPassword">Password</label> + <input class="form-control" + id="adminPassword" + name="adminPassword" + ng-model="model.newInstanceSpec.admin_pass" + type="password"> + </div> + </div> + + <div class="col-sm-3"> + <div class="form-group" + ng-class="{ 'has-error': + adminPasswordForm['confirmPassword'].$invalid && + adminPasswordForm['confirmPassword'].$dirty }"> + <label class="control-label" translate for="confirmPassword">Confirm password</label> + <input id="confirmPassword" + name="confirmPassword" + class="form-control" + hz-password-match="model.newInstanceSpec.admin_pass" + ng-model="ctrl.confirmedAdminPassword" + type="password"> + <span class="help-block" + ng-show="adminPasswordForm['confirmPassword'].$invalid && + adminPasswordForm['confirmPassword'].$dirty"> + {$ ctrl.adminPassConfError $} + </span> + </div> + </div> + </div> + </div> + </div> + </div> + </settings-service> <!-- End Admin Password Form--> </div> <!-- End Controller --> diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js index 8e33169d8..21f34cd60 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/launch-instance-model.service.spec.js @@ -1041,7 +1041,7 @@ expect(finalSpec.key_pair).toBeUndefined(); }); - it('stips null properties', function() { + it('strips null properties', function() { model.newInstanceSpec.useless = null; var finalSpec = model.createInstance(); diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/port-details.html b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/port-details.html new file mode 100644 index 000000000..ae8d82997 --- /dev/null +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/port-details.html @@ -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> diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.controller.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.controller.js index 1a4cc4dd0..462a8573e 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.controller.js +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.controller.js @@ -27,11 +27,12 @@ .controller('LaunchInstanceNetworkPortController', LaunchInstanceNetworkPortController); LaunchInstanceNetworkPortController.$inject = [ - '$scope', + 'horizon.dashboard.project.workflow.launch-instance.basePath', + 'launchInstanceModel', 'horizon.framework.widgets.action-list.button-tooltip.row-warning.service' ]; - function LaunchInstanceNetworkPortController($scope, tooltipService) { + function LaunchInstanceNetworkPortController(basePath, launchInstanceModel, tooltipService) { var ctrl = this; ctrl.portStatuses = { @@ -53,19 +54,78 @@ '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 = { - available: $scope.model.ports, - allocated: $scope.model.newInstanceSpec.ports, - displayedAvailable: [], - displayedAllocated: [] + available: launchInstanceModel.ports, + allocated: launchInstanceModel.newInstanceSpec.ports }; - ctrl.tableLimits = { - maxAllocation: -1 + 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.') + 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 = { + maxAllocation: -1 }; ctrl.tooltipModel = tooltipService; @@ -73,5 +133,17 @@ ctrl.nameOrID = function nameOrId(data) { 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; } + }; } })(); diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.html b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.html index a315416b5..9cce91105 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.html +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.html @@ -3,160 +3,13 @@ Ports provide extra communication channels to your instances. You can select ports instead of networks or a mix of both. </p> - <transfer-table tr-model="ctrl.tableDataMulti" help-text="ctrl.tableHelpText" limits="ctrl.tableLimits"> - <allocated> - <table st-table="ctrl.tableDataMulti.displayedAllocated" st-safe-src="ctrl.tableDataMulti.allocated" - hz-table class="table table-striped table-rsp table-detail"> - <thead> - <tr> - <th class="reorder"></th> - <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="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> + <transfer-table tr-model="ctrl.tableDataMulti" help-text="ctrl.tableHelpText" limits="ctrl.tableLimits" clone-content> + <hz-dynamic-table + config="$isAvailableTable ? ctrl.availableTableConfig : ctrl.allocatedTableConfig" + items="$isAvailableTable ? ($sourceItems | filterAvailable:trCtrl.allocatedIds) : $sourceItems" + item-actions="trCtrl.itemActions" + filter-facets="$isAvailableTable && ctrl.filterFacets" + table="ctrl"> + </hz-dynamic-table> + </transfer-table> <!-- End Network Ports Transfer Table --> </div> diff --git a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.spec.js b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.spec.js index 0e25313c2..da5611aa1 100644 --- a/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.spec.js +++ b/openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/networkports/ports.spec.js @@ -23,20 +23,19 @@ beforeEach(module('horizon.dashboard.project.workflow.launch-instance')); describe('LaunchInstanceNetworkPortController', function() { - var scope, ctrl; + var ctrl; + var port = {name: 'test_name', id: 'test_id'}; beforeEach(inject(function($controller) { - scope = { - model: { - newInstanceSpec: { - ports: ['port-a'] - }, - ports: ['port-a', 'port-b'] - } + var model = { + newInstanceSpec: { + ports: ['port-a'] + }, + ports: ['port-a', 'port-b'] }; - ctrl = $controller('LaunchInstanceNetworkPortController', { - $scope: scope - }); + + ctrl = $controller('LaunchInstanceNetworkPortController', + { launchInstanceModel: model }); })); it('has correct ports statuses', function() { @@ -63,25 +62,35 @@ expect(ctrl.tableHelpText.allocHelpText).toBeDefined(); }); - it('nameOrId return the name', function() { - var obj = {name: 'test_name', id: 'test_id'}; + it('nameOrID returns the name', function() { 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({'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() { expect(ctrl.tableDataMulti).toBeDefined(); expect(ctrl.tableDataMulti.available).toEqual(['port-a', 'port-b']); expect(ctrl.tableDataMulti.allocated).toEqual(['port-a']); - expect(ctrl.tableDataMulti.displayedAllocated).toEqual([]); - expect(ctrl.tableDataMulti.displayedAvailable).toEqual([]); }); }); - }); })(); |