Skip to content

Commit 9bd2441

Browse files
authored
Merge pull request #50 from stackhpc/upstream/yoga-2023-06-26
Synchronise yoga with upstream
2 parents 06e4b67 + 410ce2f commit 9bd2441

File tree

5 files changed

+158
-2
lines changed

5 files changed

+158
-2
lines changed

doc/source/admin/config-rbac.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,3 +804,37 @@ IDs.
804804
If an operator wants to prevent normal users from doing this, the
805805
``"create_rbac_policy":`` entry in ``policy.yaml`` can be adjusted
806806
from ``""`` to ``"rule:admin_only"``.
807+
808+
809+
Improve database RBAC query operations
810+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
811+
812+
Since [1]_, present in Yoga version, Neutron has indexes for
813+
"target_tenant" (now "target_project") and "action" columns in all
814+
RBAC related tables. That improves the SQL queries involving the
815+
RBAC tables [2]_. Any system before Yoga won't have these indexes
816+
but the system administrator can manually add them to the Neutron
817+
database following the next steps:
818+
819+
* Find the RBAC tables:
820+
821+
.. code-block:: console
822+
823+
$ tables=`mysql -e "use ovs_neutron; show tables;" | grep rbac`
824+
825+
826+
* Insert the indexes for the "target_tenant" and "action" columns:
827+
828+
$ for table in $tables do; mysql -e \
829+
"alter table $table add key (action); alter table $table add key (target_tenant);"; done
830+
831+
832+
In order to prevent errors during a system upgrade, [3]_ was
833+
implemented and backported up to Yoga. This patch checks if any index
834+
is already present in the Neutron tables and avoids executing the
835+
index creation command again.
836+
837+
838+
.. [1] https://review.opendev.org/c/openstack/neutron/+/810072
839+
.. [2] https://github.com/openstack/neutron-lib/blob/890d62a3df3f35bb18bf1a11e79a9e97e7dd2d2c/neutron_lib/db/model_query.py#L123-L131
840+
.. [3] https://review.opendev.org/c/openstack/neutron/+/884617

neutron/db/l3_db.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@
8181
'port: %(port_id)s.')
8282

8383

84+
# TODO(froyo): Move this exception to neutron-lib as soon as possible, and when
85+
# a new release is created and pointed to in the requirements remove this code.
86+
class FipAssociated(n_exc.InUse):
87+
message = _('Unable to complete the operation on port "%(port_id)s" '
88+
'because the port still has an associated floating IP.')
89+
90+
8491
@registry.has_registry_receivers
8592
class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
8693
base_services.WorkerBase,
@@ -1766,12 +1773,27 @@ def disassociate_floatingips(self, context, port_id, do_notify=True):
17661773
@return: set of router-ids that require notification updates
17671774
"""
17681775
with db_api.CONTEXT_WRITER.using(context):
1776+
# NOTE(froyo): Context is elevated to confirm the presence of at
1777+
# least one FIP associated to the port_id. Additional checks
1778+
# regarding the tenant's grants will be carried out in following
1779+
# lines.
17691780
if not l3_obj.FloatingIP.objects_exist(
1770-
context, fixed_port_id=port_id):
1781+
context.elevated(), fixed_port_id=port_id):
17711782
return []
17721783

17731784
floating_ip_objs = l3_obj.FloatingIP.get_objects(
17741785
context, fixed_port_id=port_id)
1786+
1787+
# NOTE(froyo): To ensure that a FIP assigned by an admin user
1788+
# cannot be disassociated by a tenant user, we raise exception to
1789+
# generate a 409 Conflict response message that prompts the tenant
1790+
# user to contact an admin, rather than a 500 error message.
1791+
if not context.is_admin:
1792+
floating_ip_objs_admin = l3_obj.FloatingIP.get_objects(
1793+
context.elevated(), fixed_port_id=port_id)
1794+
if floating_ip_objs_admin != floating_ip_objs:
1795+
raise FipAssociated(port_id=port_id)
1796+
17751797
router_ids = {fip.router_id for fip in floating_ip_objs}
17761798
old_fips = {fip.id: self._make_floatingip_dict(fip)
17771799
for fip in floating_ip_objs}

neutron/db/migration/alembic_migrations/versions/yoga/expand/ba859d649675_add_indexes_to_rbacs.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#
1515

1616
from alembic import op
17+
import sqlalchemy as sa
1718

1819

1920
"""Add indexes to RBACs
@@ -31,11 +32,34 @@
3132
OBJECTS = ('network', 'qospolicy', 'securitygroup', 'addressscope',
3233
'subnetpool', 'addressgroup')
3334
COLUMNS = ('target_tenant', 'action')
35+
_INSPECTOR = None
36+
37+
38+
def get_inspector():
39+
global _INSPECTOR
40+
if _INSPECTOR:
41+
return _INSPECTOR
42+
else:
43+
_INSPECTOR = sa.inspect(op.get_bind())
44+
45+
return _INSPECTOR
46+
47+
48+
def has_index(table, column):
49+
"""Check if the table has an index *using only* the column name provided"""
50+
inspector = get_inspector()
51+
table_indexes = inspector.get_indexes(table)
52+
for index in table_indexes:
53+
if [column] == index['column_names']:
54+
return True
55+
return False
3456

