compute: Migrate 'host set' to SDK

This was the sole outstanding command to be migrated to SDK. We also
clean up the old in-tree wrappers we have in the process and add missing
error checks for the 'host list' and 'host show' commands.

Change-Id: I5635469b63ab3370fb5118e4f8a1758002381aa5
Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
Stephen Finucane 2024-05-07 12:14:22 +01:00
parent 0f07c97e84
commit 7252a7a781
6 changed files with 122 additions and 319 deletions

View File

@ -19,7 +19,7 @@ from osc_lib import exceptions
from osc_lib.i18n import _
# TODO(dtroyer): Mingrate this to osc-lib
# TODO(dtroyer): Migrate this to osc-lib
class InvalidValue(Exception):
"""An argument value is not valid: wrong type, out of range, etc"""
@ -269,80 +269,6 @@ class APIv2(api.BaseAPI):
return self.list(url)["floating_ip_pools"]
# Hosts
def host_list(
self,
zone=None,
):
"""Lists hypervisor Hosts
https://docs.openstack.org/api-ref/compute/#list-hosts
Valid for Compute 2.0 - 2.42
:param string zone:
Availability zone
:returns: A dict of the floating IP attributes
"""
url = "/os-hosts"
if zone:
url = '/os-hosts?zone=%s' % zone
return self.list(url)["hosts"]
def host_set(
self, host=None, status=None, maintenance_mode=None, **params
):
"""Modify host properties
https://docs.openstack.org/api-ref/compute/#update-host-status
Valid for Compute 2.0 - 2.42
status
maintenance_mode
"""
url = "/os-hosts"
params = {}
if status:
params['status'] = status
if maintenance_mode:
params['maintenance_mode'] = maintenance_mode
if params == {}:
# Don't bother calling if nothing given
return None
else:
return self._request(
"PUT",
f"/{url}/{host}",
json=params,
).json()
def host_show(
self,
host=None,
):
"""Show host
https://docs.openstack.org/api-ref/compute/#show-host-details
Valid for Compute 2.0 - 2.42
"""
url = "/os-hosts"
r_host = self.find(
url,
attr='host_name',
value=host,
)
data = []
for h in r_host:
data.append(h['resource'])
return data
# Networks
def network_create(

View File

@ -15,6 +15,7 @@
"""Host action implementations"""
from openstack import exceptions as sdk_exceptions
from osc_lib.command import command
from osc_lib import utils
@ -35,34 +36,26 @@ class ListHost(command.Lister):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.sdk_connection.compute
columns = ("Host Name", "Service", "Zone")
self.log.warning(
"API has been deprecated. "
"Please consider using 'hypervisor list' instead."
"API has been deprecated; "
"consider using 'hypervisor list' instead."
)
# doing this since openstacksdk has decided not to support this
# deprecated command
hosts = (
compute_client.get('/os-hosts', microversion='2.1')
.json()
.get('hosts')
)
response = compute_client.get('/os-hosts', microversion='2.1')
sdk_exceptions.raise_from_response(response)
hosts = response.json().get('hosts')
if parsed_args.zone is not None:
filtered_hosts = []
for host in hosts:
if host['zone'] == parsed_args.zone:
filtered_hosts.append(host)
hosts = filtered_hosts
hosts = [h for h in hosts if h['zone'] == parsed_args.zone]
columns = ("Host Name", "Service", "Zone")
return columns, (utils.get_dict_properties(s, columns) for s in hosts)
class SetHost(command.Command):
_description = _("Set host properties")
_description = _("DEPRECATED: Set host properties")
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
@ -90,20 +83,33 @@ class SetHost(command.Command):
return parser
def take_action(self, parsed_args):
kwargs = {}
compute_client = self.app.client_manager.sdk_connection.compute
self.log.warning(
"API has been deprecated; "
"consider using 'compute service set' instead."
)
data = {}
if parsed_args.enable:
kwargs['status'] = 'enable'
data['status'] = 'enable'
if parsed_args.disable:
kwargs['status'] = 'disable'
data['status'] = 'disable'
if parsed_args.enable_maintenance:
kwargs['maintenance_mode'] = 'enable'
data['maintenance_mode'] = 'enable'
if parsed_args.disable_maintenance:
kwargs['maintenance_mode'] = 'disable'
data['maintenance_mode'] = 'disable'
compute_client = self.app.client_manager.compute
if not data:
# don't bother calling if nothing given
return
compute_client.api.host_set(parsed_args.host, **kwargs)
# doing this since openstacksdk has decided not to support this
# deprecated command
response = compute_client.put(
f'/os-hosts/{parsed_args.host}', json=data, microversion='2.1'
)
sdk_exceptions.raise_from_response(response)
class ShowHost(command.Lister):
@ -116,26 +122,25 @@ class ShowHost(command.Lister):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.sdk_connection.compute
columns = ("Host", "Project", "CPU", "Memory MB", "Disk GB")
self.log.warning(
"API has been deprecated. "
"Please consider using 'hypervisor show' instead."
"API has been deprecated; "
"consider using 'hypervisor show' instead."
)
# doing this since openstacksdk has decided not to support this
# deprecated command
resources = (
compute_client.get(
'/os-hosts/' + parsed_args.host, microversion='2.1'
)
.json()
.get('host')
response = compute_client.get(
f'/os-hosts/{parsed_args.host}', microversion='2.1'
)
sdk_exceptions.raise_from_response(response)
resources = response.json().get('host')
data = []
if resources is not None:
for resource in resources:
data.append(resource['resource'])
columns = ("Host", "Project", "CPU", "Memory MB", "Disk GB")
return columns, (utils.get_dict_properties(s, columns) for s in data)

View File

@ -227,114 +227,6 @@ class TestFloatingIPPool(TestComputeAPIv2):
self.assertEqual(self.LIST_FLOATING_IP_POOL_RESP, ret)
class TestHost(TestComputeAPIv2):
FAKE_HOST_RESP_1 = {
"zone": "internal",
"host_name": "myhost",
"service": "conductor",
}
FAKE_HOST_RESP_2 = {
"zone": "internal",
"host_name": "myhost",
"service": "scheduler",
}
FAKE_HOST_RESP_3 = {
"zone": "nova",
"host_name": "myhost",
"service": "compute",
}
LIST_HOST_RESP = [
FAKE_HOST_RESP_1,
FAKE_HOST_RESP_2,
FAKE_HOST_RESP_3,
]
def test_host_list_no_options(self):
self.requests_mock.register_uri(
'GET',
FAKE_URL + '/os-hosts',
json={'hosts': self.LIST_HOST_RESP},
status_code=200,
)
ret = self.api.host_list()
self.assertEqual(self.LIST_HOST_RESP, ret)
def test_host_list_zone(self):
self.requests_mock.register_uri(
'GET',
FAKE_URL + '/os-hosts?zone=nova',
json={'hosts': [self.FAKE_HOST_RESP_3]},
status_code=200,
)
self.requests_mock.register_uri(
'GET',
FAKE_URL + '/os-hosts',
json={'hosts': [self.FAKE_HOST_RESP_3]},
status_code=200,
)
ret = self.api.host_list(zone='nova')
self.assertEqual([self.FAKE_HOST_RESP_3], ret)
def test_host_set_none(self):
ret = self.api.host_set(host='myhost')
self.assertIsNone(ret)
def test_host_set(self):
self.requests_mock.register_uri(
'PUT',
FAKE_URL + '/os-hosts/myhost',
json={},
status_code=200,
)
ret = self.api.host_set(host='myhost', status='enabled')
self.assertEqual({}, ret)
def test_host_show(self):
FAKE_RESOURCE_1 = {
"cpu": 2,
"disk_gb": 1028,
"host": "c1a7de0ac9d94e4baceae031d05caae3",
"memory_mb": 8192,
"project": "(total)",
}
FAKE_RESOURCE_2 = {
"cpu": 0,
"disk_gb": 0,
"host": "c1a7de0ac9d94e4baceae031d05caae3",
"memory_mb": 512,
"project": "(used_now)",
}
FAKE_RESOURCE_3 = {
"cpu": 0,
"disk_gb": 0,
"host": "c1a7de0ac9d94e4baceae031d05caae3",
"memory_mb": 0,
"project": "(used_max)",
}
FAKE_HOST_RESP = [
{'resource': FAKE_RESOURCE_1},
{'resource': FAKE_RESOURCE_2},
{'resource': FAKE_RESOURCE_3},
]
FAKE_HOST_LIST = [
FAKE_RESOURCE_1,
FAKE_RESOURCE_2,
FAKE_RESOURCE_3,
]
self.requests_mock.register_uri(
'GET',
FAKE_URL + '/os-hosts/myhost',
json={'host': FAKE_HOST_RESP},
status_code=200,
)
ret = self.api.host_show(host='myhost')
self.assertEqual(FAKE_HOST_LIST, ret)
class TestNetwork(TestComputeAPIv2):
FAKE_NETWORK_RESP = {
'id': '1',

View File

@ -127,9 +127,6 @@ class FakeComputev2Client:
self.keypairs = mock.Mock()
self.keypairs.resource_class = fakes.FakeResource(None, {})
self.hosts = mock.Mock()
self.hosts.resource_class = fakes.FakeResource(None, {})
self.server_groups = mock.Mock()
self.server_groups.resource_class = fakes.FakeResource(None, {})
@ -1012,56 +1009,6 @@ def get_networks(networks=None, count=2):
return mock.Mock(side_effect=networks)
def create_one_host(attrs=None):
"""Create a fake host.
:param dict attrs:
A dictionary with all attributes
:return:
A FakeResource object, with uuid and other attributes
"""
attrs = attrs or {}
# Set default attributes.
host_info = {
"service_id": 1,
"host": "host1",
"uuid": 'host-id-' + uuid.uuid4().hex,
"vcpus": 10,
"memory_mb": 100,
"local_gb": 100,
"vcpus_used": 5,
"memory_mb_used": 50,
"local_gb_used": 10,
"hypervisor_type": "xen",
"hypervisor_version": 1,
"hypervisor_hostname": "devstack1",
"free_ram_mb": 50,
"free_disk_gb": 50,
"current_workload": 10,
"running_vms": 1,
"cpu_info": "",
"disk_available_least": 1,
"host_ip": "10.10.10.10",
"supported_instances": "",
"metrics": "",
"pci_stats": "",
"extra_resources": "",
"stats": "",
"numa_topology": "",
"ram_allocation_ratio": 1.0,
"cpu_allocation_ratio": 1.0,
"zone": 'zone-' + uuid.uuid4().hex,
"host_name": 'name-' + uuid.uuid4().hex,
"service": 'service-' + uuid.uuid4().hex,
"cpu": 4,
"disk_gb": 100,
'project': 'project-' + uuid.uuid4().hex,
}
host_info.update(attrs)
return host_info
def create_one_usage(attrs=None):
"""Create a fake usage.

View File

@ -11,9 +11,8 @@
# 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 unittest import mock
import uuid
from openstackclient.compute.v2 import host
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
@ -21,19 +20,51 @@ from openstackclient.tests.unit import fakes
from openstackclient.tests.unit import utils as tests_utils
@mock.patch('openstackclient.api.compute_v2.APIv2.host_list')
def _generate_fake_host():
return {
'service_id': 1,
'host': 'host1',
'uuid': 'host-id-' + uuid.uuid4().hex,
'vcpus': 10,
'memory_mb': 100,
'local_gb': 100,
'vcpus_used': 5,
'memory_mb_used': 50,
'local_gb_used': 10,
'hypervisor_type': 'xen',
'hypervisor_version': 1,
'hypervisor_hostname': 'devstack1',
'free_ram_mb': 50,
'free_disk_gb': 50,
'current_workload': 10,
'running_vms': 1,
'cpu_info': '',
'disk_available_least': 1,
'host_ip': '10.10.10.10',
'supported_instances': '',
'metrics': '',
'pci_stats': '',
'extra_resources': '',
'stats': '',
'numa_topology': '',
'ram_allocation_ratio': 1.0,
'cpu_allocation_ratio': 1.0,
'zone': 'zone-' + uuid.uuid4().hex,
'host_name': 'name-' + uuid.uuid4().hex,
'service': 'service-' + uuid.uuid4().hex,
'cpu': 4,
'disk_gb': 100,
'project': 'project-' + uuid.uuid4().hex,
}
class TestHostList(compute_fakes.TestComputev2):
_host = compute_fakes.create_one_host()
def setUp(self):
super().setUp()
self.compute_sdk_client.get.return_value = fakes.FakeResponse(
data={'hosts': [self._host]}
)
self._host = _generate_fake_host()
self.columns = ('Host Name', 'Service', 'Zone')
self.data = [
(
self._host['host_name'],
@ -42,10 +73,12 @@ class TestHostList(compute_fakes.TestComputev2):
)
]
self.compute_sdk_client.get.return_value = fakes.FakeResponse(
data={'hosts': [self._host]}
)
self.cmd = host.ListHost(self.app, None)
def test_host_list_no_option(self, h_mock):
h_mock.return_value = [self._host]
def test_host_list_no_option(self):
arglist = []
verifylist = []
@ -59,8 +92,7 @@ class TestHostList(compute_fakes.TestComputev2):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
def test_host_list_with_option(self, h_mock):
h_mock.return_value = [self._host]
def test_host_list_with_option(self):
arglist = [
'--zone',
self._host['zone'],
@ -80,76 +112,60 @@ class TestHostList(compute_fakes.TestComputev2):
self.assertEqual(self.data, list(data))
@mock.patch('openstackclient.api.compute_v2.APIv2.host_set')
class TestHostSet(compute_fakes.TestComputev2):
def setUp(self):
super().setUp()
self.host = compute_fakes.create_one_host()
self._host = _generate_fake_host()
self.compute_sdk_client.put.return_value = fakes.FakeResponse()
self.cmd = host.SetHost(self.app, None)
def test_host_set_no_option(self, h_mock):
h_mock.return_value = self.host
h_mock.update.return_value = None
def test_host_set_no_option(self):
arglist = [
self.host['host'],
self._host['host'],
]
verifylist = [
('host', self.host['host']),
('host', self._host['host']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
self.compute_sdk_client.put.assert_not_called()
h_mock.assert_called_with(self.host['host'])
def test_host_set(self, h_mock):
h_mock.return_value = self.host
h_mock.update.return_value = None
def test_host_set(self):
arglist = [
'--enable',
'--disable-maintenance',
self.host['host'],
self._host['host'],
]
verifylist = [
('enable', True),
('enable_maintenance', False),
('host', self.host['host']),
('host', self._host['host']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.assertIsNone(result)
h_mock.assert_called_with(
self.host['host'], status='enable', maintenance_mode='disable'
self.compute_sdk_client.put.assert_called_with(
f'/os-hosts/{self._host["host"]}',
json={
'maintenance_mode': 'disable',
'status': 'enable',
},
microversion='2.1',
)
@mock.patch('openstackclient.api.compute_v2.APIv2.host_show')
class TestHostShow(compute_fakes.TestComputev2):
_host = compute_fakes.create_one_host()
def setUp(self):
super().setUp()
output_data = {
"resource": {
"host": self._host['host'],
"project": self._host['project'],
"cpu": self._host['cpu'],
"memory_mb": self._host['memory_mb'],
"disk_gb": self._host['disk_gb'],
}
}
self.compute_sdk_client.get.return_value = fakes.FakeResponse(
data={'host': [output_data]}
)
self._host = _generate_fake_host()
self.columns = (
'Host',
@ -158,7 +174,6 @@ class TestHostShow(compute_fakes.TestComputev2):
'Memory MB',
'Disk GB',
)
self.data = [
(
self._host['host'],
@ -169,10 +184,25 @@ class TestHostShow(compute_fakes.TestComputev2):
)
]
self.compute_sdk_client.get.return_value = fakes.FakeResponse(
data={
'host': [
{
'resource': {
'host': self._host['host'],
'project': self._host['project'],
'cpu': self._host['cpu'],
'memory_mb': self._host['memory_mb'],
'disk_gb': self._host['disk_gb'],
}
}
],
}
)
self.cmd = host.ShowHost(self.app, None)
def test_host_show_no_option(self, h_mock):
h_mock.host_show.return_value = [self._host]
def test_host_show_no_option(self):
arglist = []
verifylist = []
@ -185,8 +215,7 @@ class TestHostShow(compute_fakes.TestComputev2):
verifylist,
)
def test_host_show_with_option(self, h_mock):
h_mock.return_value = [self._host]
def test_host_show_with_option(self):
arglist = [
self._host['host_name'],
]

View File

@ -0,0 +1,4 @@
---
upgrade:
- |
The ``host set`` command has been migrated to SDK.