Able to change monitor address and port of member

Change-Id: Ice27b281359342c99d6b5ac618feda6f24688b66
Story: 1713866
Task: 5372
This commit is contained in:
Jacky Hu 2017-11-14 16:27:24 +08:00
parent c035d81183
commit 536da0119a
20 changed files with 324 additions and 144 deletions

View File

@ -245,12 +245,15 @@ def add_member(request, **kwargs):
member = members[index] member = members[index]
conn = _get_sdk_connection(request) conn = _get_sdk_connection(request)
monitor_address = member.get('monitor_address')
member = conn.load_balancer.create_member( member = conn.load_balancer.create_member(
pool_id, pool_id,
address=member['address'], address=member['address'],
protocol_port=member['port'], protocol_port=member['port'],
subnet_id=member['subnet'], subnet_id=member['subnet'],
weight=member.get('weight')) weight=member.get('weight'),
monitor_address=monitor_address if monitor_address else None,
monitor_port=member.get('monitor_port'))
index += 1 index += 1
if kwargs.get('members_to_add'): if kwargs.get('members_to_add'):
@ -780,8 +783,11 @@ class Member(generic.View):
""" """
data = request.DATA data = request.DATA
conn = _get_sdk_connection(request) conn = _get_sdk_connection(request)
monitor_address = data.get('monitor_address')
member = conn.load_balancer.update_member( member = conn.load_balancer.update_member(
member_id, pool_id, weight=data['weight']) member_id, pool_id, weight=data.get('weight'),
monitor_address=monitor_address if monitor_address else None,
monitor_port=data.get('monitor_port'))
return _get_sdk_object_dict(member) return _get_sdk_object_dict(member)

View File

@ -607,9 +607,6 @@ msgstr "Pool-ID"
msgid "Pool Members" msgid "Pool Members"
msgstr "Pool-Mitglieder" msgstr "Pool-Mitglieder"
msgid "Pool member weight has been updated."
msgstr "Pool-Mitglied Gewicht wurde aktualisiert."
msgid "Port" msgid "Port"
msgstr "Port" msgstr "Port"
@ -822,14 +819,6 @@ msgstr "Das Zeitlimit muss eine Zahl größer oder gleich 0 sein."
msgid "The weight must be a number between 1 and 256." msgid "The weight must be a number between 1 and 256."
msgstr "Das Gewicht muss eine Zahl zwischen 1 und 256 sein." msgstr "Das Gewicht muss eine Zahl zwischen 1 und 256 sein."
msgid ""
"The weight of a member determines the portion of requests or connections it "
"services compared to the other members of the pool."
msgstr ""
"Das Gewicht eines Mitglieds bestimmt die Menge an Anfragen oder "
"Verbindungen, die es bedient, verglichen mit den anderen Mitgliedern des "
"Pools."
msgid "Timeout" msgid "Timeout"
msgstr "Zeitlimit" msgstr "Zeitlimit"
@ -938,15 +927,9 @@ msgstr "Listener aktualisieren"
msgid "Update Load Balancer" msgid "Update Load Balancer"
msgstr "Loadbalancer aktualisieren" msgstr "Loadbalancer aktualisieren"
msgid "Update Member Weight"
msgstr "Mitglied-Gewicht aktualisieren"
msgid "Update Pool" msgid "Update Pool"
msgstr "Pool aktualisieren" msgstr "Pool aktualisieren"
msgid "Update Weight"
msgstr "Gewicht aktualisieren"
msgid "" msgid ""
"Use the key-manager service to create any certificate containers before " "Use the key-manager service to create any certificate containers before "
"creating the listener.\n" "creating the listener.\n"

View File

