Merge "Added VIM host-audit to sysinv API/CLI"
This commit is contained in:
@@ -13683,3 +13683,55 @@ Will reply with updated kernel value
|
|||||||
"kernel_provisioned": "lowlatency",
|
"kernel_provisioned": "lowlatency",
|
||||||
"kernel_running": "standard"
|
"kernel_running": "standard"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
Host VIM Actions
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
These APIs allow the user to trigger actions in VIM.
|
||||||
|
|
||||||
|
Supported actions:
|
||||||
|
- host-audit
|
||||||
|
|
||||||
|
********************
|
||||||
|
Trigger Action
|
||||||
|
********************
|
||||||
|
|
||||||
|
.. rest_method:: POST /v1/ihosts/{ihost_uuid}/vim
|
||||||
|
|
||||||
|
**Normal response codes**
|
||||||
|
|
||||||
|
200
|
||||||
|
|
||||||
|
**Error response codes**
|
||||||
|
|
||||||
|
computeFault (400, 500, ...), serviceUnavailable (503),
|
||||||
|
unauthorized (401), forbidden (403), itemNotFound (404)
|
||||||
|
|
||||||
|
**Request parameters**
|
||||||
|
|
||||||
|
.. csv-table::
|
||||||
|
:header: "Parameter", "Style", "Type", "Description"
|
||||||
|
:widths: 20, 20, 20, 60
|
||||||
|
|
||||||
|
"ihost_uuid", "URI", "csapi:UUID", "The unique identifier of the host"
|
||||||
|
"vim_event", "plain", "xsd:string", "The action to trigger (host-audit)"
|
||||||
|
|
||||||
|
**Response parameters**
|
||||||
|
|
||||||
|
.. csv-table::
|
||||||
|
:header: "Parameter", "Style", "Type", "Description"
|
||||||
|
:widths: 20, 20, 20, 60
|
||||||
|
|
||||||
|
"ihost_uuid", "plain", "csapi:UUID", "The unique identifier of the host"
|
||||||
|
"hostname", "plain", "xsd:string", "The host name"
|
||||||
|
"vim_event", "plain", "xsd:string", "The action that was triggered"
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
{
|
||||||
|
"ihost_uuid": "e551b1f0-ab6d-43a9-8eb1-05c39025a161",
|
||||||
|
"hostname": "controller-0",
|
||||||
|
"vim_event": "host-audit",
|
||||||
|
}
|
@@ -15,7 +15,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.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2013-2023 Wind River Systems, Inc.
|
# Copyright (c) 2013-2023,2025 Wind River Systems, Inc.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
@@ -61,6 +61,7 @@ KERNEL = {'ihost_uuid': IHOST['uuid'],
|
|||||||
UPDATED_KERNEL = copy.deepcopy(KERNEL)
|
UPDATED_KERNEL = copy.deepcopy(KERNEL)
|
||||||
NEW_KERNEL = 'lowlatency'
|
NEW_KERNEL = 'lowlatency'
|
||||||
UPDATED_KERNEL['kernel_provisioned'] = NEW_KERNEL
|
UPDATED_KERNEL['kernel_provisioned'] = NEW_KERNEL
|
||||||
|
VIM_HOST_AUDIT_RESPONSE = {"vim_event": "host-audit"}
|
||||||
|
|
||||||
fixtures = {
|
fixtures = {
|
||||||
'/v1/ihosts':
|
'/v1/ihosts':
|
||||||
@@ -107,6 +108,13 @@ fixtures = {
|
|||||||
UPDATED_KERNEL,
|
UPDATED_KERNEL,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
'/v1/ihosts/%s/vim' % IHOST['uuid']:
|
||||||
|
{
|
||||||
|
'POST': (
|
||||||
|
{},
|
||||||
|
VIM_HOST_AUDIT_RESPONSE,
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -182,3 +190,11 @@ class HostManagerTest(testtools.TestCase):
|
|||||||
self.assertEqual(self.api.calls, expect)
|
self.assertEqual(self.api.calls, expect)
|
||||||
self.assertEqual(kernel.kernel_provisioned, 'standard')
|
self.assertEqual(kernel.kernel_provisioned, 'standard')
|
||||||
self.assertEqual(kernel.kernel_running, 'standard')
|
self.assertEqual(kernel.kernel_running, 'standard')
|
||||||
|
|
||||||
|
def test_vim_host_audit(self):
|
||||||
|
self.mgr.vim_host_audit(hostid=IHOST['uuid'])
|
||||||
|
response = {"vim_event": "host-audit"}
|
||||||
|
expect = [
|
||||||
|
('POST', f'/v1/ihosts/{IHOST["uuid"]}/vim', {}, response),
|
||||||
|
]
|
||||||
|
self.assertEqual(expect, self.api.calls)
|
||||||
|
@@ -250,3 +250,11 @@ class HostTest(test_shell.ShellTest):
|
|||||||
FAKE_KERNEL['kernel_provisioned'])
|
FAKE_KERNEL['kernel_provisioned'])
|
||||||
self.assertEqual(kernel['kernel_running'],
|
self.assertEqual(kernel['kernel_running'],
|
||||||
FAKE_KERNEL['kernel_running'])
|
FAKE_KERNEL['kernel_running'])
|
||||||
|
|
||||||
|
@mock.patch('cgtsclient.v1.ihost.ihostManager.vim_host_audit')
|
||||||
|
def test_vim_host_audit(self, mock_vim_host_audit):
|
||||||
|
self.make_env()
|
||||||
|
mock_vim_host_audit.return_value = None
|
||||||
|
results = self.shell(f"vim-host-audit {FAKE_IHOST['hostname']}")
|
||||||
|
self.assertIn("Host audit initiated successfully", results)
|
||||||
|
mock_vim_host_audit.assert_called_once_with(FAKE_IHOST['uuid'])
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2013-2024 Wind River Systems, Inc.
|
# Copyright (c) 2013-2025 Wind River Systems, Inc.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
#
|
#
|
||||||
@@ -686,3 +686,17 @@ def do_host_kernel_show(cc, args):
|
|||||||
except exc.HTTPNotFound:
|
except exc.HTTPNotFound:
|
||||||
raise exc.CommandError('Host not found: %s' % args.hostnameorid)
|
raise exc.CommandError('Host not found: %s' % args.hostnameorid)
|
||||||
_print_kernel_show(kernel, args.format)
|
_print_kernel_show(kernel, args.format)
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg('hostnameorid', metavar='<hostname or id>',
|
||||||
|
help="Name or ID of host")
|
||||||
|
def do_vim_host_audit(cc, args):
|
||||||
|
"""Perform host audit operation on specified host."""
|
||||||
|
ihost = ihost_utils._find_ihost(cc, args.hostnameorid)
|
||||||
|
try:
|
||||||
|
cc.ihost.vim_host_audit(ihost.uuid)
|
||||||
|
print(f"Host audit initiated successfully: {ihost.hostname}")
|
||||||
|
except exc.HTTPNotFound:
|
||||||
|
print("Host audit failed: host not found")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Host audit failed: {e}")
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2013-2023 Wind River Systems, Inc.
|
# Copyright (c) 2013-2023,2025 Wind River Systems, Inc.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
#
|
#
|
||||||
@@ -11,6 +11,7 @@ from cgtsclient.common import base
|
|||||||
from cgtsclient.common import utils
|
from cgtsclient.common import utils
|
||||||
from cgtsclient import exc
|
from cgtsclient import exc
|
||||||
from cgtsclient.v1 import icpu
|
from cgtsclient.v1 import icpu
|
||||||
|
from sysinv.common import constants
|
||||||
|
|
||||||
|
|
||||||
CREATION_ATTRIBUTES = ['hostname', 'personality', 'subfunctions', 'mgmt_mac',
|
CREATION_ATTRIBUTES = ['hostname', 'personality', 'subfunctions', 'mgmt_mac',
|
||||||
@@ -32,6 +33,11 @@ class ihost_kernel(base.Resource):
|
|||||||
return "<kernel %s>" % self._info
|
return "<kernel %s>" % self._info
|
||||||
|
|
||||||
|
|
||||||
|
class ihost_vim(base.Resource):
|
||||||
|
def __repr__(self):
|
||||||
|
return "<vim %s>" % self._info
|
||||||
|
|
||||||
|
|
||||||
class ihostManager(base.Manager):
|
class ihostManager(base.Manager):
|
||||||
resource_class = ihost
|
resource_class = ihost
|
||||||
|
|
||||||
@@ -152,6 +158,13 @@ class ihostManager(base.Manager):
|
|||||||
resp, body = self.api.json_request('GET', url)
|
resp, body = self.api.json_request('GET', url)
|
||||||
return ihost_kernel(self, body)
|
return ihost_kernel(self, body)
|
||||||
|
|
||||||
|
def vim_host_audit(self, hostid):
|
||||||
|
# path = self._path(hostid) + "/vim"
|
||||||
|
url = self._path(hostid) + "/vim"
|
||||||
|
body = {"vim_event": constants.HOST_AUDIT_ACTION}
|
||||||
|
resp, body = self.api.json_request('POST', url, body=body)
|
||||||
|
return ihost_vim(self, body)
|
||||||
|
|
||||||
|
|
||||||
def _find_ihost(cc, ihost_id):
|
def _find_ihost(cc, ihost_id):
|
||||||
if ihost_id.isdigit() or utils.is_uuid_like(ihost_id):
|
if ihost_id.isdigit() or utils.is_uuid_like(ihost_id):
|
||||||
|
@@ -22,6 +22,7 @@ install_command = pip install \
|
|||||||
deps = -r{toxinidir}/requirements.txt
|
deps = -r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
-e{[tox]stxdir}/config/tsconfig/tsconfig
|
-e{[tox]stxdir}/config/tsconfig/tsconfig
|
||||||
|
-e{[tox]stxdir}/config/sysinv/sysinv/sysinv
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
find {toxinidir} -not -path '{toxinidir}/.tox/*' -name '*.py[c|o]' -delete
|
find {toxinidir} -not -path '{toxinidir}/.tox/*' -name '*.py[c|o]' -delete
|
||||||
|
@@ -312,6 +312,9 @@ class V1(base.APIBase):
|
|||||||
evaluate_apps_reapply = [link.Link]
|
evaluate_apps_reapply = [link.Link]
|
||||||
"Links to the evaluate_apps_reapply resource"
|
"Links to the evaluate_apps_reapply resource"
|
||||||
|
|
||||||
|
vim = [link.Link]
|
||||||
|
"Links to the VIM resource"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def convert(self):
|
def convert(self):
|
||||||
v1 = V1()
|
v1 = V1()
|
||||||
@@ -949,6 +952,13 @@ class V1(base.APIBase):
|
|||||||
'evaluate_apps_reapply', '',
|
'evaluate_apps_reapply', '',
|
||||||
bookmark=True)]
|
bookmark=True)]
|
||||||
|
|
||||||
|
v1.vim = [link.Link.make_link('self', pecan.request.host_url,
|
||||||
|
'vim', ''),
|
||||||
|
link.Link.make_link('bookmark',
|
||||||
|
pecan.request.host_url,
|
||||||
|
'vim', '',
|
||||||
|
bookmark=True)]
|
||||||
|
|
||||||
return v1
|
return v1
|
||||||
|
|
||||||
|
|
||||||
|
@@ -88,6 +88,7 @@ from sysinv.api.controllers.v1 import patch_api
|
|||||||
from sysinv.api.controllers.v1 import ptp_instance
|
from sysinv.api.controllers.v1 import ptp_instance
|
||||||
from sysinv.api.controllers.v1 import ptp_interface
|
from sysinv.api.controllers.v1 import ptp_interface
|
||||||
from sysinv.api.controllers.v1 import kernel
|
from sysinv.api.controllers.v1 import kernel
|
||||||
|
from sysinv.api.controllers.v1 import vim
|
||||||
from sysinv.api.policies import ihosts as ihosts_policy
|
from sysinv.api.policies import ihosts as ihosts_policy
|
||||||
from sysinv.common import ceph
|
from sysinv.common import ceph
|
||||||
from sysinv.common import constants
|
from sysinv.common import constants
|
||||||
@@ -1175,6 +1176,9 @@ class HostController(rest.RestController):
|
|||||||
kernel = kernel.KernelController()
|
kernel = kernel.KernelController()
|
||||||
"Expose kernel as a sub-element of ihosts"
|
"Expose kernel as a sub-element of ihosts"
|
||||||
|
|
||||||
|
vim = vim.VIMController()
|
||||||
|
"Expose vim as a sub-element of ihosts"
|
||||||
|
|
||||||
_custom_actions = {
|
_custom_actions = {
|
||||||
'detail': ['GET'],
|
'detail': ['GET'],
|
||||||
'bulk_add': ['POST'],
|
'bulk_add': ['POST'],
|
||||||
|
83
sysinv/sysinv/sysinv/sysinv/api/controllers/v1/vim.py
Normal file
83
sysinv/sysinv/sysinv/sysinv/api/controllers/v1/vim.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
import pecan
|
||||||
|
from pecan import rest
|
||||||
|
from wsme import types as wtypes
|
||||||
|
import wsmeext.pecan as wsme_pecan
|
||||||
|
|
||||||
|
from oslo_log import log
|
||||||
|
from sysinv.api.controllers.v1 import base
|
||||||
|
from sysinv.api.controllers.v1 import types
|
||||||
|
from sysinv.common import constants
|
||||||
|
from sysinv.common import exception
|
||||||
|
from sysinv.api.controllers.v1 import vim_api
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class VIMHostAudit(base.APIBase):
|
||||||
|
"""API representation of a host audit operation."""
|
||||||
|
|
||||||
|
vim_event = wtypes.text
|
||||||
|
"The VIM event"
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.fields = ['vim_event']
|
||||||
|
for k in self.fields:
|
||||||
|
setattr(self, k, kwargs.get(k))
|
||||||
|
|
||||||
|
|
||||||
|
class VIMHostAuditResponse(base.APIBase):
|
||||||
|
"""API representation of a host audit operation."""
|
||||||
|
|
||||||
|
hostname = wtypes.text
|
||||||
|
"The hostname of the host being audited"
|
||||||
|
|
||||||
|
ihost_uuid = types.uuid
|
||||||
|
"The UUID of the host being audited"
|
||||||
|
|
||||||
|
vim_event = wtypes.text
|
||||||
|
"The VIM event"
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.fields = ['hostname', 'ihost_uuid', 'vim_event']
|
||||||
|
for k in self.fields:
|
||||||
|
setattr(self, k, kwargs.get(k))
|
||||||
|
|
||||||
|
|
||||||
|
class VIMController(rest.RestController):
|
||||||
|
"""REST controller for VIM operations."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._api_token = None
|
||||||
|
|
||||||
|
# POST ihosts/<uuid>/vim
|
||||||
|
@wsme_pecan.wsexpose(VIMHostAuditResponse, types.uuid, body=VIMHostAudit)
|
||||||
|
def post(self, host_uuid, event_request):
|
||||||
|
"""Perform host audit operation on specified hosts."""
|
||||||
|
|
||||||
|
host = pecan.request.dbapi.ihost_get(host_uuid)
|
||||||
|
|
||||||
|
if event_request.vim_event != constants.HOST_AUDIT_ACTION:
|
||||||
|
raise exception.InvalidVIMAction(vim_event=event_request.vim_event)
|
||||||
|
|
||||||
|
try:
|
||||||
|
vim_api.vim_host_action(
|
||||||
|
token=self._api_token,
|
||||||
|
uuid=host_uuid,
|
||||||
|
hostname=host.hostname,
|
||||||
|
action=constants.HOST_AUDIT_ACTION,
|
||||||
|
timeout=constants.VIM_DEFAULT_TIMEOUT_IN_SECS,
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise exception.CannotTriggerVIMHostAudit(hostname=host.hostname) from e
|
||||||
|
|
||||||
|
return VIMHostAuditResponse(
|
||||||
|
hostname=host.hostname,
|
||||||
|
ihost_uuid=host.uuid,
|
||||||
|
vim_event=constants.HOST_AUDIT_ACTION,
|
||||||
|
)
|
@@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# Copyright (c) 2015-2020 Wind River Systems, Inc.
|
# Copyright (c) 2015-2020,2025 Wind River Systems, Inc.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
#
|
#
|
||||||
@@ -102,7 +102,8 @@ def vim_host_action(token, uuid, hostname, action, timeout):
|
|||||||
_valid_actions = [constants.UNLOCK_ACTION,
|
_valid_actions = [constants.UNLOCK_ACTION,
|
||||||
constants.LOCK_ACTION,
|
constants.LOCK_ACTION,
|
||||||
constants.FORCE_LOCK_ACTION,
|
constants.FORCE_LOCK_ACTION,
|
||||||
constants.FORCE_UNSAFE_LOCK_ACTION]
|
constants.FORCE_UNSAFE_LOCK_ACTION,
|
||||||
|
constants.HOST_AUDIT_ACTION]
|
||||||
|
|
||||||
if action not in _valid_actions:
|
if action not in _valid_actions:
|
||||||
LOG.error("Unrecognized vim_host_action=%s" % action)
|
LOG.error("Unrecognized vim_host_action=%s" % action)
|
||||||
|
@@ -86,6 +86,7 @@ FORCE_UNLOCK_ACTION = 'force-unlock'
|
|||||||
LOCK_ACTION = 'lock'
|
LOCK_ACTION = 'lock'
|
||||||
FORCE_LOCK_ACTION = 'force-lock'
|
FORCE_LOCK_ACTION = 'force-lock'
|
||||||
FORCE_UNSAFE_LOCK_ACTION = 'force-unsafe-lock'
|
FORCE_UNSAFE_LOCK_ACTION = 'force-unsafe-lock'
|
||||||
|
HOST_AUDIT_ACTION = 'host-audit'
|
||||||
REBOOT_ACTION = 'reboot'
|
REBOOT_ACTION = 'reboot'
|
||||||
RESET_ACTION = 'reset'
|
RESET_ACTION = 'reset'
|
||||||
REINSTALL_ACTION = 'reinstall'
|
REINSTALL_ACTION = 'reinstall'
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
# Copyright (c) 2013-2024 Wind River Systems, Inc.
|
# Copyright (c) 2013-2025 Wind River Systems, Inc.
|
||||||
# Copyright 2010 United States Government as represented by the
|
# Copyright 2010 United States Government as represented by the
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
@@ -275,6 +275,11 @@ class ManagedIPAddress(Invalid):
|
|||||||
"modified.")
|
"modified.")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidVIMAction(Invalid):
|
||||||
|
message = _("Unsupported action: %(vim_event)s, "
|
||||||
|
"only host-audit action is supported")
|
||||||
|
|
||||||
|
|
||||||
class AddressAlreadyExists(Conflict):
|
class AddressAlreadyExists(Conflict):
|
||||||
message = _("Address %(address)s/%(prefix)s already "
|
message = _("Address %(address)s/%(prefix)s already "
|
||||||
"exists on this interface.")
|
"exists on this interface.")
|
||||||
@@ -1725,3 +1730,7 @@ class UnexpectedEvent(SysinvException):
|
|||||||
|
|
||||||
class CannotQueryPlatformUpgrade(SysinvException):
|
class CannotQueryPlatformUpgrade(SysinvException):
|
||||||
message = _("Failed to query platform upgrade state")
|
message = _("Failed to query platform upgrade state")
|
||||||
|
|
||||||
|
|
||||||
|
class CannotTriggerVIMHostAudit(SysinvException):
|
||||||
|
message = _("Failed to trigger VIM host-audit for %(hostname)s")
|
||||||
|
68
sysinv/sysinv/sysinv/sysinv/tests/api/test_vim.py
Normal file
68
sysinv/sysinv/sysinv/sysinv/tests/api/test_vim.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Tests for the API /ihosts/<uuid>/vim methods.
|
||||||
|
"""
|
||||||
|
import mock
|
||||||
|
from six.moves import http_client
|
||||||
|
from sysinv.common import constants
|
||||||
|
from sysinv.tests.api import base
|
||||||
|
from sysinv.tests.db import base as dbbase
|
||||||
|
|
||||||
|
|
||||||
|
class TestVIM(base.FunctionalTest, dbbase.BaseHostTestCase):
|
||||||
|
# API_HEADERS are a generic header passed to most API calls
|
||||||
|
API_HEADERS = {'User-Agent': 'sysinv-test'}
|
||||||
|
|
||||||
|
def _get_path(self, host_uuid):
|
||||||
|
return f'/ihosts/{host_uuid}/vim'
|
||||||
|
|
||||||
|
def _create_host(self, personality, subfunction=None,
|
||||||
|
mgmt_mac=None, mgmt_ip=None,
|
||||||
|
admin=None,
|
||||||
|
invprovision=constants.PROVISIONED, **kw):
|
||||||
|
host = self._create_test_host(personality=personality,
|
||||||
|
subfunction=subfunction,
|
||||||
|
administrative=(admin or
|
||||||
|
constants.ADMIN_UNLOCKED),
|
||||||
|
invprovision=invprovision,
|
||||||
|
**kw)
|
||||||
|
return host
|
||||||
|
|
||||||
|
|
||||||
|
class VIMHostAuditTestCase(TestVIM):
|
||||||
|
@mock.patch('sysinv.api.controllers.v1.vim_api.vim_host_action')
|
||||||
|
def test_vim_host_audit(self, mock_vim_host_action):
|
||||||
|
worker = self._create_host(constants.WORKER,
|
||||||
|
admin=constants.ADMIN_LOCKED)
|
||||||
|
host_uuid = worker['uuid']
|
||||||
|
data = {"vim_event": "host-audit"}
|
||||||
|
response = self.post_json(self._get_path(host_uuid), data, headers=self.API_HEADERS)
|
||||||
|
self.assertEqual(http_client.OK, response.status_int)
|
||||||
|
self.assertEqual('application/json', response.content_type)
|
||||||
|
self.assertEqual(response.json['vim_event'], constants.HOST_AUDIT_ACTION)
|
||||||
|
self.assertEqual(response.json['ihost_uuid'], host_uuid)
|
||||||
|
self.assertEqual(response.json['hostname'], worker['hostname'])
|
||||||
|
mock_vim_host_action.assert_called_once_with(
|
||||||
|
token=mock.ANY,
|
||||||
|
uuid=worker["uuid"],
|
||||||
|
hostname=worker["hostname"],
|
||||||
|
action=constants.HOST_AUDIT_ACTION,
|
||||||
|
timeout=constants.VIM_DEFAULT_TIMEOUT_IN_SECS
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch('sysinv.api.controllers.v1.vim_api.vim_host_action')
|
||||||
|
def test_vim_host_audit_invalid_action(self, mock_vim_host_action):
|
||||||
|
worker = self._create_host(constants.WORKER,
|
||||||
|
admin=constants.ADMIN_LOCKED)
|
||||||
|
host_uuid = worker['uuid']
|
||||||
|
data = {"vim_event": "invalid-action"}
|
||||||
|
response = self.post_json(self._get_path(host_uuid), data, headers=self.API_HEADERS,
|
||||||
|
expect_errors=True)
|
||||||
|
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
|
||||||
|
self.assertEqual('application/json', response.content_type)
|
||||||
|
self.assertIn("Unsupported action", response.json['error_message'])
|
||||||
|
mock_vim_host_action.assert_not_called()
|
92
sysinv/sysinv/sysinv/sysinv/tests/api/test_vim_api.py
Normal file
92
sysinv/sysinv/sysinv/sysinv/tests/api/test_vim_api.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
# Copyright (c) 2025 Wind River Systems, Inc.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
import mock
|
||||||
|
import json
|
||||||
|
|
||||||
|
from sysinv.common import constants
|
||||||
|
from sysinv.tests.api import base
|
||||||
|
from sysinv.api.controllers.v1 import vim_api
|
||||||
|
|
||||||
|
|
||||||
|
class VimApiTestCase(base.FunctionalTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(VimApiTestCase, self).setUp()
|
||||||
|
|
||||||
|
@mock.patch('sysinv.api.controllers.v1.vim_api.rest_api_request')
|
||||||
|
def test_vim_host_action_audit(self, mock_rest_api_request):
|
||||||
|
# Mock the rest_api_request response
|
||||||
|
mock_rest_api_request.return_value = {'status': 'success'}
|
||||||
|
|
||||||
|
# Test parameters
|
||||||
|
token = None
|
||||||
|
uuid = '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'
|
||||||
|
hostname = 'controller-0'
|
||||||
|
action = constants.HOST_AUDIT_ACTION
|
||||||
|
timeout = constants.VIM_DEFAULT_TIMEOUT_IN_SECS
|
||||||
|
|
||||||
|
# Call the function
|
||||||
|
result = vim_api.vim_host_action(token, uuid, hostname, action, timeout)
|
||||||
|
|
||||||
|
# Verify the result
|
||||||
|
self.assertEqual(result, {'status': 'success'})
|
||||||
|
|
||||||
|
# Verify rest_api_request was called with the correct parameters
|
||||||
|
expected_url = "http://localhost:30001/nfvi-plugins/v1/hosts/%s" % uuid
|
||||||
|
expected_headers = {
|
||||||
|
'Content-type': 'application/json',
|
||||||
|
'User-Agent': 'sysinv/1.0'
|
||||||
|
}
|
||||||
|
expected_payload = {
|
||||||
|
'uuid': uuid,
|
||||||
|
'hostname': hostname,
|
||||||
|
'action': action
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_rest_api_request.assert_called_once_with(
|
||||||
|
token,
|
||||||
|
"PATCH",
|
||||||
|
expected_url,
|
||||||
|
expected_headers,
|
||||||
|
json.dumps(expected_payload),
|
||||||
|
timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_vim_host_action_invalid_action(self):
|
||||||
|
# Test with an invalid action
|
||||||
|
token = None
|
||||||
|
uuid = '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'
|
||||||
|
hostname = 'controller-0'
|
||||||
|
action = 'invalid-action'
|
||||||
|
timeout = constants.VIM_DEFAULT_TIMEOUT_IN_SECS
|
||||||
|
|
||||||
|
# Call the function
|
||||||
|
result = vim_api.vim_host_action(token, uuid, hostname, action, timeout)
|
||||||
|
|
||||||
|
# Verify the result is None for invalid action
|
||||||
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
@mock.patch('sysinv.api.controllers.v1.vim_api.rest_api_request')
|
||||||
|
def test_vim_host_action_valid_actions(self, mock_rest_api_request):
|
||||||
|
# Test that all valid actions are accepted
|
||||||
|
mock_rest_api_request.return_value = {'status': 'success'}
|
||||||
|
|
||||||
|
token = None
|
||||||
|
uuid = '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'
|
||||||
|
hostname = 'controller-0'
|
||||||
|
timeout = constants.VIM_DEFAULT_TIMEOUT_IN_SECS
|
||||||
|
|
||||||
|
valid_actions = [
|
||||||
|
constants.UNLOCK_ACTION,
|
||||||
|
constants.LOCK_ACTION,
|
||||||
|
constants.FORCE_LOCK_ACTION,
|
||||||
|
constants.FORCE_UNSAFE_LOCK_ACTION,
|
||||||
|
constants.HOST_AUDIT_ACTION
|
||||||
|
]
|
||||||
|
|
||||||
|
for action in valid_actions:
|
||||||
|
result = vim_api.vim_host_action(token, uuid, hostname, action, timeout)
|
||||||
|
self.assertEqual(result, {'status': 'success'})
|
@@ -42,6 +42,7 @@ deps = -r{toxinidir}/requirements.txt
|
|||||||
-e{[tox]stxdir}/fault/fm-api/source
|
-e{[tox]stxdir}/fault/fm-api/source
|
||||||
-e{[tox]stxdir}/fault/python-fmclient/fmclient
|
-e{[tox]stxdir}/fault/python-fmclient/fmclient
|
||||||
-e{[tox]stxdir}/config/controllerconfig/controllerconfig
|
-e{[tox]stxdir}/config/controllerconfig/controllerconfig
|
||||||
|
-e{[tox]stxdir}/config/sysinv/sysinv/sysinv
|
||||||
-e{[tox]stxdir}/update/sw-patch/cgcs-patch
|
-e{[tox]stxdir}/update/sw-patch/cgcs-patch
|
||||||
-e{[tox]stxdir}/utilities/utilities/platform-util/platform-util
|
-e{[tox]stxdir}/utilities/utilities/platform-util/platform-util
|
||||||
-e{[tox]stxdir}/utilities/ceph/python-cephclient/python-cephclient
|
-e{[tox]stxdir}/utilities/ceph/python-cephclient/python-cephclient
|
||||||
|
Reference in New Issue
Block a user