Support data network assignment on an unlocked host

For SR-IOV interfaces, when operating in AIO-SX, it will be
possible to assign it to a datanetwork without host lock

Story: 2008531
Task: 41705
Change-Id: Ia4b5670ffe09255845823dc3ae2c3fc19c709fc9
Signed-off-by: Andre Fernando Zanella Kantek <AndreFernandoZanella.Kantek@windriver.com>
This commit is contained in:
Andre Fernando Zanella Kantek 2021-01-27 13:43:59 -05:00
parent 546dea3be3
commit d64f6f5924
4 changed files with 182 additions and 4 deletions

View File

@ -141,7 +141,7 @@ class InterfaceDataNetworkController(rest.RestController):
interface_datanetwork_dict['datanetwork_id'] = datanetwork_id
interface_obj = pecan.request.dbapi.iinterface_get(interface_uuid)
self._check_host(interface_obj.ihost_uuid)
self._check_host(interface_obj)
self._check_interface_class(interface_obj)
self._check_interface_mtu(interface_obj, datanetwork_obj)
@ -152,6 +152,10 @@ class InterfaceDataNetworkController(rest.RestController):
result = pecan.request.dbapi.interface_datanetwork_create(
interface_datanetwork_dict)
if interface_obj.ifclass == constants.INTERFACE_CLASS_PCI_SRIOV:
pecan.request.rpcapi.update_pcidp_config(
pecan.request.context, interface_obj.ihost_uuid)
return InterfaceDataNetwork.convert_with_links(result)
def _get_interface_datanetwork_collection(
@ -211,8 +215,14 @@ class InterfaceDataNetworkController(rest.RestController):
raise wsme.exc.ClientSideError(msg)
@staticmethod
def _check_host(host_uuid):
host = pecan.request.dbapi.ihost_get(host_uuid)
def _check_host(interface_obj):
# In general, we don't want to support changing the interface configuration
# at runtime, allowing only this specific combination, because it can have an
# impact on the host availability and services
if (cutils.is_aio_simplex_system(pecan.request.dbapi)
and interface_obj.ifclass == constants.INTERFACE_CLASS_PCI_SRIOV):
return
host = pecan.request.dbapi.ihost_get(interface_obj.ihost_uuid)
if host.administrative != constants.ADMIN_LOCKED:
msg = _("Operation Rejected: Host '%s' is adminstrative '%s' " %
(host.hostname, host.administrative))
@ -310,6 +320,9 @@ class InterfaceDataNetworkController(rest.RestController):
interface_datanetwork_uuid)
interface_obj = pecan.request.dbapi.iinterface_get(
ifdn_obj.interface_uuid)
self._check_host(interface_obj.ihost_uuid)
self._check_host(interface_obj)
pecan.request.dbapi.interface_datanetwork_destroy(
interface_datanetwork_uuid)
if interface_obj.ifclass == constants.INTERFACE_CLASS_PCI_SRIOV:
pecan.request.rpcapi.update_pcidp_config(
pecan.request.context, interface_obj.ihost_uuid)

View File

@ -6227,6 +6227,29 @@ class ConductorManager(service.PeriodicService):
self._config_apply_runtime_manifest(
context, config_uuid, config_dict, force=True)
def update_pcidp_config(self, context, host_uuid):
"""update pcidp configuration for a host
:param context: an admin context
:param host_uuid: the host uuid
"""
# update manifest files and notify agent to apply them
personalities = [constants.CONTROLLER,
constants.WORKER]
config_uuid = self._config_update_hosts(context, personalities,
host_uuids=[host_uuid])
config_dict = {
"personalities": personalities,
'host_uuids': [host_uuid],
"classes": ['platform::kubernetes::worker::pci::runtime'],
puppet_common.REPORT_INVENTORY_UPDATE:
puppet_common.REPORT_PCI_SRIOV_CONFIG,
}
self._config_apply_runtime_manifest(
context, config_uuid, config_dict, force=True)
def configure_system_https(self, context):
"""Update the system https configuration.

View File

@ -566,6 +566,22 @@ class ConductorAPI(sysinv.openstack.common.rpc.proxy.RpcProxy):
return self.call(context, self.make_msg('update_sriov_config',
host_uuid=host_uuid))
def update_pcidp_config(self, context, host_uuid):
"""Synchronously, have a conductor configure pcidp config.
Does the following tasks:
- sends a message to conductor
- who sends a message to all inventory agents
- who, upon receipt with matching host_uuid, applies the pcidp manifest
:param context: request context.
:param host_uuid: the host unique uuid
"""
LOG.debug("ConductorApi.update_pcidp_config: sending "
"update_pcidp_config to conductor")
return self.call(context, self.make_msg('update_pcidp_config',
host_uuid=host_uuid))
def update_distributed_cloud_role(self, context):
"""Synchronously, have a conductor configure the distributed cloud
role of the system.