@ -212,6 +212,24 @@ msgstr ""
"akan menerima lebih banyak lalu lintas. Harus dalam \n" "akan menerima lebih banyak lalu lintas. Harus dalam \n"
"   nomor 1 sampai 256." "   nomor 1 sampai 256."
msgid ""
"<strong>Monitor Address:</strong>\n"
" An alternate IP address used for health monitoring a backend member.\n"
" Default is null which monitors the member address."
msgstr ""
"<strong>Monitor Address:</strong>\n"
" An alternate IP address used for health monitoring a backend member.\n"
" Default is null which monitors the member address."
msgid ""
"<strong>Monitor Port:</strong>\n"
" An alternate protocol port used for health monitoring a backend member.\n"
" Default is null which monitors the member protocol port."
msgstr ""
"<strong>Monitor Port:</strong>\n"
" An alternate protocol port used for health monitoring a backend member.\n"
" Default is null which monitors the member protocol port."
msgid "A new health monitor is being created." msgid "A new health monitor is being created."
msgstr "Sebuah pemantauan kesehatan baru sedang dibuat." msgstr "Sebuah pemantauan kesehatan baru sedang dibuat."
@ -621,8 +639,8 @@ msgstr "Pool ID (ID kolam)"
msgid "Pool Members" msgid "Pool Members"
msgstr "Pool Members (anggota kolam)" msgstr "Pool Members (anggota kolam)"
msgid "Pool member weight has been updated." msgid "Pool member has been updated."
msgstr "Berat anggota kolam telah diperbarui." msgstr "Pool member has been updated."
msgid "Port" msgid "Port"
msgstr "Port" msgstr "Port"
@ -665,6 +683,9 @@ msgstr "Memberikan rincian untuk penyeimbang beban."
msgid "Provide the details for the pool." msgid "Provide the details for the pool."
msgstr "Memberikan rincian untuk kolam." msgstr "Memberikan rincian untuk kolam."
msgid "Provide the details for the member."
msgstr "Provide the details for the member."
msgid "Provider" msgid "Provider"
msgstr "Provider (penyedia)" msgstr "Provider (penyedia)"
@ -837,12 +858,11 @@ msgstr "Timeout harus berupa angka lebih besar dari atau sama dengan 0."
msgid "The weight must be a number between 1 and 256." msgid "The weight must be a number between 1 and 256."
msgstr "Berat harus dalam angka antara 1 dan 256." msgstr "Berat harus dalam angka antara 1 dan 256."
msgid "" msgid "The monitor address must be a vaid IP address."
"The weight of a member determines the portion of requests or connections it " msgstr "The monitor address must be a vaid IP address."
"services compared to the other members of the pool."
msgstr "" msgid "The monitor port must be a number between 1 and 65535."
"Berat anggota menentukan porsi dari permintaan atau koneksi layanan IT " msgstr "The monitor port must be a number between 1 and 65535."
"dibandingkan dengan anggota lain dari kolam."
msgid "Timeout" msgid "Timeout"
msgstr "Timeout (waktu habis)" msgstr "Timeout (waktu habis)"
@ -952,15 +972,12 @@ msgstr "Update Listener (perbarui pendengar)"
msgid "Update Load Balancer" msgid "Update Load Balancer"
msgstr "Update Load Balancer (perbarui penyeimbang beban)" msgstr "Update Load Balancer (perbarui penyeimbang beban)"
msgid "Update Member Weight" msgid "Update Member"
msgstr "Update Member Weight" msgstr "Update Member"
msgid "Update Pool" msgid "Update Pool"
msgstr "Update Pool (perbarui kolam)" msgstr "Update Pool (perbarui kolam)"
msgid "Update Weight"
msgstr "Update Weight (perbarui berat)"
msgid "Updated At" msgid "Updated At"
msgstr "Updated At" msgstr "Updated At"
@ -1006,6 +1023,12 @@ msgstr ""
msgid "Weight" msgid "Weight"
msgstr "Weight (berat)" msgstr "Weight (berat)"
msgid "Monitor Address"
msgstr "Monitor Address"
msgid "Monitor Port"
msgstr "Monitor Port"
msgid "" msgid ""
"Weight\n" "Weight\n"
" <span class=\"hz-icon-required fa fa-asterisk\"></span>" " <span class=\"hz-icon-required fa fa-asterisk\"></span>"
@ -1013,6 +1036,20 @@ msgstr ""
"Weight (berat) \n" "Weight (berat) \n"
" <span class=\"hz-icon-required fa fa-asterisk\"></span>" " <span class=\"hz-icon-required fa fa-asterisk\"></span>"
msgid ""
"IP Address\n"
" <span class=\"hz-icon-required fa fa-asterisk\"></span>"
msgstr ""
"IP Address\n"
" <span class=\"hz-icon-required fa fa-asterisk\"></span>"
msgid ""
"Protocol Port\n"
" <span class=\"hz-icon-required fa fa-asterisk\"></span>"
msgstr ""
"Protocol Port\n"
" <span class=\"hz-icon-required fa fa-asterisk\"></span>"
#, python-format #, python-format
msgid "" msgid ""
"You are about to disassociate the floating IP address from load balancer \"%s" "You are about to disassociate the floating IP address from load balancer \"%s"

View File

@ -386,9 +386,6 @@ msgstr "プール ID"
msgid "Pool Members" msgid "Pool Members"
msgstr "プールメンバー" msgstr "プールメンバー"
msgid "Pool member weight has been updated."
msgstr "プールメンバーのウェイトが更新されました。"
msgid "Port" msgid "Port"
msgstr "ポート" msgstr "ポート"
@ -623,15 +620,9 @@ msgstr "リスナーの更新"
msgid "Update Load Balancer" msgid "Update Load Balancer"
msgstr "ロードバランサーの更新" msgstr "ロードバランサーの更新"
msgid "Update Member Weight"
msgstr "メンバーウェイトの更新"
msgid "Update Pool" msgid "Update Pool"
msgstr "プールの更新" msgstr "プールの更新"
msgid "Update Weight"
msgstr "ウェイトの更新"
msgid "Weight" msgid "Weight"
msgstr "ウェイト" msgstr "ウェイト"

