Merge "Remove experimental flag from share replication feature"
This commit is contained in:
commit
2e0e081414
|
@ -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'
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue