From 0b9919d3841b390154f2978684f3e18ec842dba1 Mon Sep 17 00:00:00 2001 From: Joshua Kraitberg Date: Wed, 11 Jun 2025 17:45:35 -0400 Subject: [PATCH] Added VIM host-audit to sysinv API/CLI Added required functionality for a user or application to trigger a VIM host-audit. This allows async operations to notify VIM on complete. TEST PLAN PASS: AIO-SX patch upgrade PASS: AIO-SX patch downgrade PASS: AIO-SX patch upgrade, pre-bootstrap PASS: AIO-DX patch upgrade Story: 2011045 Task: 52379 Depends-On: https://review.opendev.org/c/starlingx/nfv/+/952589 Change-Id: I208a24660bf40baf95f37031975746a46479df43 Signed-off-by: Joshua Kraitberg --- api-ref/source/api-ref-sysinv-v1-config.rst | 52 +++++++++++ .../cgtsclient/tests/v1/test_ihost.py | 18 +++- .../cgtsclient/tests/v1/test_ihost_shell.py | 8 ++ .../cgts-client/cgtsclient/v1/iHost_shell.py | 16 +++- .../cgts-client/cgtsclient/v1/ihost.py | 15 ++- sysinv/cgts-client/cgts-client/tox.ini | 1 + .../sysinv/api/controllers/v1/__init__.py | 10 ++ .../sysinv/sysinv/api/controllers/v1/host.py | 4 + .../sysinv/sysinv/api/controllers/v1/vim.py | 83 +++++++++++++++++ .../sysinv/api/controllers/v1/vim_api.py | 5 +- .../sysinv/sysinv/sysinv/common/constants.py | 1 + .../sysinv/sysinv/sysinv/common/exception.py | 11 ++- .../sysinv/sysinv/tests/api/test_vim.py | 68 ++++++++++++++ .../sysinv/sysinv/tests/api/test_vim_api.py | 92 +++++++++++++++++++ sysinv/sysinv/sysinv/tox.ini | 1 + 15 files changed, 379 insertions(+), 6 deletions(-) create mode 100644 sysinv/sysinv/sysinv/sysinv/api/controllers/v1/vim.py create mode 100644 sysinv/sysinv/sysinv/sysinv/tests/api/test_vim.py create mode 100644 sysinv/sysinv/sysinv/sysinv/tests/api/test_vim_api.py diff --git a/api-ref/source/api-ref-sysinv-v1-config.rst b/api-ref/source/api-ref-sysinv-v1-config.rst index b2957d6cc6..1c026fdcaa 100644 --- a/api-ref/source/api-ref-sysinv-v1-config.rst +++ b/api-ref/source/api-ref-sysinv-v1-config.rst @@ -13675,3 +13675,55 @@ Will reply with updated kernel value "kernel_provisioned": "lowlatency", "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", + } \ No newline at end of file diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/tests/v1/test_ihost.py b/sysinv/cgts-client/cgts-client/cgtsclient/tests/v1/test_ihost.py index 700b48247a..980bb73fe7 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/tests/v1/test_ihost.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/tests/v1/test_ihost.py @@ -15,7 +15,7 @@ # License for the specific language governing permissions and limitations # 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) NEW_KERNEL = 'lowlatency' UPDATED_KERNEL['kernel_provisioned'] = NEW_KERNEL +VIM_HOST_AUDIT_RESPONSE = {"vim_event": "host-audit"} fixtures = { '/v1/ihosts': @@ -107,6 +108,13 @@ fixtures = { 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(kernel.kernel_provisioned, '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) diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/tests/v1/test_ihost_shell.py b/sysinv/cgts-client/cgts-client/cgtsclient/tests/v1/test_ihost_shell.py index 17d7e2fd5a..a96ffbf5b9 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/tests/v1/test_ihost_shell.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/tests/v1/test_ihost_shell.py @@ -250,3 +250,11 @@ class HostTest(test_shell.ShellTest): FAKE_KERNEL['kernel_provisioned']) self.assertEqual(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']) diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/iHost_shell.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/iHost_shell.py index 52dcd69ade..5ffdbe9943 100755 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/iHost_shell.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/iHost_shell.py @@ -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 # @@ -686,3 +686,17 @@ def do_host_kernel_show(cc, args): except exc.HTTPNotFound: raise exc.CommandError('Host not found: %s' % args.hostnameorid) _print_kernel_show(kernel, args.format) + + +@utils.arg('hostnameorid', metavar='', + 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}") diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/ihost.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/ihost.py index de9b3282c7..4ef3636023 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/ihost.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/ihost.py @@ -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 # @@ -11,6 +11,7 @@ from cgtsclient.common import base from cgtsclient.common import utils from cgtsclient import exc from cgtsclient.v1 import icpu +from sysinv.common import constants CREATION_ATTRIBUTES = ['hostname', 'personality', 'subfunctions', 'mgmt_mac', @@ -32,6 +33,11 @@ class ihost_kernel(base.Resource): return "" % self._info +class ihost_vim(base.Resource): + def __repr__(self): + return "" % self._info + + class ihostManager(base.Manager): resource_class = ihost @@ -152,6 +158,13 @@ class ihostManager(base.Manager): resp, body = self.api.json_request('GET', url) 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): if ihost_id.isdigit() or utils.is_uuid_like(ihost_id): diff --git a/sysinv/cgts-client/cgts-client/tox.ini b/sysinv/cgts-client/cgts-client/tox.ini index e408e11254..2098ff330e 100644 --- a/sysinv/cgts-client/cgts-client/tox.ini +++ b/sysinv/cgts-client/cgts-client/tox.ini @@ -22,6 +22,7 @@ install_command = pip install \ deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt -e{[tox]stxdir}/config/tsconfig/tsconfig + -e{[tox]stxdir}/config/sysinv/sysinv/sysinv commands = find {toxinidir} -not -path '{toxinidir}/.tox/*' -name '*.py[c|o]' -delete diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/__init__.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/__init__.py index 9c3e2a468b..98883e4a17 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/__init__.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/__init__.py @@ -312,6 +312,9 @@ class V1(base.APIBase): evaluate_apps_reapply = [link.Link] "Links to the evaluate_apps_reapply resource" + vim = [link.Link] + "Links to the VIM resource" + @classmethod def convert(self): v1 = V1() @@ -949,6 +952,13 @@ class V1(base.APIBase): 'evaluate_apps_reapply', '', 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 diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py index 45506266c1..da04b2604d 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/host.py @@ -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_interface 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.common import ceph from sysinv.common import constants @@ -1175,6 +1176,9 @@ class HostController(rest.RestController): kernel = kernel.KernelController() "Expose kernel as a sub-element of ihosts" + vim = vim.VIMController() + "Expose vim as a sub-element of ihosts" + _custom_actions = { 'detail': ['GET'], 'bulk_add': ['POST'], diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/vim.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/vim.py new file mode 100644 index 0000000000..457ad28b1d --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/vim.py @@ -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//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, + ) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/vim_api.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/vim_api.py index 99d3254371..71b33d1381 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/vim_api.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/vim_api.py @@ -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 # @@ -102,7 +102,8 @@ def vim_host_action(token, uuid, hostname, action, timeout): _valid_actions = [constants.UNLOCK_ACTION, constants.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: LOG.error("Unrecognized vim_host_action=%s" % action) diff --git a/sysinv/sysinv/sysinv/sysinv/common/constants.py b/sysinv/sysinv/sysinv/sysinv/common/constants.py index e075bd7670..f4889c9bb7 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/constants.py +++ b/sysinv/sysinv/sysinv/sysinv/common/constants.py @@ -80,6 +80,7 @@ FORCE_UNLOCK_ACTION = 'force-unlock' LOCK_ACTION = 'lock' FORCE_LOCK_ACTION = 'force-lock' FORCE_UNSAFE_LOCK_ACTION = 'force-unsafe-lock' +HOST_AUDIT_ACTION = 'host-audit' REBOOT_ACTION = 'reboot' RESET_ACTION = 'reset' REINSTALL_ACTION = 'reinstall' diff --git a/sysinv/sysinv/sysinv/sysinv/common/exception.py b/sysinv/sysinv/sysinv/sysinv/common/exception.py index efd37c4b95..9a321fc9db 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/exception.py +++ b/sysinv/sysinv/sysinv/sysinv/common/exception.py @@ -1,6 +1,6 @@ # 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 # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. @@ -275,6 +275,11 @@ class ManagedIPAddress(Invalid): "modified.") +class InvalidVIMAction(Invalid): + message = _("Unsupported action: %(vim_event)s, " + "only host-audit action is supported") + + class AddressAlreadyExists(Conflict): message = _("Address %(address)s/%(prefix)s already " "exists on this interface.") @@ -1725,3 +1730,7 @@ class UnexpectedEvent(SysinvException): class CannotQueryPlatformUpgrade(SysinvException): message = _("Failed to query platform upgrade state") + + +class CannotTriggerVIMHostAudit(SysinvException): + message = _("Failed to trigger VIM host-audit for %(hostname)s") diff --git a/sysinv/sysinv/sysinv/sysinv/tests/api/test_vim.py b/sysinv/sysinv/sysinv/sysinv/tests/api/test_vim.py new file mode 100644 index 0000000000..06db13f0c7 --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/tests/api/test_vim.py @@ -0,0 +1,68 @@ +# Copyright (c) 2025 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +""" +Tests for the API /ihosts//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() diff --git a/sysinv/sysinv/sysinv/sysinv/tests/api/test_vim_api.py b/sysinv/sysinv/sysinv/sysinv/tests/api/test_vim_api.py new file mode 100644 index 0000000000..426ca6a551 --- /dev/null +++ b/sysinv/sysinv/sysinv/sysinv/tests/api/test_vim_api.py @@ -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'}) diff --git a/sysinv/sysinv/sysinv/tox.ini b/sysinv/sysinv/sysinv/tox.ini index a4d06ca816..96d0d2fa82 100644 --- a/sysinv/sysinv/sysinv/tox.ini +++ b/sysinv/sysinv/sysinv/tox.ini @@ -42,6 +42,7 @@ deps = -r{toxinidir}/requirements.txt -e{[tox]stxdir}/fault/fm-api/source -e{[tox]stxdir}/fault/python-fmclient/fmclient -e{[tox]stxdir}/config/controllerconfig/controllerconfig + -e{[tox]stxdir}/config/sysinv/sysinv/sysinv -e{[tox]stxdir}/update/sw-patch/cgcs-patch -e{[tox]stxdir}/utilities/utilities/platform-util/platform-util -e{[tox]stxdir}/utilities/ceph/python-cephclient/python-cephclient