Support /v1/shards

Unit Tests Boilerplate was generated by GPT-4o

Generated-By: GPT-4o
Change-Id: I6f45cc678a5931e3525fb85256c1608e75fe089f
This commit is contained in:
Sharpz7
2024-11-01 21:12:27 +00:00
committed by Jay Faulkner
parent 2c189bc457
commit f2bed8f253
8 changed files with 264 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
# 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 logging
from osc_lib.command import command
from osc_lib import utils as oscutils
from ironicclient.v1 import resource_fields as res_fields
class ListBaremetalShard(command.Lister):
"""List baremetal shards."""
log = logging.getLogger(__name__ + ".ListBaremetalShard")
def take_action(self, parsed_args):
self.log.debug("take_action(%s)", parsed_args)
client = self.app.client_manager.baremetal
data = client.shard.list()
columns = res_fields.SHARD_RESOURCE.fields
labels = res_fields.SHARD_RESOURCE.labels
return (labels,
(oscutils.get_item_properties(s, columns) for s in data))

View File

@@ -255,6 +255,14 @@ RUNBOOK = {
'steps': baremetal_runbook_steps, 'steps': baremetal_runbook_steps,
'extra': baremetal_runbook_extra, 'extra': baremetal_runbook_extra,
} }
baremetal_shard_name = 'example_shard'
baremetal_shard_count = 47
SHARD = {
'name': baremetal_shard_name,
'count': baremetal_shard_count,
}
NODE_HISTORY = [ NODE_HISTORY = [
{ {
'uuid': 'abcdef1', 'uuid': 'abcdef1',

View File

@@ -0,0 +1,57 @@
# 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 copy
from ironicclient.osc.v1 import baremetal_shard
from ironicclient.tests.unit.osc.v1 import fakes as baremetal_fakes
class TestBaremetalShard(baremetal_fakes.TestBaremetal):
def setUp(self):
super(TestBaremetalShard, self).setUp()
self.baremetal_mock = self.app.client_manager.baremetal
self.baremetal_mock.reset_mock()
class TestBaremetalShardList(TestBaremetalShard):
def setUp(self):
super(TestBaremetalShardList, self).setUp()
# Return a list containing mocked shard data to
# simulate the expected output
self.baremetal_mock.shard.list.return_value = [
baremetal_fakes.FakeBaremetalResource(
None,
copy.deepcopy(baremetal_fakes.SHARD),
loaded=True,
),
]
# Get the command object to test
self.cmd = baremetal_shard.ListBaremetalShard(self.app, None)
def test_shard_list(self):
arglist = []
verifylist = []
# Parse arguments and invoke command
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
# Define expected columns and data output
collist = ("Name", "Count")
datalist = ((baremetal_fakes.baremetal_shard_name,
baremetal_fakes.baremetal_shard_count), )
self.assertEqual(collist, columns)
self.assertEqual(datalist, tuple(data))

View File

@@ -0,0 +1,115 @@
# 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 unittest
from unittest.mock import patch
from ironicclient.v1.shard import ShardManager
class TestShardManager(unittest.TestCase):
@patch('ironicclient.common.base.Manager._list')
def test_list_shards(self, mock_list):
# Mock response for the list of shards
mock_response = [
{'name': 'example_shard1', 'count': 47},
{'name': 'example_shard2', 'count': 46},
{'name': None, 'count': 3} # Nodes with no shard assigned
]
# Configure mock to return the mocked response
mock_list.return_value = mock_response
# Initialize the ShardManager
shard_manager = ShardManager(api=None) # `api=None` for simplicity
# Perform the test call
result = shard_manager.list(os_ironic_api_version="1.82")
# Assertions
mock_list.assert_called_once_with(
shard_manager._path(None),
"shards",
os_ironic_api_version="1.82",
global_request_id=None
)
self.assertEqual(len(result), 3)
self.assertEqual(result[0]['name'], 'example_shard1')
self.assertEqual(result[0]['count'], 47)
self.assertEqual(result[1]['name'], 'example_shard2')
self.assertEqual(result[1]['count'], 46)
self.assertIsNone(result[2]['name'])
self.assertEqual(result[2]['count'], 3)
@patch('ironicclient.common.base.Manager._list')
def test_list_shards_empty(self, mock_list):
# Test when the shards list is empty
mock_list.return_value = []
# Initialize the ShardManager
shard_manager = ShardManager(api=None)
# Perform the test call
result = shard_manager.list(os_ironic_api_version="1.82")
# Assertions
mock_list.assert_called_once_with(
shard_manager._path(None),
"shards",
os_ironic_api_version="1.82",
global_request_id=None
)
self.assertEqual(result, [])
@patch('ironicclient.common.base.Manager._list')
def test_list_shards_with_global_request_id(self, mock_list):
# Test with global request ID
mock_response = [
{'name': 'example_shard1', 'count': 47},
{'name': 'example_shard2', 'count': 46}
]
mock_list.return_value = mock_response
# Initialize the ShardManager
shard_manager = ShardManager(api=None)
# Perform the test call with global_request_id
result = shard_manager.list(
os_ironic_api_version="1.82",
global_request_id="req-12345"
)
# Assertions
mock_list.assert_called_once_with(
shard_manager._path(None),
"shards",
os_ironic_api_version="1.82",
global_request_id="req-12345"
)
self.assertEqual(result, mock_response)
@patch('ironicclient.common.base.Manager._list')
def test_list_shards_api_version_mismatch(self, mock_list):
# Simulate a 404 error for an unsupported API version
mock_list.side_effect = ValueError(
"404 Not Found: The requested version is not supported"
)
# Initialize the ShardManager
shard_manager = ShardManager(api=None)
# Perform the test call and assert exception is raised
with self.assertRaises(ValueError) as context:
shard_manager.list(os_ironic_api_version="1.50")
self.assertIn("404 Not Found", str(context.exception))

View File

@@ -28,6 +28,7 @@ from ironicclient.v1 import node
from ironicclient.v1 import port from ironicclient.v1 import port
from ironicclient.v1 import portgroup from ironicclient.v1 import portgroup
from ironicclient.v1 import runbook from ironicclient.v1 import runbook
from ironicclient.v1 import shard
from ironicclient.v1 import volume_connector from ironicclient.v1 import volume_connector
from ironicclient.v1 import volume_target from ironicclient.v1 import volume_target
@@ -106,6 +107,7 @@ class Client(object):
self.allocation = allocation.AllocationManager(self.http_client) self.allocation = allocation.AllocationManager(self.http_client)
self.deploy_template = deploy_template.DeployTemplateManager( self.deploy_template = deploy_template.DeployTemplateManager(
self.http_client) self.http_client)
self.shard = shard.ShardManager(self.http_client)
@property @property
def current_api_version(self): def current_api_version(self):

View File

@@ -47,6 +47,7 @@ class Resource(object):
'conductor': 'Conductor', 'conductor': 'Conductor',
'conductor_group': 'Conductor Group', 'conductor_group': 'Conductor Group',
'console_enabled': 'Console Enabled', 'console_enabled': 'Console Enabled',
'count': 'Count',
'created_at': 'Created At', 'created_at': 'Created At',
'default_bios_interface': 'Default BIOS Interface', 'default_bios_interface': 'Default BIOS Interface',
'default_boot_interface': 'Default Boot Interface', 'default_boot_interface': 'Default Boot Interface',
@@ -643,3 +644,8 @@ NODE_HISTORY_DETAILED_RESOURCE = Resource(
'conductor', 'conductor',
'user'] 'user']
) )
SHARD_RESOURCE = Resource(
['name',
'count']
)

39
ironicclient/v1/shard.py Normal file
View File

@@ -0,0 +1,39 @@
# 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 ironicclient.common import base
class Shard(base.Resource):
def __repr__(self):
return "<Shard %s>" % self._info
class ShardManager(base.Manager):
resource_class = Shard
_resource_name = 'shards'
def list(self, os_ironic_api_version=None, global_request_id=None):
"""Retrieve a list of shards.
:param os_ironic_api_version: String version (e.g. "1.35") to use for
the request. If not specified, the client's default is used.
:param global_request_id: String containing global request ID header
value (in form "req-<UUID>") to use for the request.
:returns: A list of conductors.
"""
header_values = {"os_ironic_api_version": os_ironic_api_version,
"global_request_id": global_request_id}
return self._list(self._path(None), "shards", **header_values)

View File

@@ -118,6 +118,7 @@ openstack.baremetal.v1 =
baremetal_port_group_set = ironicclient.osc.v1.baremetal_portgroup:SetBaremetalPortGroup baremetal_port_group_set = ironicclient.osc.v1.baremetal_portgroup:SetBaremetalPortGroup
baremetal_port_group_show = ironicclient.osc.v1.baremetal_portgroup:ShowBaremetalPortGroup baremetal_port_group_show = ironicclient.osc.v1.baremetal_portgroup:ShowBaremetalPortGroup
baremetal_port_group_unset = ironicclient.osc.v1.baremetal_portgroup:UnsetBaremetalPortGroup baremetal_port_group_unset = ironicclient.osc.v1.baremetal_portgroup:UnsetBaremetalPortGroup
baremetal_shard_list = ironicclient.osc.v1.baremetal_shard:ListBaremetalShard
baremetal_volume_connector_create = ironicclient.osc.v1.baremetal_volume_connector:CreateBaremetalVolumeConnector baremetal_volume_connector_create = ironicclient.osc.v1.baremetal_volume_connector:CreateBaremetalVolumeConnector
baremetal_volume_connector_delete = ironicclient.osc.v1.baremetal_volume_connector:DeleteBaremetalVolumeConnector baremetal_volume_connector_delete = ironicclient.osc.v1.baremetal_volume_connector:DeleteBaremetalVolumeConnector
baremetal_volume_connector_list = ironicclient.osc.v1.baremetal_volume_connector:ListBaremetalVolumeConnector baremetal_volume_connector_list = ironicclient.osc.v1.baremetal_volume_connector:ListBaremetalVolumeConnector