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:
parent
7d6ef621c3
commit
07564879ae
@ -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"
|
||||
|
||||
|
@ -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 = {}
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -730,29 +730,30 @@ 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,
|
||||
"build_timeout": self.build_timeout})
|
||||
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)
|
||||
|
||||
def wait_for_migration_task_state(self, share_id, dest_host,
|
||||
@ -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):
|
||||
|
@ -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'
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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')
|
||||
|
||||
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,
|
||||
)
|
||||
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, 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',
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user