Add monitor address and port to pool member

Closes-Bug: #1541579

Change-Id: I8b49726bc2b7fbc37e1cd203a405f4bcbd0b7125
Signed-off-by: cheng <tangch318@gmail.com>
This commit is contained in:
cheng
2017-04-26 23:25:23 -04:00
parent 8fdcb88dee
commit 594a5935e8
14 changed files with 245 additions and 48 deletions

View File

@@ -1347,6 +1347,10 @@ Pool Members
+------------------+---------+------------------------------------+
| operating_status | String | Network status of the pool member |
+------------------+---------+------------------------------------+
| monitor_address | String | IP address of the member monitor |
+------------------+---------+------------------------------------+
| monitor_port | String | Port for the member to monitor |
+------------------+---------+------------------------------------+
List Members
************
@@ -1378,7 +1382,9 @@ Retrieve a list of pool members.
"weight": 10,
"subnet_id": "6fd8cb41-f56d-49f0-bf19-db3dbf3191dc",
"enabled": true,
"operating_status": "ONLINE"
"operating_status": "ONLINE",
"monitor_address": "10.0.0.2",
"monitor_port": 80
}
]
@@ -1411,7 +1417,9 @@ Retrieve details of a pool member.
"weight": 10,
"subnet_id": "9e58c7ae-9da2-45f2-9a2a-97e39d3ad69e",
"enabled": true,
"operating_status": "ONLINE"
"operating_status": "ONLINE",
"monitor_address": "10.0.0.2",
"monitor_port": 80
}
Create Member
@@ -1436,19 +1444,23 @@ Create a pool member.
|
+---------------+----------+
| Parameters | Required |
+===============+==========+
| ip_address | yes |
+---------------+----------+
| protocol_port | yes |
+---------------+----------+
| weight | yes |
+---------------+----------+
| subnet_id | no |
+---------------+----------+
| enabled | no |
+---------------+----------+
+----------------+----------+
| Parameters | Required |
+================+==========+
| ip_address | yes |
+----------------+----------+
| protocol_port | yes |
+----------------+----------+
| weight | yes |
+----------------+----------+
| subnet_id | no |
+----------------+----------+
| enabled | no |
+----------------+----------+
| monitor_address| no |
+----------------+----------+
| monitor_port | no |
+----------------+----------+
**Request Example**::
@@ -1457,7 +1469,9 @@ Create a pool member.
"protocol_port": 80,
"weight": 10,
"subnet_id": "f9c3a146-a3e3-406d-9f38-e7cd1847a670",
"enabled": true
"enabled": true,
"monitor_address": "10.0.0.2",
"monitor_port": 80
}
**Response Example**::
@@ -1469,7 +1483,9 @@ Create a pool member.
"weight": 10,
"subnet_id": "f9c3a146-a3e3-406d-9f38-e7cd1847a670",
"enabled": true,
"operating_status": "ONLINE"
"operating_status": "ONLINE",
"monitor_address": "10.0.0.2",
"monitor_port": 80
}
Update Member
@@ -1494,22 +1510,28 @@ Modify mutable attributes of a pool member.
|
+---------------+----------+
| Parameters | Required |
+===============+==========+
| protocol_port | no |
+---------------+----------+
| weight | no |
+---------------+----------+
| enabled | no |
+---------------+----------+
+-----------------+----------+
| Parameters | Required |
+=================+==========+
| protocol_port | no |
+-----------------+----------+
| weight | no |
+-----------------+----------+
| enabled | no |
+-----------------+----------+
| monitor_address | no |
+-----------------+----------+
| monitor_port | no |
+-----------------+----------+
**Request Example**::
{
"protocol_port": 80,
"weight": 10,
"enabled": true
"enabled": true,
"monitor_address": "10.0.0.2",
"monitor_port": 80
}
**Response Example**::
@@ -1521,7 +1543,9 @@ Modify mutable attributes of a pool member.
"weight": 10,
"subnet_id": "c91661f3-3831-4799-9c2c-681554196d62",
"enabled": true,
"operating_status": "ONLINE"
"operating_status": "ONLINE",
"monitor_address": "10.0.0.2",
"monitor_port": 80
}
Delete Member

View File