3557

3658
def upgrade():
3759
for object in OBJECTS:
3860
table = object + 'rbacs'
3961
ix = 'ix_' + table + '_'
4062
for column in COLUMNS:
41-
op.create_index(op.f(ix + column), table, [column], unique=False)
63+
if not has_index(table, column):
64+
op.create_index(op.f(ix + column), table, [column],
65+
unique=False)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright 2017 OpenStack Foundation
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
#
15+
16+
from oslo_db.sqlalchemy import utils as db_utils
17+
18+
from neutron.db.migration.alembic_migrations.versions.yoga.expand import \
19+
ba859d649675_add_indexes_to_rbacs as _migration
20+
from neutron.tests.functional.db import test_migrations
21+
22+
23+
class TestAddIndexesToRbacsMixin(object):
24+
"""Validates binding_index for NetworkDhcpAgentBinding migration."""
25+
26+
@staticmethod
27+
def get_index(table_indexes, column):
28+
for index in table_indexes:
29+
if [column] == index['column_names']:
30+
return True
31+
return False
32+
33+
def _pre_upgrade_ba859d649675(self, engine):
34+
for table in _migration.OBJECTS:
35+
table_indexes = db_utils.get_indexes(engine, table + 'rbacs')
36+
for column in _migration.COLUMNS:
37+
self.assertFalse(self.get_index(table_indexes, column))
38+
39+
def _check_ba859d649675(self, engine, data):
40+
for table in _migration.OBJECTS:
41+
table_indexes = db_utils.get_indexes(engine, table + 'rbacs')
42+
for column in _migration.COLUMNS:
43+
self.assertTrue(self.get_index(table_indexes, column))
44+
45+
46+
class TestAddIndexesToRbacsMySQL(
47+
TestAddIndexesToRbacsMixin,
48+
test_migrations.TestWalkMigrationsMySQL):
49+
pass
50+
51+
52+
class TestAddIndexesToRbacsPostgreSQL(
53+
TestAddIndexesToRbacsMixin,
54+
test_migrations.TestWalkMigrationsPostgreSQL):
55+
pass

neutron/tests/unit/db/test_l3_db.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,27 @@ def test_prevent_l3_port_existing_floating_ip(self, gp):
328328

329329
self.db.prevent_l3_port_deletion(ctx, None)
330330

331+
@mock.patch.object(l3_obj.FloatingIP, 'objects_exist')
332+
@mock.patch.object(l3_obj.FloatingIP, 'get_objects')
333+
def test_disassociate_floatingips_conflict_by_fip_attached(self,
334+
get_objects,
335+
objects_exist):
336+
context_tenant = context.Context('tenant', 'tenant', is_admin=False)
337+
objects_exist.return_value = True
338+
get_objects.side_effect = [
339+
[],
340+
[{'id': 'floating_ip1', 'port_id': 'port_id'}]]
341+
self.assertRaises(l3_db.FipAssociated,
342+
self.db.disassociate_floatingips,
343+
context_tenant,
344+
'port_id')
345+
objects_exist.assert_called_once_with(
346+
mock.ANY, fixed_port_id='port_id')
347+
expected_calls = [
348+
mock.call(context_tenant, fixed_port_id='port_id'),
349+
mock.call(mock.ANY, fixed_port_id='port_id')]
350+
get_objects.assert_has_calls(expected_calls)
351+
331352
@mock.patch.object(directory, 'get_plugin')
332353
def test_subscribe_address_scope_of_subnetpool(self, gp):
333354
l3_db.L3RpcNotifierMixin()

0 commit comments

Comments
 (0)