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:
Nakul Dahiwade 2017-03-29 16:58:30 +00:00 committed by Michael Johnson
parent 7164a24f21
commit f6b01f86b2
8 changed files with 369 additions and 0 deletions

View File

@ -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

View File

@ -7,3 +7,4 @@ Load Balancer Resources
v2/load_balancer
v2/listener
v2/pool
v2/member

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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})