Merge "Introduce share attachment client calls"

This commit is contained in:
Zuul
2026-05-16 07:35:57 +00:00
committed by Gerrit Code Review
5 changed files with 286 additions and 1 deletions
@@ -0,0 +1,6 @@
---
features:
- |
Added client support for Nova's share attachment feature. Features
include attach, detach, list and show share attachments for a
Nova VM.
@@ -0,0 +1,121 @@
# 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 tempest.lib.api_schema.response.compute.v2_96 import servers as servers296
###########################################################################
#
# 2.97:
#
# Add support for share attachments to servers:
# - POST /servers/{server_id}/shares (attach share)
# - GET /servers/{server_id}/shares (list share attachments)
# - GET /servers/{server_id}/shares/{share_id} (show share attachment)
# - DELETE /servers/{server_id}/shares/{share_id} (detach share)
#
###########################################################################
common_share_attachment_info = {
'type': 'object',
'properties': {
'share_id': {'type': 'string'},
'status': {'type': 'string'},
'tag': {'type': 'string'}
},
'additionalProperties': False,
'required': ['share_id', 'status', 'tag']
}
attach_share = {
'status_code': [201],
'response_body': {
'type': 'object',
'properties': {
'share': common_share_attachment_info
},
'additionalProperties': False,
'required': ['share']
}
}
detach_share = {
'status_code': [200]
}
show_share_attachment = {
'status_code': [200],
'response_body': {
'type': 'object',
'properties': {
'share': {
'type': 'object',
'properties': {
'uuid': {'type': 'string'},
'share_id': {'type': 'string'},
'status': {'type': 'string'},
'tag': {'type': 'string'},
'export_location': {'type': 'string'}
},
'additionalProperties': False,
# uuid and export_location are optional (admin only)
'required': ['share_id', 'status', 'tag']
}
},
'additionalProperties': False,
'required': ['share']
}
}
list_share_attachments = {
'status_code': [200],
'response_body': {
'type': 'object',
'properties': {
'shares': {
'type': 'array',
'items': common_share_attachment_info
}
},
'additionalProperties': False,
'required': ['shares']
}
}
# NOTE(zhufl): Below are the unchanged schema in this microversion. We
# need to keep this schema in this file to have the generic way to select the
# right schema based on self.schema_versions_info mapping in service client.
# ****** Schemas unchanged since microversion 2.96***
get_server = copy.deepcopy(servers296.get_server)
list_servers_detail = copy.deepcopy(servers296.list_servers_detail)
update_server = copy.deepcopy(servers296.update_server)
rebuild_server = copy.deepcopy(servers296.rebuild_server)
rebuild_server_with_admin_pass = copy.deepcopy(
servers296.rebuild_server_with_admin_pass)
attach_volume = copy.deepcopy(servers296.attach_volume)
show_volume_attachment = copy.deepcopy(servers296.show_volume_attachment)
list_volume_attachments = copy.deepcopy(servers296.list_volume_attachments)
list_servers = copy.deepcopy(servers296.list_servers)
show_server_diagnostics = copy.deepcopy(servers296.show_server_diagnostics)
get_remote_consoles = copy.deepcopy(servers296.get_remote_consoles)
list_tags = copy.deepcopy(servers296.list_tags)
update_all_tags = copy.deepcopy(servers296.update_all_tags)
delete_all_tags = copy.deepcopy(servers296.delete_all_tags)
check_tag_existence = copy.deepcopy(servers296.check_tag_existence)
update_tag = copy.deepcopy(servers296.update_tag)
delete_tag = copy.deepcopy(servers296.delete_tag)
show_instance_action = copy.deepcopy(servers296.show_instance_action)
list_instance_actions = copy.deepcopy(servers296.list_instance_actions)
create_backup = copy.deepcopy(servers296.create_backup)
list_live_migrations = copy.deepcopy(servers296.list_live_migrations)
+66 -1
View File
@@ -48,6 +48,7 @@ from tempest.lib.api_schema.response.compute.v2_84 import servers as schemav284
from tempest.lib.api_schema.response.compute.v2_89 import servers as schemav289
from tempest.lib.api_schema.response.compute.v2_9 import servers as schemav29
from tempest.lib.api_schema.response.compute.v2_96 import servers as schemav296
from tempest.lib.api_schema.response.compute.v2_97 import servers as schemav297
from tempest.lib.api_schema.response.compute.v2_98 import servers as schemav298
from tempest.lib.api_schema.response.compute.v2_99 import servers as schemav299
from tempest.lib.api_schema.response.compute.v2_100 import \
@@ -85,7 +86,8 @@ class ServersClient(base_compute_client.BaseComputeClient):
{'min': '2.80', 'max': '2.83', 'schema': schemav280},
{'min': '2.84', 'max': '2.88', 'schema': schemav284},
{'min': '2.89', 'max': '2.95', 'schema': schemav289},
{'min': '2.96', 'max': '2.97', 'schema': schemav296},
{'min': '2.96', 'max': '2.96', 'schema': schemav296},
{'min': '2.97', 'max': '2.97', 'schema': schemav297},
{'min': '2.98', 'max': '2.98', 'schema': schemav298},
{'min': '2.99', 'max': '2.99', 'schema': schemav299},
{'min': '2.100', 'max': None, 'schema': schemav2100},
@@ -967,3 +969,66 @@ class ServersClient(base_compute_client.BaseComputeClient):
return self.action(server_id, 'evacuate',
evacuate_schema,
**kwargs)
def attach_share(self, server_id, **kwargs):
"""Attach a share to a server.
For a full list of available parameters, please refer to the official
API reference:
https://docs.openstack.org/api-ref/compute/#attach-a-share-to-an-instance
Available from microversion 2.97.
"""
post_body = json.dumps({'share': kwargs})
resp, body = self.post('servers/%s/shares' % server_id,
post_body)
body = json.loads(body)
schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.attach_share, resp, body)
return rest_client.ResponseBody(resp, body)
def detach_share(self, server_id, share_id):
"""Detach a share from a server instance.
For a full list of available parameters, please refer to the official
API reference:
https://docs.openstack.org/api-ref/compute/#detach-a-share-from-an-instance
Available from microversion 2.97.
"""
resp, body = self.delete('servers/%s/shares/%s' %
(server_id, share_id))
schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.detach_share, resp, body)
return rest_client.ResponseBody(resp, body)
def show_share_attachment(self, server_id, share_id):
"""Return details about the given share attachment.
For a full list of available parameters, please refer to the official
API reference:
https://docs.openstack.org/api-ref/compute/#show-a-detail-of-a-share-attachment
Available from microversion 2.97.
"""
resp, body = self.get('servers/%s/shares/%s' % (
server_id, share_id))
body = json.loads(body)
schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.show_share_attachment, resp, body)
return rest_client.ResponseBody(resp, body)
def list_share_attachments(self, server_id):
"""Return the list of share attachments for a given instance.
For a full list of available parameters, please refer to the official
API reference:
https://docs.openstack.org/api-ref/compute/#list-share-attachments-for-an-instance
Available from microversion 2.97.
"""
resp, body = self.get('servers/%s/shares' % server_id)
body = json.loads(body)
schema = self.get_schema(self.schema_versions_info)
self.validate_response(schema.list_share_attachments, resp, body)
return rest_client.ResponseBody(resp, body)
@@ -1059,3 +1059,96 @@ class TestServersClientMinV26(base.BaseServiceTest):
console_type='serial',
protocol='serial',
)
class TestServersClientMinV297(base.BaseServiceTest):
FAKE_SHARE_ATTACHMENT = {
"share_id": "b6c0975b-6c5d-4b5b-82fc-1d4bb63f6edc",
"status": "active",
"tag": "fake-tag"
}
FAKE_SHARE_ATTACHMENT_DETAIL = {
"uuid": "c7d1a86c-7d6e-4c6c-93gd-2e5cc74g7fde",
"share_id": "b6c0975b-6c5d-4b5b-82fc-1d4bb63f6edc",
"status": "active",
"tag": "fake-tag",
"export_location": "fake-export-location"
}
def setUp(self):
super(TestServersClientMinV297, self).setUp()
fake_auth = fake_auth_provider.FakeAuthProvider()
self.client = servers_client.ServersClient(fake_auth, 'compute',
'regionOne')
base_compute_client.COMPUTE_MICROVERSION = '2.97'
self.server_id = "893c7791-f1df-4c3d-8383-3caae9656c62"
def tearDown(self):
super(TestServersClientMinV297, self).tearDown()
base_compute_client.COMPUTE_MICROVERSION = None
def test_attach_share_with_str_body(self):
self._test_attach_share()
def test_attach_share_with_bytes_body(self):
self._test_attach_share(bytes_body=True)
def _test_attach_share(self, bytes_body=False):
self.check_service_client_function(
self.client.attach_share,
'tempest.lib.common.rest_client.RestClient.post',
{'share': self.FAKE_SHARE_ATTACHMENT},
bytes_body,
status=201,
server_id=self.server_id
)
def test_detach_share_with_str_body(self):
self._test_detach_share()
def test_detach_share_with_bytes_body(self):
self._test_detach_share(bytes_body=True)
def _test_detach_share(self, bytes_body=False):
self.check_service_client_function(
self.client.detach_share,
'tempest.lib.common.rest_client.RestClient.delete',
{},
bytes_body,
status=200,
server_id=self.server_id,
share_id=self.FAKE_SHARE_ATTACHMENT['share_id']
)
def test_show_share_attachment_with_str_body(self):
self._test_show_share_attachment()
def test_show_share_attachment_with_bytes_body(self):
self._test_show_share_attachment(bytes_body=True)
def _test_show_share_attachment(self, bytes_body=False):
self.check_service_client_function(
self.client.show_share_attachment,
'tempest.lib.common.rest_client.RestClient.get',
{'share': self.FAKE_SHARE_ATTACHMENT_DETAIL},
bytes_body,
server_id=self.server_id,
share_id=self.FAKE_SHARE_ATTACHMENT['share_id']
)
def test_list_share_attachments_with_str_body(self):
self._test_list_share_attachments()
def test_list_share_attachments_with_bytes_body(self):
self._test_list_share_attachments(bytes_body=True)
def _test_list_share_attachments(self, bytes_body=False):
self.check_service_client_function(
self.client.list_share_attachments,
'tempest.lib.common.rest_client.RestClient.get',
{'shares': [self.FAKE_SHARE_ATTACHMENT]},
bytes_body,
server_id=self.server_id
)