Able to change admin state for all resources

Also make the workflow more independent and predictable. Previously it
is using the extensible workflow methods and does not make a copy of
a step for each workflow, which would mess up with each other.

Change-Id: I2e808282ed3c180105978f359fff7558d9e78ca7
Story: 1713844
Task: 5361
This commit is contained in:
Jacky Hu 2017-12-10 14:48:00 +08:00 committed by Hengqing Hu
parent b307277abe
commit 8a8cd2b85c
26 changed files with 373 additions and 185 deletions

View File

@ -129,7 +129,9 @@ def create_loadbalancer(request):
vip_subnet_id=data['loadbalancer']['subnet'],
name=data['loadbalancer'].get('name'),
description=data['loadbalancer'].get('description'),
vip_address=data['loadbalancer'].get('ip'))
vip_address=data['loadbalancer'].get('ip'),
admin_state_up=data['loadbalancer'].get('admin_state_up')
)
if data.get('listener'):
# There is work underway to add a new API to LBaaS v2 that will
@ -166,7 +168,9 @@ def create_listener(request, **kwargs):
description=data['listener'].get('description'),
connection_limit=data['listener'].get('connection_limit'),
default_tls_container_ref=default_tls_ref,
sni_container_refs=None)
sni_container_refs=None,
admin_state_up=data['listener'].get('admin_state_up')
)
if data.get('pool'):
args = (request, kwargs['loadbalancer_id'], create_pool)
@ -201,7 +205,9 @@ def create_pool(request, **kwargs):
session_persistence=session_persistence,
listener_id=kwargs['listener_id'],
name=data['pool'].get('name'),
description=data['pool'].get('description'))
description=data['pool'].get('description'),
admin_state_up=data['pool'].get('admin_state_up')
)
if data.get('members'):
args = (request, kwargs['loadbalancer_id'], add_member)
@ -232,7 +238,9 @@ def create_health_monitor(request, **kwargs):
pool_id=kwargs['pool_id'],
http_method=data['monitor'].get('method'),
url_path=data['monitor'].get('path'),
expected_codes=data['monitor'].get('status'))
expected_codes=data['monitor'].get('status'),
admin_state_up=data['monitor'].get('admin_state_up')
)
return _get_sdk_object_dict(health_mon)
@ -265,7 +273,9 @@ def add_member(request, **kwargs):
subnet_id=member['subnet'],
weight=member.get('weight'),
monitor_address=monitor_address if monitor_address else None,
monitor_port=member.get('monitor_port'))
monitor_port=member.get('monitor_port'),
admin_state_up=member.get('admin_state_up')
)
index += 1
if kwargs.get('members_to_add'):
@ -326,7 +336,8 @@ def update_loadbalancer(request, **kwargs):
loadbalancer = conn.load_balancer.update_load_balancer(
loadbalancer_id,
name=data['loadbalancer'].get('name'),
description=data['loadbalancer'].get('description'))
description=data['loadbalancer'].get('description'),
admin_state_up=data['loadbalancer'].get('admin_state_up'))
return _get_sdk_object_dict(loadbalancer)
@ -344,7 +355,9 @@ def update_listener(request, **kwargs):
listener=listener_id,
name=data['listener'].get('name'),
description=data['listener'].get('description'),
connection_limit=data['listener'].get('connection_limit'))
connection_limit=data['listener'].get('connection_limit'),
admin_state_up=data['listener'].get('admin_state_up')
)
if data.get('pool'):
args = (request, loadbalancer_id, update_pool)
@ -379,7 +392,9 @@ def update_pool(request, **kwargs):
lb_algorithm=data['pool']['method'],
session_persistence=session_persistence,
name=data['pool'].get('name'),
description=data['pool'].get('description'))
description=data['pool'].get('description'),
admin_state_up=data['pool'].get('admin_state_up')
)
# Assemble the lists of member id's to add and remove, if any exist
request_member_data = data.get('members', [])
@ -419,7 +434,9 @@ def update_monitor(request, **kwargs):
max_retries_down=data['monitor'].get('retry_down'),
http_method=data['monitor'].get('method'),
url_path=data['monitor'].get('path'),
expected_codes=data['monitor'].get('status'))
expected_codes=data['monitor'].get('status'),
admin_state_up=data['monitor'].get('admin_state_up')
)
return _get_sdk_object_dict(healthmonitor)
@ -625,7 +642,7 @@ class Listener(generic.View):
conn.load_balancer.members(pool_id))
resources['members'] = member_list
if pool.get('healt_hmonitor_id'):
if pool.get('health_monitor_id'):
monitor_id = pool['health_monitor_id']
monitor = conn.load_balancer.find_health_monitor(
monitor_id)
@ -844,7 +861,9 @@ class Member(generic.View):
member = conn.load_balancer.update_member(
member_id, pool_id, weight=data.get('weight'),
monitor_address=monitor_address if monitor_address else None,
monitor_port=data.get('monitor_port'))
monitor_port=data.get('monitor_port'),
admin_state_up=data.get('admin_state_up')
)
return _get_sdk_object_dict(member)

