From cb3ae497c0a6349dfea0a41788b962a4cd3ef3eb Mon Sep 17 00:00:00 2001 From: Reedip Banerjee Date: Fri, 13 Nov 2015 12:32:27 +0530 Subject: [PATCH] Support for Name field in Members and HMs This patch adds support to enable naming LBaasV2 Members and Health Monitors(HMs). DocImpact Closes-Bug: #1515506 Change-Id: Ieb66386fac3a5a4dace0112838fe9afde212f055 --- neutron_lbaas/db/loadbalancer/models.py | 3 + .../mitaka/expand/4a408dd491c2_UpdateName.py | 35 ++++++++ neutron_lbaas/extensions/loadbalancerv2.py | 11 ++- .../services/loadbalancer/data_models.py | 6 +- .../db/loadbalancer/test_db_loadbalancerv2.py | 81 ++++++++++++++----- .../loadbalancer/test_loadbalancer_plugin.py | 27 ++++--- 6 files changed, 129 insertions(+), 34 deletions(-) create mode 100644 neutron_lbaas/db/migration/alembic_migrations/versions/mitaka/expand/4a408dd491c2_UpdateName.py diff --git a/neutron_lbaas/db/loadbalancer/models.py b/neutron_lbaas/db/loadbalancer/models.py index f49440ed4..c687b356f 100644 --- a/neutron_lbaas/db/loadbalancer/models.py +++ b/neutron_lbaas/db/loadbalancer/models.py @@ -14,6 +14,7 @@ # under the License. +from neutron.api.v2 import attributes as attr from neutron.db import model_base from neutron.db import models_v2 from neutron.db import servicetype_db as st_db @@ -85,6 +86,7 @@ class MemberV2(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant): subnet_id = sa.Column(sa.String(36), nullable=True) provisioning_status = sa.Column(sa.String(16), nullable=False) operating_status = sa.Column(sa.String(16), nullable=False) + name = sa.Column(sa.String(attr.NAME_MAX_LEN), nullable=True) @property def root_loadbalancer(self): @@ -109,6 +111,7 @@ class HealthMonitorV2(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant): expected_codes = sa.Column(sa.String(64), nullable=True) provisioning_status = sa.Column(sa.String(16), nullable=False) admin_state_up = sa.Column(sa.Boolean(), nullable=False) + name = sa.Column(sa.String(attr.NAME_MAX_LEN), nullable=True) @property def root_loadbalancer(self): diff --git a/neutron_lbaas/db/migration/alembic_migrations/versions/mitaka/expand/4a408dd491c2_UpdateName.py b/neutron_lbaas/db/migration/alembic_migrations/versions/mitaka/expand/4a408dd491c2_UpdateName.py new file mode 100644 index 000000000..dcd2d6127 --- /dev/null +++ b/neutron_lbaas/db/migration/alembic_migrations/versions/mitaka/expand/4a408dd491c2_UpdateName.py @@ -0,0 +1,35 @@ +# Copyright 2015 NEC Corporation +# +# 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. + +"""Addition of Name column to lbaas_members and lbaas_healthmonitors table + +Revision ID: 4a408dd491c2 +Revises: 3345facd0452 +Create Date: 2015-11-16 11:47:43.061649 + +""" + +# revision identifiers, used by Alembic. +revision = '4a408dd491c2' +down_revision = '3345facd0452' + +from alembic import op +import sqlalchemy as sa + +LB_TAB_NAME = ['lbaas_members', 'lbaas_healthmonitors'] + + +def upgrade(): + for table in LB_TAB_NAME: + op.add_column(table, sa.Column('name', sa.String(255), nullable=True)) diff --git a/neutron_lbaas/extensions/loadbalancerv2.py b/neutron_lbaas/extensions/loadbalancerv2.py index 00624693f..244e8773a 100644 --- a/neutron_lbaas/extensions/loadbalancerv2.py +++ b/neutron_lbaas/extensions/loadbalancerv2.py @@ -316,7 +316,11 @@ RESOURCE_ATTRIBUTE_MAP = { 'admin_state_up': {'allow_post': True, 'allow_put': True, 'default': True, 'convert_to': attr.convert_to_boolean, - 'is_visible': True} + 'is_visible': True}, + 'name': {'allow_post': True, 'allow_put': True, + 'validate': {'type:string': attr.NAME_MAX_LEN}, + 'default': '', + 'is_visible': True} } } @@ -352,7 +356,10 @@ SUB_RESOURCE_ATTRIBUTE_MAP = { 'subnet_id': {'allow_post': True, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True}, - + 'name': {'allow_post': True, 'allow_put': True, + 'validate': {'type:string': attr.NAME_MAX_LEN}, + 'default': '', + 'is_visible': True}, } } } diff --git a/neutron_lbaas/services/loadbalancer/data_models.py b/neutron_lbaas/services/loadbalancer/data_models.py index 6b7c21977..ef972515e 100644 --- a/neutron_lbaas/services/loadbalancer/data_models.py +++ b/neutron_lbaas/services/loadbalancer/data_models.py @@ -271,7 +271,7 @@ class HealthMonitor(BaseDataModel): def __init__(self, id=None, tenant_id=None, type=None, delay=None, timeout=None, max_retries=None, http_method=None, url_path=None, expected_codes=None, provisioning_status=None, - admin_state_up=None, pool=None): + admin_state_up=None, pool=None, name=None): self.id = id self.tenant_id = tenant_id self.type = type @@ -284,6 +284,7 @@ class HealthMonitor(BaseDataModel): self.provisioning_status = provisioning_status self.admin_state_up = admin_state_up self.pool = pool + self.name = name def attached_to_loadbalancer(self): return bool(self.pool and self.pool.listener and @@ -378,7 +379,7 @@ class Member(BaseDataModel): def __init__(self, id=None, tenant_id=None, pool_id=None, address=None, protocol_port=None, weight=None, admin_state_up=None, subnet_id=None, operating_status=None, - provisioning_status=None, pool=None): + provisioning_status=None, pool=None, name=None): self.id = id self.tenant_id = tenant_id self.pool_id = pool_id @@ -390,6 +391,7 @@ class Member(BaseDataModel): self.operating_status = operating_status self.provisioning_status = provisioning_status self.pool = pool + self.name = name def attached_to_loadbalancer(self): return bool(self.pool and self.pool.listener and diff --git a/neutron_lbaas/tests/unit/db/loadbalancer/test_db_loadbalancerv2.py b/neutron_lbaas/tests/unit/db/loadbalancer/test_db_loadbalancerv2.py index 597a745c8..9f373b2ab 100644 --- a/neutron_lbaas/tests/unit/db/loadbalancer/test_db_loadbalancerv2.py +++ b/neutron_lbaas/tests/unit/db/loadbalancer/test_db_loadbalancerv2.py @@ -129,7 +129,7 @@ class LbaasTestMixin(object): return pool_res def _get_member_optional_args(self): - return 'weight', 'admin_state_up' + return 'weight', 'admin_state_up', 'name' def _create_member(self, fmt, pool_id, address, protocol_port, subnet_id, expected_res_status=None, **kwargs): @@ -142,7 +142,6 @@ class LbaasTestMixin(object): for arg in args: if arg in kwargs and kwargs[arg] is not None: data['member'][arg] = kwargs[arg] - member_req = self.new_create_request('pools', data, fmt=fmt, @@ -156,7 +155,7 @@ class LbaasTestMixin(object): def _get_healthmonitor_optional_args(self): return ('weight', 'admin_state_up', 'expected_codes', 'url_path', - 'http_method') + 'http_method', 'name') def _create_healthmonitor(self, fmt, pool_id, type, delay, timeout, max_retries, expected_res_status=None, **kwargs): @@ -1625,13 +1624,14 @@ class LbaasMemberTests(MemberTestBase): 'weight': 1, 'admin_state_up': True, 'tenant_id': self._tenant_id, - 'subnet_id': '' + 'subnet_id': '', + 'name': 'member1' } expected.update(extras) expected['subnet_id'] = self.test_subnet_id - with self.member(pool_id=self.pool_id) as member: + with self.member(pool_id=self.pool_id, name='member1') as member: member_id = member['member'].get('id') self.assertTrue(member_id) @@ -1667,12 +1667,14 @@ class LbaasMemberTests(MemberTestBase): ('tenant_id', self._tenant_id), ('protocol_port', 80), ('weight', 10), - ('admin_state_up', False)] + ('admin_state_up', False), + ('name', 'member2')] with self.member(pool_id=self.pool_id) as member: member_id = member['member']['id'] resp, pool1_update = self._get_pool_api(self.pool_id) self.assertEqual(1, len(pool1_update['pool']['members'])) - data = {'member': {'weight': 10, 'admin_state_up': False}} + data = {'member': {'weight': 10, 'admin_state_up': False, + 'name': 'member2'}} resp, body = self._update_member_api(self.pool_id, member_id, data) for k, v in keys: self.assertEqual(v, body['member'][k]) @@ -1694,15 +1696,18 @@ class LbaasMemberTests(MemberTestBase): ('tenant_id', self._tenant_id), ('protocol_port', 80), ('weight', 1), - ('admin_state_up', True)] - with self.member(pool_id=self.pool_id) as member: + ('admin_state_up', True), + ('name', 'member1')] + with self.member(pool_id=self.pool_id, + name='member1') as member: member_id = member['member']['id'] resp, body = self._get_member_api(self.pool_id, member_id) for k, v in keys: self.assertEqual(v, body['member'][k]) def test_list_members(self): - with self.member(pool_id=self.pool_id, protocol_port=81): + with self.member(pool_id=self.pool_id, + name='member1', protocol_port=81): resp, body = self._list_members_api(self.pool_id) self.assertEqual(1, len(body['members'])) @@ -1776,6 +1781,17 @@ class LbaasMemberTests(MemberTestBase): 'WRONG_POOL_ID', member_id, data) self.assertEqual(webob.exc.HTTPNotFound.code, resp.status_int) + def test_create_member_invalid_name(self): + data = {'member': {'address': '127.0.0.1', + 'protocol_port': 80, + 'weight': 1, + 'admin_state_up': True, + 'tenant_id': self._tenant_id, + 'subnet_id': self.test_subnet_id, + 'name': 123}} + resp, body = self._create_member_api('POOL_ID', data) + self.assertEqual(webob.exc.HTTPBadRequest.code, resp.status_int) + def test_delete_member_invalid_pool_id(self): with self.member(pool_id=self.pool_id) as member: member_id = member['member']['id'] @@ -1783,7 +1799,8 @@ class LbaasMemberTests(MemberTestBase): self.assertEqual(webob.exc.HTTPNotFound.code, resp.status_int) def test_get_pool_shows_members(self): - with self.member(pool_id=self.pool_id) as member: + with self.member(pool_id=self.pool_id, + name='member1') as member: expected = {'id': member['member']['id']} resp, body = self._get_pool_api(self.pool_id) self.assertIn(expected, body['pool']['members']) @@ -1834,12 +1851,14 @@ class LbaasHealthMonitorTests(HealthMonitorTestBase): 'expected_codes': '200', 'admin_state_up': True, 'tenant_id': self._tenant_id, - 'pools': [{'id': self.pool_id}] + 'pools': [{'id': self.pool_id}], + 'name': 'monitor1' } expected.update(extras) - with self.healthmonitor(pool_id=self.pool_id) as healthmonitor: + with self.healthmonitor(pool_id=self.pool_id, + name='monitor1') as healthmonitor: hm_id = healthmonitor['healthmonitor'].get('id') self.assertTrue(hm_id) @@ -1868,12 +1887,15 @@ class LbaasHealthMonitorTests(HealthMonitorTestBase): 'expected_codes': '200', 'admin_state_up': True, 'tenant_id': self._tenant_id, - 'pools': [{'id': self.pool_id}] + 'pools': [{'id': self.pool_id}], + 'name': 'monitor1' + } expected.update(extras) - with self.healthmonitor(pool_id=self.pool_id) as healthmonitor: + with self.healthmonitor(pool_id=self.pool_id, + name='monitor1') as healthmonitor: hm_id = healthmonitor['healthmonitor']['id'] resp, body = self._get_healthmonitor_api(hm_id) actual = {} @@ -1895,18 +1917,21 @@ class LbaasHealthMonitorTests(HealthMonitorTestBase): 'expected_codes': '200,404', 'admin_state_up': True, 'tenant_id': self._tenant_id, - 'pools': [{'id': self.pool_id}] + 'pools': [{'id': self.pool_id}], + 'name': 'monitor2' } expected.update(extras) - with self.healthmonitor(pool_id=self.pool_id) as healthmonitor: + with self.healthmonitor(pool_id=self.pool_id, + name='monitor1') as healthmonitor: hm_id = healthmonitor['healthmonitor']['id'] data = {'healthmonitor': {'delay': 30, 'timeout': 10, 'max_retries': 4, 'expected_codes': '200,404', - 'url_path': '/index.html'}} + 'url_path': '/index.html', + 'name': 'monitor2'}} resp, body = self._update_healthmonitor_api(hm_id, data) actual = {} for k, v in body['healthmonitor'].items(): @@ -2089,6 +2114,17 @@ class LbaasHealthMonitorTests(HealthMonitorTestBase): resp, body = self._create_healthmonitor_api(data) self.assertEqual(webob.exc.HTTPNotFound.code, resp.status_int) + def test_create_healthmonitor_invalid_name(self): + data = {'healthmonitor': {'type': lb_const.HEALTH_MONITOR_TCP, + 'delay': 1, + 'timeout': 1, + 'max_retries': 1, + 'tenant_id': self._tenant_id, + 'pool_id': self.pool_id, + 'name': 123}} + resp, body = self._create_healthmonitor_api(data) + self.assertEqual(webob.exc.HTTPBadRequest.code, resp.status_int) + def test_only_one_healthmonitor_per_pool(self): with self.healthmonitor(pool_id=self.pool_id): data = {'healthmonitor': {'type': lb_const.HEALTH_MONITOR_TCP, @@ -2111,10 +2147,12 @@ class LbaasHealthMonitorTests(HealthMonitorTestBase): 'expected_codes': '200', 'admin_state_up': True, 'tenant_id': self._tenant_id, - 'pools': [{'id': self.pool_id}] + 'pools': [{'id': self.pool_id}], + 'name': 'monitor1' } - with self.healthmonitor(pool_id=self.pool_id) as healthmonitor: + with self.healthmonitor(pool_id=self.pool_id, + name='monitor1') as healthmonitor: hm_id = healthmonitor['healthmonitor']['id'] expected['id'] = hm_id resp, body = self._get_healthmonitor_api(hm_id) @@ -2131,7 +2169,8 @@ class LbaasHealthMonitorTests(HealthMonitorTestBase): 'expected_codes': '200', 'admin_state_up': True, 'tenant_id': self._tenant_id, - 'pools': [{'id': self.pool_id}] + 'pools': [{'id': self.pool_id}], + 'name': '', } with self.healthmonitor(pool_id=self.pool_id) as healthmonitor: diff --git a/neutron_lbaas/tests/unit/services/loadbalancer/test_loadbalancer_plugin.py b/neutron_lbaas/tests/unit/services/loadbalancer/test_loadbalancer_plugin.py index 2c8915a2d..d3d43e657 100644 --- a/neutron_lbaas/tests/unit/services/loadbalancer/test_loadbalancer_plugin.py +++ b/neutron_lbaas/tests/unit/services/loadbalancer/test_loadbalancer_plugin.py @@ -807,7 +807,8 @@ class LoadBalancerExtensionV2TestCase(base.ExtensionTestCase): 'weight': 1, 'subnet_id': subnet_id, 'admin_state_up': True, - 'tenant_id': _uuid()}} + 'tenant_id': _uuid(), + 'name': 'member1'}} return_value = copy.copy(data['member']) return_value.update({'id': member_id}) @@ -831,7 +832,8 @@ class LoadBalancerExtensionV2TestCase(base.ExtensionTestCase): return_value = [{'name': 'member1', 'admin_state_up': True, 'tenant_id': _uuid(), - 'id': member_id}] + 'id': member_id, + 'name': 'member1'}] instance = self.plugin.return_value instance.get_pools.return_value = return_value @@ -850,7 +852,8 @@ class LoadBalancerExtensionV2TestCase(base.ExtensionTestCase): update_data = {'member': {'admin_state_up': False}} return_value = {'admin_state_up': False, 'tenant_id': _uuid(), - 'id': member_id} + 'id': member_id, + 'name': 'member1'} instance = self.plugin.return_value instance.update_pool_member.return_value = return_value @@ -872,7 +875,8 @@ class LoadBalancerExtensionV2TestCase(base.ExtensionTestCase): member_id = _uuid() return_value = {'admin_state_up': False, 'tenant_id': _uuid(), - 'id': member_id} + 'id': member_id, + 'name': 'member1'} instance = self.plugin.return_value instance.get_pool_member.return_value = return_value @@ -911,7 +915,8 @@ class LoadBalancerExtensionV2TestCase(base.ExtensionTestCase): 'expected_codes': '200-300', 'admin_state_up': True, 'tenant_id': _uuid(), - 'pool_id': _uuid()}} + 'pool_id': _uuid(), + 'name': 'monitor1'}} return_value = copy.copy(data['healthmonitor']) return_value.update({'id': health_monitor_id}) del return_value['pool_id'] @@ -939,7 +944,8 @@ class LoadBalancerExtensionV2TestCase(base.ExtensionTestCase): 'expected_codes': '200-300', 'admin_state_up': True, 'tenant_id': _uuid(), - 'pool_id': _uuid()}} + 'pool_id': _uuid(), + 'name': 'monitor1'}} res = self.api.post(_get_path('lbaas/healthmonitors', fmt=self.fmt), self.serialize(data), @@ -952,7 +958,8 @@ class LoadBalancerExtensionV2TestCase(base.ExtensionTestCase): return_value = [{'type': 'HTTP', 'admin_state_up': True, 'tenant_id': _uuid(), - 'id': health_monitor_id}] + 'id': health_monitor_id, + 'name': 'monitor1'}] instance = self.plugin.return_value instance.get_healthmonitors.return_value = return_value @@ -969,7 +976,8 @@ class LoadBalancerExtensionV2TestCase(base.ExtensionTestCase): return_value = {'type': 'HTTP', 'admin_state_up': False, 'tenant_id': _uuid(), - 'id': health_monitor_id} + 'id': health_monitor_id, + 'name': 'monitor1'} instance = self.plugin.return_value instance.update_healthmonitor.return_value = return_value @@ -991,7 +999,8 @@ class LoadBalancerExtensionV2TestCase(base.ExtensionTestCase): return_value = {'type': 'HTTP', 'admin_state_up': False, 'tenant_id': _uuid(), - 'id': health_monitor_id} + 'id': health_monitor_id, + 'name': 'monitor1'} instance = self.plugin.return_value instance.get_healthmonitor.return_value = return_value