diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index 809c20f98d..8f897ca8af 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -17,6 +17,7 @@ import logging +from openstack import exceptions as sdk_exceptions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -55,16 +56,26 @@ class CreateAgent(command.ShowOne): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - args = ( - parsed_args.os, - parsed_args.architecture, - parsed_args.version, - parsed_args.url, - parsed_args.md5hash, - parsed_args.hypervisor, + compute_client = self.app.client_manager.sdk_connection.compute + + # doing this since openstacksdk has decided not to support this + # deprecated command + data = { + 'agent': { + 'hypervisor': parsed_args.hypervisor, + 'os': parsed_args.os, + 'architecture': parsed_args.architecture, + 'version': parsed_args.version, + 'url': parsed_args.url, + 'md5hash': parsed_args.md5hash, + }, + } + response = compute_client.post( + '/os-agents', json=data, microversion='2.1' ) - agent = compute_client.agents.create(*args)._info.copy() + sdk_exceptions.raise_from_response(response) + agent = response.json().get('agent') + return zip(*sorted(agent.items())) @@ -84,11 +95,16 @@ class DeleteAgent(command.Command): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute result = 0 for id in parsed_args.id: try: - compute_client.agents.delete(id) + # doing this since openstacksdk has decided not to support this + # deprecated command + response = compute_client.delete( + f'/os-agents/{id}', microversion='2.1' + ) + sdk_exceptions.raise_from_response(response) except Exception as e: result += 1 LOG.error( @@ -123,7 +139,7 @@ class ListAgent(command.Lister): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute columns = ( "Agent ID", "Hypervisor", @@ -133,30 +149,36 @@ class ListAgent(command.Lister): "Md5Hash", "URL", ) - data = compute_client.agents.list(parsed_args.hypervisor) - return ( - columns, - ( - utils.get_item_properties( - s, - columns, - ) - for s in data - ), - ) + + # doing this since openstacksdk has decided not to support this + # deprecated command + path = '/os-agents' + if parsed_args.hypervisor: + path += f'?hypervisor={parsed_args.hypervisor}' + + response = compute_client.get(path, microversion='2.1') + sdk_exceptions.raise_from_response(response) + agents = response.json().get('agents') + + return columns, (utils.get_dict_properties(s, columns) for s in agents) class SetAgent(command.Command): """Set compute agent properties. - The compute agent functionality is hypervisor specific and is only + The compute agent functionality is hypervisor-specific and is only supported by the XenAPI hypervisor driver. It was removed from nova in the 23.0.0 (Wallaby) release. """ def get_parser(self, prog_name): parser = super().get_parser(prog_name) - parser.add_argument("id", metavar="", help=_("ID of the agent")) + parser.add_argument( + "id", + metavar="", + type=int, + help=_("ID of the agent"), + ) parser.add_argument( "--agent-version", dest="version", @@ -172,30 +194,34 @@ class SetAgent(command.Command): return parser def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - data = compute_client.agents.list(hypervisor=None) - agent = {} + compute_client = self.app.client_manager.sdk_connection.compute - for s in data: - if s.agent_id == int(parsed_args.id): - agent['version'] = s.version - agent['url'] = s.url - agent['md5hash'] = s.md5hash - if agent == {}: + response = compute_client.get('/os-agents', microversion='2.1') + sdk_exceptions.raise_from_response(response) + agents = response.json().get('agents') + data = {} + for agent in agents: + if agent['agent_id'] == parsed_args.id: + data['version'] = agent['version'] + data['url'] = agent['url'] + data['md5hash'] = agent['md5hash'] + break + else: msg = _("No agent with a ID of '%(id)s' exists.") - raise exceptions.CommandError(msg % parsed_args.id) + raise exceptions.CommandError(msg % {'id': parsed_args.id}) if parsed_args.version: - agent['version'] = parsed_args.version + data['version'] = parsed_args.version if parsed_args.url: - agent['url'] = parsed_args.url + data['url'] = parsed_args.url if parsed_args.md5hash: - agent['md5hash'] = parsed_args.md5hash + data['md5hash'] = parsed_args.md5hash - args = ( - parsed_args.id, - agent['version'], - agent['url'], - agent['md5hash'], + data = {'para': data} + + # doing this since openstacksdk has decided not to support this + # deprecated command + response = compute_client.put( + f'/os-agents/{parsed_args.id}', json=data, microversion='2.1' ) - compute_client.agents.update(*args) + sdk_exceptions.raise_from_response(response) diff --git a/openstackclient/tests/unit/compute/v2/test_agent.py b/openstackclient/tests/unit/compute/v2/test_agent.py index 3969e9d8fc..eb8c0774aa 100644 --- a/openstackclient/tests/unit/compute/v2/test_agent.py +++ b/openstackclient/tests/unit/compute/v2/test_agent.py @@ -11,153 +11,162 @@ # 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 http +import random from unittest import mock -from unittest.mock import call +import uuid from osc_lib import exceptions from openstackclient.compute.v2 import agent from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes +from openstackclient.tests.unit import fakes from openstackclient.tests.unit import utils as tests_utils -class TestAgent(compute_fakes.TestComputev2): - attr = {} - attr['agent_id'] = 1 - fake_agent = compute_fakes.create_one_agent(attr) +def _generate_fake_agent(): + return { + 'agent_id': random.randint(1, 1000), + 'os': 'agent-os-' + uuid.uuid4().hex, + 'architecture': 'agent-architecture', + 'version': '8.0', + 'url': 'http://127.0.0.1', + 'md5hash': 'agent-md5hash', + 'hypervisor': 'hypervisor', + } - columns = ( - 'agent_id', - 'architecture', - 'hypervisor', - 'md5hash', - 'os', - 'url', - 'version', - ) - - data = ( - fake_agent.agent_id, - fake_agent.architecture, - fake_agent.hypervisor, - fake_agent.md5hash, - fake_agent.os, - fake_agent.url, - fake_agent.version, - ) +class TestAgentCreate(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.agents_mock = self.compute_client.agents - self.agents_mock.reset_mock() + self._agent = _generate_fake_agent() + self.columns = ( + 'agent_id', + 'architecture', + 'hypervisor', + 'md5hash', + 'os', + 'url', + 'version', + ) + self.data = ( + self._agent['agent_id'], + self._agent['architecture'], + self._agent['hypervisor'], + self._agent['md5hash'], + self._agent['os'], + self._agent['url'], + self._agent['version'], + ) - -class TestAgentCreate(TestAgent): - def setUp(self): - super().setUp() - - self.agents_mock.create.return_value = self.fake_agent + self.compute_sdk_client.post.return_value = fakes.FakeResponse( + data={'agent': self._agent} + ) self.cmd = agent.CreateAgent(self.app, None) def test_agent_create(self): arglist = [ - self.fake_agent.os, - self.fake_agent.architecture, - self.fake_agent.version, - self.fake_agent.url, - self.fake_agent.md5hash, - self.fake_agent.hypervisor, + self._agent['os'], + self._agent['architecture'], + self._agent['version'], + self._agent['url'], + self._agent['md5hash'], + self._agent['hypervisor'], + ] + verifylist = [ + ('os', self._agent['os']), + ('architecture', self._agent['architecture']), + ('version', self._agent['version']), + ('url', self._agent['url']), + ('md5hash', self._agent['md5hash']), + ('hypervisor', self._agent['hypervisor']), ] - verifylist = [ - ('os', self.fake_agent.os), - ('architecture', self.fake_agent.architecture), - ('version', self.fake_agent.version), - ('url', self.fake_agent.url), - ('md5hash', self.fake_agent.md5hash), - ('hypervisor', self.fake_agent.hypervisor), - ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.agents_mock.create.assert_called_with( - parsed_args.os, - parsed_args.architecture, - parsed_args.version, - parsed_args.url, - parsed_args.md5hash, - parsed_args.hypervisor, + + self.compute_sdk_client.post.assert_called_with( + '/os-agents', + json={ + 'agent': { + 'hypervisor': parsed_args.hypervisor, + 'os': parsed_args.os, + 'architecture': parsed_args.architecture, + 'version': parsed_args.version, + 'url': parsed_args.url, + 'md5hash': parsed_args.md5hash, + }, + }, + microversion='2.1', ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, data) -class TestAgentDelete(TestAgent): - fake_agents = compute_fakes.create_agents(count=2) - +class TestAgentDelete(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.agents_mock.get.return_value = self.fake_agents + self.compute_sdk_client.delete.return_value = fakes.FakeResponse( + status_code=http.HTTPStatus.NO_CONTENT + ) + self.cmd = agent.DeleteAgent(self.app, None) def test_delete_one_agent(self): - arglist = [self.fake_agents[0].agent_id] - + arglist = ['123'] verifylist = [ - ('id', [self.fake_agents[0].agent_id]), + ('id', ['123']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.agents_mock.delete.assert_called_with( - self.fake_agents[0].agent_id + + self.compute_sdk_client.delete.assert_called_once_with( + '/os-agents/123', + microversion='2.1', ) self.assertIsNone(result) def test_delete_multiple_agents(self): - arglist = [] - for n in self.fake_agents: - arglist.append(n.agent_id) + arglist = ['1', '2', '3'] verifylist = [ - ('id', arglist), + ('id', ['1', '2', '3']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - calls = [] - for n in self.fake_agents: - calls.append(call(n.agent_id)) - self.agents_mock.delete.assert_has_calls(calls) + calls = [ + mock.call(f'/os-agents/{x}', microversion='2.1') for x in arglist + ] + self.compute_sdk_client.delete.assert_has_calls(calls) self.assertIsNone(result) def test_delete_multiple_agents_exception(self): - arglist = [ - self.fake_agents[0].agent_id, - self.fake_agents[1].agent_id, - 'x-y-z', - ] + arglist = ['1', '2', '999'] verifylist = [ - ('id', arglist), + ('id', ['1', '2', '999']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - ret_delete = [None, None, exceptions.NotFound('404')] - self.agents_mock.delete = mock.Mock(side_effect=ret_delete) + self.compute_sdk_client.delete.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NO_CONTENT), + fakes.FakeResponse(status_code=http.HTTPStatus.NO_CONTENT), + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + ] self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) calls = [ - call(self.fake_agents[0].agent_id), - call(self.fake_agents[1].agent_id), + mock.call(f'/os-agents/{x}', microversion='2.1') for x in arglist ] - self.agents_mock.delete.assert_has_calls(calls) + self.compute_sdk_client.delete.assert_has_calls(calls) def test_agent_delete_no_input(self): arglist = [] @@ -171,36 +180,37 @@ class TestAgentDelete(TestAgent): ) -class TestAgentList(TestAgent): - agents = compute_fakes.create_agents(count=3) - list_columns = ( - "Agent ID", - "Hypervisor", - "OS", - "Architecture", - "Version", - "Md5Hash", - "URL", - ) - - list_data = [] - for _agent in agents: - list_data.append( - ( - _agent.agent_id, - _agent.hypervisor, - _agent.os, - _agent.architecture, - _agent.version, - _agent.md5hash, - _agent.url, - ) - ) - +class TestAgentList(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.agents_mock.list.return_value = self.agents + _agents = [_generate_fake_agent() for _ in range(3)] + + self.columns = ( + "Agent ID", + "Hypervisor", + "OS", + "Architecture", + "Version", + "Md5Hash", + "URL", + ) + self.data = [ + ( + _agent['agent_id'], + _agent['hypervisor'], + _agent['os'], + _agent['architecture'], + _agent['version'], + _agent['md5hash'], + _agent['url'], + ) + for _agent in _agents + ] + + self.compute_sdk_client.get.return_value = fakes.FakeResponse( + data={'agents': _agents}, + ) self.cmd = agent.ListAgent(self.app, None) def test_agent_list(self): @@ -210,8 +220,12 @@ class TestAgentList(TestAgent): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.list_columns, columns) - self.assertEqual(self.list_data, list(data)) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + self.compute_sdk_client.get.assert_called_once_with( + '/os-agents', + microversion='2.1', + ) def test_agent_list_with_hypervisor(self): arglist = [ @@ -225,101 +239,129 @@ class TestAgentList(TestAgent): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.assertEqual(self.list_columns, columns) - self.assertEqual(self.list_data, list(data)) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + self.compute_sdk_client.get.assert_called_once_with( + '/os-agents?hypervisor=hypervisor', + microversion='2.1', + ) -class TestAgentSet(TestAgent): +class TestAgentSet(compute_fakes.TestComputev2): def setUp(self): super().setUp() - self.agents_mock.update.return_value = self.fake_agent - self.agents_mock.list.return_value = [self.fake_agent] + self.agent = _generate_fake_agent() + self.compute_sdk_client.get.return_value = fakes.FakeResponse( + data={'agents': [self.agent]}, + ) + self.compute_sdk_client.put.return_value = fakes.FakeResponse() + self.cmd = agent.SetAgent(self.app, None) def test_agent_set_nothing(self): arglist = [ - '1', + str(self.agent['agent_id']), ] verifylist = [ - ('id', '1'), + ('id', self.agent['agent_id']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.agents_mock.update.assert_called_with( - parsed_args.id, - self.fake_agent.version, - self.fake_agent.url, - self.fake_agent.md5hash, + self.compute_sdk_client.put.assert_called_once_with( + f'/os-agents/{self.agent["agent_id"]}', + json={ + 'para': { + 'version': self.agent['version'], + 'url': self.agent['url'], + 'md5hash': self.agent['md5hash'], + }, + }, + microversion='2.1', ) self.assertIsNone(result) def test_agent_set_version(self): arglist = [ - '1', + str(self.agent['agent_id']), '--agent-version', 'new-version', ] verifylist = [ - ('id', '1'), + ('id', self.agent['agent_id']), ('version', 'new-version'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.agents_mock.update.assert_called_with( - parsed_args.id, - parsed_args.version, - self.fake_agent.url, - self.fake_agent.md5hash, + self.compute_sdk_client.put.assert_called_once_with( + f'/os-agents/{self.agent["agent_id"]}', + json={ + 'para': { + 'version': parsed_args.version, + 'url': self.agent['url'], + 'md5hash': self.agent['md5hash'], + }, + }, + microversion='2.1', ) self.assertIsNone(result) def test_agent_set_url(self): arglist = [ - '1', + str(self.agent['agent_id']), '--url', 'new-url', ] verifylist = [ - ('id', '1'), + ('id', self.agent['agent_id']), ('url', 'new-url'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.agents_mock.update.assert_called_with( - parsed_args.id, - self.fake_agent.version, - parsed_args.url, - self.fake_agent.md5hash, + self.compute_sdk_client.put.assert_called_once_with( + f'/os-agents/{self.agent["agent_id"]}', + json={ + 'para': { + 'version': self.agent['version'], + 'url': parsed_args.url, + 'md5hash': self.agent['md5hash'], + }, + }, + microversion='2.1', ) self.assertIsNone(result) def test_agent_set_md5hash(self): arglist = [ - '1', + str(self.agent['agent_id']), '--md5hash', 'new-md5hash', ] verifylist = [ - ('id', '1'), + ('id', self.agent['agent_id']), ('md5hash', 'new-md5hash'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.agents_mock.update.assert_called_with( - parsed_args.id, - self.fake_agent.version, - self.fake_agent.url, - parsed_args.md5hash, + self.compute_sdk_client.put.assert_called_once_with( + f'/os-agents/{self.agent["agent_id"]}', + json={ + 'para': { + 'version': self.agent['version'], + 'url': self.agent['url'], + 'md5hash': parsed_args.md5hash, + }, + }, + microversion='2.1', ) self.assertIsNone(result) diff --git a/releasenotes/notes/migrate-agent-commands-1c50ffcb75f91418.yaml b/releasenotes/notes/migrate-agent-commands-1c50ffcb75f91418.yaml new file mode 100644 index 0000000000..7f55cfc302 --- /dev/null +++ b/releasenotes/notes/migrate-agent-commands-1c50ffcb75f91418.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + The ``compute agent *`` commands have been migrated to SDK.