View File

@ -536,9 +536,6 @@ msgstr "ИД пула"
msgid "Pool Members" msgid "Pool Members"
msgstr "Участники пула" msgstr "Участники пула"
msgid "Pool member weight has been updated."
msgstr "Весь элемента пула был изменен."
msgid "Port" msgid "Port"
msgstr "Порт" msgstr "Порт"
@ -718,13 +715,6 @@ msgstr "Значение таймаута должно быть больше и
msgid "The weight must be a number between 1 and 256." msgid "The weight must be a number between 1 and 256."
msgstr "Значение веса должно быть в диапозоне между 1 и 256." msgstr "Значение веса должно быть в диапозоне между 1 и 256."
msgid ""
"The weight of a member determines the portion of requests or connections it "
"services compared to the other members of the pool."
msgstr ""
"Вес участника определяет долю обслуживаемых запросов или соединений по "
"отношению к другим участникам пула."
msgid "Timeout" msgid "Timeout"
msgstr "Таймаут" msgstr "Таймаут"
@ -833,15 +823,9 @@ msgstr "Обновить получатель"
msgid "Update Load Balancer" msgid "Update Load Balancer"
msgstr "Обновить Балансировщик Нагрузки" msgstr "Обновить Балансировщик Нагрузки"
msgid "Update Member Weight"
msgstr "Обновить весь участника"
msgid "Update Pool" msgid "Update Pool"
msgstr "Обновить пул" msgstr "Обновить пул"
msgid "Update Weight"
msgstr "Обновить вес"
msgid "Weight" msgid "Weight"
msgstr "Вес" msgstr "Вес"

View File

@ -581,9 +581,6 @@ msgstr "资源池ID"
msgid "Pool Members" msgid "Pool Members"
msgstr "资源池成员" msgstr "资源池成员"
msgid "Pool member weight has been updated."
msgstr "负载均衡池成员权重已经更新。"
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
@ -775,11 +772,6 @@ msgstr "超时时限必须为一个大于等于0的整数。"
msgid "The weight must be a number between 1 and 256." msgid "The weight must be a number between 1 and 256."
msgstr "权重必须为1到256的整数。" msgstr "权重必须为1到256的整数。"
msgid ""
"The weight of a member determines the portion of requests or connections it "
"services compared to the other members of the pool."
msgstr "成员的权重决定了它在服务中与其他池成员的请求和连接的占比。"
msgid "Timeout" msgid "Timeout"
msgstr "超时时限" msgstr "超时时限"
@ -887,15 +879,9 @@ msgstr "更新监听器"
msgid "Update Load Balancer" msgid "Update Load Balancer"
msgstr "更新负载均衡器" msgstr "更新负载均衡器"
msgid "Update Member Weight"
msgstr "更新成员权重"
msgid "Update Pool" msgid "Update Pool"
msgstr "更新资源池" msgstr "更新资源池"
msgid "Update Weight"
msgstr "更新权重"
msgid "" msgid ""
"Use the key-manager service to create any certificate containers before " "Use the key-manager service to create any certificate containers before "
"creating the listener.\n" "creating the listener.\n"

View File

