Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 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.

"""add_flavor_permission_rules

Revision ID: f177f53f978b
Revises: cdeec0c85668
Create Date: 2025-10-24 15:43:26.554412
"""

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = 'f177f53f978b'
down_revision = 'cdeec0c85668'
branch_labels = None
depends_on = None


def upgrade():
op.create_table(
'flavor_permission_rules',
sa.Column('created_at', sa.DateTime),
sa.Column('updated_at', sa.DateTime),
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('uuid', sa.String(36), nullable=False),
sa.Column('project_id', sa.String(255), nullable=False),
sa.Column('flavor_id', sa.Integer, sa.ForeignKey('flavors.id'),
nullable=True),
sa.Column(
'type',
sa.Enum('allow', 'deny', name='flavor_permission_rules0type'),
nullable=False
),
sa.UniqueConstraint('uuid', name='uniq_flavor_permission_rules0uuid'),
sa.UniqueConstraint(
'flavor_id', 'project_id',
name='uniq_flavor_permission_rules0flavor_id0project_id'
),
sa.Index('flavor_permission_rules_uuid_idx', 'uuid'),
sa.Index('flavor_permission_rules_project_id_idx', 'project_id'),
mysql_engine='InnoDB',
mysql_charset='utf8',
)
27 changes: 27 additions & 0 deletions nova/db/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,33 @@ class FlavorProjects(BASE):
primaryjoin='FlavorProjects.flavor_id == Flavors.id')


class FlavorPermissionRule(BASE):
"""Represents a flavor permission rule for a project"""

__tablename__ = 'flavor_permission_rules'
__table_args__ = (
schema.UniqueConstraint(
'uuid', name='uniq_flavor_permission_rules0uuid'),
schema.UniqueConstraint(
'flavor_id',
'project_id',
name='uniq_flavor_permission_rules0flavor_id0project_id'
),
sa.Index('flavor_permission_rules_uuid_idx', 'uuid'),
sa.Index('flavor_permission_rules_project_id_idx', 'project_id'),
)

id = sa.Column(sa.Integer, primary_key=True)
uuid = sa.Column(sa.String(36), nullable=False)
project_id = sa.Column(sa.String(255), nullable=False)
# Use NULL to represent all flavors
flavor_id = sa.Column(
sa.Integer, sa.ForeignKey('flavors.id'), nullable=True)
type = sa.Column(
sa.Enum('allow', 'deny', name='flavor_permission_rules0type'),
nullable=False)


class BuildRequest(BASE):
"""Represents the information passed to the scheduler."""

Expand Down
15 changes: 15 additions & 0 deletions nova/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,21 @@ class FlavorAccessExists(NovaException):
"and project %(project_id)s combination.")


class FlavorPermissionRuleExists(NovaException):
msg_fmt = _("Flavor permission rule already exists for uuid %(uuid)s or "
"combination of project %(project_id)s and flavor "
"%(flavor_id)s.")


class FlavorPermissionRuleNotFound(NotFound):
msg_fmt = _("Flavor permission rule not found for id %(id)s")


class FlavorPermissionRuleNotFoundForProjectFlavor(NotFound):
msg_fmt = _("Flavor permission rule not found for project %(project_id)s "
"and flavor %(flavor_id)s combination.")


class InvalidSharedStorage(NovaException):
msg_fmt = _("%(path)s is not on shared storage: %(reason)s")

Expand Down
1 change: 1 addition & 0 deletions nova/objects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def register_all():
__import__('nova.objects.ec2')
__import__('nova.objects.external_event')
__import__('nova.objects.flavor')
__import__('nova.objects.flavor_permission_rule')
__import__('nova.objects.host_mapping')
__import__('nova.objects.hv_spec')
__import__('nova.objects.image_meta')
Expand Down
11 changes: 11 additions & 0 deletions nova/objects/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,13 @@ class InstanceTaskState(BaseNovaEnum):
SHELVING_OFFLOADING, UNSHELVING, IN_CLUSTER_VMOTION)


class FlavorPermissionRuleType(BaseNovaEnum):
ALLOW = 'allow'
DENY = 'deny'

ALL = (ALLOW, DENY)


class InstancePowerState(Enum):
_UNUSED = '_unused'
NOSTATE = 'pending'
Expand Down Expand Up @@ -1389,6 +1396,10 @@ class InstanceTaskStateField(BaseEnumField):
AUTO_TYPE = InstanceTaskState()


class FlavorPermissionRuleTypeField(BaseEnumField):
AUTO_TYPE = FlavorPermissionRuleType()


class InstancePowerStateField(BaseEnumField):
AUTO_TYPE = InstancePowerState()

Expand Down
2 changes: 2 additions & 0 deletions nova/objects/flavor.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ def _flavor_destroy(context, flavor_id=None, flavorid=None):
filter_by(flavor_id=result.id).delete()
context.session.query(api_models.FlavorExtraSpecs).\
filter_by(flavor_id=result.id).delete()
context.session.query(api_models.FlavorPermissionRule).\
filter_by(flavor_id=result.id).delete()
context.session.delete(result)
return result

Expand Down
Loading