View File

@ -36,7 +36,7 @@
scope.workflow = workflowService(
gettext('Create Listener'),
'fa fa-cloud-download',
['listener', 'pool', 'members', 'monitor']
['listener', 'certificates', 'pool', 'members', 'monitor']
);
scope.model.initialize('listener', false, loadbalancerId);
}

View File

@ -45,40 +45,17 @@
function EditListenerWizardController($scope, $q, model, workflowService, gettext) {
var scope = $scope;
var defer = $q.defer();
var steps = ['listener'];
var protocol = scope.launchContext.protocol;
if (protocol === 'TERMINATED_HTTPS') {
steps = ['listener', 'certificates'];
}
scope.model = model;
scope.submit = scope.model.submit;
scope.workflow = workflowService(
gettext('Update Listener'),
'fa fa-pencil', ['listener'],
defer.promise);
var allSteps = scope.workflow.allSteps.concat([scope.workflow.certificatesStep]);
scope.model.initialize('listener', scope.launchContext.id).then(addSteps).then(ready);
function addSteps() {
var steps = scope.model.visibleResources;
steps.map(getStep).forEach(function addStep(step) {
if (!stepExists(step.id)) {
scope.workflow.append(step);
}
});
}
function getStep(id) {
return allSteps.filter(function findStep(step) {
return step.id === id;
})[0];
}
function stepExists(id) {
return scope.workflow.steps.some(function exists(step) {
return step.id === id;
});
}
function ready() {
defer.resolve();
}
'fa fa-pencil', steps);
scope.model.initialize('listener', scope.launchContext.id);
}
})();

View File

@ -30,8 +30,6 @@
};
var workflow = {
steps: [{id: 'listener'}],
allSteps: [{id: 'listener'}, {id: 'pool'}, {id: 'monitor'}],
certificatesStep: {id: 'certificates'},
append: angular.noop
};
@ -62,24 +60,17 @@
expect(scope.workflow).toBe(workflow);
});
it('initializes workflow with correct properties', function() {
it('initializes workflow with correct properties', inject(function($controller) {
scope.launchContext = { id: '1234', protocol: 'TERMINATED_HTTPS' };
ctrl = $controller('EditListenerWizardController', { $scope: scope });
expect(workflowSpy).toHaveBeenCalledWith('Update Listener',
'fa fa-pencil', ['listener'], jasmine.any(Object));
});
'fa fa-pencil', jasmine.any(Object));
}));
it('defines scope.submit', function() {
expect(scope.submit).toBe(model.submit);
expect(scope.submit()).toBe('updated');
});
it('adds necessary steps after initializing', function() {
model.visibleResources = ['listener', 'pool', 'monitor'];
spyOn(workflow, 'append');
scope.$apply();
expect(workflow.append).toHaveBeenCalledWith({id: 'pool'});
expect(workflow.append).toHaveBeenCalledWith({id: 'monitor'});
});
});
})();

