Add CLI commands for Manage-Unmanage of Share Servers

- Added CLI commands for managing and unmanaging share servers.
- Updated CLI command for managing shares to accept
  share_server_id parameter.

API microversion has been bumped to 2.49.

Partially-implements: bp manage-unmanage-with-share-servers
Change-Id: If1403079b20471645bf869da74bf4db37d59811c
This commit is contained in:
Rodrigo Barbieri 2018-10-27 09:18:21 -03:00 committed by Tom Barron
parent 7d6ef621c3
commit 07564879ae
16 changed files with 558 additions and 47 deletions

View File

@ -65,6 +65,10 @@ iniset $MANILACLIENT_CONF DEFAULT access_types_mapping "nfs:ip,cifs:user"
# Dummy driver is capable of running share migration tests
iniset $MANILACLIENT_CONF DEFAULT run_migration_tests "True"
# Dummy driver is capable of running share manage tests
iniset $MANILACLIENT_CONF DEFAULT run_manage_tests "True"
# Running mountable snapshot tests in dummy driver
iniset $MANILACLIENT_CONF DEFAULT run_mount_snapshot_tests "True"

View File

@ -27,7 +27,7 @@ from manilaclient import utils
LOG = logging.getLogger(__name__)
MAX_VERSION = '2.47'
MAX_VERSION = '2.49'
MIN_VERSION = '2.0'
DEPRECATED_VERSION = '1.0'
_VERSIONED_METHOD_MAP = {}

View File

@ -86,3 +86,11 @@ MESSAGE_SORT_KEY_VALUES = (
'detail_id', 'resource_id', 'message_level', 'expires_at',
'request_id', 'created_at'
)
STATUS_AVAILABLE = 'available'
STATUS_ERROR = 'error'
STATUS_ACTIVE = 'active'
STATUS_MANAGE_ERROR = 'manage_error'
STATUS_UNMANAGE_ERROR = 'unmanage_error'
STATUS_DELETING = 'deleting'
STATUS_CREATING = 'creating'

View File

@ -178,6 +178,12 @@ share_opts = [
help="Defines whether to run mountable snapshots tests or "
"not. Disable this feature if used driver doesn't "
"support it."),
cfg.BoolOpt("run_manage_tests",
default=False,
help="Defines whether to run manage/unmanage tests or "
"not. Disable this feature if used driver does not "
"support it."),
]
# 2. Generate config

View File

