Merge "Remove experimental flag from share replication feature"

This commit is contained in:
Zuul 2020-09-09 15:22:10 +00:00 committed by Gerrit Code Review
commit 2e0e081414
5 changed files with 119 additions and 53 deletions

View File

@ -40,6 +40,8 @@ REPLICATION_PROMOTION_CHOICES = (
REPLICATION_STATE_ACTIVE = 'active'
REPLICATION_STATE_IN_SYNC = 'in_sync'
REPLICATION_STATE_OUT_OF_SYNC = 'out_of_sync'
MIN_SHARE_REPLICATION_VERSION = '2.11'
SHARE_REPLICA_GRADUATION_VERSION = '2.56'
# Access Rules
RULE_STATE_ACTIVE = 'active'

View File

@ -29,7 +29,7 @@ ShareGroup = [
help="The minimum api microversion is configured to be the "
"value of the minimum microversion supported by Manila."),
cfg.StrOpt("max_api_microversion",
default="2.55",
default="2.56",
help="The maximum api microversion is configured to be the "
"value of the latest microversion supported by Manila."),
cfg.StrOpt("region",

View File

@ -1654,20 +1654,23 @@ class SharesV2Client(shares_client.SharesClient):
'share_id': share_id,
'availability_zone': availability_zone,
}
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
body = json.dumps({'share_replica': post_body})
resp, body = self.post(uri, body,
headers=EXPERIMENTAL,
extra_headers=True,
headers=headers,
extra_headers=extra_headers,
version=version)
self.expected_success(202, resp.status)
return self._parse_resp(body)
def get_share_replica(self, replica_id, version=LATEST_MICROVERSION):
"""Get the details of share_replica."""
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
resp, body = self.get("share-replicas/%s" % replica_id,
headers=EXPERIMENTAL,
extra_headers=True,
headers=headers,
extra_headers=extra_headers,
version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
@ -1676,8 +1679,10 @@ class SharesV2Client(shares_client.SharesClient):
"""Get list of replicas."""
uri = "share-replicas/detail"
uri += ("?share_id=%s" % share_id) if share_id is not None else ''
resp, body = self.get(uri, headers=EXPERIMENTAL,
extra_headers=True, version=version)
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
resp, body = self.get(uri, headers=headers,
extra_headers=extra_headers, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
@ -1686,17 +1691,21 @@ class SharesV2Client(shares_client.SharesClient):
"""Get summary list of replicas."""
uri = "share-replicas"
uri += ("?share_id=%s" % share_id) if share_id is not None else ''
resp, body = self.get(uri, headers=EXPERIMENTAL,
extra_headers=True, version=version)
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
resp, body = self.get(uri, headers=headers,
extra_headers=extra_headers, version=version)
self.expected_success(200, resp.status)
return self._parse_resp(body)
def delete_share_replica(self, replica_id, version=LATEST_MICROVERSION):
"""Delete share_replica."""
uri = "share-replicas/%s" % replica_id
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
resp, body = self.delete(uri,
headers=EXPERIMENTAL,
extra_headers=True,
headers=headers,
extra_headers=extra_headers,
version=version)
self.expected_success(202, resp.status)
return body
@ -1709,9 +1718,11 @@ class SharesV2Client(shares_client.SharesClient):
'promote': None,
}
body = json.dumps(post_body)
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
resp, body = self.post(uri, body,
headers=EXPERIMENTAL,
extra_headers=True,
headers=headers,
extra_headers=extra_headers,
version=version)
self.expected_success(expected_status, resp.status)
return self._parse_resp(body)
@ -1720,8 +1731,10 @@ class SharesV2Client(shares_client.SharesClient):
expected_status=200,
version=LATEST_MICROVERSION):
uri = "share-replicas/%s/export-locations" % replica_id
resp, body = self.get(uri, headers=EXPERIMENTAL,
extra_headers=True, version=version)
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
resp, body = self.get(uri, headers=headers,
extra_headers=extra_headers, version=version)
self.expected_success(expected_status, resp.status)
return self._parse_resp(body)
@ -1731,8 +1744,10 @@ class SharesV2Client(shares_client.SharesClient):
version=LATEST_MICROVERSION):
uri = "share-replicas/%s/export-locations/%s" % (replica_id,
export_location_id)
resp, body = self.get(uri, headers=EXPERIMENTAL,
extra_headers=True, version=version)
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
resp, body = self.get(uri, headers=headers,
extra_headers=extra_headers, version=version)
self.expected_success(expected_status, resp.status)
return self._parse_resp(body)
@ -1779,9 +1794,11 @@ class SharesV2Client(shares_client.SharesClient):
}
}
body = json.dumps(post_body)
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
resp, body = self.post(uri, body,
headers=EXPERIMENTAL,
extra_headers=True,
headers=headers,
extra_headers=extra_headers,
version=version)
self.expected_success(202, resp.status)
return self._parse_resp(body)
@ -1797,9 +1814,11 @@ class SharesV2Client(shares_client.SharesClient):
}
}
body = json.dumps(post_body)
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
resp, body = self.post(uri, body,
headers=EXPERIMENTAL,
extra_headers=True,
headers=headers,
extra_headers=extra_headers,
version=version)
self.expected_success(202, resp.status)
return self._parse_resp(body)
@ -1812,9 +1831,11 @@ class SharesV2Client(shares_client.SharesClient):
'resync': None
}
body = json.dumps(post_body)
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
resp, body = self.post(uri, body,
headers=EXPERIMENTAL,
extra_headers=True,
headers=headers,
extra_headers=extra_headers,
version=version)
self.expected_success(expected_result, resp.status)
return self._parse_resp(body)
@ -1827,9 +1848,11 @@ class SharesV2Client(shares_client.SharesClient):
'force_delete': None
}
body = json.dumps(post_body)
headers, extra_headers = utils.get_extra_headers(
version, constants.SHARE_REPLICA_GRADUATION_VERSION)
resp, body = self.post(uri, body,
headers=EXPERIMENTAL,
extra_headers=True,
headers=headers,
extra_headers=extra_headers,
version=version)
self.expected_success(202, resp.status)
return self._parse_resp(body)

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import ddt
from tempest import config
from tempest.lib import decorators
from testtools import testcase as tc
@ -24,8 +25,10 @@ from manila_tempest_tests import utils
CONF = config.CONF
_MIN_SUPPORTED_MICROVERSION = '2.11'
LATEST_MICROVERSION = CONF.share.max_api_microversion
@ddt.ddt
class ReplicationAdminTest(base.BaseSharesMixedTest):
@classmethod
@ -80,8 +83,13 @@ class ReplicationAdminTest(base.BaseSharesMixedTest):
@decorators.idempotent_id('0213cdfd-6a0f-4f24-a154-69796888a64a')
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
def test_promote_out_of_sync_share_replica(self):
@ddt.data(
*set([constants.MIN_SHARE_REPLICATION_VERSION,
constants.SHARE_REPLICA_GRADUATION_VERSION,
LATEST_MICROVERSION]))
def test_promote_out_of_sync_share_replica(self, version):
"""Test promote 'out_of_sync' share replica to active state."""
self.skip_if_microversion_not_supported(version)
if (self.replication_type
not in constants.REPLICATION_PROMOTION_CHOICES):
msg = "Option backend_replication_type should be one of (%s)!"
@ -91,13 +99,13 @@ class ReplicationAdminTest(base.BaseSharesMixedTest):
share_type_id=self.share_type_id, client=self.admin_client,
availability_zone=self.share_zone, share_network_id=self.sn_id)
original_replica = self.admin_client.list_share_replicas(
share_id=share['id'])[0]
share_id=share['id'], version=version)[0]
# NOTE(Yogi1): Cleanup needs to be disabled for replica that is
# being promoted since it will become the 'primary'/'active' replica.
replica = self.create_share_replica(
share["id"], self.replica_zone, cleanup=False,
client=self.admin_client)
client=self.admin_client, version=version)
# Wait for replica state to update after creation
self.admin_client.wait_for_share_replica_status(
replica['id'], constants.REPLICATION_STATE_IN_SYNC,
@ -105,7 +113,7 @@ class ReplicationAdminTest(base.BaseSharesMixedTest):
# List replicas
replica_list = self.admin_client.list_share_replicas(
share_id=share['id'])
share_id=share['id'], version=version)
# Check if there is only 1 'active' replica before promotion.
active_replicas = self._filter_share_replica_list(
@ -114,86 +122,116 @@ class ReplicationAdminTest(base.BaseSharesMixedTest):
# Set replica_state to 'out_of_sync'
self.admin_client.reset_share_replica_state(
replica['id'], constants.REPLICATION_STATE_OUT_OF_SYNC)
replica['id'], constants.REPLICATION_STATE_OUT_OF_SYNC,
version=version)
self.admin_client.wait_for_share_replica_status(
replica['id'], constants.REPLICATION_STATE_OUT_OF_SYNC,
status_attr='replica_state')
# Promote 'out_of_sync' replica to 'active' state.
self.promote_share_replica(replica['id'], self.admin_client)
self.promote_share_replica(replica['id'], self.admin_client,
version=version)
# Original replica will need to be cleaned up before the promoted
# replica can be deleted.
self.addCleanup(self.delete_share_replica, original_replica['id'])
# Check if there is still only 1 'active' replica after promotion.
replica_list = self.admin_client.list_share_replicas(
share_id=self.share["id"])
share_id=self.share["id"], version=version)
new_active_replicas = self._filter_share_replica_list(
replica_list, constants.REPLICATION_STATE_ACTIVE)
self.assertEqual(1, len(new_active_replicas))
@decorators.idempotent_id('22a199b7-f4f6-4ede-b09f-8047a9d01cad')
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
def test_force_delete_share_replica(self):
@ddt.data(
*set([constants.MIN_SHARE_REPLICATION_VERSION,
constants.SHARE_REPLICA_GRADUATION_VERSION,
LATEST_MICROVERSION]))
def test_force_delete_share_replica(self, version):
"""Test force deleting a replica that is in 'error_deleting' status."""
self.skip_if_microversion_not_supported(version)
replica = self.create_share_replica(self.share['id'],
self.replica_zone,
cleanup_in_class=False,
client=self.admin_client)
client=self.admin_client,
version=version)
self.admin_client.reset_share_replica_status(
replica['id'], constants.STATUS_ERROR_DELETING)
replica['id'], constants.STATUS_ERROR_DELETING, version=version)
self.admin_client.wait_for_share_replica_status(
replica['id'], constants.STATUS_ERROR_DELETING)
self.admin_client.force_delete_share_replica(replica['id'])
self.admin_client.force_delete_share_replica(replica['id'],
version=version)
self.admin_client.wait_for_resource_deletion(replica_id=replica['id'])
@decorators.idempotent_id('16bd90f0-c478-4a99-8633-b18703ff56fa')
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
def test_reset_share_replica_status(self):
@ddt.data(
*set([constants.MIN_SHARE_REPLICATION_VERSION,
constants.SHARE_REPLICA_GRADUATION_VERSION,
LATEST_MICROVERSION]))
def test_reset_share_replica_status(self, version):
"""Test resetting a replica's 'status' attribute."""
self.skip_if_microversion_not_supported(version)
replica = self.create_share_replica(self.share['id'],
self.replica_zone,
cleanup_in_class=False,
client=self.admin_client)
client=self.admin_client,
version=version)
self.admin_client.reset_share_replica_status(replica['id'],
constants.STATUS_ERROR)
constants.STATUS_ERROR,
version=version)
self.admin_client.wait_for_share_replica_status(
replica['id'], constants.STATUS_ERROR)
@decorators.idempotent_id('258844da-a853-42b6-87db-b16e616018c6')
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
def test_reset_share_replica_state(self):
@ddt.data(
*set([constants.MIN_SHARE_REPLICATION_VERSION,
constants.SHARE_REPLICA_GRADUATION_VERSION,
LATEST_MICROVERSION]))
def test_reset_share_replica_state(self, version):
"""Test resetting a replica's 'replica_state' attribute."""
self.skip_if_microversion_not_supported(version)
replica = self.create_share_replica(self.share['id'],
self.replica_zone,
cleanup_in_class=False,
client=self.admin_client)
client=self.admin_client,
version=version)
self.admin_client.reset_share_replica_state(replica['id'],
constants.STATUS_ERROR)
constants.STATUS_ERROR,
version=version)
self.admin_client.wait_for_share_replica_status(
replica['id'], constants.STATUS_ERROR, status_attr='replica_state')
@decorators.idempotent_id('2969565a-85e8-4c61-9dfb-cc7f7ca9f6dd')
@tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
def test_resync_share_replica(self):
@ddt.data(
*set([constants.MIN_SHARE_REPLICATION_VERSION,
constants.SHARE_REPLICA_GRADUATION_VERSION,
LATEST_MICROVERSION]))
def test_resync_share_replica(self, version):
"""Test resyncing a replica."""
self.skip_if_microversion_not_supported(version)
replica = self.create_share_replica(self.share['id'],
self.replica_zone,
cleanup_in_class=False,
client=self.admin_client)
client=self.admin_client,
version=version)
self.admin_client.wait_for_share_replica_status(
replica['id'], constants.REPLICATION_STATE_IN_SYNC,
status_attr='replica_state')
# Set replica_state to 'out_of_sync'.
self.admin_client.reset_share_replica_state(
replica['id'], constants.REPLICATION_STATE_OUT_OF_SYNC)
replica['id'], constants.REPLICATION_STATE_OUT_OF_SYNC,
version=version)
self.admin_client.wait_for_share_replica_status(
replica['id'], constants.REPLICATION_STATE_OUT_OF_SYNC,
status_attr='replica_state')
# Attempt resync
self.admin_client.resync_share_replica(replica['id'])
self.admin_client.resync_share_replica(replica['id'], version=version)
self.admin_client.wait_for_share_replica_status(
replica['id'], constants.REPLICATION_STATE_IN_SYNC,
status_attr='replica_state')

