Fix Octavia v1 API project_id for POST
This patch corrects the project_id handling for POST calls to the Octavia v1 API. For load balancer create calls we use the specified project_id if the user is an admin or noauth is specified. If no project_id is specified in the request we use the project_id from the context. If no project_id can be found we raise an exception. For the other object POST methods we use the project_id from the parent load balancer. Change-Id: Ibf59541b8811e3bbe36cfec039f91e20036102e4 Closes-Bug: #1624145
This commit is contained in:
parent
9cd1bab382
commit
108ab27952
@ -135,3 +135,9 @@ class BaseController(rest.RestController):
|
|||||||
if db_quotas.member is None:
|
if db_quotas.member is None:
|
||||||
db_quotas.member = CONF.quotas.default_member_quota
|
db_quotas.member = CONF.quotas.default_member_quota
|
||||||
return db_quotas
|
return db_quotas
|
||||||
|
|
||||||
|
def _get_lb_project_id(self, session, id):
|
||||||
|
"""Get the project_id of the load balancer from the database."""
|
||||||
|
lb = self._get_db_obj(session, self.repositories.load_balancer,
|
||||||
|
data_models.LoadBalancer, id)
|
||||||
|
return lb.project_id
|
||||||
|
@ -96,6 +96,9 @@ class HealthMonitorController(base.BaseController):
|
|||||||
"""Creates a health monitor on a pool."""
|
"""Creates a health monitor on a pool."""
|
||||||
context = pecan.request.context.get('octavia_context')
|
context = pecan.request.context.get('octavia_context')
|
||||||
|
|
||||||
|
health_monitor.project_id = self._get_lb_project_id(
|
||||||
|
context.session, self.load_balancer_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db_hm = self.repositories.health_monitor.get(
|
db_hm = self.repositories.health_monitor.get(
|
||||||
context.session, pool_id=self.pool_id)
|
context.session, pool_id=self.pool_id)
|
||||||
|
@ -77,6 +77,7 @@ class L7PolicyController(base.BaseController):
|
|||||||
def post(self, l7policy):
|
def post(self, l7policy):
|
||||||
"""Creates a l7policy on a listener."""
|
"""Creates a l7policy on a listener."""
|
||||||
context = pecan.request.context.get('octavia_context')
|
context = pecan.request.context.get('octavia_context')
|
||||||
|
|
||||||
l7policy_dict = validate.sanitize_l7policy_api_args(
|
l7policy_dict = validate.sanitize_l7policy_api_args(
|
||||||
l7policy.to_dict(render_unsets=True), create=True)
|
l7policy.to_dict(render_unsets=True), create=True)
|
||||||
# Make sure any pool specified by redirect_pool_id exists
|
# Make sure any pool specified by redirect_pool_id exists
|
||||||
|
@ -88,6 +88,7 @@ class L7RuleController(base.BaseController):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise exceptions.L7RuleValidation(error=e)
|
raise exceptions.L7RuleValidation(error=e)
|
||||||
context = pecan.request.context.get('octavia_context')
|
context = pecan.request.context.get('octavia_context')
|
||||||
|
|
||||||
self._check_l7policy_max_rules(context.session)
|
self._check_l7policy_max_rules(context.session)
|
||||||
l7rule_dict = db_prepare.create_l7rule(
|
l7rule_dict = db_prepare.create_l7rule(
|
||||||
l7rule.to_dict(render_unsets=True), self.l7policy_id)
|
l7rule.to_dict(render_unsets=True), self.l7policy_id)
|
||||||
|
@ -162,6 +162,9 @@ class ListenersController(base.BaseController):
|
|||||||
"""Creates a listener on a load balancer."""
|
"""Creates a listener on a load balancer."""
|
||||||
context = pecan.request.context.get('octavia_context')
|
context = pecan.request.context.get('octavia_context')
|
||||||
|
|
||||||
|
listener.project_id = self._get_lb_project_id(context.session,
|
||||||
|
self.load_balancer_id)
|
||||||
|
|
||||||
lock_session = db_api.get_session(autocommit=False)
|
lock_session = db_api.get_session(autocommit=False)
|
||||||
if self.repositories.check_quota_met(
|
if self.repositories.check_quota_met(
|
||||||
context.session,
|
context.session,
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
from oslo_db import exception as odb_exceptions
|
from oslo_db import exception as odb_exceptions
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
@ -33,7 +34,7 @@ from octavia.db import api as db_api
|
|||||||
from octavia.db import prepare as db_prepare
|
from octavia.db import prepare as db_prepare
|
||||||
from octavia.i18n import _LI
|
from octavia.i18n import _LI
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -103,6 +104,17 @@ class LoadBalancersController(base.BaseController):
|
|||||||
def post(self, load_balancer):
|
def post(self, load_balancer):
|
||||||
"""Creates a load balancer."""
|
"""Creates a load balancer."""
|
||||||
context = pecan.request.context.get('octavia_context')
|
context = pecan.request.context.get('octavia_context')
|
||||||
|
|
||||||
|
project_id = context.project_id
|
||||||
|
if context.is_admin or CONF.auth_strategy == constants.NOAUTH:
|
||||||
|
if load_balancer.project_id:
|
||||||
|
project_id = load_balancer.project_id
|
||||||
|
|
||||||
|
if not project_id:
|
||||||
|
raise exceptions.MissingAPIProjectID()
|
||||||
|
|
||||||
|
load_balancer.project_id = project_id
|
||||||
|
|
||||||
# Validate the subnet id
|
# Validate the subnet id
|
||||||
if load_balancer.vip.subnet_id:
|
if load_balancer.vip.subnet_id:
|
||||||
if not validate.subnet_exists(load_balancer.vip.subnet_id):
|
if not validate.subnet_exists(load_balancer.vip.subnet_id):
|
||||||
|
@ -94,6 +94,10 @@ class MembersController(base.BaseController):
|
|||||||
def post(self, member):
|
def post(self, member):
|
||||||
"""Creates a pool member on a pool."""
|
"""Creates a pool member on a pool."""
|
||||||
context = pecan.request.context.get('octavia_context')
|
context = pecan.request.context.get('octavia_context')
|
||||||
|
|
||||||
|
member.project_id = self._get_lb_project_id(context.session,
|
||||||
|
self.load_balancer_id)
|
||||||
|
|
||||||
# Validate member subnet
|
# Validate member subnet
|
||||||
if member.subnet_id and not validate.subnet_exists(member.subnet_id):
|
if member.subnet_id and not validate.subnet_exists(member.subnet_id):
|
||||||
raise exceptions.NotFound(resource='Subnet',
|
raise exceptions.NotFound(resource='Subnet',
|
||||||
|
@ -134,6 +134,9 @@ class PoolsController(base.BaseController):
|
|||||||
# pool_dict:
|
# pool_dict:
|
||||||
context = pecan.request.context.get('octavia_context')
|
context = pecan.request.context.get('octavia_context')
|
||||||
|
|
||||||
|
pool.project_id = self._get_lb_project_id(context.session,
|
||||||
|
self.load_balancer_id)
|
||||||
|
|
||||||
lock_session = db_api.get_session(autocommit=False)
|
lock_session = db_api.get_session(autocommit=False)
|
||||||
if self.repositories.check_quota_met(
|
if self.repositories.check_quota_met(
|
||||||
context.session,
|
context.session,
|
||||||
|
@ -21,6 +21,8 @@ from wsmeext import pecan as wsme_pecan
|
|||||||
|
|
||||||
from octavia.api.v1.controllers import base
|
from octavia.api.v1.controllers import base
|
||||||
from octavia.api.v1.types import quotas as quota_types
|
from octavia.api.v1.types import quotas as quota_types
|
||||||
|
from octavia.common import constants
|
||||||
|
from octavia.common import exceptions
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
CONF.import_group('quotas', 'octavia.common.config')
|
CONF.import_group('quotas', 'octavia.common.config')
|
||||||
@ -52,6 +54,17 @@ class QuotasController(base.BaseController):
|
|||||||
def put(self, project_id, quotas):
|
def put(self, project_id, quotas):
|
||||||
"""Update any or all quotas for a project."""
|
"""Update any or all quotas for a project."""
|
||||||
context = pecan.request.context.get('octavia_context')
|
context = pecan.request.context.get('octavia_context')
|
||||||
|
|
||||||
|
new_project_id = context.project_id
|
||||||
|
if context.is_admin or CONF.auth_strategy == constants.NOAUTH:
|
||||||
|
if project_id:
|
||||||
|
new_project_id = project_id
|
||||||
|
|
||||||
|
if not new_project_id:
|
||||||
|
raise exceptions.MissingAPIProjectID()
|
||||||
|
|
||||||
|
project_id = new_project_id
|
||||||
|
|
||||||
quotas_dict = quotas.to_dict()
|
quotas_dict = quotas.to_dict()
|
||||||
self.repositories.quotas.update(context.session, project_id,
|
self.repositories.quotas.update(context.session, project_id,
|
||||||
**quotas_dict)
|
**quotas_dict)
|
||||||
|
@ -48,6 +48,7 @@ class HealthMonitorPOST(base.BaseType):
|
|||||||
expected_codes = wtypes.wsattr(
|
expected_codes = wtypes.wsattr(
|
||||||
wtypes.text, default=constants.HEALTH_MONITOR_DEFAULT_EXPECTED_CODES)
|
wtypes.text, default=constants.HEALTH_MONITOR_DEFAULT_EXPECTED_CODES)
|
||||||
enabled = wtypes.wsattr(bool, default=True)
|
enabled = wtypes.wsattr(bool, default=True)
|
||||||
|
# TODO(johnsom) Remove after deprecation (R series)
|
||||||
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,6 +89,7 @@ class ListenerPOST(base.BaseType):
|
|||||||
tls_certificate_id = wtypes.wsattr(wtypes.StringType(max_length=255))
|
tls_certificate_id = wtypes.wsattr(wtypes.StringType(max_length=255))
|
||||||
tls_termination = wtypes.wsattr(TLSTermination)
|
tls_termination = wtypes.wsattr(TLSTermination)
|
||||||
sni_containers = [wtypes.StringType(max_length=255)]
|
sni_containers = [wtypes.StringType(max_length=255)]
|
||||||
|
# TODO(johnsom) Remove after deprecation (R series)
|
||||||
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
||||||
default_pool_id = wtypes.wsattr(wtypes.UuidType())
|
default_pool_id = wtypes.wsattr(wtypes.UuidType())
|
||||||
default_pool = wtypes.wsattr(pool.PoolPOST)
|
default_pool = wtypes.wsattr(pool.PoolPOST)
|
||||||
|
@ -39,6 +39,7 @@ class MemberPOST(base.BaseType):
|
|||||||
protocol_port = wtypes.wsattr(wtypes.IntegerType(), mandatory=True)
|
protocol_port = wtypes.wsattr(wtypes.IntegerType(), mandatory=True)
|
||||||
weight = wtypes.wsattr(wtypes.IntegerType(), default=1)
|
weight = wtypes.wsattr(wtypes.IntegerType(), default=1)
|
||||||
subnet_id = wtypes.wsattr(wtypes.UuidType())
|
subnet_id = wtypes.wsattr(wtypes.UuidType())
|
||||||
|
# TODO(johnsom) Remove after deprecation (R series)
|
||||||
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,6 +97,7 @@ class PoolPOST(base.BaseType):
|
|||||||
wtypes.Enum(str, *constants.SUPPORTED_LB_ALGORITHMS),
|
wtypes.Enum(str, *constants.SUPPORTED_LB_ALGORITHMS),
|
||||||
mandatory=True)
|
mandatory=True)
|
||||||
session_persistence = wtypes.wsattr(SessionPersistencePOST)
|
session_persistence = wtypes.wsattr(SessionPersistencePOST)
|
||||||
|
# TODO(johnsom) Remove after deprecation (R series)
|
||||||
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
project_id = wtypes.wsattr(wtypes.StringType(max_length=36))
|
||||||
health_monitor = wtypes.wsattr(health_monitor.HealthMonitorPOST)
|
health_monitor = wtypes.wsattr(health_monitor.HealthMonitorPOST)
|
||||||
members = wtypes.wsattr([member.MemberPOST])
|
members = wtypes.wsattr([member.MemberPOST])
|
||||||
|
@ -253,3 +253,8 @@ class ProjectBusyException(APIException):
|
|||||||
|
|
||||||
class MissingProjectID(OctaviaException):
|
class MissingProjectID(OctaviaException):
|
||||||
message = _('Missing project ID in request where one is required.')
|
message = _('Missing project ID in request where one is required.')
|
||||||
|
|
||||||
|
|
||||||
|
class MissingAPIProjectID(APIException):
|
||||||
|
msg = _('Missing project ID in request where one is required.')
|
||||||
|
code = 400
|
||||||
|
@ -117,7 +117,7 @@ class TestHealthMonitor(base.BaseAPITest):
|
|||||||
self.pool.get('id'),
|
self.pool.get('id'),
|
||||||
constants.HEALTH_MONITOR_HTTP,
|
constants.HEALTH_MONITOR_HTTP,
|
||||||
1, 1, 1, 1, project_id=pid)
|
1, 1, 1, 1, project_id=pid)
|
||||||
self.assertEqual(pid, api_hm.get('project_id'))
|
self.assertEqual(self.project_id, api_hm.get('project_id'))
|
||||||
|
|
||||||
def test_create_over_quota(self):
|
def test_create_over_quota(self):
|
||||||
self.check_quota_met_true_mock.start()
|
self.check_quota_met_true_mock.start()
|
||||||
|
@ -113,6 +113,7 @@ class TestListener(base.BaseAPITest):
|
|||||||
self.assertIn(sni, sni_ex)
|
self.assertIn(sni, sni_ex)
|
||||||
self.assertIsNotNone(listener_api.pop('created_at'))
|
self.assertIsNotNone(listener_api.pop('created_at'))
|
||||||
self.assertIsNone(listener_api.pop('updated_at'))
|
self.assertIsNone(listener_api.pop('updated_at'))
|
||||||
|
lb_listener['project_id'] = self.project_id
|
||||||
self.assertEqual(lb_listener, listener_api)
|
self.assertEqual(lb_listener, listener_api)
|
||||||
self.assert_correct_lb_status(self.lb.get('id'),
|
self.assert_correct_lb_status(self.lb.get('id'),
|
||||||
constants.PENDING_UPDATE,
|
constants.PENDING_UPDATE,
|
||||||
|
@ -123,7 +123,7 @@ class TestMember(base.BaseAPITest):
|
|||||||
api_member = self.create_member(self.lb.get('id'),
|
api_member = self.create_member(self.lb.get('id'),
|
||||||
self.pool.get('id'),
|
self.pool.get('id'),
|
||||||
'10.0.0.1', 80, project_id=pid)
|
'10.0.0.1', 80, project_id=pid)
|
||||||
self.assertEqual(pid, api_member.get('project_id'))
|
self.assertEqual(self.project_id, api_member.get('project_id'))
|
||||||
|
|
||||||
def test_create_with_duplicate_id(self):
|
def test_create_with_duplicate_id(self):
|
||||||
member = self.create_member(self.lb.get('id'),
|
member = self.create_member(self.lb.get('id'),
|
||||||
|
@ -166,7 +166,7 @@ class TestPool(base.BaseAPITest):
|
|||||||
constants.PROTOCOL_HTTP,
|
constants.PROTOCOL_HTTP,
|
||||||
constants.LB_ALGORITHM_ROUND_ROBIN,
|
constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||||
project_id=pid)
|
project_id=pid)
|
||||||
self.assertEqual(pid, api_pool.get('project_id'))
|
self.assertEqual(self.project_id, api_pool.get('project_id'))
|
||||||
|
|
||||||
def test_create_with_duplicate_id(self):
|
def test_create_with_duplicate_id(self):
|
||||||
pool = self.create_pool(self.lb.get('id'),
|
pool = self.create_pool(self.lb.get('id'),
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
deprecations:
|
||||||
|
- |
|
||||||
|
The project_id attribute of the POST method on the following objects
|
||||||
|
is now deprecated\: listener, pool, health monitor, and member.
|
||||||
|
These objects will use the parent load balancer's project_id.
|
||||||
|
Values passed into the project_id on those objects will be ignored
|
||||||
|
until the deprecation cycle has expired, at which point they will
|
||||||
|
cause an error.
|
Loading…
Reference in New Issue
Block a user