@ -21,6 +21,7 @@ from tempest.lib.cli import base
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from manilaclient.common import constants
from manilaclient import config
from manilaclient.tests.functional import client
from manilaclient.tests.functional import utils
@ -272,7 +273,8 @@ class BaseTestCase(base.ClientTestBase):
else:
cls.method_resources.insert(0, resource)
if wait_for_creation:
client.wait_for_share_status(share['id'], 'available')
client.wait_for_resource_status(share['id'],
constants.STATUS_AVAILABLE)
return share
@classmethod
@ -369,7 +371,7 @@ class BaseTestCase(base.ClientTestBase):
cleanup_in_class=cleanup_in_class, microversion=microversion,
wait_for_creation=False, client=client)
client.wait_for_share_status(share['id'], "error")
client.wait_for_resource_status(share['id'], constants.STATUS_ERROR)
message = client.wait_for_message(share['id'])
resource = {

View File

@ -730,28 +730,29 @@ class ManilaCLIClient(base.CLIClient):
SHARE, res_id=share, interval=5, timeout=300,
microversion=microversion)
def wait_for_share_status(self, share, status, microversion=None):
def wait_for_resource_status(self, resource_id, status, microversion=None,
resource_type="share"):
"""Waits for a share to reach a given status."""
body = self.get_share(share, microversion=microversion)
share_name = body['name']
get_func = getattr(self, 'get_' + resource_type)
body = get_func(resource_id, microversion=microversion)
share_status = body['status']
start = int(time.time())
while share_status != status:
time.sleep(self.build_interval)
body = self.get_share(share, microversion=microversion)
body = get_func(resource_id, microversion=microversion)
share_status = body['status']
if share_status == status:
return
elif 'error' in share_status.lower():
raise exceptions.ShareBuildErrorException(share=share)
raise exceptions.ShareBuildErrorException(share=resource_id)
if int(time.time()) - start >= self.build_timeout:
message = (
"Share %(share_name)s failed to reach %(status)s status "
"within the required time (%(build_timeout)s s)." % {
"share_name": share_name, "status": status,
message = ("Resource %(resource_id)s failed to reach "
"%(status)s status within the required time "
"(%(build_timeout)s)." %
{"resource_id": resource_id, "status": status,
"build_timeout": self.build_timeout})
raise tempest_lib_exc.TimeoutException(message)
@ -1441,6 +1442,32 @@ class ManilaCLIClient(base.CLIClient):
SHARE_SERVER, res_id=share_server, interval=3, timeout=60,
microversion=microversion)
def unmanage_share(self, server_id):
return self.manila('unmanage %s ' % server_id)
def unmanage_server(self, share_server_id):
return self.manila('share-server-unmanage %s ' % share_server_id)
def share_server_manage(self, host, share_network, identifier,
driver_options=None):
if driver_options:
command = ('share-server-manage %s %s %s %s' %
(host, share_network, identifier, driver_options))
else:
command = ('share-server-manage %s %s %s' % (host, share_network,
identifier))
managed_share_server_raw = self.manila(command)
managed_share_server = output_parser.details(managed_share_server_raw)
return managed_share_server['id']
def manage_share(self, host, protocol, export_location, share_server):
managed_share_raw = self.manila(
'manage %s %s %s --share-server-id %s' % (host, protocol,
export_location,
share_server))
managed_share = output_parser.details(managed_share_raw)
return managed_share['id']
# user messages
def wait_for_message(self, resource_id):

View File

@ -14,11 +14,15 @@
# under the License.
import ddt
import testtools
from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
from manilaclient.common import constants
from manilaclient import config
from manilaclient.tests.functional import base
from manilaclient.tests.functional import utils
CONF = config.CONF
@ -79,12 +83,10 @@ class ShareServersReadWriteBase(base.BaseTestCase):
message = "Can run only with DHSS=True mode"
raise cls.skipException(message)
def test_get_and_delete_share_server(self):
def _create_share_and_share_network(self):
name = data_utils.rand_name('autotest_share_name')
description = data_utils.rand_name('autotest_share_description')
# We create separate share network to be able to delete share server
# further knowing that it is not used by any other concurrent test.
common_share_network = self.client.get_share_network(
self.client.share_network)
neutron_net_id = (
@ -107,7 +109,22 @@ class ShareServersReadWriteBase(base.BaseTestCase):
description=description,
share_network=share_network['id'],
client=self.client,
wait_for_creation=True
)
self.share = self.client.get_share(self.share['id'])
return self.share, share_network
def _delete_share_and_share_server(self, share_id, share_server_id):
# Delete share
self.client.delete_share(share_id)
self.client.wait_for_share_deletion(share_id)
# Delete share server
self.client.delete_share_server(share_server_id)
self.client.wait_for_share_server_deletion(share_server_id)
def test_get_and_delete_share_server(self):
self.share, share_network = self._create_share_and_share_network()
share_server_id = self.client.get_share(
self.share['id'])['share_server_id']
@ -117,17 +134,62 @@ class ShareServersReadWriteBase(base.BaseTestCase):
'id', 'host', 'status', 'created_at', 'updated_at',
'share_network_id', 'share_network_name', 'project_id',
)
if utils.is_microversion_supported('2.49'):
expected_keys += ('identifier', 'is_auto_deletable')
for key in expected_keys:
self.assertIn(key, server)
# Delete share
self.client.delete_share(self.share['id'])
self.client.wait_for_share_deletion(self.share['id'])
self._delete_share_and_share_server(self.share['id'], share_server_id)
# Delete share server
self.client.delete_share_server(share_server_id)
@testtools.skipUnless(
CONF.run_manage_tests, 'Share Manage/Unmanage tests are disabled.')
@utils.skip_if_microversion_not_supported('2.49')
def test_manage_and_unmanage_share_server(self):
share, share_network = self._create_share_and_share_network()
share_server_id = self.client.get_share(
self.share['id'])['share_server_id']
server = self.client.get_share_server(share_server_id)
server_host = server['host']
export_location = self.client.list_share_export_locations(
self.share['id'])[0]['Path']
share_host = share['host']
identifier = server['identifier']
self.assertEqual('True', server['is_auto_deletable'])
# Unmanages share
self.client.unmanage_share(share['id'])
self.client.wait_for_share_deletion(share['id'])
server = self.client.get_share_server(share_server_id)
self.assertEqual('False', server['is_auto_deletable'])
# Unmanages share server
self.client.unmanage_server(share_server_id)
self.client.wait_for_share_server_deletion(share_server_id)
# Manage share server
managed_share_server_id = self.client.share_server_manage(
server_host, share_network['id'], identifier)
self.client.wait_for_resource_status(
managed_share_server_id, constants.STATUS_ACTIVE,
resource_type='share_server')
managed_server = self.client.get_share_server(managed_share_server_id)
self.assertEqual('False', managed_server['is_auto_deletable'])
# Manage share
managed_share_id = self.client.manage_share(
share_host, self.protocol, export_location,
managed_share_server_id)
self.client.wait_for_resource_status(managed_share_id,
constants.STATUS_AVAILABLE)
self._delete_share_and_share_server(managed_share_id,
managed_share_server_id)
class ShareServersReadWriteNFSTest(ShareServersReadWriteBase):
protocol = 'nfs'