View File

@ -678,10 +678,11 @@ class BaseSharesTest(test.BaseTestCase):
@classmethod
def create_share_replica(cls, share_id, availability_zone, client=None,
cleanup_in_class=False, cleanup=True):
cleanup_in_class=False, cleanup=True,
version=CONF.share.max_api_microversion):
client = client or cls.shares_v2_client
replica = client.create_share_replica(
share_id, availability_zone=availability_zone)
share_id, availability_zone=availability_zone, version=version)
resource = {
"type": "share_replica",
"id": replica["id"],
@ -699,18 +700,20 @@ class BaseSharesTest(test.BaseTestCase):
return replica
@classmethod
def delete_share_replica(cls, replica_id, client=None):
def delete_share_replica(cls, replica_id, client=None,
version=CONF.share.max_api_microversion):
client = client or cls.shares_v2_client
try:
client.delete_share_replica(replica_id)
client.delete_share_replica(replica_id, version=version)
client.wait_for_resource_deletion(replica_id=replica_id)
except exceptions.NotFound:
pass
@classmethod
def promote_share_replica(cls, replica_id, client=None):
def promote_share_replica(cls, replica_id, client=None,
version=CONF.share.max_api_microversion):
client = client or cls.shares_v2_client
replica = client.promote_share_replica(replica_id)
replica = client.promote_share_replica(replica_id, version=version)
client.wait_for_share_replica_status(
replica["id"],
constants.REPLICATION_STATE_ACTIVE,