@ -26,6 +26,12 @@
.member-address { .member-address {
width: 18em; width: 18em;
} }
.member-monitor-port {
width: 6em;
}
.member-monitor-address {
width: 18em;
}
} }
/* The IP addresses list displayed when hovering over the IP address in the /* The IP addresses list displayed when hovering over the IP address in the
@ -88,4 +94,4 @@ detail-status {
text-align: center; text-align: center;
margin-top: 10px; margin-top: 10px;
} }
} }

View File

@ -18,11 +18,13 @@
angular angular
.module('horizon.dashboard.project.lbaasv2.members') .module('horizon.dashboard.project.lbaasv2.members')
.controller('EditWeightModalController', EditWeightModalController); .controller('EditMemberModalController', EditMemberModalController);
EditWeightModalController.$inject = [ EditMemberModalController.$inject = [
'$uibModalInstance', '$uibModalInstance',
'horizon.app.core.openstack-service-api.lbaasv2', 'horizon.app.core.openstack-service-api.lbaasv2',
'horizon.dashboard.project.lbaasv2.basePath',
'horizon.dashboard.project.lbaasv2.patterns',
'horizon.framework.util.i18n.gettext', 'horizon.framework.util.i18n.gettext',
// Dependencies injected with resolve by $uibModal.open // Dependencies injected with resolve by $uibModal.open
'poolId', 'poolId',
@ -31,9 +33,9 @@
/** /**
* @ngdoc controller * @ngdoc controller
* @name EditWeightModalController * @name EditMemberModalController
* @description * @description
* Controller used by the modal service for editing the weight of a pool member. * Controller used by the modal service for editing a pool member.
* *
* @param $uibModalInstance The angular bootstrap $uibModalInstance service. * @param $uibModalInstance The angular bootstrap $uibModalInstance service.
* @param api The LBaaS v2 API service. * @param api The LBaaS v2 API service.
@ -41,22 +43,37 @@
* @param poolId The pool ID. * @param poolId The pool ID.
* @param member The pool member to update. * @param member The pool member to update.
* *
* @returns The Edit Weight modal controller. * @returns The Edit Member modal controller.
*/ */
function EditWeightModalController($uibModalInstance, api, gettext, poolId, member) { function EditMemberModalController($uibModalInstance, api, basePath,
patterns, gettext, poolId, member) {
var ctrl = this; var ctrl = this;
// IP address validation pattern
ctrl.ipPattern = [patterns.ipv4, patterns.ipv6].join('|');
ctrl.address = member.address;
ctrl.protocol_port = member.protocol_port;
ctrl.weight = member.weight; ctrl.weight = member.weight;
ctrl.monitor_address = member.monitor_address;
ctrl.monitor_port = member.monitor_port;
ctrl.cancel = cancel; ctrl.cancel = cancel;
ctrl.save = save; ctrl.save = save;
ctrl.saving = false; ctrl.saving = false;
ctrl.weightError = gettext('The weight must be a number between 1 and 256.'); ctrl.weightError = gettext('The weight must be a number between 1 and 256.');
ctrl.monitorAddressError = gettext('The monitor address must be a vaid IP address.');
ctrl.monitorPortError = gettext('The monitor port must be a number between 1 and 65535.');
ctrl.helpUrl = basePath + 'workflow/members/members.help.html';
function save() { function save() {
ctrl.saving = true; ctrl.saving = true;
return api.editMember(poolId, member.id, { weight: ctrl.weight }) return api.editMember(poolId, member.id, {
.then(onSuccess, onFailure); weight: ctrl.weight,
monitor_address: ctrl.monitor_address,
monitor_port: ctrl.monitor_port
}).then(onSuccess, onFailure);
} }
function cancel() { function cancel() {

View File

@ -16,7 +16,7 @@
(function () { (function () {
'use strict'; 'use strict';
describe('LBaaS v2 Member Edit Weight Controller', function() { describe('LBaaS v2 Member Edit Controller', function() {
var ctrl, api, $controller, $uibModalInstance, $scope, $q; var ctrl, api, $controller, $uibModalInstance, $scope, $q;
var fail = false; var fail = false;
@ -37,7 +37,11 @@
$provide.value('poolId', 'pool1'); $provide.value('poolId', 'pool1');
$provide.value('member', { $provide.value('member', {
id: 'member1', id: 'member1',
weight: 1 address: '3.3.3.3',
protocol_port: '443',
weight: 1,
monitor_address: '1.1.1.1',
monitor_port: 80
}); });
$provide.value('horizon.app.core.openstack-service-api.lbaasv2', { $provide.value('horizon.app.core.openstack-service-api.lbaasv2', {
editMember: function() { editMember: function() {
@ -52,24 +56,34 @@
$uibModalInstance = $injector.get('$uibModalInstance'); $uibModalInstance = $injector.get('$uibModalInstance');
$scope = $injector.get('$rootScope').$new(); $scope = $injector.get('$rootScope').$new();
$q = $injector.get('$q'); $q = $injector.get('$q');
ctrl = $controller('EditWeightModalController'); ctrl = $controller('EditMemberModalController');
})); }));
it('should define controller properties', function() { it('should define controller properties', function() {
expect(ctrl.cancel).toBeDefined(); expect(ctrl.cancel).toBeDefined();
expect(ctrl.save).toBeDefined(); expect(ctrl.save).toBeDefined();
expect(ctrl.saving).toBe(false); expect(ctrl.saving).toBe(false);
expect(ctrl.address).toBeDefined();
expect(ctrl.protocol_port).toBeDefined();
expect(ctrl.weight).toBe(1); expect(ctrl.weight).toBe(1);
expect(ctrl.ipPattern).toBeDefined();
expect(ctrl.helpUrl).toBeDefined();
expect(ctrl.weightError).toBe('The weight must be a number between 1 and 256.'); expect(ctrl.weightError).toBe('The weight must be a number between 1 and 256.');
expect(ctrl.monitorAddressError).toBe('The monitor address must be a vaid IP address.');
expect(ctrl.monitorPortError).toBe('The monitor port must be a number between 1 and 65535.');
}); });
it('should edit member weight', function() { it('should edit member weight, monitor address and port', function() {
spyOn(api, 'editMember').and.callThrough(); spyOn(api, 'editMember').and.callThrough();
spyOn($uibModalInstance, 'close'); spyOn($uibModalInstance, 'close');
ctrl.save(); ctrl.save();
$scope.$apply(); $scope.$apply();
expect(ctrl.saving).toBe(true); expect(ctrl.saving).toBe(true);
expect(api.editMember).toHaveBeenCalledWith('pool1', 'member1', { weight: 1 }); expect(api.editMember).toHaveBeenCalledWith('pool1', 'member1', {
weight: 1,
monitor_address: '1.1.1.1',
monitor_port: 80
});
expect($uibModalInstance.close).toHaveBeenCalled(); expect($uibModalInstance.close).toHaveBeenCalled();
}); });

View File

@ -0,0 +1,96 @@
<div class="modal-header">
<h3 class="modal-title">
<span translate>Update Member</span>
</h3>
</div>
<div class="modal-body lbaas-modal">
<div ng-form="form">
<p translate>Provide the details for the member.</p>
<div class="row">
<div class="col-sm-12 col-md-6 col-lg-4">
<div class="form-group"
ng-class="{ 'has-error': form.address.$invalid && form.address.$dirty }">
<label translate class="control-label" for="address">
IP Address
<span class="hz-icon-required fa fa-asterisk"></span>
</label>
<input name="address" id="address" type="text" class="form-control"
ng-model="modal.address" ng-pattern="::modal.ipPattern" ng-required="true" ng-disabled="true">
</div>
</div>
<div class="col-sm-12 col-md-6 col-lg-4">
<div class="form-group"
ng-class="{ 'has-error': form.protocol_port.$invalid && form.protocol_port.$dirty }">
<label translate class="control-label" for="protocol_port">
Protocol Port
<span class="hz-icon-required fa fa-asterisk"></span>
</label>
<input name="protocol_port" id="protocol_port" type="number" class="form-control"
ng-model="modal.protocol_port" ng-pattern="/^\d+$/" min="1" max="65535" ng-required="true" ng-disabled="true">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-6 col-lg-4">
<div class="form-group required"
ng-class="{ 'has-error': form.weight.$invalid && form.weight.$dirty }">
<label translate class="control-label" for="weight">
Weight
<span class="hz-icon-required fa fa-asterisk"></span>
</label>
<input name="weight" id="weight" type="number" class="form-control"
ng-model="modal.weight" ng-pattern="/^\d+$/" min="1" max="256"
ng-required="true">
<span class="help-block" ng-show="form.weight.$invalid && form.weight.$dirty">
{$ ::modal.weightError $}
</span>
</div>
</div>
<div class="col-sm-12 col-md-6 col-lg-4">
<div class="form-group"
ng-class="{ 'has-error': form.monitor_address.$invalid && form.monitor_address.$dirty }">
<label translate class="control-label" for="monitor_address">
Monitor Address
</label>
<input name="monitor_address" id="monitor_address" type="text" class="form-control"
ng-model="modal.monitor_address" ng-pattern="::modal.ipPattern"
>
<span class="help-block" ng-show="form.monitor_address.$invalid && form.monitor_address.$dirty">
{$ ::modal.monitorAddressError $}
</span>
</div>
</div>
<div class="col-sm-12 col-md-6 col-lg-4">
<div class="form-group"
ng-class="{ 'has-error': form.monitor_port.$invalid && form.monitor_port.$dirty }">
<label translate class="control-label" for="monitor_port">
Monitor Port
</label>
<input name="monitor_port" id="monitor_port" type="number" class="form-control"
ng-model="modal.monitor_port" ng-pattern="/^\d+$/" min="1" max="65535"
>
<span class="help-block" ng-show="form.monitor_port.$invalid && form.monitor_port.$dirty">
{$ ::modal.monitorPortError $}
</span>
</div>
</div>
</div>
</div>
<help-panel class="wizard-help">
<ng-include src="::modal.helpUrl"></ng-include>
</help-panel>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-default" ng-click="modal.cancel()">
<span class="fa fa-close"></span>
<span translate>Cancel</span>
</button>
<button class="btn btn-sm btn-primary"
ng-click="modal.save()"
ng-disabled="form.$invalid || modal.saving">
<span class="fa" ng-class="modal.saving ? 'fa-spinner fa-spin' : 'fa-check'"></span>
<span translate>Update</span>
</button>
</div>

View File

@ -18,7 +18,7 @@
angular angular
.module('horizon.dashboard.project.lbaasv2.members') .module('horizon.dashboard.project.lbaasv2.members')
.factory('horizon.dashboard.project.lbaasv2.members.actions.edit-weight.modal.service', .factory('horizon.dashboard.project.lbaasv2.members.actions.edit-member.modal.service',
modalService); modalService);
modalService.$inject = [ modalService.$inject = [
@ -33,10 +33,10 @@
/** /**
* @ngdoc service * @ngdoc service
* @ngname horizon.dashboard.project.lbaasv2.members.actions.edit-weight.modal.service * @ngname horizon.dashboard.project.lbaasv2.members.actions.edit-member.modal.service
* *
* @description * @description
* Provides the service for the pool member Edit Weight action. * Provides the service for the pool member Edit Member action.
* *
* @param $q The angular service for promises. * @param $q The angular service for promises.
* @param $uibModal The angular bootstrap $uibModal service. * @param $uibModal The angular bootstrap $uibModal service.
@ -46,7 +46,7 @@
* @param toastService The horizon toast service. * @param toastService The horizon toast service.
* @param gettext The horizon gettext function for translation. * @param gettext The horizon gettext function for translation.
* *
* @returns The Edit Weight modal service. * @returns The Edit Member modal service.
*/ */
function modalService( function modalService(
@ -99,8 +99,8 @@
function open(item) { function open(item) {
var spec = { var spec = {
backdrop: 'static', backdrop: 'static',
controller: 'EditWeightModalController as modal', controller: 'EditMemberModalController as modal',
templateUrl: basePath + 'members/actions/edit-weight/modal.html', templateUrl: basePath + 'members/actions/edit-member/modal.html',
resolve: { resolve: {
poolId: function() { poolId: function() {
return poolId; return poolId;
@ -114,7 +114,7 @@
} }
function onModalClose() { function onModalClose() {
toastService.add('success', gettext('Pool member weight has been updated.')); toastService.add('success', gettext('Pool member has been updated.'));
$route.reload(); $route.reload();
} }

View File

@ -16,7 +16,7 @@
(function () { (function () {
'use strict'; 'use strict';
describe('LBaaS v2 Member Edit Weight Service', function() { describe('LBaaS v2 Member Edit Service', function() {
var service, policy, $scope, $route, $uibModal, toast; var service, policy, $scope, $route, $uibModal, toast;
var member = { id: 'member1' }; var member = { id: 'member1' };
@ -65,7 +65,7 @@
$route = $injector.get('$route'); $route = $injector.get('$route');
$uibModal = $injector.get('$uibModal'); $uibModal = $injector.get('$uibModal');
service = $injector.get( service = $injector.get(
'horizon.dashboard.project.lbaasv2.members.actions.edit-weight.modal.service'); 'horizon.dashboard.project.lbaasv2.members.actions.edit-member.modal.service');
service.init('pool1', fakePromise()); service.init('pool1', fakePromise());
})); }));
@ -101,7 +101,7 @@
spyOn($route, 'reload'); spyOn($route, 'reload');
service.perform(member); service.perform(member);
$scope.$apply(); $scope.$apply();
expect(toast.add).toHaveBeenCalledWith('success', 'Pool member weight has been updated.'); expect(toast.add).toHaveBeenCalledWith('success', 'Pool member has been updated.');
expect($route.reload).toHaveBeenCalled(); expect($route.reload).toHaveBeenCalled();
}); });

View File

@ -1,39 +0,0 @@
<div class="modal-header">
<h3 class="modal-title">
<span translate>Update Member Weight</span>
</h3>
</div>
<div class="modal-body lbaas-modal">
<p translate>The weight of a member determines the portion of requests or connections it services compared to the other members of the pool.</p>
<div ng-form="form">
<div class="row">
<div class="col-sm-12 col-md-6">
<div class="form-group required"
ng-class="{ 'has-error': form.weight.$invalid && form.weight.$dirty }">
<label translate class="control-label" for="weight">
Weight
<span class="hz-icon-required fa fa-asterisk"></span>
</label>
<input name="weight" id="weight" type="number" class="form-control"
ng-model="modal.weight" ng-pattern="/^\d+$/" min="1" max="256"
ng-required="true">
<span class="help-block" ng-show="form.weight.$invalid && form.weight.$dirty">
{$ ::modal.weightError $}
</span>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-sm btn-default" ng-click="modal.cancel()">
<span class="fa fa-close"></span>
<span translate>Cancel</span>
</button>
<button class="btn btn-sm btn-primary"
ng-click="modal.save()"
ng-disabled="form.$invalid || modal.saving">
<span class="fa" ng-class="modal.saving ? 'fa-spinner fa-spin' : 'fa-check'"></span>
<span translate>Update</span>
</button>
</div>

View File

@ -23,7 +23,7 @@
rowActions.$inject = [ rowActions.$inject = [
'horizon.framework.util.i18n.gettext', 'horizon.framework.util.i18n.gettext',
'horizon.dashboard.project.lbaasv2.loadbalancers.service', 'horizon.dashboard.project.lbaasv2.loadbalancers.service',
'horizon.dashboard.project.lbaasv2.members.actions.edit-weight.modal.service' 'horizon.dashboard.project.lbaasv2.members.actions.edit-member.modal.service'
]; ];
/** /**
@ -35,11 +35,11 @@
* *
* @param gettext The horizon gettext function for translation. * @param gettext The horizon gettext function for translation.
* @param loadBalancersService The LBaaS v2 load balancers service. * @param loadBalancersService The LBaaS v2 load balancers service.
* @param editWeight The LBaaS v2 pool member edit weight service. * @param editMember The LBaaS v2 pool member edit service.
* @returns Members row actions service object. * @returns Members row actions service object.
*/ */
function rowActions(gettext, loadBalancersService, editWeight) { function rowActions(gettext, loadBalancersService, editMember) {
var loadBalancerIsActionable, poolId; var loadBalancerIsActionable, poolId;
var service = { var service = {
@ -59,9 +59,9 @@
function actions() { function actions() {
return [{ return [{
service: editWeight.init(poolId, loadBalancerIsActionable), service: editMember.init(poolId, loadBalancerIsActionable),
template: { template: {
text: gettext('Update Weight') text: gettext('Edit')
} }
}]; }];
} }

View File

@ -36,7 +36,7 @@
it('should define correct table row actions', function() { it('should define correct table row actions', function() {
expect(actions.length).toBe(1); expect(actions.length).toBe(1);
expect(actions[0].template.text).toBe('Update Weight'); expect(actions[0].template.text).toBe('Edit');
}); });
it('should have the "allowed" and "perform" functions', function() { it('should have the "allowed" and "perform" functions', function() {

View File

@ -33,6 +33,10 @@
<dd>{$ ctrl.member.subnet_id $}</dd> <dd>{$ ctrl.member.subnet_id $}</dd>
<dt translate>Weight</dt> <dt translate>Weight</dt>
<dd>{$ ctrl.member.weight $}</dd> <dd>{$ ctrl.member.weight $}</dd>
<dt translate>Monitor Address</dt>
<dd>{$ ::ctrl.member.monitor_address | noValue:('None' | translate) $}</dd>
<dt translate>Monitor Port</dt>
<dd>{$ ::ctrl.member.monitor_port | noValue:('None' | translate) $}</dd>
<dt translate>Created At</dt> <dt translate>Created At</dt>
<dd>{$ ::ctrl.member.created_at $}</dd> <dd>{$ ::ctrl.member.created_at $}</dd>
<dt translate>Updated At</dt> <dt translate>Updated At</dt>

View File

@ -35,6 +35,8 @@
<input type="checkbox" hz-select-all="table.items"> <input type="checkbox" hz-select-all="table.items">
</th> </th>
<th class="expander"></th>
<th class="rsp-p1" st-sort="id" st-sort-default="id" translate>ID</th> <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="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>Protocol Port</th>
@ -54,7 +56,7 @@
Include action-col if you want to perform actions. Include action-col if you want to perform actions.
rsp-p1 rsp-p2 are responsive priority as user resizes window. rsp-p1 rsp-p2 are responsive priority as user resizes window.
--> -->
<tr ng-repeat="item in table.items track by item.id" <tr ng-repeat-start="item in table.items track by item.id"
ng-class="{'st-selected': checked[item.id]}"> ng-class="{'st-selected': checked[item.id]}">
<td class="multi_select_column"> <td class="multi_select_column">
@ -62,6 +64,12 @@
ng-model="tCtrl.selections[item.id].checked" ng-model="tCtrl.selections[item.id].checked"
hz-select="item"> hz-select="item">
</td> </td>
<td class="expander">
<span class="fa fa-chevron-right"
hz-expand-detail
duration="200">
</span>
</td>
<td class="rsp-p1"><a ng-href="project/load_balancer/{$ ::table.loadbalancerId $}/listeners/{$ ::table.listenerId $}/pools/{$ ::table.poolId $}/members/{$ ::item.id $}">{$ ::item.id $}</a></td> <td class="rsp-p1"><a ng-href="project/load_balancer/{$ ::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.address $}</td>
<td class="rsp-p1">{$ ::item.protocol_port $}</td> <td class="rsp-p1">{$ ::item.protocol_port $}</td>
@ -77,7 +85,30 @@
</td> </td>
</tr> </tr>
<tr table-status table="table" column-count="6"></tr> <tr ng-repeat-end class="detail-row">
<!--
Detail-row:
Contains detailed information on this item.
Can be toggled using the chevron button.
Ensure colspan is greater or equal to number of column-headers.
-->
<td class="detail" colspan="9">
<div class="row">
<dl class="col-sm-2">
<dt translate>Monitor Address</dt>
<dd>{$ ::item.monitor_address | noValue:('None' | translate) $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>Monitor Port</dt>
<dd>{$ ::item.monitor_port | noValue:('None' | translate) $}</dd>
</dl>
</div>
</td>
</tr>
<tr table-status table="table" column-count="9"></tr>
</tbody> </tbody>
<!-- <!--

View File

@ -26,3 +26,13 @@
to the other members of the pool. A higher weight means it will receive more traffic. Must be to the other members of the pool. A higher weight means it will receive more traffic. Must be
a number from 1 to 256. a number from 1 to 256.
</p> </p>
<p translate>
<strong>Monitor Address:</strong>
An alternate IP address used for health monitoring a backend member.
Default is null which monitors the member address.
</p>
<p translate>
<strong>Monitor Port:</strong>
An alternate protocol port used for health monitoring a backend member.
Default is null which monitors the member protocol port.
</p>

View File

@ -11,6 +11,8 @@
class="table table-striped table-rsp table-detail"> class="table table-striped table-rsp table-detail">
<thead> <thead>
<tr> <tr>
<th class="expander"></th>
<th class="rsp-p1" translate> <th class="rsp-p1" translate>
IP Address IP Address
<span class="hz-icon-required fa fa-asterisk" ng-show="ctrl.tableData.displayedAllocated.length > 0"> <span class="hz-icon-required fa fa-asterisk" ng-show="ctrl.tableData.displayedAllocated.length > 0">
@ -35,7 +37,13 @@
</div> </div>
</td> </td>
</tr> </tr>
<tr ng-repeat="row in ctrl.tableData.displayedAllocated track by row.id"> <tr ng-repeat-start="row in ctrl.tableData.displayedAllocated track by row.id">
<td class="expander">
<span class="fa fa-chevron-right"
hz-expand-detail
duration="200">
</span>
</td>
<td class="rsp-p1"> <td class="rsp-p1">
<div ng-if="!row.addresses" <div ng-if="!row.addresses"
class="form-group required member-address" class="form-group required member-address"
@ -97,6 +105,48 @@
</action-list> </action-list>
</td> </td>
</tr> </tr>
<tr ng-repeat-end class="detail-row">
<!--
Detail-row:
Contains detailed information on this item.
Can be toggled using the chevron button.
Ensure colspan is greater or equal to number of column-headers.
-->
<td class="detail" colspan="7">
<div class="row">
<dl class="col-lg-5 col-md-5 col-sm-5">
<dt translate>Monitor Address</dt>
<dd
class="form-group member-monitor-address"
ng-class="{ 'has-error': memberDetailsForm['{$ ::row.id $}-monitor-address'].$invalid && memberDetailsForm['{$ ::row.id $}-monitor-address'].$dirty }">
<input name="{$ ::row.id $}-monitor-address" type="text" class="form-control"
ng-model="row.monitor_address" ng-pattern="::ctrl.ipPattern"
ng-disabled="row.allocatedMember"
popover-placement="top" popover-append-to-body="true"
popover-trigger="focus"
ng-attr-popover="{$ memberDetailsForm[row.id + '-monitor-address'].$invalid && memberDetailsForm[row.id + '-monitor-address'].$dirty ? ctrl.ipError : '' $}">
</dd>
</dl>
<dl class="col-lg-5 col-md-5 col-sm-5">
<dt translate>Monitor Port</dt>
<dd class="form-group member-monitor-port"
ng-class="{ 'has-error': memberDetailsForm['{$ ::row.id $}-monitor-port'].$invalid && memberDetailsForm['{$ ::row.id $}-monitor-port'].$dirty }">
<input name="{$ ::row.id $}-monitor-port" type="number" class="form-control"
ng-model="row.monitor_port" ng-pattern="/^\d+$/" min="1" max="65535"
ng-disabled="row.allocatedMember"
popover-placement="top" popover-append-to-body="true"
popover-trigger="focus"
ng-attr-popover="{$ memberDetailsForm[row.id + '-monitor-port'].$invalid && memberDetailsForm[row.id + '-monitor-port'].$dirty ? ctrl.portError : '' $}">
</dd>
</dl>
</div>
</td>
</tr>
<tr table-status table="table" column-count="7"></tr>
<tr> <tr>
<td colspan="5"> <td colspan="5">
<action-list class="pull-right"> <action-list class="pull-right">

View File

@ -501,7 +501,9 @@
members.push({ members.push({
id: server.id, id: server.id,
name: server.name, name: server.name,
weight: 1 weight: 1,
monitor_address: null,
monitor_port: null
}); });
} }
}); });
@ -661,6 +663,8 @@
subnet: mapSubnetObj(member.subnet_id), subnet: mapSubnetObj(member.subnet_id),
port: member.protocol_port, port: member.protocol_port,
weight: member.weight, weight: member.weight,
monitor_address: member.monitor_address,
monitor_port: member.monitor_port,
allocatedMember: true allocatedMember: true
}); });
}); });