View File

@ -0,0 +1,126 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# -*- encoding: utf-8 -*-
#
#
# Copyright (c) 2021 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import mock
from six.moves import http_client
import uuid
from sysinv.api.controllers.v1 import interface as api_if_v1
from sysinv.common import constants
from sysinv.tests.api import base
from sysinv.tests.db import utils as dbutils
class InterfaceDataNetworkTestCase(base.FunctionalTest):
def setUp(self):
super(InterfaceDataNetworkTestCase, self).setUp()
p = mock.patch.object(api_if_v1, '_get_lower_interface_macs')
self.mock_lower_macs = p.start()
self.mock_lower_macs.return_value = {'enp0s18': '08:00:27:8a:87:48',
'enp0s19': '08:00:27:ea:93:8e'}
self.addCleanup(p.stop)
p = mock.patch('sysinv.common.utils.is_aio_simplex_system')
self.mock_utils_is_aio_simplex_system = p.start()
self.mock_utils_is_aio_simplex_system.return_value = True
self.addCleanup(p.stop)
self.system = dbutils.create_test_isystem()
self.load = dbutils.create_test_load()
self.controller = dbutils.create_test_ihost(
id='1',
uuid=None,
forisystemid=self.system.id,
hostname='controller-0',
personality=constants.CONTROLLER,
subfunctions=constants.WORKER,
administrative=constants.ADMIN_UNLOCKED,
invprovision=constants.PROVISIONED,
)
self.datanetwork = dbutils.create_test_datanetwork(
name='test1',
uuid=str(uuid.uuid4()),
network_type=constants.DATANETWORK_TYPE_VLAN,
mtu=1500)
self.if_sriov0 = dbutils.create_test_interface(
ifname='sriov0',
ifclass=constants.INTERFACE_CLASS_PCI_SRIOV,
forihostid=self.controller.id,
ihost_uuid=self.controller.uuid)
self.if_data0 = dbutils.create_test_interface(
ifname='data0',
ifclass=constants.INTERFACE_CLASS_DATA,
forihostid=self.controller.id,
ihost_uuid=self.controller.uuid)
self.if_sriov1 = dbutils.create_test_interface(
ifname='sriov1',
ifclass=constants.INTERFACE_CLASS_PCI_SRIOV,
forihostid=self.controller.id,
ihost_uuid=self.controller.uuid)
def _post_and_check(self, ndict, expect_errors=False):
response = self.post_json('%s' % self._get_path(), ndict,
expect_errors)
if expect_errors:
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
else:
self.assertEqual(http_client.OK, response.status_int)
return response
def _get_path(self, path=None):
if path:
return '/interface_datanetworks/' + path
else:
return '/interface_datanetworks'
class InterfaceDataNetworkCreateTestCase(InterfaceDataNetworkTestCase):
def setUp(self):
super(InterfaceDataNetworkCreateTestCase, self).setUp()
def test_assign_interface_datanetwork(self):
# system interface-datanetwork-assign controller-0 sriov0 test1
sriov0_assign_dn = dbutils.post_get_test_interface_datanetwork(
interface_uuid=self.if_sriov0.uuid,
datanetwork_uuid=self.datanetwork.uuid)
self._post_and_check(sriov0_assign_dn, expect_errors=False)
# system interface-datanetwork-list controller-0
if_dn_list = self.get_json('/ihosts/%s/interface_datanetworks'
% self.controller.uuid, expect_errors=False)
self.assertEqual('test1', if_dn_list['interface_datanetworks'][0]['datanetwork_name'])
self.assertEqual('sriov0', if_dn_list['interface_datanetworks'][0]['ifname'])
# system interface-datanetwork-remove {uuid}
self.delete('/interface_datanetworks/%s'
% if_dn_list['interface_datanetworks'][0]['uuid'],
expect_errors=False)
def test_assign_interface_datanetwork_error_non_sriov(self):
# system interface-datanetwork-assign controller-0 data0 test1
# rejected because host is unlocked
data0_assign_dn = dbutils.post_get_test_interface_datanetwork(
interface_uuid=self.if_data0.uuid,
datanetwork_uuid=self.datanetwork.uuid)
self._post_and_check(data0_assign_dn, expect_errors=True)
def test_assign_interface_datanetwork_error_non_aio_sx(self):
self.mock_utils_is_aio_simplex_system.return_value = False
# system interface-datanetwork-assign controller-0 sriov1 test1
# rejected because system is not AIO-SX
sriov1_assign_dn = dbutils.post_get_test_interface_datanetwork(
interface_uuid=self.if_sriov1.uuid,
datanetwork_uuid=self.datanetwork.uuid)
self._post_and_check(sriov1_assign_dn, expect_errors=True)
self.mock_utils_is_aio_simplex_system.return_value = True