View File

@ -50,6 +50,11 @@
patterns, gettext, poolId, member) {
var ctrl = this;
ctrl.adminStateUpOptions = [
{ label: gettext('Yes'), value: true },
{ label: gettext('No'), value: false }
];
// IP address validation pattern
ctrl.ipPattern = [patterns.ipv4, patterns.ipv6].join('|');
@ -58,6 +63,7 @@
ctrl.weight = member.weight;
ctrl.monitor_address = member.monitor_address;
ctrl.monitor_port = member.monitor_port;
ctrl.admin_state_up = member.admin_state_up;
ctrl.cancel = cancel;
ctrl.save = save;
ctrl.saving = false;
@ -72,7 +78,8 @@
return api.editMember(poolId, member.id, {
weight: ctrl.weight,
monitor_address: ctrl.monitor_address,
monitor_port: ctrl.monitor_port
monitor_port: ctrl.monitor_port,
admin_state_up: ctrl.admin_state_up
}).then(onSuccess, onFailure);
}

View File

@ -41,7 +41,8 @@
protocol_port: '443',
weight: 1,
monitor_address: '1.1.1.1',
monitor_port: 80
monitor_port: 80,
admin_state_up: true
});
$provide.value('horizon.app.core.openstack-service-api.lbaasv2', {
editMember: function() {
@ -82,7 +83,8 @@
expect(api.editMember).toHaveBeenCalledWith('pool1', 'member1', {
weight: 1,
monitor_address: '1.1.1.1',
monitor_port: 80
monitor_port: 80,
admin_state_up: true
});
expect($uibModalInstance.close).toHaveBeenCalled();
});

View File

@ -77,6 +77,23 @@
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-8 col-md-6">
<div class="form-group">
<label class="control-label required" translate>Admin State Up</label>
<div class="form-field">
<div class="btn-group">
<label class="btn btn-default"
ng-repeat="option in modal.adminStateUpOptions"
ng-model="modal.admin_state_up"
uib-btn-radio="option.value">{$ ::option.label $}</label>
</div>
</div>
</div>
</div>
</div>
</div>
<help-panel class="wizard-help">
<ng-include src="::modal.helpUrl"></ng-include>

View File

@ -47,41 +47,13 @@
function EditPoolWizardController($scope, $routeParams, $q, model, workflowService, gettext) {
var scope = $scope;
var defer = $q.defer();
var loadbalancerId = $routeParams.loadbalancerId;
scope.model = model;
scope.submit = scope.model.submit;
scope.workflow = workflowService(
gettext('Update Pool'),
'fa fa-pencil', ['pool'],
defer.promise);
scope.model.initialize(
'pool', scope.launchContext.id, loadbalancerId).then(addSteps).then(ready);
function addSteps() {
var steps = scope.model.visibleResources;
steps.map(getStep).forEach(function addStep(step) {
if (!stepExists(step.id)) {
scope.workflow.append(step);
}
});
}
function getStep(id) {
return scope.workflow.allSteps.filter(function findStep(step) {
return step.id === id;
})[0];
}
function stepExists(id) {
return scope.workflow.steps.some(function exists(step) {
return step.id === id;
});
}
function ready() {
defer.resolve();
}
'fa fa-pencil', ['pool']);
scope.model.initialize('pool', scope.launchContext.id, loadbalancerId);
}
})();

View File

@ -30,8 +30,6 @@
};
var workflow = {
steps: [{id: 'pool'}],
allSteps: [{id: 'pool'}, {id: 'pool'}, {id: 'monitor'}],
certificatesStep: {id: 'certificates'},
append: angular.noop
};
@ -66,21 +64,13 @@
it('initializes workflow with correct properties', function() {
expect(workflowSpy).toHaveBeenCalledWith('Update Pool',
'fa fa-pencil', ['pool'], jasmine.any(Object));
'fa fa-pencil', ['pool']);
});
it('defines scope.submit', function() {
expect(scope.submit).toBe(model.submit);
expect(scope.submit()).toBe('updated');
});
it('adds necessary steps after initializing', function() {
model.visibleResources = ['pool', 'monitor'];
spyOn(workflow, 'append');
scope.$apply();
expect(workflow.append).toHaveBeenCalledWith({id: 'monitor'});
});
});
})();

View File

@ -6,7 +6,7 @@
help-text="::ctrl.tableHelp">
<!-- Allocated-->
<allocated validate-number-min="1" ng-model="ctrl.tableData.allocated.length">
<allocated validate-number-min="0" ng-model="ctrl.tableData.allocated.length">
<table st-table="ctrl.tableData.displayedAllocated"
st-safe-src="ctrl.tableData.allocated" hz-table
class="table table-striped table-rsp table-detail">

View File