@@ -15,6 +15,7 @@
from wsme import types as wtypes
from octavia.api.common import types as base
from octavia.common import constants
class MemberResponse(base.BaseType):
@@ -29,6 +30,8 @@ class MemberResponse(base.BaseType):
project_id = wtypes.wsattr(wtypes.StringType())
created_at = wtypes.wsattr(wtypes.datetime.datetime)
updated_at = wtypes.wsattr(wtypes.datetime.datetime)
monitor_address = wtypes.wsattr(base.IPAddressType())
monitor_port = wtypes.wsattr(wtypes.IntegerType())
class MemberPOST(base.BaseType):
@@ -41,6 +44,10 @@ class MemberPOST(base.BaseType):
subnet_id = wtypes.wsattr(wtypes.UuidType())
# TODO(johnsom) Remove after deprecation (R series)
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
monitor_port = wtypes.wsattr(wtypes.IntegerType(
minimum=constants.MIN_PORT_NUMBER, maximum=constants.MAX_PORT_NUMBER),
default=None)
monitor_address = wtypes.wsattr(base.IPAddressType(), default=None)
class MemberPUT(base.BaseType):
@@ -48,3 +55,6 @@ class MemberPUT(base.BaseType):
protocol_port = wtypes.wsattr(wtypes.IntegerType())
enabled = wtypes.wsattr(bool)
weight = wtypes.wsattr(wtypes.IntegerType())
monitor_address = wtypes.wsattr(base.IPAddressType())
monitor_port = wtypes.wsattr(wtypes.IntegerType(
minimum=constants.MIN_PORT_NUMBER, maximum=constants.MAX_PORT_NUMBER))

View File

@@ -37,6 +37,8 @@ class MemberResponse(BaseMemberType):
project_id = wtypes.wsattr(wtypes.StringType())
created_at = wtypes.wsattr(wtypes.datetime.datetime)
updated_at = wtypes.wsattr(wtypes.datetime.datetime)
monitor_address = wtypes.wsattr(types.IPAddressType())
monitor_port = wtypes.wsattr(wtypes.IntegerType())
@classmethod
def from_data_model(cls, data_model, children=False):
@@ -69,6 +71,10 @@ class MemberPOST(BaseMemberType):
subnet_id = wtypes.wsattr(wtypes.UuidType())
# TODO(johnsom) Remove after deprecation (R series)
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
monitor_port = wtypes.wsattr(wtypes.IntegerType(
minimum=constants.MIN_PORT_NUMBER, maximum=constants.MAX_PORT_NUMBER),
default=None)
monitor_address = wtypes.wsattr(types.IPAddressType(), default=None)
class MemberRootPOST(types.BaseType):
@@ -81,6 +87,9 @@ class MemberPUT(BaseMemberType):
admin_state_up = wtypes.wsattr(bool)
weight = wtypes.wsattr(wtypes.IntegerType(
minimum=constants.MIN_WEIGHT, maximum=constants.MAX_WEIGHT))
monitor_port = wtypes.wsattr(wtypes.IntegerType(
minimum=constants.MIN_PORT_NUMBER, maximum=constants.MAX_PORT_NUMBER))
monitor_address = wtypes.wsattr(types.IPAddressType())
class MemberRootPUT(types.BaseType):

View File

@@ -316,7 +316,7 @@ class Member(BaseDataModel):
protocol_port=None, weight=None, enabled=None,
subnet_id=None, operating_status=None, pool=None,
created_at=None, updated_at=None, provisioning_status=None,
name=None):
name=None, monitor_address=None, monitor_port=None):
self.id = id
self.project_id = project_id
self.pool_id = pool_id
@@ -331,6 +331,8 @@ class Member(BaseDataModel):
self.updated_at = updated_at
self.provisioning_status = provisioning_status
self.name = name
self.monitor_address = monitor_address
self.monitor_port = monitor_port
def delete(self):
for mem in self.pool.members:

View File

@@ -271,7 +271,9 @@ class JinjaTemplater(object):
'weight': member.weight,
'enabled': member.enabled,
'subnet_id': member.subnet_id,
'operating_status': member.operating_status
'operating_status': member.operating_status,
'monitor_address': member.monitor_address,
'monitor_port': member.monitor_port
}
def _transform_health_monitor(self, monitor):

View File

