Added a new port extension: NUMA affinity policy. This extension adds the "numa_affinity_policy" parameter to the "port" API and specifies the NUMA affinity policy per port. This parameter is passed to Nova when a virtual machine is created. Nova will use this information to schedule the virtual machine. For backwards compatibility, this parameter will be "None" by default. Depends-On: https://review.opendev.org/#/c/740058/ Closes-Bug: #1886798 Change-Id: Ie3d68c098ddb727ab8333aa1de4064e67a4f00a7changes/67/740067/28
parent
62daa0dcbc
commit
a217a5d290
@ -1 +1 @@
|
||||
I38991de2b4
|
||||
532aa95457e2
|
||||
|
@ -0,0 +1,44 @@
|
||||
# Copyright 2020 OpenStack Foundation
|
||||
#
|
||||
# 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 alembic import op
|
||||
from neutron_lib import constants as n_const
|
||||
from neutron_lib.db import constants as db_const
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
"""port_numa_affinity_policy
|
||||
|
||||
Revision ID: 532aa95457e2
|
||||
Revises: I38991de2b4
|
||||
Create Date: 2020-07-10 14:59:18.868245
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '532aa95457e2'
|
||||
down_revision = 'I38991de2b4'
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table('portnumaaffinitypolicies',
|
||||
sa.Column('port_id',
|
||||
sa.String(length=db_const.UUID_FIELD_SIZE),
|
||||
sa.ForeignKey('ports.id', ondelete='CASCADE'),
|
||||
primary_key=True),
|
||||
sa.Column('numa_affinity_policy',
|
||||
sa.Enum(*n_const.PORT_NUMA_POLICIES,
|
||||
name='numa_affinity_policy'))
|
||||
)
|
@ -0,0 +1,36 @@
|
||||
# Copyright 2020 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 neutron_lib import constants as n_const
|
||||
from neutron_lib.db import constants as db_const
|
||||
from neutron_lib.db import model_base
|
||||
import sqlalchemy as sa
|
||||
|
||||
from neutron.db import models_v2
|
||||
|
||||
|
||||
class PortNumaAffinityPolicy(model_base.BASEV2):
|
||||
__tablename__ = 'portnumaaffinitypolicies'
|
||||
port_id = sa.Column(sa.String(db_const.UUID_FIELD_SIZE),
|
||||
sa.ForeignKey('ports.id', ondelete='CASCADE'),
|
||||
primary_key=True)
|
||||
numa_affinity_policy = sa.Column(sa.Enum(*n_const.PORT_NUMA_POLICIES,
|
||||
name='numa_affinity_policy'))
|
||||
port = sa.orm.relationship(
|
||||
models_v2.Port, load_on_pending=True,
|
||||
backref=sa.orm.backref('numa_affinity_policy', uselist=False,
|
||||
cascade='delete', lazy='joined'))
|
||||
|
||||
revises_on_change = ('port', )
|
@ -0,0 +1,67 @@
|
||||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
#
|
||||
# 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 neutron_lib.api.definitions import port_numa_affinity_policy as pnap
|
||||
from neutron_lib.api.definitions import portbindings
|
||||
from neutron_lib import exceptions as n_exc
|
||||
|
||||
from neutron.objects.port.extensions import port_numa_affinity_policy as \
|
||||
pnap_obj
|
||||
|
||||
|
||||
class PortNumaAffinityPolicyDbMixin(object):
|
||||
"""Mixin class to add NUMA affinity policy to a port"""
|
||||
|
||||
def _process_create_port(self, context, data, result):
|
||||
if not data.get(pnap.NUMA_AFFINITY_POLICY):
|
||||
result[pnap.NUMA_AFFINITY_POLICY] = None
|
||||
return
|
||||
|
||||
obj = pnap_obj.PortNumaAffinityPolicy(
|
||||
context, port_id=result['id'],
|
||||
numa_affinity_policy=data[pnap.NUMA_AFFINITY_POLICY])
|
||||
obj.create()
|
||||
result[pnap.NUMA_AFFINITY_POLICY] = data[pnap.NUMA_AFFINITY_POLICY]
|
||||
|
||||
def _process_update_port(self, context, data, result):
|
||||
if pnap.NUMA_AFFINITY_POLICY not in data:
|
||||
return
|
||||
|
||||
if (result[portbindings.VIF_TYPE] not in
|
||||
portbindings.VIF_UNPLUGGED_TYPES):
|
||||
raise n_exc.PortBoundNUMAAffinityPolicy(
|
||||
port_id=result['id'], host_id=result[portbindings.HOST_ID],
|
||||
numa_affinity_policy=data[pnap.NUMA_AFFINITY_POLICY])
|
||||
|
||||
obj = pnap_obj.PortNumaAffinityPolicy.get_object(context,
|
||||
port_id=result['id'])
|
||||
|
||||
if data[pnap.NUMA_AFFINITY_POLICY]:
|
||||
if not obj:
|
||||
return self._process_create_port(context, data, result)
|
||||
obj.update_fields(
|
||||
{pnap.NUMA_AFFINITY_POLICY: data[pnap.NUMA_AFFINITY_POLICY]})
|
||||
obj.update()
|
||||
else:
|
||||
if obj:
|
||||
obj.delete()
|
||||
|
||||
result[pnap.NUMA_AFFINITY_POLICY] = data[pnap.NUMA_AFFINITY_POLICY]
|
||||
|
||||
def _extend_port_dict(self, port_db, result):
|
||||
if port_db.numa_affinity_policy:
|
||||
result[pnap.NUMA_AFFINITY_POLICY] = (
|
||||
port_db.numa_affinity_policy.numa_affinity_policy)
|
||||
else:
|
||||
result[pnap.NUMA_AFFINITY_POLICY] = None
|
@ -0,0 +1,20 @@
|
||||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
#
|
||||
# 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 neutron_lib.api.definitions import port_numa_affinity_policy
|
||||
from neutron_lib.api import extensions as api_extensions
|
||||
|
||||
|
||||
class Port_numa_affinity_policy(api_extensions.APIExtensionDescriptor):
|
||||
api_definition = port_numa_affinity_policy
|
@ -0,0 +1,44 @@
|
||||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
#
|
||||
# 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 neutron_lib import constants as lib_constants
|
||||
from neutron_lib.objects import common_types
|
||||
from oslo_versionedobjects import fields as obj_fields
|
||||
|
||||
from neutron.db.models import port_numa_affinity_policy
|
||||
from neutron.objects import base
|
||||
|
||||
|
||||
# TODO(ralonsoh): move to neutron_lib.objects.common_types
|
||||
class NumaAffinityPoliciesEnumField(obj_fields.AutoTypedField):
|
||||
AUTO_TYPE = obj_fields.Enum(valid_values=lib_constants.PORT_NUMA_POLICIES)
|
||||
|
||||
|
||||
@base.NeutronObjectRegistry.register
|
||||
class PortNumaAffinityPolicy(base.NeutronDbObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
db_model = port_numa_affinity_policy.PortNumaAffinityPolicy
|
||||
|
||||
primary_keys = ['port_id']
|
||||
|
||||
new_facade = True
|
||||
|
||||
fields = {
|
||||
'port_id': common_types.UUIDField(),
|
||||
'numa_affinity_policy': NumaAffinityPoliciesEnumField(),
|
||||
}
|
||||
|
||||
foreign_keys = {'Port': {'port_id': 'id'}}
|
@ -0,0 +1,47 @@
|
||||
# Copyright 2020 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 neutron_lib.api.definitions import port_numa_affinity_policy as pnap
|
||||
from neutron_lib.plugins.ml2 import api
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron.db import port_numa_affinity_policy_db
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PortNumaAffinityPolicyExtensionDriver(
|
||||
api.ExtensionDriver,
|
||||
port_numa_affinity_policy_db.PortNumaAffinityPolicyDbMixin):
|
||||
|
||||
_supported_extension_alias = pnap.ALIAS
|
||||
|
||||
def initialize(self):
|
||||
LOG.info('PortNumaAffinityPolicyExtensionDriver initialization '
|
||||
'complete')
|
||||
|
||||
@property
|
||||
def extension_alias(self):
|
||||
return self._supported_extension_alias
|
||||
|
||||
def process_create_port(self, context, data, result):
|
||||
self._process_create_port(context, data, result)
|
||||
|
||||
def process_update_port(self, context, data, result):
|
||||
self._process_update_port(context, data, result)
|
||||
|
||||
def extend_port_dict(self, session, port_db, result):
|
||||
self._extend_port_dict(port_db, result)
|
@ -0,0 +1,85 @@
|
||||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
#
|
||||
# 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 ddt
|
||||
from neutron_lib.api.definitions import port_numa_affinity_policy as apidef
|
||||
from neutron_lib.api.definitions import portbindings
|
||||
from neutron_lib import constants
|
||||
|
||||
from neutron.db import db_base_plugin_v2
|
||||
from neutron.db import port_numa_affinity_policy_db as pnap_db
|
||||
from neutron.tests.unit.db import test_db_base_plugin_v2
|
||||
|
||||
|
||||
class PortNumaAffinityPolicyExtensionExtensionTestPlugin(
|
||||
db_base_plugin_v2.NeutronDbPluginV2,
|
||||
pnap_db.PortNumaAffinityPolicyDbMixin):
|
||||
"""Test plugin to mixin the port NUMA affinity policy extension."""
|
||||
|
||||
supported_extension_aliases = [apidef.ALIAS]
|
||||
|
||||
def create_port(self, context, port):
|
||||
with context.session.begin(subtransactions=True):
|
||||
new_port = super(
|
||||
PortNumaAffinityPolicyExtensionExtensionTestPlugin,
|
||||
self).create_port(context, port)
|
||||
self._process_create_port(context, port['port'], new_port)
|
||||
return new_port
|
||||
|
||||
def update_port(self, context, id, port):
|
||||
with context.session.begin(subtransactions=True):
|
||||
updated_port = super(
|
||||
PortNumaAffinityPolicyExtensionExtensionTestPlugin,
|
||||
self).update_port(context, id, port)
|
||||
updated_port[portbindings.VIF_TYPE] = portbindings.VIF_TYPE_UNBOUND
|
||||
self._process_update_port(context, port['port'], updated_port)
|
||||
return updated_port
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class PortNumaAffinityPolicyExtensionExtensionTestCase(
|
||||
test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
|
||||
"""Test API extension numa_affinity_policy attributes."""
|
||||
|
||||
def setUp(self, *args):
|
||||
plugin = ('neutron.tests.unit.extensions.test_port_numa_affinity_'
|
||||
'policy.PortNumaAffinityPolicyExtensionExtensionTestPlugin')
|
||||
super(PortNumaAffinityPolicyExtensionExtensionTestCase,
|
||||
self).setUp(plugin=plugin)
|
||||
|
||||
def _create_and_check_port_nap(self, numa_affinity_policy):
|
||||
name = 'numa_affinity_policy'
|
||||
keys = [('name', name), ('admin_state_up', True),
|
||||
('status', self.port_create_status),
|
||||
('numa_affinity_policy', numa_affinity_policy)]
|
||||
with self.port(name=name,
|
||||
numa_affinity_policy=numa_affinity_policy) as port:
|
||||
for k, v in keys:
|
||||
self.assertEqual(v, port['port'][k])
|
||||
return port
|
||||
|
||||
def _update_and_check_port_nap(self, port, numa_affinity_policy):
|
||||
data = {'port': {'numa_affinity_policy': numa_affinity_policy}}
|
||||
req = self.new_update_request('ports', data,
|
||||
port['port']['id'])
|
||||
res = self.deserialize(self.fmt, req.get_response(self.api))
|
||||
self.assertEqual(numa_affinity_policy,
|
||||
res['port']['numa_affinity_policy'])
|
||||
|
||||
@ddt.data(*constants.PORT_NUMA_POLICIES, None)
|
||||
def test_create_and_update_port_numa_affinity_policy(self,
|
||||
numa_affinity_policy):
|
||||
port = self._create_and_check_port_nap(numa_affinity_policy)
|
||||
for new_nap in (*constants.PORT_NUMA_POLICIES, None):
|
||||
self._update_and_check_port_nap(port, new_nap)
|
@ -0,0 +1,38 @@
|
||||
# Copyright (c) 2020 Red Hat, Inc.
|
||||
#
|
||||
# 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 neutron_lib import constants
|
||||
|
||||
from neutron.objects.port.extensions import port_numa_affinity_policy
|
||||
from neutron.tests.unit.objects import test_base as obj_test_base
|
||||
from neutron.tests.unit import testlib_api
|
||||
|
||||
|
||||
class PortNumaAffinityPolicyIfaceObjectTestCase(
|
||||
obj_test_base.BaseObjectIfaceTestCase):
|
||||
|
||||
_test_class = port_numa_affinity_policy.PortNumaAffinityPolicy
|
||||
|
||||
|
||||
class PortNumaAffinityPolicyDbObjectTestCase(
|
||||
obj_test_base.BaseDbObjectTestCase,
|
||||
testlib_api.SqlTestCase):
|
||||
|
||||
_test_class = port_numa_affinity_policy.PortNumaAffinityPolicy
|
||||
|
||||
def setUp(self):
|
||||
super(PortNumaAffinityPolicyDbObjectTestCase, self).setUp()
|
||||
self.update_obj_fields(
|
||||
{'port_id': lambda: self._create_test_port_id(),
|
||||
'numa_affinity_policy': constants.PORT_NUMA_POLICY_PREFERRED})
|
Loading…
Reference in new issue