Merge "Cinder client reset-state improvements"
This commit is contained in:
commit
0470ec8c51
@ -56,6 +56,20 @@ def _stub_group_snapshot(detailed=True, **kwargs):
|
||||
return group_snapshot
|
||||
|
||||
|
||||
def _stub_snapshot(**kwargs):
|
||||
snapshot = {
|
||||
"created_at": "2012-08-28T16:30:31.000000",
|
||||
"display_description": None,
|
||||
"display_name": None,
|
||||
"id": '11111111-1111-1111-1111-111111111111',
|
||||
"size": 1,
|
||||
"status": "available",
|
||||
"volume_id": '00000000-0000-0000-0000-000000000000',
|
||||
}
|
||||
snapshot.update(kwargs)
|
||||
return snapshot
|
||||
|
||||
|
||||
class FakeClient(fakes.FakeClient, client.Client):
|
||||
|
||||
def __init__(self, api_version=None, *args, **kwargs):
|
||||
@ -399,6 +413,37 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient):
|
||||
def put_group_snapshots_1234(self, **kw):
|
||||
return (200, {}, {'group_snapshot': {}})
|
||||
|
||||
def post_groups_1234_action(self, **kw):
|
||||
return (202, {}, {})
|
||||
|
||||
def get_groups_5678(self, **kw):
|
||||
return (200, {}, {'group':
|
||||
_stub_group(id='5678')})
|
||||
|
||||
def post_groups_5678_action(self, **kw):
|
||||
return (202, {}, {})
|
||||
|
||||
def post_snapshots_1234_action(self, **kw):
|
||||
return (202, {}, {})
|
||||
|
||||
def get_snapshots_1234(self, **kw):
|
||||
return (200, {}, {'snapshot': _stub_snapshot(id='1234')})
|
||||
|
||||
def post_snapshots_5678_action(self, **kw):
|
||||
return (202, {}, {})
|
||||
|
||||
def get_snapshots_5678(self, **kw):
|
||||
return (200, {}, {'snapshot': _stub_snapshot(id='5678')})
|
||||
|
||||
def post_group_snapshots_1234_action(self, **kw):
|
||||
return (202, {}, {})
|
||||
|
||||
def post_group_snapshots_5678_action(self, **kw):
|
||||
return (202, {}, {})
|
||||
|
||||
def get_group_snapshots_5678(self, **kw):
|
||||
return (200, {}, {'group_snapshot': _stub_group_snapshot(id='5678')})
|
||||
|
||||
def delete_group_snapshots_1234(self, **kw):
|
||||
return (202, {}, {})
|
||||
|
||||
|
@ -454,6 +454,80 @@ class ShellTest(utils.TestCase):
|
||||
self.run_command('--os-volume-api-version 3.3 message-list')
|
||||
self.assert_called('GET', '/messages')
|
||||
|
||||
@ddt.data('volume', 'backup', 'snapshot', None)
|
||||
def test_reset_state_entity_not_found(self, entity_type):
|
||||
cmd = 'reset-state 999999'
|
||||
if entity_type is not None:
|
||||
cmd += ' --type %s' % entity_type
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
@ddt.data({'entity_types': [{'name': 'volume', 'version': '3.0',
|
||||
'command': 'os-reset_status'},
|
||||
{'name': 'backup', 'version': '3.0',
|
||||
'command': 'os-reset_status'},
|
||||
{'name': 'snapshot', 'version': '3.0',
|
||||
'command': 'os-reset_status'},
|
||||
{'name': None, 'version': '3.0',
|
||||
'command': 'os-reset_status'},
|
||||
{'name': 'group', 'version': '3.20',
|
||||
'command': 'reset_status'},
|
||||
{'name': 'group-snapshot', 'version': '3.19',
|
||||
'command': 'reset_status'}],
|
||||
'r_id': ['1234'],
|
||||
'states': ['available', 'error', None]},
|
||||
{'entity_types': [{'name': 'volume', 'version': '3.0',
|
||||
'command': 'os-reset_status'},
|
||||
{'name': 'backup', 'version': '3.0',
|
||||
'command': 'os-reset_status'},
|
||||
{'name': 'snapshot', 'version': '3.0',
|
||||
'command': 'os-reset_status'},
|
||||
{'name': None, 'version': '3.0',
|
||||
'command': 'os-reset_status'},
|
||||
{'name': 'group', 'version': '3.20',
|
||||
'command': 'reset_status'},
|
||||
{'name': 'group-snapshot', 'version': '3.19',
|
||||
'command': 'reset_status'}],
|
||||
'r_id': ['1234', '5678'],
|
||||
'states': ['available', 'error', None]})
|
||||
@ddt.unpack
|
||||
def test_reset_state_normal(self, entity_types, r_id, states):
|
||||
for state in states:
|
||||
for t in entity_types:
|
||||
if state is None:
|
||||
expected = {t['command']: {}}
|
||||
cmd = ('--os-volume-api-version '
|
||||
'%s reset-state %s') % (t['version'],
|
||||
' '.join(r_id))
|
||||
else:
|
||||
expected = {t['command']: {'status': state}}
|
||||
cmd = ('--os-volume-api-version '
|
||||
'%s reset-state '
|
||||
'--state %s %s') % (t['version'],
|
||||
state, ' '.join(r_id))
|
||||
if t['name'] is not None:
|
||||
cmd += ' --type %s' % t['name']
|
||||
|
||||
self.run_command(cmd)
|
||||
|
||||
name = t['name'] if t['name'] else 'volume'
|
||||
for re in r_id:
|
||||
self.assert_called_anytime('POST', '/%ss/%s/action'
|
||||
% (name.replace('-', '_'), re),
|
||||
body=expected)
|
||||
|
||||
@ddt.data({'command': '--attach-status detached',
|
||||
'expected': {'attach_status': 'detached'}},
|
||||
{'command': '--state in-use --attach-status attached',
|
||||
'expected': {'status': 'in-use',
|
||||
'attach_status': 'attached'}},
|
||||
{'command': '--reset-migration-status',
|
||||
'expected': {'migration_status': 'none'}})
|
||||
@ddt.unpack
|
||||
def test_reset_state_volume_additional_status(self, command, expected):
|
||||
self.run_command('reset-state %s 1234' % command)
|
||||
expected = {'os-reset_status': expected}
|
||||
self.assert_called('POST', '/volumes/1234/action', body=expected)
|
||||
|
||||
def test_snapshot_list_with_metadata(self):
|
||||
self.run_command('--os-volume-api-version 3.22 '
|
||||
'snapshot-list --metadata key1=val1')
|
||||
|
@ -99,7 +99,8 @@ class VolumeBackupManager(base.ManagerWithFind):
|
||||
|
||||
def reset_state(self, backup, state):
|
||||
"""Update the specified volume backup with the provided state."""
|
||||
return self._action('os-reset_status', backup, {'status': state})
|
||||
return self._action('os-reset_status', backup,
|
||||
{'status': state} if state else {})
|
||||
|
||||
def _action(self, action, backup, info=None, **kwargs):
|
||||
"""Perform a volume backup action."""
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
|
||||
from cinderclient.apiclient import base as common_base
|
||||
from cinderclient import api_versions
|
||||
from cinderclient import base
|
||||
from cinderclient import utils
|
||||
|
||||
@ -34,6 +35,10 @@ class GroupSnapshot(base.Resource):
|
||||
"""Update the name or description for this group snapshot."""
|
||||
return self.manager.update(self, **kwargs)
|
||||
|
||||
def reset_state(self, state):
|
||||
"""Reset the group snapshot's state with specified one."""
|
||||
return self.manager.reset_state(self, state)
|
||||
|
||||
|
||||
class GroupSnapshotManager(base.ManagerWithFind):
|
||||
"""Manage :class:`GroupSnapshot` resources."""
|
||||
@ -74,6 +79,16 @@ class GroupSnapshotManager(base.ManagerWithFind):
|
||||
return self._get("/group_snapshots/%s" % group_snapshot_id,
|
||||
"group_snapshot")
|
||||
|
||||
@api_versions.wraps('3.19')
|
||||
def reset_state(self, group_snapshot, state):
|
||||
"""Update the provided group snapshot with the provided state.
|
||||
|
||||
:param group_snapshot: The :class:`GroupSnapshot` to set the state.
|
||||
:param state: The state of the group snapshot to be set.
|
||||
"""
|
||||
body = {'status': state} if state else {}
|
||||
return self._action('reset_status', group_snapshot, body)
|
||||
|
||||
def list(self, detailed=True, search_opts=None):
|
||||
"""Lists all group snapshots.
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
"""Group interface (v3 extension)."""
|
||||
|
||||
from cinderclient import api_versions
|
||||
from cinderclient import base
|
||||
from cinderclient.apiclient import base as common_base
|
||||
from cinderclient import utils
|
||||
@ -33,6 +34,10 @@ class Group(base.Resource):
|
||||
"""Update the name or description for this group."""
|
||||
return self.manager.update(self, **kwargs)
|
||||
|
||||
def reset_state(self, state):
|
||||
"""Reset the group's state with specified one"""
|
||||
return self.manager.reset_state(self, state)
|
||||
|
||||
|
||||
class GroupManager(base.ManagerWithFind):
|
||||
"""Manage :class:`Group` resources."""
|
||||
@ -64,6 +69,16 @@ class GroupManager(base.ManagerWithFind):
|
||||
|
||||
return self._create('/groups', body, 'group')
|
||||
|
||||
@api_versions.wraps('3.20')
|
||||
def reset_state(self, group, state):
|
||||
"""Update the provided group with the provided state.
|
||||
|
||||
:param group: The :class:`Group` to set the state.
|
||||
:param state: The state of the group to be set.
|
||||
"""
|
||||
body = {'status': state} if state else {}
|
||||
return self._action('reset_status', group, body)
|
||||
|
||||
def create_from_src(self, group_snapshot_id, source_group_id,
|
||||
name=None, description=None, user_id=None,
|
||||
project_id=None):
|
||||
|
@ -33,6 +33,13 @@ from cinderclient import utils
|
||||
from cinderclient.v2.shell import * # flake8: noqa
|
||||
|
||||
|
||||
RESET_STATE_RESOURCES = {'volume': utils.find_volume,
|
||||
'backup': shell_utils.find_backup,
|
||||
'snapshot': shell_utils.find_volume_snapshot,
|
||||
'group': shell_utils.find_group,
|
||||
'group-snapshot': shell_utils.find_group_snapshot}
|
||||
|
||||
|
||||
@utils.arg('--group_id',
|
||||
metavar='<group_id>',
|
||||
default=None,
|
||||
@ -194,6 +201,63 @@ def do_list(cs, args):
|
||||
sortby_index=sortby_index)
|
||||
|
||||
|
||||
@utils.arg('entity', metavar='<entity>', nargs='+',
|
||||
help='Name or ID of entity to update.')
|
||||
@utils.arg('--type', metavar='<type>', default='volume',
|
||||
choices=RESET_STATE_RESOURCES.keys(),
|
||||
help="Type of entity to update. Available resources "
|
||||
"are: 'volume', 'snapshot', 'backup', "
|
||||
"'group' (since 3.20) and "
|
||||
"'group-snapshot' (since 3.19), Default=volume.")
|
||||
@utils.arg('--state', metavar='<state>', default=None,
|
||||
help=("The state to assign to the entity. "
|
||||
"NOTE: This command simply changes the state of the "
|
||||
"entity in the database with no regard to actual status, "
|
||||
"exercise caution when using. Default=None, that means the "
|
||||
"state is unchanged."))
|
||||
@utils.arg('--attach-status', metavar='<attach-status>', default=None,
|
||||
help=('This only used in volume entity. The attach status to '
|
||||
'assign to the volume in the DataBase, with no regard to '
|
||||
'the actual status. Valid values are "attached" and '
|
||||
'"detached". Default=None, that means the status '
|
||||
'is unchanged.'))
|
||||
@utils.arg('--reset-migration-status',
|
||||
action='store_true',
|
||||
help=('This only used in volume entity. Clears the migration '
|
||||
'status of the volume in the DataBase that indicates the '
|
||||
'volume is source or destination of volume migration, '
|
||||
'with no regard to the actual status.'))
|
||||
def do_reset_state(cs, args):
|
||||
"""Explicitly updates the entity state in the Cinder database.
|
||||
|
||||
Being a database change only, this has no impact on the true state of the
|
||||
entity and may not match the actual state. This can render a entity
|
||||
unusable in the case of changing to the 'available' state.
|
||||
"""
|
||||
failure_count = 0
|
||||
single = (len(args.entity) == 1)
|
||||
|
||||
migration_status = 'none' if args.reset_migration_status else None
|
||||
collector = RESET_STATE_RESOURCES[args.type]
|
||||
argument = (args.state,)
|
||||
if args.type == 'volume':
|
||||
argument += (args.attach_status, migration_status)
|
||||
|
||||
for entity in args.entity:
|
||||
try:
|
||||
collector(cs, entity).reset_state(*argument)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
failure_count += 1
|
||||
msg = "Reset state for entity %s failed: %s" % (entity, e)
|
||||
if not single:
|
||||
print(msg)
|
||||
|
||||
if failure_count == len(args.entity):
|
||||
msg = "Unable to reset the state for the specified entity(s)."
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
|
||||
@utils.arg('size',
|
||||
metavar='<size>',
|
||||
nargs='?',
|
||||
|
@ -150,7 +150,8 @@ class SnapshotManager(base.ManagerWithFind):
|
||||
|
||||
def reset_state(self, snapshot, state):
|
||||
"""Update the specified snapshot with the provided state."""
|
||||
return self._action('os-reset_status', snapshot, {'status': state})
|
||||
return self._action('os-reset_status', snapshot,
|
||||
{'status': state} if state else {})
|
||||
|
||||
def _action(self, action, snapshot, info=None, **kwargs):
|
||||
"""Perform a snapshot action."""
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- Use 'cinder reset-state' as generic resource reset
|
||||
state command for resource 'volume', 'snapshot', 'backup'
|
||||
'group' and 'group-snapshot'. Also change volume's
|
||||
default status from 'available' to none when no
|
||||
status is specified.
|
Loading…
Reference in New Issue
Block a user