Add snapshot instances admin CLIs
Support new API entry points for share snapshot instances: - share-snapshot-instance-list - share-snapshot-instance-show - share-snapshot-instance-reset-status Implements: blueprint snapshot-instances Change-Id: Ica1e81012f19926e0f1ba9cd6d8eecc5fbbf40b5
This commit is contained in:
parent
947989952a
commit
5de1ef3844
@ -27,7 +27,7 @@ from manilaclient import utils
|
|||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
MAX_VERSION = '2.17'
|
MAX_VERSION = '2.19'
|
||||||
MIN_VERSION = '2.0'
|
MIN_VERSION = '2.0'
|
||||||
DEPRECATED_VERSION = '1.0'
|
DEPRECATED_VERSION = '1.0'
|
||||||
_VERSIONED_METHOD_MAP = {}
|
_VERSIONED_METHOD_MAP = {}
|
||||||
|
@ -125,6 +125,11 @@ share_opts = [
|
|||||||
default="TESTDOMAIN\\Administrator",
|
default="TESTDOMAIN\\Administrator",
|
||||||
help="Username, that will be used in share access tests for "
|
help="Username, that will be used in share access tests for "
|
||||||
"user type of access."),
|
"user type of access."),
|
||||||
|
cfg.BoolOpt("run_snapshot_tests",
|
||||||
|
default=True,
|
||||||
|
help="Defines whether to run tests that use share snapshots "
|
||||||
|
"or not. Disable this feature if used driver doesn't "
|
||||||
|
"support it."),
|
||||||
]
|
]
|
||||||
|
|
||||||
# 2. Generate config
|
# 2. Generate config
|
||||||
|
@ -105,6 +105,11 @@ class BaseTestCase(base.ClientTestBase):
|
|||||||
res_id, microversion=res["microversion"])
|
res_id, microversion=res["microversion"])
|
||||||
client.wait_for_share_deletion(
|
client.wait_for_share_deletion(
|
||||||
res_id, microversion=res["microversion"])
|
res_id, microversion=res["microversion"])
|
||||||
|
elif res["type"] is "snapshot":
|
||||||
|
client.delete_snapshot(
|
||||||
|
res_id, microversion=res["microversion"])
|
||||||
|
client.wait_for_snapshot_deletion(
|
||||||
|
res_id, microversion=res["microversion"])
|
||||||
else:
|
else:
|
||||||
LOG.warning("Provided unsupported resource type for "
|
LOG.warning("Provided unsupported resource type for "
|
||||||
"cleanup '%s'. Skipping." % res["type"])
|
"cleanup '%s'. Skipping." % res["type"])
|
||||||
@ -277,3 +282,31 @@ class BaseTestCase(base.ClientTestBase):
|
|||||||
else:
|
else:
|
||||||
cls.method_resources.insert(0, resource)
|
cls.method_resources.insert(0, resource)
|
||||||
return ss
|
return ss
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_snapshot(cls, share, name=None, description=None,
|
||||||
|
force=False, client=None, wait_for_creation=True,
|
||||||
|
cleanup_in_class=False, microversion=None):
|
||||||
|
if client is None:
|
||||||
|
client = cls.get_admin_client()
|
||||||
|
data = {
|
||||||
|
'share': share,
|
||||||
|
'name': name,
|
||||||
|
'description': description,
|
||||||
|
'force': force,
|
||||||
|
'microversion': microversion,
|
||||||
|
}
|
||||||
|
snapshot = client.create_snapshot(**data)
|
||||||
|
resource = {
|
||||||
|
"type": "snapshot",
|
||||||
|
"id": snapshot["id"],
|
||||||
|
"client": client,
|
||||||
|
"microversion": microversion,
|
||||||
|
}
|
||||||
|
if cleanup_in_class:
|
||||||
|
cls.class_resources.insert(0, resource)
|
||||||
|
else:
|
||||||
|
cls.method_resources.insert(0, resource)
|
||||||
|
if wait_for_creation:
|
||||||
|
client.wait_for_snapshot_status(snapshot['id'], 'available')
|
||||||
|
return snapshot
|
||||||
|
@ -125,6 +125,8 @@ class ManilaCLIClient(base.CLIClient):
|
|||||||
func = self.is_share_network_deleted
|
func = self.is_share_network_deleted
|
||||||
elif res_type == SHARE:
|
elif res_type == SHARE:
|
||||||
func = self.is_share_deleted
|
func = self.is_share_deleted
|
||||||
|
elif res_type == SNAPSHOT:
|
||||||
|
func = self.is_snapshot_deleted
|
||||||
else:
|
else:
|
||||||
raise exceptions.InvalidResource(message=res_type)
|
raise exceptions.InvalidResource(message=res_type)
|
||||||
|
|
||||||
@ -692,6 +694,112 @@ class ManilaCLIClient(base.CLIClient):
|
|||||||
metadata = output_parser.details(metadata_raw)
|
metadata = output_parser.details(metadata_raw)
|
||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
|
def create_snapshot(self, share, name=None, description=None,
|
||||||
|
force=False, microversion=None):
|
||||||
|
"""Creates a snapshot."""
|
||||||
|
cmd = 'snapshot-create %(share)s ' % {'share': share}
|
||||||
|
if name is None:
|
||||||
|
name = data_utils.rand_name('autotest_snapshot_name')
|
||||||
|
cmd += '--name %s ' % name
|
||||||
|
if description is None:
|
||||||
|
description = data_utils.rand_name('autotest_snapshot_description')
|
||||||
|
cmd += '--description %s ' % description
|
||||||
|
if force:
|
||||||
|
cmd += '--force %s' % force
|
||||||
|
snapshot_raw = self.manila(cmd, microversion=microversion)
|
||||||
|
snapshot = output_parser.details(snapshot_raw)
|
||||||
|
return snapshot
|
||||||
|
|
||||||
|
@not_found_wrapper
|
||||||
|
def get_snapshot(self, snapshot, microversion=None):
|
||||||
|
"""Retrieves a snapshot by its Name or ID."""
|
||||||
|
snapshot_raw = self.manila('snapshot-show %s' % snapshot,
|
||||||
|
microversion=microversion)
|
||||||
|
snapshot = output_parser.details(snapshot_raw)
|
||||||
|
return snapshot
|
||||||
|
|
||||||
|
@not_found_wrapper
|
||||||
|
@forbidden_wrapper
|
||||||
|
def delete_snapshot(self, snapshot, microversion=None):
|
||||||
|
"""Deletes snapshot by Names or IDs."""
|
||||||
|
return self.manila(
|
||||||
|
"snapshot-delete %s" % snapshot, microversion=microversion)
|
||||||
|
|
||||||
|
def list_snapshot_instances(self, snapshot_id=None, columns=None,
|
||||||
|
detailed=None, microversion=None):
|
||||||
|
"""List snapshot instances."""
|
||||||
|
cmd = 'snapshot-instance-list '
|
||||||
|
if snapshot_id:
|
||||||
|
cmd += '--snapshot %s' % snapshot_id
|
||||||
|
if columns is not None:
|
||||||
|
cmd += ' --columns ' + columns
|
||||||
|
if detailed:
|
||||||
|
cmd += ' --detailed True '
|
||||||
|
snapshot_instances_raw = self.manila(cmd, microversion=microversion)
|
||||||
|
snapshot_instances = utils.listing(snapshot_instances_raw)
|
||||||
|
return snapshot_instances
|
||||||
|
|
||||||
|
def get_snapshot_instance(self, id=None, microversion=None):
|
||||||
|
"""Get snapshot instance."""
|
||||||
|
cmd = 'snapshot-instance-show %s ' % id
|
||||||
|
snapshot_instance_raw = self.manila(cmd, microversion=microversion)
|
||||||
|
snapshot_instance = output_parser.details(snapshot_instance_raw)
|
||||||
|
return snapshot_instance
|
||||||
|
|
||||||
|
def reset_snapshot_instance(self, id=None, state=None, microversion=None):
|
||||||
|
"""Reset snapshot instance status."""
|
||||||
|
cmd = 'snapshot-instance-reset-state %s ' % id
|
||||||
|
if state:
|
||||||
|
cmd += '--state %s' % state
|
||||||
|
snapshot_instance_raw = self.manila(cmd, microversion=microversion)
|
||||||
|
snapshot_instance = utils.listing(snapshot_instance_raw)
|
||||||
|
return snapshot_instance
|
||||||
|
|
||||||
|
def is_snapshot_deleted(self, snapshot, microversion=None):
|
||||||
|
"""Indicates whether snapshot is deleted or not.
|
||||||
|
|
||||||
|
:param snapshot: str -- Name or ID of snapshot
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.get_snapshot(snapshot, microversion=microversion)
|
||||||
|
return False
|
||||||
|
except tempest_lib_exc.NotFound:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def wait_for_snapshot_deletion(self, snapshot, microversion=None):
|
||||||
|
"""Wait for snapshot deletion by its Name or ID.
|
||||||
|
|
||||||
|
:param snapshot: str -- Name or ID of snapshot
|
||||||
|
"""
|
||||||
|
self.wait_for_resource_deletion(
|
||||||
|
SNAPSHOT, res_id=snapshot, interval=5, timeout=300,
|
||||||
|
microversion=microversion)
|
||||||
|
|
||||||
|
def wait_for_snapshot_status(self, snapshot, status, microversion=None):
|
||||||
|
"""Waits for a snapshot to reach a given status."""
|
||||||
|
body = self.get_snapshot(snapshot, microversion=microversion)
|
||||||
|
snapshot_name = body['name']
|
||||||
|
snapshot_status = body['status']
|
||||||
|
start = int(time.time())
|
||||||
|
|
||||||
|
while snapshot_status != status:
|
||||||
|
time.sleep(self.build_interval)
|
||||||
|
body = self.get_snapshot(snapshot, microversion=microversion)
|
||||||
|
snapshot_status = body['status']
|
||||||
|
|
||||||
|
if snapshot_status == status:
|
||||||
|
return
|
||||||
|
elif 'error' in snapshot_status.lower():
|
||||||
|
raise exceptions.SnapshotBuildErrorException(snapshot=snapshot)
|
||||||
|
|
||||||
|
if int(time.time()) - start >= self.build_timeout:
|
||||||
|
message = (
|
||||||
|
"Snapshot %(snapshot_name)s failed to reach %(status)s "
|
||||||
|
"status within the required time (%(timeout)s s)." % {
|
||||||
|
"snapshot_name": snapshot_name, "status": status,
|
||||||
|
"timeout": self.build_timeout})
|
||||||
|
raise tempest_lib_exc.TimeoutException(message)
|
||||||
|
|
||||||
@not_found_wrapper
|
@not_found_wrapper
|
||||||
def list_access(self, share_id, columns=None, microversion=None):
|
def list_access(self, share_id, columns=None, microversion=None):
|
||||||
"""Returns list of access rules for a share.
|
"""Returns list of access rules for a share.
|
||||||
|
@ -33,7 +33,7 @@ class InvalidData(exceptions.TempestException):
|
|||||||
|
|
||||||
|
|
||||||
class ShareTypeNotFound(exceptions.NotFound):
|
class ShareTypeNotFound(exceptions.NotFound):
|
||||||
message = "Share type '%(share_type)s' was not found"
|
message = "Share type '%(share_type)s' was not found."
|
||||||
|
|
||||||
|
|
||||||
class InvalidConfiguration(exceptions.TempestException):
|
class InvalidConfiguration(exceptions.TempestException):
|
||||||
@ -41,7 +41,11 @@ class InvalidConfiguration(exceptions.TempestException):
|
|||||||
|
|
||||||
|
|
||||||
class ShareBuildErrorException(exceptions.TempestException):
|
class ShareBuildErrorException(exceptions.TempestException):
|
||||||
message = "Share %(share)s failed to build and is in ERROR status"
|
message = "Share %(share)s failed to build and is in ERROR status."
|
||||||
|
|
||||||
|
|
||||||
|
class SnapshotBuildErrorException(exceptions.TempestException):
|
||||||
|
message = "Snapshot %(snapshot)s failed to build and is in ERROR status."
|
||||||
|
|
||||||
|
|
||||||
class AccessRuleCreateErrorException(exceptions.TempestException):
|
class AccessRuleCreateErrorException(exceptions.TempestException):
|
||||||
|
122
manilaclient/tests/functional/test_snapshot_instances.py
Normal file
122
manilaclient/tests/functional/test_snapshot_instances.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
# Copyright 2016 Huawei inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 ddt
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
import testtools
|
||||||
|
|
||||||
|
from manilaclient import config
|
||||||
|
from manilaclient.tests.functional import base
|
||||||
|
from manilaclient.tests.functional import utils
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
@ddt.ddt
|
||||||
|
@testtools.skipUnless(CONF.run_snapshot_tests,
|
||||||
|
'Snapshot tests disabled.')
|
||||||
|
@utils.skip_if_microversion_not_supported('2.19')
|
||||||
|
class SnapshotInstancesTest(base.BaseTestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(SnapshotInstancesTest, cls).setUpClass()
|
||||||
|
cls.share = cls.create_share(
|
||||||
|
client=cls.get_user_client(),
|
||||||
|
cleanup_in_class=True)
|
||||||
|
cls.snapshot = cls.create_snapshot(share=cls.share['id'],
|
||||||
|
client=cls.get_user_client(),
|
||||||
|
cleanup_in_class=True)
|
||||||
|
|
||||||
|
def test_list_all_snapshot_instances(self):
|
||||||
|
snapshot_instances = self.admin_client.list_snapshot_instances()
|
||||||
|
|
||||||
|
self.assertTrue(len(snapshot_instances) > 0)
|
||||||
|
expected_keys = ('ID', 'Snapshot ID', 'Status')
|
||||||
|
for si in snapshot_instances:
|
||||||
|
for key in expected_keys:
|
||||||
|
self.assertIn(key, si)
|
||||||
|
self.assertTrue(uuidutils.is_uuid_like(si['ID']))
|
||||||
|
self.assertTrue(uuidutils.is_uuid_like(si['Snapshot ID']))
|
||||||
|
|
||||||
|
def test_list_all_snapshot_instances_details(self):
|
||||||
|
snapshot_instances = self.admin_client.list_snapshot_instances(
|
||||||
|
detailed=True)
|
||||||
|
|
||||||
|
self.assertTrue(len(snapshot_instances) > 0)
|
||||||
|
expected_keys = ('ID', 'Snapshot ID', 'Status', 'Created_at',
|
||||||
|
'Updated_at', 'Share_id', 'Share_instance_id',
|
||||||
|
'Progress', 'Provider_location')
|
||||||
|
for si in snapshot_instances:
|
||||||
|
for key in expected_keys:
|
||||||
|
self.assertIn(key, si)
|
||||||
|
for key in ('ID', 'Snapshot ID', 'Share_id', 'Share_instance_id'):
|
||||||
|
self.assertTrue(
|
||||||
|
uuidutils.is_uuid_like(si[key]))
|
||||||
|
|
||||||
|
def test_list_snapshot_instance_with_snapshot(self):
|
||||||
|
snapshot_instances = self.admin_client.list_snapshot_instances(
|
||||||
|
snapshot_id=self.snapshot['id'])
|
||||||
|
|
||||||
|
self.assertEqual(1, len(snapshot_instances))
|
||||||
|
expected_keys = ('ID', 'Snapshot ID', 'Status')
|
||||||
|
for si in snapshot_instances:
|
||||||
|
for key in expected_keys:
|
||||||
|
self.assertIn(key, si)
|
||||||
|
self.assertTrue(uuidutils.is_uuid_like(si['ID']))
|
||||||
|
self.assertTrue(uuidutils.is_uuid_like(si['Snapshot ID']))
|
||||||
|
|
||||||
|
def test_list_snapshot_instance_with_columns(self):
|
||||||
|
snapshot_instances = self.admin_client.list_snapshot_instances(
|
||||||
|
self.snapshot['id'], columns='id,status')
|
||||||
|
|
||||||
|
self.assertTrue(len(snapshot_instances) > 0)
|
||||||
|
expected_keys = ('Id', 'Status')
|
||||||
|
unexpected_keys = ('Snapshot ID', )
|
||||||
|
for si in snapshot_instances:
|
||||||
|
for key in expected_keys:
|
||||||
|
self.assertIn(key, si)
|
||||||
|
for key in unexpected_keys:
|
||||||
|
self.assertNotIn(key, si)
|
||||||
|
self.assertTrue(uuidutils.is_uuid_like(si['Id']))
|
||||||
|
|
||||||
|
def test_get_snapshot_instance(self):
|
||||||
|
snapshot_instances = self.admin_client.list_snapshot_instances(
|
||||||
|
self.snapshot['id'])
|
||||||
|
|
||||||
|
snapshot_instance = self.admin_client.get_snapshot_instance(
|
||||||
|
snapshot_instances[0]['ID'])
|
||||||
|
self.assertTrue(len(snapshot_instance) > 0)
|
||||||
|
expected_keys = ('id', 'snapshot_id', 'status', 'created_at',
|
||||||
|
'updated_at', 'share_id', 'share_instance_id',
|
||||||
|
'progress', 'provider_location')
|
||||||
|
|
||||||
|
for key in expected_keys:
|
||||||
|
self.assertIn(key, snapshot_instance)
|
||||||
|
for key in ('id', 'snapshot_id', 'share_id', 'share_instance_id'):
|
||||||
|
self.assertTrue(
|
||||||
|
uuidutils.is_uuid_like(snapshot_instance[key]))
|
||||||
|
|
||||||
|
def test_snapshot_instance_reset_state(self):
|
||||||
|
snapshot_instances = self.admin_client.list_snapshot_instances(
|
||||||
|
self.snapshot['id'])
|
||||||
|
self.admin_client.reset_snapshot_instance(
|
||||||
|
snapshot_instances[0]['ID'], 'error')
|
||||||
|
snapshot_instance = self.admin_client.get_snapshot_instance(
|
||||||
|
snapshot_instances[0]['ID'])
|
||||||
|
|
||||||
|
self.assertEqual('error', snapshot_instance['status'])
|
||||||
|
self.admin_client.reset_snapshot_instance(snapshot_instance['id'],
|
||||||
|
'available')
|
@ -749,6 +749,53 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
|
|||||||
|
|
||||||
get_types_3_share_type_access = get_types_3_os_share_type_access
|
get_types_3_share_type_access = get_types_3_os_share_type_access
|
||||||
|
|
||||||
|
fake_snapshot_instance = {
|
||||||
|
"id": "1234",
|
||||||
|
"snapshot_id": "5678",
|
||||||
|
"status": "error",
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_snapshot_instances(self, **kw):
|
||||||
|
instances = {
|
||||||
|
'snapshot_instances': [
|
||||||
|
self.fake_snapshot_instance,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return (200, {}, instances)
|
||||||
|
|
||||||
|
def get_snapshot_instances_detail(self, **kw):
|
||||||
|
instances = {
|
||||||
|
'snapshot_instances': [
|
||||||
|
{
|
||||||
|
'id': '1234',
|
||||||
|
'snapshot_id': '5679',
|
||||||
|
'created_at': 'fake',
|
||||||
|
'updated_at': 'fake',
|
||||||
|
'status': 'fake',
|
||||||
|
'share_id': 'fake',
|
||||||
|
'share_instance_id': 'fake',
|
||||||
|
'progress': 'fake',
|
||||||
|
'provider_location': 'fake',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return (200, {}, instances)
|
||||||
|
|
||||||
|
def get_snapshot_instances_1234(self, **kw):
|
||||||
|
instances = {'snapshot_instance': self.fake_snapshot_instance}
|
||||||
|
return (200, {}, instances)
|
||||||
|
|
||||||
|
def post_snapshot_instances_1234_action(self, body, **kw):
|
||||||
|
_body = None
|
||||||
|
resp = 202
|
||||||
|
assert len(list(body)) == 1
|
||||||
|
action = list(body)[0]
|
||||||
|
if action == 'reset_status':
|
||||||
|
assert 'status' in body.get(action)
|
||||||
|
else:
|
||||||
|
raise AssertionError("Unexpected share action: %s" % action)
|
||||||
|
return (resp, {}, _body)
|
||||||
|
|
||||||
|
|
||||||
def fake_create(url, body, response_key):
|
def fake_create(url, body, response_key):
|
||||||
return {'url': url, 'body': body, 'resp_key': response_key}
|
return {'url': url, 'body': body, 'resp_key': response_key}
|
||||||
|
95
manilaclient/tests/unit/v2/test_share_snapshot_instances.py
Normal file
95
manilaclient/tests/unit/v2/test_share_snapshot_instances.py
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# Copyright 2016 Huawei inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 ddt
|
||||||
|
import mock
|
||||||
|
|
||||||
|
from manilaclient import api_versions
|
||||||
|
from manilaclient import exceptions
|
||||||
|
from manilaclient import extension
|
||||||
|
from manilaclient.tests.unit import utils
|
||||||
|
from manilaclient.tests.unit.v2 import fakes
|
||||||
|
from manilaclient.v2 import share_snapshot_instances
|
||||||
|
|
||||||
|
|
||||||
|
extensions = [
|
||||||
|
extension.Extension('share_snapshot_instances', share_snapshot_instances),
|
||||||
|
]
|
||||||
|
cs = fakes.FakeClient(extensions=extensions)
|
||||||
|
|
||||||
|
|
||||||
|
@ddt.ddt
|
||||||
|
class SnapshotInstancesTest(utils.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(SnapshotInstancesTest, self).setUp()
|
||||||
|
microversion = api_versions.APIVersion("2.19")
|
||||||
|
mock_microversion = mock.Mock(api_version=microversion)
|
||||||
|
self.manager = share_snapshot_instances.ShareSnapshotInstanceManager(
|
||||||
|
api=mock_microversion)
|
||||||
|
|
||||||
|
@ddt.data(True, False)
|
||||||
|
def test_list(self, detailed):
|
||||||
|
if detailed:
|
||||||
|
url = '/snapshot-instances/detail'
|
||||||
|
else:
|
||||||
|
url = '/snapshot-instances'
|
||||||
|
self.mock_object(self.manager, '_list', mock.Mock())
|
||||||
|
self.manager.list(detailed=detailed)
|
||||||
|
self.manager._list.assert_called_once_with(url, 'snapshot_instances')
|
||||||
|
|
||||||
|
@ddt.data(True, False)
|
||||||
|
def test_list_with_snapshot(self, detailed):
|
||||||
|
if detailed:
|
||||||
|
url = '/snapshot-instances/detail'
|
||||||
|
else:
|
||||||
|
url = '/snapshot-instances'
|
||||||
|
self.mock_object(self.manager, '_list', mock.Mock())
|
||||||
|
self.manager.list(detailed=detailed, snapshot='snapshot_id')
|
||||||
|
self.manager._list.assert_called_once_with(
|
||||||
|
(url + '?snapshot_id=snapshot_id'), 'snapshot_instances',)
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
self.mock_object(self.manager, '_get', mock.Mock())
|
||||||
|
self.manager.get('fake_snapshot_instance')
|
||||||
|
self.manager._get.assert_called_once_with(
|
||||||
|
'/snapshot-instances/' + 'fake_snapshot_instance',
|
||||||
|
'snapshot_instance')
|
||||||
|
|
||||||
|
def test_reset_instance_state(self):
|
||||||
|
state = 'available'
|
||||||
|
|
||||||
|
self.mock_object(self.manager, '_action', mock.Mock())
|
||||||
|
self.manager.reset_state('fake_instance', state)
|
||||||
|
self.manager._action.assert_called_once_with(
|
||||||
|
"reset_status", 'fake_instance', {"status": state})
|
||||||
|
|
||||||
|
@ddt.data('get', 'list', 'reset_state')
|
||||||
|
def test_upsupported_microversion(self, method_name):
|
||||||
|
unsupported_microversions = ('1.0', '2.18')
|
||||||
|
arguments = {
|
||||||
|
'instance': 'FAKE_INSTANCE',
|
||||||
|
}
|
||||||
|
if method_name in ('list'):
|
||||||
|
arguments.clear()
|
||||||
|
|
||||||
|
for microversion in unsupported_microversions:
|
||||||
|
microversion = api_versions.APIVersion(microversion)
|
||||||
|
mock_microversion = mock.Mock(api_version=microversion)
|
||||||
|
manager = share_snapshot_instances.ShareSnapshotInstanceManager(
|
||||||
|
api=mock_microversion)
|
||||||
|
method = getattr(manager, method_name)
|
||||||
|
self.assertRaises(exceptions.UnsupportedVersion,
|
||||||
|
method, **arguments)
|
@ -1826,3 +1826,39 @@ class ShellTest(test_utils.TestCase):
|
|||||||
self.assert_called(
|
self.assert_called(
|
||||||
'POST', '/share-replicas/1234/action',
|
'POST', '/share-replicas/1234/action',
|
||||||
body={action_name: {attr: 'xyzzyspoon!'}})
|
body={action_name: {attr: 'xyzzyspoon!'}})
|
||||||
|
|
||||||
|
def test_snapshot_instance_list_all(self):
|
||||||
|
self.run_command('snapshot-instance-list')
|
||||||
|
self.assert_called('GET', '/snapshot-instances')
|
||||||
|
|
||||||
|
def test_snapshot_instance_list_all_detail(self):
|
||||||
|
self.run_command('snapshot-instance-list --detail True')
|
||||||
|
self.assert_called('GET', '/snapshot-instances/detail')
|
||||||
|
|
||||||
|
@mock.patch.object(cliutils, 'print_list', mock.Mock())
|
||||||
|
def test_snapshot_instance_list_select_column(self):
|
||||||
|
self.run_command('snapshot-instance-list --columns id,status')
|
||||||
|
self.assert_called('GET', '/snapshot-instances')
|
||||||
|
cliutils.print_list.assert_called_once_with(
|
||||||
|
mock.ANY, ['Id', 'Status'])
|
||||||
|
|
||||||
|
@mock.patch.object(shell_v2, '_find_share_snapshot', mock.Mock())
|
||||||
|
def test_snapshot_instance_list_for_snapshot(self):
|
||||||
|
fsnapshot = type('FakeSansphot', (object,),
|
||||||
|
{'id': 'fake-snapshot-id'})
|
||||||
|
shell_v2._find_share_snapshot.return_value = fsnapshot
|
||||||
|
cmd = 'snapshot-instance-list --snapshot %s'
|
||||||
|
self.run_command(cmd % fsnapshot.id)
|
||||||
|
|
||||||
|
self.assert_called(
|
||||||
|
'GET', '/snapshot-instances?snapshot_id=fake-snapshot-id')
|
||||||
|
|
||||||
|
def test_snapshot_instance_show(self):
|
||||||
|
self.run_command('snapshot-instance-show 1234')
|
||||||
|
self.assert_called('GET', '/snapshot-instances/1234')
|
||||||
|
|
||||||
|
def test_snapshot_instance_reset_state(self):
|
||||||
|
self.run_command('snapshot-instance-reset-state 1234')
|
||||||
|
expected = {'reset_status': {'status': 'available'}}
|
||||||
|
self.assert_called('POST', '/snapshot-instances/1234/action',
|
||||||
|
body=expected)
|
||||||
|
@ -36,6 +36,7 @@ from manilaclient.v2 import share_instances
|
|||||||
from manilaclient.v2 import share_networks
|
from manilaclient.v2 import share_networks
|
||||||
from manilaclient.v2 import share_replicas
|
from manilaclient.v2 import share_replicas
|
||||||
from manilaclient.v2 import share_servers
|
from manilaclient.v2 import share_servers
|
||||||
|
from manilaclient.v2 import share_snapshot_instances
|
||||||
from manilaclient.v2 import share_snapshots
|
from manilaclient.v2 import share_snapshots
|
||||||
from manilaclient.v2 import share_type_access
|
from manilaclient.v2 import share_type_access
|
||||||
from manilaclient.v2 import share_types
|
from manilaclient.v2 import share_types
|
||||||
@ -220,6 +221,8 @@ class Client(object):
|
|||||||
share_instance_export_locations.ShareInstanceExportLocationManager(
|
share_instance_export_locations.ShareInstanceExportLocationManager(
|
||||||
self))
|
self))
|
||||||
self.share_snapshots = share_snapshots.ShareSnapshotManager(self)
|
self.share_snapshots = share_snapshots.ShareSnapshotManager(self)
|
||||||
|
self.share_snapshot_instances = (
|
||||||
|
share_snapshot_instances.ShareSnapshotInstanceManager(self))
|
||||||
|
|
||||||
self.share_types = share_types.ShareTypeManager(self)
|
self.share_types = share_types.ShareTypeManager(self)
|
||||||
self.share_type_access = share_type_access.ShareTypeAccessManager(self)
|
self.share_type_access = share_type_access.ShareTypeAccessManager(self)
|
||||||
|
74
manilaclient/v2/share_snapshot_instances.py
Normal file
74
manilaclient/v2/share_snapshot_instances.py
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# Copyright 2016 Huawei inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 manilaclient import api_versions
|
||||||
|
from manilaclient import base
|
||||||
|
from manilaclient.openstack.common.apiclient import base as common_base
|
||||||
|
|
||||||
|
|
||||||
|
class ShareSnapshotInstance(common_base.Resource):
|
||||||
|
"""A snapshot instance is an instance of a snapshot."""
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<SnapshotInstance: %s>" % self.id
|
||||||
|
|
||||||
|
def reset_state(self, state):
|
||||||
|
"""Update snapshot instance's 'status' attr."""
|
||||||
|
self.manager.reset_state(self, state)
|
||||||
|
|
||||||
|
|
||||||
|
class ShareSnapshotInstanceManager(base.ManagerWithFind):
|
||||||
|
"""Manage :class:`SnapshotInstances` resources."""
|
||||||
|
resource_class = ShareSnapshotInstance
|
||||||
|
|
||||||
|
@api_versions.wraps("2.19")
|
||||||
|
def get(self, instance):
|
||||||
|
"""Get a snapshot instance.
|
||||||
|
|
||||||
|
:param instance: either snapshot instance object or text with its ID.
|
||||||
|
:rtype: :class:`ShareSnapshotInstance`
|
||||||
|
"""
|
||||||
|
snapshot_instance_id = common_base.getid(instance)
|
||||||
|
return self._get("/snapshot-instances/%s" % snapshot_instance_id,
|
||||||
|
"snapshot_instance")
|
||||||
|
|
||||||
|
@api_versions.wraps("2.19")
|
||||||
|
def list(self, detailed=False, snapshot=None):
|
||||||
|
"""List all snapshot instances."""
|
||||||
|
if detailed:
|
||||||
|
url = '/snapshot-instances/detail'
|
||||||
|
else:
|
||||||
|
url = '/snapshot-instances'
|
||||||
|
|
||||||
|
if snapshot:
|
||||||
|
url += '?snapshot_id=%s' % common_base.getid(snapshot)
|
||||||
|
return self._list(url, 'snapshot_instances')
|
||||||
|
|
||||||
|
@api_versions.wraps("2.19")
|
||||||
|
def reset_state(self, instance, state):
|
||||||
|
"""Reset the 'status' attr of the snapshot instance.
|
||||||
|
|
||||||
|
:param instance: either snapshot instance object or its UUID.
|
||||||
|
:param state: state to set the snapshot instance's 'status' attr to.
|
||||||
|
"""
|
||||||
|
return self._action("reset_status", instance, {"status": state})
|
||||||
|
|
||||||
|
def _action(self, action, instance, info=None, **kwargs):
|
||||||
|
"""Perform a snapshot instance 'action'."""
|
||||||
|
body = {action: info}
|
||||||
|
self.run_hooks('modify_body_for_action', body, **kwargs)
|
||||||
|
url = ('/snapshot-instances/%s/action' %
|
||||||
|
common_base.getid(instance))
|
||||||
|
return self.api.client.post(url, body=body)
|
@ -220,6 +220,12 @@ def _print_share_snapshot(cs, snapshot):
|
|||||||
cliutils.print_dict(info)
|
cliutils.print_dict(info)
|
||||||
|
|
||||||
|
|
||||||
|
def _find_share_snapshot_instance(cs, snapshot_instance):
|
||||||
|
"""Get a share snapshot instance by ID."""
|
||||||
|
return apiclient_utils.find_resource(
|
||||||
|
cs.share_snapshot_instances, snapshot_instance)
|
||||||
|
|
||||||
|
|
||||||
def _find_share_network(cs, share_network):
|
def _find_share_network(cs, share_network):
|
||||||
"""Get a share network by ID or name."""
|
"""Get a share network by ID or name."""
|
||||||
return apiclient_utils.find_resource(cs.share_networks, share_network)
|
return apiclient_utils.find_resource(cs.share_networks, share_network)
|
||||||
@ -1742,6 +1748,76 @@ def do_snapshot_reset_state(cs, args):
|
|||||||
snapshot.reset_state(args.state)
|
snapshot.reset_state(args.state)
|
||||||
|
|
||||||
|
|
||||||
|
@api_versions.wraps("2.19")
|
||||||
|
@cliutils.arg(
|
||||||
|
'--snapshot',
|
||||||
|
metavar='<snapshot>',
|
||||||
|
default=None,
|
||||||
|
help='Filter results by share snapshot ID.')
|
||||||
|
@cliutils.arg(
|
||||||
|
'--columns',
|
||||||
|
metavar='<columns>',
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help='Comma separated list of columns to be displayed '
|
||||||
|
'e.g. --columns "id"')
|
||||||
|
@cliutils.arg(
|
||||||
|
'--detailed',
|
||||||
|
metavar='<detailed>',
|
||||||
|
default=False,
|
||||||
|
help='Show detailed information about snapshot instances.'
|
||||||
|
' (Default=False)')
|
||||||
|
def do_snapshot_instance_list(cs, args):
|
||||||
|
"""List share snapshot instances."""
|
||||||
|
snapshot = (_find_share_snapshot(cs, args.snapshot)
|
||||||
|
if args.snapshot else None)
|
||||||
|
if args.columns is not None:
|
||||||
|
list_of_keys = _split_columns(columns=args.columns)
|
||||||
|
elif args.detailed:
|
||||||
|
list_of_keys = ['ID', 'Snapshot ID', 'Status', 'Created_at',
|
||||||
|
'Updated_at', 'Share_id', 'Share_instance_id',
|
||||||
|
'Progress', 'Provider_location']
|
||||||
|
else:
|
||||||
|
list_of_keys = ['ID', 'Snapshot ID', 'Status']
|
||||||
|
|
||||||
|
instances = cs.share_snapshot_instances.list(
|
||||||
|
detailed=args.detailed, snapshot=snapshot)
|
||||||
|
|
||||||
|
cliutils.print_list(instances, list_of_keys)
|
||||||
|
|
||||||
|
|
||||||
|
@api_versions.wraps("2.19")
|
||||||
|
@cliutils.arg(
|
||||||
|
'snapshot_instance',
|
||||||
|
metavar='<snapshot_instance>',
|
||||||
|
help='ID of the share snapshot instance.')
|
||||||
|
def do_snapshot_instance_show(cs, args):
|
||||||
|
"""Show details about a share snapshot instance."""
|
||||||
|
snapshot_instance = _find_share_snapshot_instance(
|
||||||
|
cs, args.snapshot_instance)
|
||||||
|
cliutils.print_dict(snapshot_instance._info)
|
||||||
|
|
||||||
|
|
||||||
|
@cliutils.arg(
|
||||||
|
'snapshot_instance',
|
||||||
|
metavar='<snapshot_instance>',
|
||||||
|
help='ID of the snapshot instance to modify.')
|
||||||
|
@cliutils.arg(
|
||||||
|
'--state',
|
||||||
|
metavar='<state>',
|
||||||
|
default='available',
|
||||||
|
help=('Indicate which state to assign the snapshot instance. '
|
||||||
|
'Options include available, error, creating, deleting, '
|
||||||
|
'error_deleting. If no state is provided, available '
|
||||||
|
'will be used.'))
|
||||||
|
@api_versions.wraps("2.19")
|
||||||
|
def do_snapshot_instance_reset_state(cs, args):
|
||||||
|
"""Explicitly update the state of a share snapshot instance."""
|
||||||
|
snapshot_instance = _find_share_snapshot_instance(
|
||||||
|
cs, args.snapshot_instance)
|
||||||
|
snapshot_instance.reset_state(args.state)
|
||||||
|
|
||||||
|
|
||||||
@cliutils.arg(
|
@cliutils.arg(
|
||||||
'share',
|
'share',
|
||||||
metavar='<share>',
|
metavar='<share>',
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Add list, show, and reset-status admin commands for snapshot instances.
|
Loading…
Reference in New Issue
Block a user