@ -40,6 +40,11 @@
var ctrl = this;
ctrl.protocolChange = protocolChange;
ctrl.adminStateUpOptions = [
{ label: gettext('Yes'), value: true },
{ label: gettext('No'), value: false }
];
// Error text for invalid fields
ctrl.portNumberError = gettext('The port must be a number between 1 and 65535.');
ctrl.portUniqueError = gettext(
@ -61,16 +66,6 @@
members.forEach(function setMemberPort(member) {
member.port = { HTTP: 80, TERMINATED_HTTPS: 80 }[protocol];
});
var workflow = $scope.workflow;
var certificates = workflow.steps.some(function checkCertificatesStep(step) {
return step.id === 'certificates';
});
if (protocol === 'TERMINATED_HTTPS' && !certificates) {
workflow.after('listener', workflow.certificatesStep);
} else if (protocol !== 'TERMINATED_HTTPS' && certificates) {
workflow.remove('certificates');
}
}
function listenerPortExists(port) {

View File

@ -27,7 +27,6 @@
beforeEach(inject(function($controller) {
workflow = {
steps: [{ id: 'listener' }],
certificatesStep: { id: 'certificates' },
after: angular.noop,
remove: angular.noop
};
@ -54,29 +53,6 @@
expect(ctrl.portUniqueError).toBeDefined();
});
it('should show certificates step if selecting TERMINATED_HTTPS', function() {
workflow.steps.push(workflow.certificatesStep);
spyOn(workflow, 'after');
ctrl.protocolChange('TERMINATED_HTTPS');
expect(workflow.after).not.toHaveBeenCalled();
workflow.steps.splice(1, 1);
ctrl.protocolChange('TERMINATED_HTTPS');
expect(workflow.after).toHaveBeenCalledWith('listener', workflow.certificatesStep);
});
it('should hide certificates step if not selecting TERMINATED_HTTPS', function() {
spyOn(workflow, 'remove');
ctrl.protocolChange('HTTP');
expect(workflow.remove).not.toHaveBeenCalled();
workflow.steps.push(workflow.certificatesStep);
ctrl.protocolChange('HTTP');
expect(workflow.remove).toHaveBeenCalledWith('certificates');
});
it('should update port on protocol change to HTTP', function() {
ctrl.protocolChange('HTTP');
expect(listener.port).toBe(81);

View File

@ -81,4 +81,22 @@
</div>
<div class="row">
<div class="col-xs-12 col-sm-8 col-md-6">
<div class="form-group">
<label class="control-label required" translate>Admin State Up</label>
<div class="form-field">
<div class="btn-group">
<label class="btn btn-default"
ng-repeat="option in ctrl.adminStateUpOptions"
ng-model="model.spec.listener.admin_state_up"
uib-btn-radio="option.value">{$ ::option.label $}</label>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -40,6 +40,11 @@
var ctrl = this;
ctrl.adminStateUpOptions = [
{ label: gettext('Yes'), value: true },
{ label: gettext('No'), value: false }
];
// Error text for invalid fields
ctrl.ipError = gettext('The IP address is not valid.');

View File

@ -51,4 +51,23 @@
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-8 col-md-6">
<div class="form-group">
<label class="control-label required" translate>Admin State Up</label>
<div class="form-field">
<div class="btn-group">
<label class="btn btn-default"
ng-repeat="option in ctrl.adminStateUpOptions"
ng-model="model.spec.loadbalancer.admin_state_up"
uib-btn-radio="option.value">{$ ::option.label $}</label>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -45,6 +45,11 @@
var ctrl = this;
var memberCounter = 0;
ctrl.adminStateUpOptions = [
{ label: gettext('Yes'), value: true },
{ label: gettext('No'), value: false }
];
// Error text for invalid fields
ctrl.portError = gettext('The port must be a number between 1 and 65535.');
ctrl.weightError = gettext('The weight must be a number between 1 and 256.');
@ -115,7 +120,10 @@
address: null,
subnet: null,
port: { HTTP: 80 }[protocol],
weight: 1
weight: 1,
monitor_address: null,
monitor_port: null,
admin_state_up: true
});
}

View File

@ -112,7 +112,7 @@
Can be toggled using the chevron button.
Ensure colspan is greater or equal to number of column-headers.
-->
<td class="detail" colspan="7">
<td class="detail" colspan="8">
<div class="row">
<dl class="col-lg-5 col-md-5 col-sm-5">
@ -140,15 +140,29 @@
ng-attr-popover="{$ memberDetailsForm[row.id + '-monitor-port'].$invalid && memberDetailsForm[row.id + '-monitor-port'].$dirty ? ctrl.portError : '' $}">
</dd>
</dl>
<dl class="col-lg-5 col-md-5 col-sm-5">
<dt class="control-label required" translate>Admin State Up</dt>
<dd class="form-group">
<div class="form-field">
<div class="btn-group">
<label class="btn btn-default"
ng-repeat="option in ctrl.adminStateUpOptions"
ng-model="row.admin_state_up"
ng-disabled="row.allocatedMember"
uib-btn-radio="option.value">{$ ::option.label $}</label>
</div>
</div>
</dd>
</dl>
</div>
</td>
</tr>
<tr table-status table="table" column-count="7"></tr>
<tr table-status table="table" column-count="8"></tr>
<tr>
<td colspan="5">
<td colspan="6">
<action-list class="pull-right">
<action action-classes="'btn btn-sm btn-default'"
callback="ctrl.allocateExternalMember">

View File

@ -139,7 +139,8 @@
name: null,
description: null,
ip: null,
subnet: null
subnet: null,
admin_state_up: true
},
listener: {
id: null,
@ -147,7 +148,8 @@
description: null,
protocol: null,
port: null,
connection_limit: -1
connection_limit: -1,
admin_state_up: true
},
pool: {
id: null,
@ -156,7 +158,8 @@
protocol: null,
method: null,
type: null,
cookie: null
cookie: null,
admin_state_up: true
},
monitor: {
id: null,
@ -167,7 +170,8 @@
timeout: 5,
method: 'GET',
status: '200',
path: '/'
path: '/',
admin_state_up: true
},
members: [],
certificates: []
@ -504,7 +508,8 @@
name: server.name,
weight: 1,
monitor_address: null,
monitor_port: null
monitor_port: null,
admin_state_up: true
});
}
});
@ -623,6 +628,7 @@
spec.description = loadbalancer.description;
spec.ip = loadbalancer.vip_address;
spec.subnet = loadbalancer.vip_subnet_id;
spec.admin_state_up = loadbalancer.admin_state_up;
}
function setListenerSpec(listener) {
@ -633,6 +639,7 @@
spec.protocol = listener.protocol;
spec.port = listener.protocol_port;
spec.connection_limit = listener.connection_limit;
spec.admin_state_up = listener.admin_state_up;
}
function setPoolSpec(pool) {
@ -642,6 +649,7 @@
spec.description = pool.description;
spec.protocol = pool.protocol;
spec.method = pool.lb_algorithm;
spec.admin_state_up = pool.admin_state_up;
if (angular.isObject(pool.session_persistence)) {
var type = pool.session_persistence.type;
var cookie = pool.session_persistence.cookie_name;
@ -667,6 +675,7 @@
weight: member.weight,
monitor_address: member.monitor_address,
monitor_port: member.monitor_port,
admin_state_up: member.admin_state_up,
allocatedMember: true
});
});
@ -684,6 +693,7 @@
spec.method = monitor.http_method;
spec.status = monitor.expected_codes;
spec.path = monitor.url_path;
spec.admin_state_up = monitor.admin_state_up;
}
function onGetHealthMonitor(response) {

View File

@ -26,6 +26,7 @@
beforeEach(function() {
listenerResources = {
listener: {
admin_state_up: true,
id: '1234',
name: 'Listener 1',
description: 'listener description',
@ -36,6 +37,7 @@
sni_container_refs: ['container2']
},
pool: {
admin_state_up: true,
id: '1234',
name: 'Pool 1',
protocol: 'HTTP',
@ -63,6 +65,7 @@
}
],
monitor: {
admin_state_up: true,
id: '1234',
type: 'HTTP',
delay: 1,
@ -94,6 +97,7 @@
},
getLoadBalancer: function() {
var loadbalancer = {
admin_state_up: true,
id: '1234',
name: 'Load Balancer 1',
vip_address: '1.2.3.4',
@ -952,10 +956,10 @@
// to implement tests for them.
it('has the right number of properties', function() {
expect(Object.keys(model.spec).length).toBe(8);
expect(Object.keys(model.spec.loadbalancer).length).toBe(4);
expect(Object.keys(model.spec.listener).length).toBe(6);
expect(Object.keys(model.spec.pool).length).toBe(7);
expect(Object.keys(model.spec.monitor).length).toBe(9);
expect(Object.keys(model.spec.loadbalancer).length).toBe(5);
expect(Object.keys(model.spec.listener).length).toBe(7);
expect(Object.keys(model.spec.pool).length).toBe(8);
expect(Object.keys(model.spec.monitor).length).toBe(10);
expect(model.spec.members).toEqual([]);
});
@ -1249,18 +1253,21 @@
expect(finalSpec.loadbalancer.description).toBeUndefined();
expect(finalSpec.loadbalancer.ip).toBe('1.2.3.4');
expect(finalSpec.loadbalancer.subnet).toBe(model.subnets[0].id);
expect(finalSpec.loadbalancer.admin_state_up).toBe(true);
expect(finalSpec.listener.name).toBe('Listener 1');
expect(finalSpec.listener.description).toBeUndefined();
expect(finalSpec.listener.protocol).toBe('TCP');
expect(finalSpec.listener.port).toBe(80);
expect(finalSpec.listener.connection_limit).toBe(999);
expect(finalSpec.listener.admin_state_up).toBe(true);
expect(finalSpec.pool.name).toBe('pool name');
expect(finalSpec.pool.description).toBe('pool description');
expect(finalSpec.pool.protocol).toBe('TCP');
expect(finalSpec.pool.method).toBe('LEAST_CONNECTIONS');
expect(finalSpec.pool.type).toBe('SOURCE_IP');
expect(finalSpec.pool.admin_state_up).toBe(true);
expect(finalSpec.members.length).toBe(3);
expect(finalSpec.members[0].address).toBe('1.2.3.4');
@ -1290,6 +1297,7 @@
expect(finalSpec.monitor.retry_down).toBe(1);
expect(finalSpec.monitor.timeout).toBe(1);
expect(finalSpec.certificates).toBeUndefined();
expect(finalSpec.monitor.admin_state_up).toBe(true);
});
it('should set final spec certificates', function() {

View File

@ -40,6 +40,11 @@
var ctrl = this;
ctrl.adminStateUpOptions = [
{ label: gettext('Yes'), value: true },
{ label: gettext('No'), value: false }
];
// Error text for invalid fields
/* eslint-disable max-len */
ctrl.intervalError = gettext('The health check interval must be greater than or equal to the timeout.');

View File

@ -124,4 +124,23 @@
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-8 col-md-6">
<div class="form-group">
<label class="control-label required" translate>Admin State Up</label>
<div class="form-field">
<div class="btn-group">
<label class="btn btn-default"
ng-repeat="option in ctrl.adminStateUpOptions"
ng-model="model.spec.monitor.admin_state_up"
uib-btn-radio="option.value">{$ ::option.label $}</label>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,49 @@
/*
* 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.
* 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')
.controller('PoolDetailsController', PoolDetailsController);
PoolDetailsController.$inject = [
'horizon.dashboard.project.lbaasv2.patterns',
'horizon.framework.util.i18n.gettext'
];
/**
* @ngdoc controller
* @name PoolDetailsController
* @description
* The `PoolDetailsController` controller provides functions for
* configuring the pool step of the LBaaS wizard.
* @param patterns The LBaaS v2 patterns constant.
* @param gettext The horizon gettext function for translation.
* @returns undefined
*/
function PoolDetailsController(patterns, gettext) {
var ctrl = this;
ctrl.adminStateUpOptions = [
{ label: gettext('Yes'), value: true },
{ label: gettext('No'), value: false }
];
}
})();

View File

@ -0,0 +1,37 @@
/*
* 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.
* 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('Pool Details Step', function() {
beforeEach(module('horizon.framework.util'));
beforeEach(module('horizon.dashboard.project.lbaasv2'));
describe('PoolDetailsController', function() {
var ctrl;
beforeEach(inject(function($controller) {
ctrl = $controller('PoolDetailsController');
}));
it('should define adminStateUpOptions', function() {
expect(ctrl.adminStateUpOptions).toBeDefined();
});
});
});
})();

View File

@ -1,4 +1,4 @@
<div>
<div ng-controller="PoolDetailsController as ctrl">
<p translate>Provide the details for the pool.</p>
<div class="row">
@ -63,4 +63,23 @@
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-8 col-md-6">
<div class="form-group">
<label class="control-label required" translate>Admin State Up</label>
<div class="form-field">
<div class="btn-group">
<label class="btn btn-default"
ng-repeat="option in ctrl.adminStateUpOptions"
ng-model="model.spec.pool.admin_state_up"
uib-btn-radio="option.value">{$ ::option.label $}</label>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -62,37 +62,62 @@
templateUrl: basePath + 'workflow/monitor/monitor.html',
helpUrl: basePath + 'workflow/monitor/monitor.help.html',
formName: 'monitorDetailsForm'
},
{
id: 'certificates',
title: gettext('SSL Certificates'),
templateUrl: basePath + 'workflow/certificates/certificates.html',
helpUrl: basePath + 'workflow/certificates/certificates.help.html',
formName: 'certificateDetailsForm'
}
];
// This step is kept separate from the rest because it is only added to the workflow by
// the Listener Details step if the TERMINATED_HTTPS protocol is selected.
var certificatesStep = {
id: 'certificates',
title: gettext('SSL Certificates'),
templateUrl: basePath + 'workflow/certificates/certificates.html',
helpUrl: basePath + 'workflow/certificates/certificates.help.html',
formName: 'certificateDetailsForm'
};
return initWorkflow;
function initWorkflow(title, icon, steps, promise) {
function initWorkflow(title, icon, steps) {
var filteredSteps = steps ? workflowSteps.filter(function(step) {
return steps.indexOf(step.id) > -1;
}) : workflowSteps;
var filteredSteps = Array.isArray(steps) ? workflowSteps
.filter(
function(workflowStep) {
return steps.some(function(step) {
if (step.id) {
return step.id === workflowStep.id;
} else {
return step === workflowStep.id;
}
});
}
) : workflowSteps;
// If a promise is provided then add a checkReadiness function to the first step so
// that the workflow will not show until the promise is resolved. There must always
// be at least one step in the workflow.
if (promise) {
filteredSteps[0] = angular.copy(filteredSteps[0]);
filteredSteps[0].checkReadiness = function() {
return promise;
};
if (filteredSteps.length === 0) {
filteredSteps = workflowSteps;
}
filteredSteps = filteredSteps.map(function(filteredStep) {
filteredStep = angular.copy(filteredStep);
if (!Array.isArray(steps)) {
return filteredStep;
}
var step = steps.filter(function(step) {
if (step.id) {
return step.id === filteredStep.id;
} else {
return step === filteredStep.id;
}
})[0];
var deferred;
if (step) {
deferred = step.deferred;
}
if (deferred) {
var promise = deferred.promise;
filteredStep.checkReadiness = function() {
return promise;
};
}
return filteredStep;
});
return dashboardWorkflow({
title: title,
btnText: {
@ -101,9 +126,7 @@
btnIcon: {
finish: icon
},
steps: filteredSteps,
allSteps: workflowSteps,
certificatesStep: certificatesStep
steps: filteredSteps
});
}
}

View File

@ -17,7 +17,7 @@
'use strict';
describe('LBaaS v2 Workflow Service', function() {
var workflowService;
var workflowService, $q;
beforeEach(module('horizon.app.core'));
beforeEach(module('horizon.framework.util'));
@ -29,6 +29,7 @@
workflowService = $injector.get(
'horizon.dashboard.project.lbaasv2.workflow.workflow'
);
$q = $injector.get('$q');
}));
it('should be defined', function () {
@ -43,25 +44,24 @@
it('should have default steps defined', function () {
var workflow = workflowService('My Workflow');
expect(workflow.steps).toBeDefined();
expect(workflow.steps.length).toBe(5);
expect(workflow.steps.length).toBe(6);
var forms = [
'loadBalancerDetailsForm',
'listenerDetailsForm',
'poolDetailsForm',
'memberDetailsForm',
'monitorDetailsForm'
'monitorDetailsForm',
'certificateDetailsForm'
];
forms.forEach(function(expectedForm, idx) {
expect(workflow.steps[idx].formName).toBe(expectedForm);
});
});
it('should have a step for ssl certificates', function () {
var workflow = workflowService('My Workflow');
expect(workflow.certificatesStep).toBeDefined();
expect(workflow.certificatesStep.title).toBe('SSL Certificates');
workflow = workflowService('My Workflow', 'foo', []);
forms.forEach(function(expectedForm, idx) {
expect(workflow.steps[idx].formName).toBe(expectedForm);
});
});
it('can filter steps', function () {
@ -81,10 +81,18 @@
});
it('can wait for all steps to be ready', function () {
var workflow = workflowService('My Workflow', 'foo', null, 'promise');
var steps = ['listener', 'certificates'].map(function(step) {
return {
id: step,
deferred: $q.defer()
};
});
var workflow = workflowService('My Workflow', 'foo', steps);
expect(workflow.steps[0].checkReadiness).toBeDefined();
expect(workflow.steps[0].checkReadiness()).toBe('promise');
expect(workflow.steps[0].checkReadiness()).toBe(steps[0].deferred.promise);
expect(workflow.steps[1].checkReadiness).toBeDefined();
expect(workflow.steps[1].checkReadiness()).toBe(steps[1].deferred.promise);
});
it('can be extended', function () {