Introduce Member for Octavia (load balancing)
This patch introduces Resource for Member. Co-Authored-By: Michael Johnson <johnsomor@gmail.com> Change-Id: Ie325270096bff313f11cabe2cba7129dd02acd6f
This commit is contained in:
parent
7164a24f21
commit
f6b01f86b2
|
@ -45,3 +45,15 @@ Pool Operations
|
|||
.. automethod:: openstack.load_balancer.v2._proxy.Proxy.get_pool
|
||||
.. automethod:: openstack.load_balancer.v2._proxy.Proxy.pools
|
||||
.. automethod:: openstack.load_balancer.v2._proxy.Proxy.update_pool
|
||||
|
||||
Member Operations
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autoclass:: openstack.load_balancer.v2._proxy.Proxy
|
||||
|
||||
.. automethod:: openstack.load_balancer.v2._proxy.Proxy.create_member
|
||||
.. automethod:: openstack.load_balancer.v2._proxy.Proxy.delete_member
|
||||
.. automethod:: openstack.load_balancer.v2._proxy.Proxy.find_member
|
||||
.. automethod:: openstack.load_balancer.v2._proxy.Proxy.get_member
|
||||
.. automethod:: openstack.load_balancer.v2._proxy.Proxy.members
|
||||
.. automethod:: openstack.load_balancer.v2._proxy.Proxy.update_member
|
||||
|
|
|
@ -7,3 +7,4 @@ Load Balancer Resources
|
|||
v2/load_balancer
|
||||
v2/listener
|
||||
v2/pool
|
||||
v2/member
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
openstack.load_balancer.v2.member
|
||||
=================================
|
||||
|
||||
.. automodule:: openstack.load_balancer.v2.member
|
||||
|
||||
The Member Class
|
||||
----------------
|
||||
|
||||
The ``Member`` class inherits from :class:`~openstack.resource.Resource`.
|
||||
|
||||
.. autoclass:: openstack.load_balancer.v2.member.Member
|
||||
:members:
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
from openstack.load_balancer.v2 import listener as _listener
|
||||
from openstack.load_balancer.v2 import load_balancer as _lb
|
||||
from openstack.load_balancer.v2 import member as _member
|
||||
from openstack.load_balancer.v2 import pool as _pool
|
||||
from openstack import proxy2
|
||||
|
||||
|
@ -256,3 +257,114 @@ class Proxy(proxy2.BaseProxy):
|
|||
:rtype: :class:`~openstack.load_balancer.v2.pool.Pool`
|
||||
"""
|
||||
return self._update(_pool.Pool, pool, **attrs)
|
||||
|
||||
def create_member(self, pool, **attrs):
|
||||
"""Create a new member from attributes
|
||||
|
||||
:param pool: The pool can be either the ID of a pool or a
|
||||
:class:`~openstack.load_balancer.v2.pool.Pool` instance
|
||||
that the member will be created in.
|
||||
:param dict attrs: Keyword arguments which will be used to create
|
||||
a :class:`~openstack.load_balancer.v2.member.Member`,
|
||||
comprised of the properties on the Member class.
|
||||
|
||||
:returns: The results of member creation
|
||||
:rtype: :class:`~openstack.load_balancer.v2.member.Member`
|
||||
"""
|
||||
poolobj = self._get_resource(_pool.Pool, pool)
|
||||
return self._create(_member.Member, pool_id=poolobj.id,
|
||||
**attrs)
|
||||
|
||||
def delete_member(self, member, pool, ignore_missing=True):
|
||||
"""Delete a member
|
||||
|
||||
:param member:
|
||||
The member can be either the ID of a member or a
|
||||
:class:`~openstack.load_balancer.v2.member.Member` instance.
|
||||
:param pool: The pool can be either the ID of a pool or a
|
||||
:class:`~openstack.load_balancer.v2.pool.Pool` instance
|
||||
that the member belongs to.
|
||||
:param bool ignore_missing: When set to ``False``
|
||||
:class:`~openstack.exceptions.ResourceNotFound` will be
|
||||
raised when the member does not exist.
|
||||
When set to ``True``, no exception will be set when
|
||||
attempting to delete a nonexistent member.
|
||||
|
||||
:returns: ``None``
|
||||
"""
|
||||
poolobj = self._get_resource(_pool.Pool, pool)
|
||||
self._delete(_member.Member, member,
|
||||
ignore_missing=ignore_missing, pool_id=poolobj.id)
|
||||
|
||||
def find_member(self, name_or_id, pool, ignore_missing=True):
|
||||
"""Find a single member
|
||||
|
||||
:param str name_or_id: The name or ID of a member.
|
||||
:param pool: The pool can be either the ID of a pool or a
|
||||
:class:`~openstack.load_balancer.v2.pool.Pool` instance
|
||||
that the member belongs to.
|
||||
:param bool ignore_missing: When set to ``False``
|
||||
:class:`~openstack.exceptions.ResourceNotFound` will be
|
||||
raised when the resource does not exist.
|
||||
When set to ``True``, None will be returned when
|
||||
attempting to find a nonexistent resource.
|
||||
|
||||
:returns: One :class:`~openstack.load_balancer.v2.member.Member`
|
||||
or None
|
||||
"""
|
||||
poolobj = self._get_resource(_pool.Pool, pool)
|
||||
return self._find(_member.Member, name_or_id,
|
||||
ignore_missing=ignore_missing, pool_id=poolobj.id)
|
||||
|
||||
def get_member(self, member, pool):
|
||||
"""Get a single member
|
||||
|
||||
:param member: The member can be the ID of a member or a
|
||||
:class:`~openstack.load_balancer.v2.member.Member`
|
||||
instance.
|
||||
:param pool: The pool can be either the ID of a pool or a
|
||||
:class:`~openstack.load_balancer.v2.pool.Pool` instance
|
||||
that the member belongs to.
|
||||
|
||||
:returns: One :class:`~openstack.load_balancer.v2.member.Member`
|
||||
:raises: :class:`~openstack.exceptions.ResourceNotFound`
|
||||
when no resource can be found.
|
||||
"""
|
||||
poolobj = self._get_resource(_pool.Pool, pool)
|
||||
return self._get(_member.Member, member,
|
||||
pool_id=poolobj.id)
|
||||
|
||||
def members(self, pool, **query):
|
||||
"""Return a generator of members
|
||||
|
||||
:param pool: The pool can be either the ID of a pool or a
|
||||
:class:`~openstack.load_balancer.v2.pool.Pool` instance
|
||||
that the member belongs to.
|
||||
:param dict query: Optional query parameters to be sent to limit
|
||||
the resources being returned. Valid parameters are:
|
||||
|
||||
:returns: A generator of member objects
|
||||
:rtype: :class:`~openstack.load_balancer.v2.member.Member`
|
||||
"""
|
||||
poolobj = self._get_resource(_pool.Pool, pool)
|
||||
return self._list(_member.Member, paginated=True,
|
||||
pool_id=poolobj.id, **query)
|
||||
|
||||
def update_member(self, member, pool, **attrs):
|
||||
"""Update a member
|
||||
|
||||
:param member: Either the ID of a member or a
|
||||
:class:`~openstack.load_balancer.v2.member.Member`
|
||||
instance.
|
||||
:param pool: The pool can be either the ID of a pool or a
|
||||
:class:`~openstack.load_balancer.v2.pool.Pool` instance
|
||||
that the member belongs to.
|
||||
:param dict attrs: The attributes to update on the member
|
||||
represented by ``member``.
|
||||
|
||||
:returns: The updated member
|
||||
:rtype: :class:`~openstack.load_balancer.v2.member.Member`
|
||||
"""
|
||||
poolobj = self._get_resource(_pool.Pool, pool)
|
||||
return self._update(_member.Member, member,
|
||||
pool_id=poolobj.id, **attrs)
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# 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.
|
||||
|
||||
from openstack.load_balancer import load_balancer_service as lb_service
|
||||
from openstack import resource2 as resource
|
||||
|
||||
|
||||
class Member(resource.Resource):
|
||||
resource_key = 'member'
|
||||
resources_key = 'members'
|
||||
base_path = '/v2.0/lbaas/pools/%(pool_id)s/members'
|
||||
service = lb_service.LoadBalancerService()
|
||||
|
||||
# capabilities
|
||||
allow_create = True
|
||||
allow_get = True
|
||||
allow_update = True
|
||||
allow_delete = True
|
||||
allow_list = True
|
||||
|
||||
_query_mapping = resource.QueryParameters(
|
||||
'address', 'name', 'protocol_port', 'subnet_id', 'weight',
|
||||
'created_at', 'updated_at', 'provisioning_status', 'operating_status',
|
||||
'project_id', 'monitor_address', 'monitor_port',
|
||||
is_admin_state_up='admin_state_up',
|
||||
)
|
||||
|
||||
# Properties
|
||||
#: The IP address of the member.
|
||||
address = resource.Body('address')
|
||||
#: Timestamp when the member was created.
|
||||
created_at = resource.Body('created_at')
|
||||
#: The administrative state of the member, which is up ``True`` or
|
||||
#: down ``False``. *Type: bool*
|
||||
is_admin_state_up = resource.Body('admin_state_up', type=bool)
|
||||
#: IP address used to monitor this member
|
||||
monitor_address = resource.Body('monitor_address')
|
||||
#: Port used to monitor this member
|
||||
monitor_port = resource.Body('monitor_port', type=int)
|
||||
#: Name of the member.
|
||||
name = resource.Body('name')
|
||||
#: Operating status of the member.
|
||||
operating_status = resource.Body('operating_status')
|
||||
#: The ID of the owning pool.
|
||||
pool_id = resource.URI('pool_id')
|
||||
#: The provisioning status of this member.
|
||||
provisioning_status = resource.Body('provisioning_status')
|
||||
#: The ID of the project this member is associated with.
|
||||
project_id = resource.Body('project_id')
|
||||
#: The port on which the application is hosted.
|
||||
protocol_port = resource.Body('protocol_port', type=int)
|
||||
#: Subnet ID in which to access this member.
|
||||
subnet_id = resource.Body('subnet_id')
|
||||
#: Timestamp when the member was last updated.
|
||||
updated_at = resource.Body('updated_at')
|
||||
#: A positive integer value that indicates the relative portion of traffic
|
||||
#: that this member should receive from the pool. For example, a member
|
||||
#: with a weight of 10 receives five times as much traffic as a member
|
||||
#: with weight of 2.
|
||||
weight = resource.Body('weight', type=int)
|
|
@ -15,6 +15,7 @@ import uuid
|
|||
|
||||
from openstack.load_balancer.v2 import listener
|
||||
from openstack.load_balancer.v2 import load_balancer
|
||||
from openstack.load_balancer.v2 import member
|
||||
from openstack.load_balancer.v2 import pool
|
||||
from openstack.tests.functional import base
|
||||
from openstack.tests.functional.load_balancer import base as lb_base
|
||||
|
@ -26,16 +27,20 @@ class TestLoadBalancer(lb_base.BaseLBFunctionalTest):
|
|||
|
||||
LB_NAME = uuid.uuid4().hex
|
||||
LISTENER_NAME = uuid.uuid4().hex
|
||||
MEMBER_NAME = uuid.uuid4().hex
|
||||
POOL_NAME = uuid.uuid4().hex
|
||||
UPDATE_NAME = uuid.uuid4().hex
|
||||
LB_ID = None
|
||||
LISTENER_ID = None
|
||||
MEMBER_ID = None
|
||||
POOL_ID = None
|
||||
VIP_SUBNET_ID = None
|
||||
PROJECT_ID = None
|
||||
PROTOCOL = 'HTTP'
|
||||
PROTOCOL_PORT = 80
|
||||
LB_ALGORITHM = 'ROUND_ROBIN'
|
||||
MEMBER_ADDRESS = '192.0.2.16'
|
||||
WEIGHT = 10
|
||||
|
||||
# Note: Creating load balancers can be slow on some hosts due to nova
|
||||
# instance boot times (up to ten minutes) so we are consolidating
|
||||
|
@ -75,11 +80,24 @@ class TestLoadBalancer(lb_base.BaseLBFunctionalTest):
|
|||
cls.lb_wait_for_status(test_lb, status='ACTIVE',
|
||||
failures=['ERROR'])
|
||||
|
||||
test_member = cls.conn.load_balancer.create_member(
|
||||
pool=cls.POOL_ID, name=cls.MEMBER_NAME, address=cls.MEMBER_ADDRESS,
|
||||
protocol_port=cls.PROTOCOL_PORT, weight=cls.WEIGHT)
|
||||
assert isinstance(test_member, member.Member)
|
||||
cls.assertIs(cls.MEMBER_NAME, test_member.name)
|
||||
cls.MEMBER_ID = test_member.id
|
||||
cls.lb_wait_for_status(test_lb, status='ACTIVE',
|
||||
failures=['ERROR'])
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
test_lb = cls.conn.load_balancer.get_load_balancer(cls.LB_ID)
|
||||
cls.lb_wait_for_status(test_lb, status='ACTIVE', failures=['ERROR'])
|
||||
|
||||
cls.conn.load_balancer.delete_member(
|
||||
cls.MEMBER_ID, cls.POOL_ID, ignore_missing=False)
|
||||
cls.lb_wait_for_status(test_lb, status='ACTIVE', failures=['ERROR'])
|
||||
|
||||
cls.conn.load_balancer.delete_pool(cls.POOL_ID, ignore_missing=False)
|
||||
cls.lb_wait_for_status(test_lb, status='ACTIVE', failures=['ERROR'])
|
||||
|
||||
|
@ -180,3 +198,39 @@ class TestLoadBalancer(lb_base.BaseLBFunctionalTest):
|
|||
self.lb_wait_for_status(test_lb, status='ACTIVE', failures=['ERROR'])
|
||||
test_pool = self.conn.load_balancer.get_pool(self.POOL_ID)
|
||||
self.assertEqual(self.POOL_NAME, test_pool.name)
|
||||
|
||||
def test_member_find(self):
|
||||
test_member = self.conn.load_balancer.find_member(self.MEMBER_NAME,
|
||||
self.POOL_ID)
|
||||
self.assertEqual(self.MEMBER_ID, test_member.id)
|
||||
|
||||
def test_member_get(self):
|
||||
test_member = self.conn.load_balancer.get_member(self.MEMBER_ID,
|
||||
self.POOL_ID)
|
||||
self.assertEqual(self.MEMBER_NAME, test_member.name)
|
||||
self.assertEqual(self.MEMBER_ID, test_member.id)
|
||||
self.assertEqual(self.MEMBER_ADDRESS, test_member.address)
|
||||
self.assertEqual(self.PROTOCOL_PORT, test_member.protocol_port)
|
||||
self.assertEqual(self.WEIGHT, test_member.weight)
|
||||
|
||||
def test_member_list(self):
|
||||
names = [mb.name for mb in self.conn.load_balancer.members(
|
||||
self.POOL_ID)]
|
||||
self.assertIn(self.MEMBER_NAME, names)
|
||||
|
||||
def test_member_update(self):
|
||||
test_lb = self.conn.load_balancer.get_load_balancer(self.LB_ID)
|
||||
|
||||
self.conn.load_balancer.update_member(self.MEMBER_ID, self.POOL_ID,
|
||||
name=self.UPDATE_NAME)
|
||||
self.lb_wait_for_status(test_lb, status='ACTIVE', failures=['ERROR'])
|
||||
test_member = self.conn.load_balancer.get_member(self.MEMBER_ID,
|
||||
self.POOL_ID)
|
||||
self.assertEqual(self.UPDATE_NAME, test_member.name)
|
||||
|
||||
self.conn.load_balancer.update_member(self.MEMBER_ID, self.POOL_ID,
|
||||
name=self.MEMBER_NAME)
|
||||
self.lb_wait_for_status(test_lb, status='ACTIVE', failures=['ERROR'])
|
||||
test_member = self.conn.load_balancer.get_member(self.MEMBER_ID,
|
||||
self.POOL_ID)
|
||||
self.assertEqual(self.MEMBER_NAME, test_member.name)
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# 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.
|
||||
|
||||
import testtools
|
||||
import uuid
|
||||
|
||||
from openstack.load_balancer.v2 import member
|
||||
|
||||
IDENTIFIER = 'IDENTIFIER'
|
||||
EXAMPLE = {
|
||||
'address': '192.0.2.16',
|
||||
'admin_state_up': True,
|
||||
'id': IDENTIFIER,
|
||||
'monitor_address': '192.0.2.17',
|
||||
'monitor_port': 9,
|
||||
'name': 'test_member',
|
||||
'pool_id': uuid.uuid4(),
|
||||
'project_id': uuid.uuid4(),
|
||||
'protocol_port': 5,
|
||||
'subnet_id': uuid.uuid4(),
|
||||
'weight': 7,
|
||||
}
|
||||
|
||||
|
||||
class TestPoolMember(testtools.TestCase):
|
||||
|
||||
def test_basic(self):
|
||||
test_member = member.Member()
|
||||
self.assertEqual('member', test_member.resource_key)
|
||||
self.assertEqual('members', test_member.resources_key)
|
||||
self.assertEqual('/v2.0/lbaas/pools/%(pool_id)s/members',
|
||||
test_member.base_path)
|
||||
self.assertEqual('load-balancer', test_member.service.service_type)
|
||||
self.assertTrue(test_member.allow_create)
|
||||
self.assertTrue(test_member.allow_get)
|
||||
self.assertTrue(test_member.allow_update)
|
||||
self.assertTrue(test_member.allow_delete)
|
||||
self.assertTrue(test_member.allow_list)
|
||||
|
||||
def test_make_it(self):
|
||||
test_member = member.Member(**EXAMPLE)
|
||||
self.assertEqual(EXAMPLE['address'], test_member.address)
|
||||
self.assertTrue(test_member.is_admin_state_up)
|
||||
self.assertEqual(EXAMPLE['id'], test_member.id)
|
||||
self.assertEqual(EXAMPLE['monitor_address'],
|
||||
test_member.monitor_address)
|
||||
self.assertEqual(EXAMPLE['monitor_port'], test_member.monitor_port)
|
||||
self.assertEqual(EXAMPLE['name'], test_member.name)
|
||||
self.assertEqual(EXAMPLE['pool_id'], test_member.pool_id)
|
||||
self.assertEqual(EXAMPLE['project_id'], test_member.project_id)
|
||||
self.assertEqual(EXAMPLE['protocol_port'], test_member.protocol_port)
|
||||
self.assertEqual(EXAMPLE['subnet_id'], test_member.subnet_id)
|
||||
self.assertEqual(EXAMPLE['weight'], test_member.weight)
|
|
@ -10,14 +10,20 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
from openstack.load_balancer.v2 import _proxy
|
||||
from openstack.load_balancer.v2 import listener
|
||||
from openstack.load_balancer.v2 import load_balancer as lb
|
||||
from openstack.load_balancer.v2 import member
|
||||
from openstack.load_balancer.v2 import pool
|
||||
from openstack.tests.unit import test_proxy_base2
|
||||
|
||||
|
||||
class TestLoadBalancerProxy(test_proxy_base2.TestProxyBase):
|
||||
|
||||
POOL_ID = uuid.uuid4()
|
||||
|
||||
def setUp(self):
|
||||
super(TestLoadBalancerProxy, self).setUp()
|
||||
self.proxy = _proxy.Proxy(self.session)
|
||||
|
@ -96,3 +102,44 @@ class TestLoadBalancerProxy(test_proxy_base2.TestProxyBase):
|
|||
def test_pool_update(self):
|
||||
self.verify_update(self.proxy.update_pool,
|
||||
pool.Pool)
|
||||
|
||||
def test_members(self):
|
||||
self.verify_list(self.proxy.members,
|
||||
member.Member,
|
||||
paginated=True,
|
||||
method_kwargs={'pool': self.POOL_ID},
|
||||
expected_kwargs={'pool_id': self.POOL_ID})
|
||||
|
||||
def test_member_get(self):
|
||||
self.verify_get(self.proxy.get_member,
|
||||
member.Member,
|
||||
method_kwargs={'pool': self.POOL_ID},
|
||||
expected_kwargs={'pool_id': self.POOL_ID})
|
||||
|
||||
def test_member_create(self):
|
||||
self.verify_create(self.proxy.create_member,
|
||||
member.Member,
|
||||
method_kwargs={'pool': self.POOL_ID},
|
||||
expected_kwargs={'pool_id': self.POOL_ID})
|
||||
|
||||
def test_member_delete(self):
|
||||
self.verify_delete(self.proxy.delete_member,
|
||||
member.Member,
|
||||
True,
|
||||
method_kwargs={'pool': self.POOL_ID},
|
||||
expected_kwargs={'pool_id': self.POOL_ID})
|
||||
|
||||
def test_member_find(self):
|
||||
self._verify2('openstack.proxy2.BaseProxy._find',
|
||||
self.proxy.find_member,
|
||||
method_args=["MEMBER", self.POOL_ID],
|
||||
expected_args=[member.Member, "MEMBER"],
|
||||
expected_kwargs={"pool_id": self.POOL_ID,
|
||||
"ignore_missing": True})
|
||||
|
||||
def test_member_update(self):
|
||||
self._verify2('openstack.proxy2.BaseProxy._update',
|
||||
self.proxy.update_member,
|
||||
method_args=["MEMBER", self.POOL_ID],
|
||||
expected_args=[member.Member, "MEMBER"],
|
||||
expected_kwargs={"pool_id": self.POOL_ID})
|
||||
|
|
Loading…
Reference in New Issue