@@ -141,9 +141,20 @@ frontend {{ listener.id }}
{% macro member_macro(constants, pool, member) %}
{% if pool.health_monitor and pool.health_monitor.enabled %}
{% set hm_opt = " check inter %ds fall %d rise %d"|format(
{% if member.monitor_address %}
{% set monitor_addr_opt = " addr %s"|format(member.monitor_address) %}
{% else %}
{% set monitor_addr_opt = "" %}
{% endif %}
{% if member.monitor_port %}
{% set monitor_port_opt = " port %s"|format(member.monitor_port) %}
{% else %}
{% set monitor_port_opt = "" %}
{% endif %}
{% set hm_opt = " check inter %ds fall %d rise %d%s%s"|format(
pool.health_monitor.delay, pool.health_monitor.fall_threshold,
pool.health_monitor.rise_threshold) %}
pool.health_monitor.rise_threshold, monitor_addr_opt,
monitor_port_opt) %}
{% else %}
{% set hm_opt = "" %}
{% endif %}

View File

@@ -0,0 +1,42 @@
# Copyright 2017 EayunStack, Inc.
#
# 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.
"""add monitor address and port to member
Revision ID: 27e54d00c3cd
Revises: 5309960964f8
Create Date: 2017-05-01 23:12:16.695581
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '27e54d00c3cd'
down_revision = '5309960964f8'
def upgrade():
op.add_column(u'member',
sa.Column(u'monitor_address',
sa.String(64),
nullable=True)
)
op.add_column(u'member',
sa.Column(u'monitor_port',
sa.Integer(),
nullable=True)
)

View File

@@ -177,6 +177,8 @@ class Member(base_models.BASE, base_models.IdMixin, base_models.ProjectMixin,
ip_address = sa.Column('ip_address', sa.String(64), nullable=False)
protocol_port = sa.Column(sa.Integer, nullable=False)
weight = sa.Column(sa.Integer, nullable=True)
monitor_address = sa.Column(sa.String(64), nullable=True)
monitor_port = sa.Column(sa.Integer, nullable=True)
provisioning_status = sa.Column(
sa.String(16),
sa.ForeignKey("provisioning_status.name",

View File

@@ -612,7 +612,9 @@ class TestLoadBalancerGraph(base.BaseAPITest):
'weight': 1,
'enabled': True,
'subnet_id': None,
'operating_status': constants.NO_MONITOR
'operating_status': constants.NO_MONITOR,
'monitor_address': None,
'monitor_port': None
}
expected_member.update(create_member)
return create_member, expected_member

View File

@@ -176,6 +176,31 @@ class TestMember(base.BaseAPITest):
self.listener.get('id'),
constants.ACTIVE, constants.ONLINE)
def test_create_with_monitor_address_and_port(self):
api_member = self.create_member_with_listener(
self.lb.get('id'), self.listener.get('id'),
self.pool.get('id'), '10.0.0.1', 80,
monitor_address='192.0.2.2',
monitor_port=9090)
self.assertEqual('10.0.0.1', api_member.get('ip_address'))
self.assertEqual(80, api_member.get('protocol_port'))
self.assertEqual('192.0.2.2', api_member.get('monitor_address'))
self.assertEqual(9090, api_member.get('monitor_port'))
self.assert_correct_lb_status(self.lb.get('id'),
constants.PENDING_UPDATE,
constants.ONLINE)
self.assert_correct_listener_status(self.lb.get('id'),
self.listener.get('id'),
constants.PENDING_UPDATE,
constants.ONLINE)
self.set_lb_status(self.lb.get('id'))
self.assert_correct_lb_status(self.lb.get('id'),
constants.ACTIVE,
constants.ONLINE)
self.assert_correct_listener_status(self.lb.get('id'),
self.listener.get('id'),
constants.ACTIVE, constants.ONLINE)
def test_duplicate_create(self):
member = {'ip_address': '10.0.0.1', 'protocol_port': 80,
'project_id': self.project_id}

View File

@@ -171,6 +171,28 @@ class TestMember(base.BaseAPITest):
lb_id=self.lb_id, listener_id=self.listener_id,
pool_id=self.pool_with_listener_id, member_id=api_member.get('id'))
def test_create_with_monitor_address_and_port(self):
api_member = self.create_member(
self.pool_with_listener_id, '10.0.0.1', 80,
monitor_address='192.0.2.3',
monitor_port=80).get(self.root_tag)
self.assertEqual('10.0.0.1', api_member['address'])
self.assertEqual(80, api_member['protocol_port'])
self.assertEqual('192.0.2.3', api_member['monitor_address'])
self.assertEqual(80, api_member['monitor_port'])
self.assert_correct_status(
lb_id=self.lb_id, listener_id=self.listener_id,
pool_id=self.pool_with_listener_id, member_id=api_member.get('id'),
lb_prov_status=constants.PENDING_UPDATE,
listener_prov_status=constants.PENDING_UPDATE,
pool_prov_status=constants.PENDING_UPDATE,
member_prov_status=constants.PENDING_CREATE,
member_op_status=constants.NO_MONITOR)
self.set_lb_status(self.lb_id)
self.assert_correct_status(
lb_id=self.lb_id, listener_id=self.listener_id,
pool_id=self.pool_with_listener_id, member_id=api_member.get('id'))
@testtools.skip("Enable this with v2 Health Monitor patch")
def test_create_with_health_monitor(self):
self.create_health_monitor_with_listener(

View File

@@ -126,6 +126,30 @@ class TestHaproxyCfg(base.TestCase):
sample_configs.sample_base_expected_config(backend=be),
rendered_obj)
def test_render_template_member_monitor_addr_port(self):
be = ("backend sample_pool_id_1\n"
" mode http\n"
" balance roundrobin\n"
" cookie SRV insert indirect nocache\n"
" timeout check 31s\n"
" option httpchk GET /index.html\n"
" http-check expect rstatus 418\n"
" fullconn 98\n"
" server sample_member_id_1 10.0.0.99:82 "
"weight 13 check inter 30s fall 3 rise 2 "
"addr 192.168.1.1 port 9000 "
"cookie sample_member_id_1\n"
" server sample_member_id_2 10.0.0.98:82 "
"weight 13 check inter 30s fall 3 rise 2 "
"addr 192.168.1.1 port 9000 "
"cookie sample_member_id_2\n\n")
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(monitor_ip_port=True))
self.assertEqual(
sample_configs.sample_base_expected_config(backend=be),
rendered_obj)
def test_render_template_https(self):
fe = ("frontend sample_listener_id_1\n"
" option tcplog\n"

View File

@@ -72,7 +72,9 @@ RET_MEMBER_1 = {
'weight': 13,
'subnet_id': '10.0.0.1/24',
'enabled': True,
'operating_status': 'ACTIVE'}
'operating_status': 'ACTIVE',
'monitor_address': None,
'monitor_port': None}
RET_MEMBER_2 = {
'id': 'sample_member_id_2',
@@ -81,7 +83,9 @@ RET_MEMBER_2 = {
'weight': 13,
'subnet_id': '10.0.0.1/24',
'enabled': True,
'operating_status': 'ACTIVE'}
'operating_status': 'ACTIVE',
'monitor_address': None,
'monitor_port': None}
RET_MEMBER_3 = {
'id': 'sample_member_id_3',
@@ -90,7 +94,9 @@ RET_MEMBER_3 = {
'weight': 13,
'subnet_id': '10.0.0.1/24',
'enabled': True,
'operating_status': 'ACTIVE'}
'operating_status': 'ACTIVE',
'monitor_address': None,
'monitor_port': None}
RET_POOL_1 = {
'id': 'sample_pool_id_1',
@@ -399,7 +405,7 @@ def sample_listener_tuple(proto=None, monitor=True, persistence=True,
persistence_type=None, persistence_cookie=None,
tls=False, sni=False, peer_port=None, topology=None,
l7=False, enabled=True, insert_headers=None,
be_proto=None):
be_proto=None, monitor_ip_port=False):
proto = 'HTTP' if proto is None else proto
if be_proto is None:
be_proto = 'HTTP' if proto is 'TERMINATED_HTTPS' else proto
@@ -418,11 +424,13 @@ def sample_listener_tuple(proto=None, monitor=True, persistence=True,
sample_pool_tuple(
proto=be_proto, monitor=monitor, persistence=persistence,
persistence_type=persistence_type,
persistence_cookie=persistence_cookie),
persistence_cookie=persistence_cookie,
monitor_ip_port=monitor_ip_port),
sample_pool_tuple(
proto=be_proto, monitor=monitor, persistence=persistence,
persistence_type=persistence_type,
persistence_cookie=persistence_cookie, sample_pool=2)]
persistence_cookie=persistence_cookie, sample_pool=2,
monitor_ip_port=monitor_ip_port)]
l7policies = [
sample_l7policy_tuple('sample_l7policy_id_1', sample_policy=1),
sample_l7policy_tuple('sample_l7policy_id_2', sample_policy=2),
@@ -435,7 +443,8 @@ def sample_listener_tuple(proto=None, monitor=True, persistence=True,
sample_pool_tuple(
proto=be_proto, monitor=monitor, persistence=persistence,
persistence_type=persistence_type,
persistence_cookie=persistence_cookie)]
persistence_cookie=persistence_cookie,
monitor_ip_port=monitor_ip_port)]
l7policies = []
return in_listener(
id='sample_listener_id_1',
@@ -448,7 +457,8 @@ def sample_listener_tuple(proto=None, monitor=True, persistence=True,
default_pool=sample_pool_tuple(
proto=be_proto, monitor=monitor, persistence=persistence,
persistence_type=persistence_type,
persistence_cookie=persistence_cookie),
persistence_cookie=persistence_cookie,
monitor_ip_port=monitor_ip_port),
connection_limit=98,
tls_certificate_id='cont_id_1' if tls else '',
sni_container_ids=['cont_id_2', 'cont_id_3'] if sni else [],
@@ -505,7 +515,7 @@ def sample_tls_container_tuple(id='cont_id_1', certificate=None,
def sample_pool_tuple(proto=None, monitor=True, persistence=True,
persistence_type=None, persistence_cookie=None,
sample_pool=1):
sample_pool=1, monitor_ip_port=False):
proto = 'HTTP' if proto is None else proto
in_pool = collections.namedtuple(
'pool', 'id, protocol, lb_algorithm, members, health_monitor,'
@@ -516,13 +526,16 @@ def sample_pool_tuple(proto=None, monitor=True, persistence=True,
mon = None
if sample_pool == 1:
id = 'sample_pool_id_1'
members = [sample_member_tuple('sample_member_id_1', '10.0.0.99'),
sample_member_tuple('sample_member_id_2', '10.0.0.98')]
members = [sample_member_tuple('sample_member_id_1', '10.0.0.99',
monitor_ip_port=monitor_ip_port),
sample_member_tuple('sample_member_id_2', '10.0.0.98',
monitor_ip_port=monitor_ip_port)]
if monitor is True:
mon = sample_health_monitor_tuple(proto=proto)
elif sample_pool == 2:
id = 'sample_pool_id_2'
members = [sample_member_tuple('sample_member_id_3', '10.0.0.97')]
members = [sample_member_tuple('sample_member_id_3', '10.0.0.97',
monitor_ip_port=monitor_ip_port)]
if monitor is True:
mon = sample_health_monitor_tuple(proto=proto, sample_hm=2)
return in_pool(
@@ -536,11 +549,15 @@ def sample_pool_tuple(proto=None, monitor=True, persistence=True,
operating_status='ACTIVE')
def sample_member_tuple(id, ip, enabled=True, operating_status='ACTIVE'):
def sample_member_tuple(id, ip, enabled=True, operating_status='ACTIVE',
monitor_ip_port=False):
in_member = collections.namedtuple('member',
'id, ip_address, protocol_port, '
'weight, subnet_id, '
'enabled, operating_status')
'enabled, operating_status, '
'monitor_address, monitor_port')
monitor_address = '192.168.1.1' if monitor_ip_port else None
monitor_port = 9000 if monitor_ip_port else None
return in_member(
id=id,
ip_address=ip,
@@ -548,7 +565,9 @@ def sample_member_tuple(id, ip, enabled=True, operating_status='ACTIVE'):
weight=13,
subnet_id='10.0.0.1/24',
enabled=enabled,
operating_status=operating_status)
operating_status=operating_status,
monitor_address=monitor_address,
monitor_port=monitor_port)
def sample_session_persistence_tuple(persistence_type=None,

View File

@@ -0,0 +1,3 @@
---
features:
- Add monitor address and port to member