View File

@ -19,6 +19,7 @@ from tempest.lib.common.utils import data_utils
from tempest.lib import exceptions
import testtools
from manilaclient.common import constants
from manilaclient import config
from manilaclient.tests.functional import base
@ -151,7 +152,8 @@ class SharesListReadWriteTest(base.BaseTestCase):
for share_id in (cls.private_share['id'], cls.public_share['id'],
cls.admin_private_share['id']):
cls.get_admin_client().wait_for_share_status(share_id, 'available')
cls.get_admin_client().wait_for_resource_status(
share_id, constants.STATUS_AVAILABLE)
def _list_shares(self, filters=None):
filters = filters or dict()

View File

@ -179,16 +179,17 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
def get_share_servers_1234(self, **kw):
share_servers = {
'share_servers': {
'share_server': {
'id': 1234,
'share_network_id': 'fake_network_id_1',
'backend_details': {},
},
}
return (200, {}, share_servers)
def get_share_servers_5678(self, **kw):
share_servers = {
'share_servers': {
'share_server': {
'id': 5678,
'share_network_id': 'fake_network_id_2',
},
@ -429,6 +430,36 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
post_shares_manage = post_os_share_manage
def post_share_servers_manage(self, body, **kw):
_body = {'share_server': {'id': 'fake'}}
resp = 202
if not ('host' in body['share_server']
and 'share_network' in body['share_server']
and 'identifier' in body['share_server']):
resp = 422
result = (resp, {}, _body)
return result
def post_share_servers_1234_action(self, body, **kw):
_body = None
assert len(list(body)) == 1
action = list(body)[0]
if action in ('reset_status', ):
assert 'status' in body.get(
'reset_status', body.get('os-reset_status'))
_body = {
'reset_status': {'status': body['reset_status']['status']}
}
elif action in ('unmanage', ):
assert 'force' in body[action]
resp = 202
result = (resp, {}, _body)
return result
def post_os_share_unmanage_1234_unmanage(self, **kw):
_body = None
resp = 202

View File

@ -13,8 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import ddt
import mock
from manilaclient.common.apiclient import base as common_base
from manilaclient.common import constants
from manilaclient.tests.unit import utils
from manilaclient.tests.unit.v2 import fakes
from manilaclient.v2 import share_servers
@ -65,6 +68,7 @@ class ShareServerTest(utils.TestCase):
"has not been raised.")
@ddt.ddt
class ShareServerManagerTest(utils.TestCase):
def setUp(self):
@ -79,6 +83,69 @@ class ShareServerManagerTest(utils.TestCase):
share_servers.RESOURCES_PATH,
share_servers.RESOURCES_NAME)
@ddt.data(None, {}, {'opt1': 'fake_opt1', 'opt12': 'fake_opt2'})
def test_manage(self, driver_options):
host = 'fake_host'
share_network_id = 'fake_share_net_id'
identifier = 'ff-aa-kk-ee-00'
if driver_options is None:
driver_options = {}
expected_body = {
'host': host,
'share_network_id': share_network_id,
'identifier': identifier,
'driver_options': driver_options
}
with mock.patch.object(self.manager, '_create',
mock.Mock(return_value='fake')):
result = self.manager.manage(host, share_network_id, identifier,
driver_options)
self.manager._create.assert_called_once_with(
share_servers.RESOURCES_PATH + '/manage',
{'share_server': expected_body}, 'share_server'
)
self.assertEqual('fake', result)
@ddt.data(True, False)
def test_unmanage(self, force):
share_server = {'id': 'fake'}
with mock.patch.object(self.manager, '_action',
mock.Mock(return_value='fake')):
result = self.manager.unmanage(share_server, force)
self.manager._action.assert_called_once_with(
"unmanage", share_server, {'force': force})
self.assertEqual('fake', result)
def test_reset_state(self):
share_server = {'id': 'fake'}
state = constants.STATUS_AVAILABLE
with mock.patch.object(self.manager, '_action',
mock.Mock(return_value='fake')):
result = self.manager.reset_state(share_server, state)
self.manager._action.assert_called_once_with(
"reset_status", share_server, {"status": state})
self.assertEqual('fake', result)
@ddt.data(("reset_status", {"status": constants.STATUS_AVAILABLE}),
("unmanage", {"id": "fake_id"}))
@ddt.unpack
def test__action(self, action, info):
action = ""
share_server = {"id": 'fake_id'}
expected_url = '/share-servers/%s/action' % share_server['id']
expected_body = {action: info}
with mock.patch.object(self.manager.api.client, 'post',
mock.Mock(return_value='fake')):
self.mock_object(common_base, 'getid',
mock.Mock(return_value=share_server['id']))
result = self.manager._action(action, share_server, info)
self.manager.api.client.post.assert_called_once_with(
expected_url, body=expected_body
)
self.assertEqual('fake', result)
def test_list_with_one_search_opt(self):
host = 'fake_host'
query_string = "?host=%s" % host

View File

@ -162,9 +162,11 @@ class SharesTest(utils.TestCase):
("2.7", "/shares/manage", None),
("2.8", "/shares/manage", True),
("2.8", "/shares/manage", False),
("2.49", "/shares/manage", False, '1234'),
)
@ddt.unpack
def test_manage_share(self, microversion, resource_path, is_public=False):
def test_manage_share(self, microversion, resource_path, is_public=False,
share_server_id=None):
service_host = "fake_service_host"
protocol = "fake_protocol"
export_path = "fake_export_path"
@ -180,6 +182,7 @@ class SharesTest(utils.TestCase):
"driver_options": driver_options,
"name": name,
"description": description,
"share_server_id": share_server_id,
}
version = api_versions.APIVersion(microversion)
if version >= api_versions.APIVersion('2.8'):
@ -190,15 +193,19 @@ class SharesTest(utils.TestCase):
with mock.patch.object(manager, "_create",
mock.Mock(return_value="fake")):
if version >= api_versions.APIVersion('2.8'):
if version < api_versions.APIVersion('2.8'):
result = manager.manage(
service_host, protocol, export_path, driver_options,
share_type, name, description)
elif (api_versions.APIVersion('2.8') <= version
< api_versions.APIVersion('2.49')):
result = manager.manage(
service_host, protocol, export_path, driver_options,
share_type, name, description, is_public)
else:
result = manager.manage(
service_host, protocol, export_path, driver_options,
share_type, name, description)
share_type, name, description, is_public, share_server_id)
self.assertEqual(manager._create.return_value, result)
manager._create.assert_called_once_with(

View File

@ -637,21 +637,25 @@ class ShellTest(test_utils.TestCase):
'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
'share_type': 'fake_share_type',
'share_server_id': None,
}},
{'cmd_args': '--share_type fake_share_type',
'valid_params': {
'driver_options': {},
'share_type': 'fake_share_type',
'share_server_id': None,
}},
{'cmd_args': '',
'valid_params': {
'driver_options': {},
'share_type': None,
'share_server_id': None,
}},
{'cmd_args': '--public',
'valid_params': {
'driver_options': {},
'share_type': None,
'share_server_id': None,
},
'is_public': True,
'version': '--os-share-api-version 2.8',
@ -660,10 +664,38 @@ class ShellTest(test_utils.TestCase):
'valid_params': {
'driver_options': {},
'share_type': None,
'share_server_id': None,
},
'is_public': False,
'version': '--os-share-api-version 2.8',
},
{'cmd_args': '--driver_options opt1=opt1 opt2=opt2'
' --share_type fake_share_type',
'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
'share_type': 'fake_share_type',
'share_server_id': None,
},
'version': '--os-share-api-version 2.49',
},
{'cmd_args': '--driver_options opt1=opt1 opt2=opt2'
' --share_type fake_share_type'
' --share_server_id fake_server',
'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
'share_type': 'fake_share_type',
'share_server_id': 'fake_server',
},
'version': '--os-share-api-version 2.49',
},
{'cmd_args': '--driver_options opt1=opt1 opt2=opt2'
' --share_type fake_share_type'
' --share_server_id fake_server',
'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
'share_type': 'fake_share_type',
'share_server_id': 'fake_server',
}},
)
@ddt.unpack
def test_manage(self, cmd_args, valid_params, is_public=False,
@ -685,15 +717,88 @@ class ShellTest(test_utils.TestCase):
'name': None,
'description': None,
'is_public': is_public,
'share_server_id': valid_params['share_server_id'],
}
}
expected['share'].update(valid_params)
self.assert_called('POST', '/shares/manage', body=expected)
def test_manage_invalid_param_share_server_id(self):
self.assertRaises(
exceptions.CommandError,
self.run_command,
'--os-share-api-version 2.48'
+ ' manage fake_service fake_protocol '
+ ' fake_export_path '
+ ' --driver_options opt1=opt1 opt2=opt2'
+ ' --share_type fake_share_type'
+ ' --share_server_id fake_server')
@ddt.data({'driver_args': '--driver_options opt1=opt1 opt2=opt2',
'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
},
'version': '--os-share-api-version 2.49',
},
{'driver_args': '--driver_options opt1=opt1 opt2=opt2',
'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
},
},
{'driver_args': "",
'valid_params': {
'driver_options': {}
},
'version': '--os-share-api-version 2.49',
})
@ddt.unpack
def test_share_server_manage(self, driver_args, valid_params,
version=None):
fake_share_network = type(
'FakeShareNetwork', (object,), {'id': '3456'})
self.mock_object(
shell_v2, '_find_share_network',
mock.Mock(return_value=fake_share_network))
command = "" if version is None else version
command += (' share-server-manage fake_host fake_share_net_id '
+ ' 88-as-23-f3-45 ' + driver_args)
self.run_command(command)
expected = {
'share_server': {
'host': 'fake_host',
'share_network_id': fake_share_network.id,
'identifier': '88-as-23-f3-45',
'driver_options': driver_args
}
}
expected['share_server'].update(valid_params)
self.assert_called('POST', '/share-servers/manage', body=expected)
@ddt.data(constants.STATUS_ERROR, constants.STATUS_ACTIVE,
constants.STATUS_MANAGE_ERROR, constants.STATUS_UNMANAGE_ERROR,
constants.STATUS_DELETING, constants.STATUS_CREATING)
def test_share_server_reset_state(self, status):
self.run_command('share-server-reset-state 1234 --state %s ' % status)
expected = {'reset_status': {'status': status}}
self.assert_called('POST', '/share-servers/1234/action', body=expected)
def test_unmanage(self):
self.run_command('unmanage 1234')
self.assert_called('POST', '/shares/1234/action')
def test_share_server_unmanage(self):
self.run_command('share-server-unmanage 1234')
self.assert_called('POST', '/share-servers/1234/action',
body={'unmanage': {'force': False}})
def test_share_server_unmanage_force(self):
self.run_command('share-server-unmanage 1234 --force')
self.assert_called('POST', '/share-servers/1234/action',
body={'unmanage': {'force': True}})
@ddt.data({'cmd_args': '--driver_options opt1=opt1 opt2=opt2',
'valid_params': {
'driver_options': {'opt1': 'opt1', 'opt2': 'opt2'},
@ -3116,3 +3221,18 @@ class ShellTest(test_utils.TestCase):
cmd + option + separator + 'fake',
version=version
)
def test_share_server_unmanage_all_fail(self):
# All of 2345, 5678, 9999 throw exception
cmd = '--os-share-api-version 2.49'
cmd += ' share-server-unmanage'
cmd += ' 2345 5678 9999'
self.assertRaises(exceptions.CommandError,
self.run_command, cmd)
def test_share_server_unmanage_some_fail(self):
# 5678 and 9999 throw exception
self.run_command('share-server-unmanage 1234 5678 9999')
expected = {'unmanage': {'force': False}}
self.assert_called('POST', '/share-servers/1234/action',
body=expected)

View File

@ -13,13 +13,15 @@
# License for the specific language governing permissions and limitations
# under the License.
from manilaclient import api_versions
from manilaclient import base
from manilaclient.common.apiclient import base as common_base
RESOURCES_PATH = '/share-servers'
RESOURCE_PATH = '/share-servers/%s'
RESOURCES_NAME = 'share_servers'
RESOURCES_PATH = '/share-servers'
RESOURCE_PATH = RESOURCES_PATH + '/%s'
RESOURCE_NAME = 'share_server'
ACTION_PATH = RESOURCE_PATH + '/action'
class ShareServer(common_base.Resource):
@ -36,6 +38,14 @@ class ShareServer(common_base.Resource):
"""Delete this share server."""
self.manager.delete(self)
def unmanage(self, force=False):
"""Unmanage this share server."""
self.manager.unmanage(self, force)
def reset_state(self, state):
"""Update the share server with the provided state."""
self.manager.reset_state(self, state)
class ShareServerManager(base.ManagerWithFind):
"""Manage :class:`ShareServer` resources."""
@ -86,3 +96,44 @@ class ShareServerManager(base.ManagerWithFind):
"""
query_string = self._build_query_string(search_opts)
return self._list(RESOURCES_PATH + query_string, RESOURCES_NAME)
@api_versions.wraps("2.49")
def manage(self, host, share_network_id, identifier, driver_options=None):
driver_options = driver_options or {}
body = {
'host': host,
'share_network_id': share_network_id,
'identifier': identifier,
'driver_options': driver_options,
}
resource_path = RESOURCE_PATH % 'manage'
return self._create(resource_path, {'share_server': body},
'share_server')
@api_versions.wraps("2.49")
def unmanage(self, share_server, force=False):
return self._action("unmanage", share_server, {'force': force})
@api_versions.wraps("2.49")
def reset_state(self, share_server, state):
"""Update the provided share server with the provided state.
:param share_server: either share_server object or text with its ID.
:param state: text with new state to set for share.
"""
return self._action("reset_status", share_server, {"status": state})
def _action(self, action, share_server, info=None):
"""Perform a share server 'action'.
:param action: text with action name.
:param share_server: either share_server object or text with its ID.
:param info: dict with data for specified 'action'.
:param kwargs: dict with data to be provided for action hooks.
"""
body = {action: info}
self.run_hooks('modify_body_for_action', body)
url = ACTION_PATH % common_base.getid(share_server)
return self.api.client.post(url, body=body)

View File

@ -215,7 +215,7 @@ class ShareManager(base.ManagerWithFind):
def _do_manage(self, service_host, protocol, export_path,
driver_options=None, share_type=None,
name=None, description=None, is_public=None,
resource_path="/shares/manage"):
share_server_id=None, resource_path="/shares/manage"):
"""Manage some existing share.
:param service_host: text - host where manila share service is running
@ -226,6 +226,7 @@ class ShareManager(base.ManagerWithFind):
:param name: text - name of new share
:param description: - description for new share
:param is_public: - visibility for new share
:param share_server_id: text - id of share server associated with share
"""
driver_options = driver_options if driver_options else dict()
body = {
@ -236,6 +237,7 @@ class ShareManager(base.ManagerWithFind):
'driver_options': driver_options,
'name': name,
'description': description,
'share_server_id': share_server_id,
}
if is_public is not None:
@ -246,25 +248,36 @@ class ShareManager(base.ManagerWithFind):
@api_versions.wraps("1.0", "2.6")
def manage(self, service_host, protocol, export_path, driver_options=None,
share_type=None, name=None, description=None):
is_public = None
return self._do_manage(
service_host, protocol, export_path, driver_options, share_type,
name, description, is_public, resource_path="/os-share-manage")
service_host, protocol, export_path, driver_options=driver_options,
share_type=share_type, name=name, description=description,
resource_path="/os-share-manage")
@api_versions.wraps("2.7", "2.7") # noqa
def manage(self, service_host, protocol, export_path, driver_options=None,
share_type=None, name=None, description=None):
is_public = None
return self._do_manage(
service_host, protocol, export_path, driver_options, share_type,
name, description, is_public, resource_path="/shares/manage")
service_host, protocol, export_path, driver_options=driver_options,
share_type=share_type, name=name, description=description,
resource_path="/shares/manage")
@api_versions.wraps("2.8") # noqa
@api_versions.wraps("2.8", "2.48") # noqa
def manage(self, service_host, protocol, export_path, driver_options=None,
share_type=None, name=None, description=None, is_public=False):
return self._do_manage(
service_host, protocol, export_path, driver_options, share_type,
name, description, is_public, "/shares/manage")
service_host, protocol, export_path, driver_options=driver_options,
share_type=share_type, name=name, description=description,
is_public=is_public, resource_path="/shares/manage")
@api_versions.wraps("2.49") # noqa
def manage(self, service_host, protocol, export_path, driver_options=None,
share_type=None, name=None, description=None, is_public=False,
share_server_id=None):
return self._do_manage(
service_host, protocol, export_path, driver_options=driver_options,
share_type=share_type, name=name, description=description,
is_public=is_public, share_server_id=share_server_id,
resource_path="/shares/manage")
@api_versions.wraps("1.0", "2.6")
def unmanage(self, share):

View File

@ -1120,20 +1120,95 @@ def do_share_export_location_show(cs, args):
help="Level of visibility for share. Defines whether other tenants are "
"able to see it or not. Available only for microversion >= 2.8. "
"(Default=False)")
@cliutils.arg(
'--share_server_id', '--share-server-id',
metavar='<share-server-id>',
default=None,
action='single_alias',
help="Share server associated with share when using a share type with "
"'driver_handles_share_servers' extra_spec set to True. Available "
"only for microversion >= 2.49. (Default=None)")
def do_manage(cs, args):
"""Manage share not handled by Manila (Admin only)."""
driver_options = _extract_key_value_options(args, 'driver_options')
if cs.api_version.matches(api_versions.APIVersion("2.49"),
api_versions.APIVersion()):
share = cs.shares.manage(
args.service_host, args.protocol, args.export_path,
driver_options=driver_options, share_type=args.share_type,
name=args.name, description=args.description,
is_public=args.public,
)
is_public=args.public, share_server_id=args.share_server_id)
else:
if args.share_server_id:
raise exceptions.CommandError("Invalid parameter "
"--share_server_id specified. This"
" parameter is only supported on"
" microversion 2.49 or newer.")
share = cs.shares.manage(
args.service_host, args.protocol, args.export_path,
driver_options=driver_options, share_type=args.share_type,
name=args.name, description=args.description,
is_public=args.public)
_print_share(cs, share)
@api_versions.wraps("2.49")
@cliutils.arg(
'host',
metavar='<host>',
type=str,
help='Backend name as "<node_hostname>@<backend_name>".')
@cliutils.arg(
'share_network',
metavar='<share_network>',
help="Share network where share server has network allocations in.")
@cliutils.arg(
'identifier',
metavar='<identifier>',
type=str,
help='A driver-specific share server identifier required by the driver to '
'manage the share server.')
@cliutils.arg(
'--driver_options', '--driver-options',
type=str,
nargs='*',
metavar='<key=value>',
action='single_alias',
help='One or more driver-specific key=value pairs that may be necessary to'
' manage the share server (Optional, Default=None).',
default=None)
def do_share_server_manage(cs, args):
"""Manage share server not handled by Manila (Admin only)."""
driver_options = _extract_key_value_options(args, 'driver_options')
share_network = _find_share_network(cs, args.share_network)
share_server = cs.share_servers.manage(
args.host, share_network.id, args.identifier,
driver_options=driver_options)
cliutils.print_dict(share_server._info)
@cliutils.arg(
'share_server_id',
metavar='<share_server_id>',
help='ID of the share server to modify.')
@cliutils.arg(
'--state',
metavar='<state>',
default=constants.STATUS_ACTIVE,
help=('Indicate which state to assign the share server. Options include '
'active, error, creating, deleting, managing, unmanaging, '
'manage_error and unmanage_error. If no state is provided, active '
'will be used.'))
@api_versions.wraps("2.49")
def do_share_server_reset_state(cs, args):
"""Explicitly update the state of a share server (Admin only)."""
cs.share_servers.reset_state(args.share_server_id, args.state)
@api_versions.wraps("2.12")
@cliutils.arg(
'share',
@ -1188,6 +1263,36 @@ def do_unmanage(cs, args):
share_ref.unmanage()
@api_versions.wraps("2.49")
@cliutils.arg(
'share_server',
metavar='<share_server>',
nargs='+',
help='ID of the share server(s).')
@cliutils.arg(
'--force',
dest='force',
action="store_true",
required=False,
default=False,
help="Enforces the unmanage share server operation, even if the back-end "
"driver does not support it.")
def do_share_server_unmanage(cs, args):
"""Unmanage share server (Admin only)."""
failure_count = 0
for server in args.share_server:
try:
cs.share_servers.unmanage(server, args.force)
except Exception as e:
failure_count += 1
print("Unmanage for share server %s failed: %s" % (server, e),
file=sys.stderr)
if failure_count == len(args.share_server):
raise exceptions.CommandError("Unable to unmanage any of the "
"specified share servers.")
@api_versions.wraps("2.12")
@cliutils.arg(
'snapshot',

View File

@ -0,0 +1,6 @@
---
features:
- Added CLI commands to manage and unmanage share servers.
- Updated CLI command for managing shares to accept
``share_server_id`` parameter.