Merge "Support manage_existing in Huawei driver"

This commit is contained in:
Jenkins 2015-08-06 01:59:45 +00:00 committed by Gerrit Code Review
commit f96ebefc22
5 changed files with 332 additions and 5 deletions

View File

@ -59,6 +59,10 @@ class HuaweiBase(object):
def shrink_share(self, share, new_size, share_server):
"""Shrinks size of existing share."""
@abc.abstractmethod
def manage_existing(self, share, driver_options):
"""Manage existing share."""
@abc.abstractmethod
def get_network_allocations_number(self):
"""Get number of network interfaces to be created."""

View File

@ -46,7 +46,8 @@ class HuaweiNasDriver(driver.ShareDriver):
API version history:
1.0 - Initial version.
1.1 - Add shrink share
1.1 - Add shrink share.
1.2 - Add manage share.
"""
def __init__(self, *args, **kwargs):
@ -155,6 +156,13 @@ class HuaweiNasDriver(driver.ShareDriver):
LOG.debug("Get network allocations number.")
return self.plugin.get_network_allocations_number()
def manage_existing(self, share, driver_options):
"""Manage existing share."""
LOG.debug("Manage existing share to manila.")
share_size, location = self.plugin.manage_existing(share,
driver_options)
return {'size': share_size, 'export_locations': location}
def _update_share_stats(self):
"""Retrieve status info from share group."""

View File

@ -16,6 +16,7 @@
import time
from oslo_log import log
from oslo_utils import strutils
from oslo_utils import units
from manila.common import constants as common_constants
@ -24,8 +25,10 @@ from manila.i18n import _, _LI, _LW
from manila.share.drivers.huawei import base as driver
from manila.share.drivers.huawei import constants
from manila.share.drivers.huawei.v3 import helper
from manila.share import share_types
from manila.share import utils as share_utils
LOG = log.getLogger(__name__)
@ -424,6 +427,59 @@ class V3StorageConnection(driver.HuaweiBase):
fsid = self.helper._create_filesystem(fileParam)
return fsid
def manage_existing(self, share, driver_options):
"""Manage existing share."""
driver_mode = share_types.get_share_type_extra_specs(
share['share_type_id'],
common_constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS)
if strutils.bool_from_string(driver_mode):
msg = _("%(mode)s != False") % {
'mode':
common_constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS
}
raise exception.ManageExistingShareTypeMismatch(reason=msg)
share_proto = share['share_proto']
share_name = share['name']
old_export_location = share['export_locations'][0]['path']
pool_name = share_utils.extract_host(share['host'], level='pool')
share_url_type = self.helper._get_share_url_type(share_proto)
old_share_name = self.helper._get_share_name_by_export_location(
old_export_location, share_proto)
share = self.helper._get_share_by_name(old_share_name,
share_url_type)
if not share:
err_msg = (_("Can not get share ID by share %s.")
% old_export_location)
LOG.error(err_msg)
raise exception.InvalidShare(reason=err_msg)
fs_id = share['FSID']
fs = self.helper._get_fs_info_by_id(fs_id)
if not self.check_fs_status(fs['HEALTHSTATUS'],
fs['RUNNINGSTATUS']):
raise exception.InvalidShare(
reason=(_('Invalid status of filesystem: %(health)s '
'%(running)s.')
% {'health': fs['HEALTHSTATUS'],
'running': fs['RUNNINGSTATUS']}))
if pool_name and pool_name != fs['POOLNAME']:
raise exception.InvalidHost(
reason=(_('The current pool(%(fs_pool)s) of filesystem '
'does not match the input pool(%(host_pool)s).')
% {'fs_pool': fs['POOLNAME'],
'host_pool': pool_name}))
self.helper._change_fs_name(fs_id, share_name)
share_size = int(fs['CAPACITY']) / units.Mi / 2
location = self._get_location_path(share_name, share_proto)
return (share_size, [location])
def _get_location_path(self, share_name, share_proto):
root = self.helper._read_xml()
target_ip = root.findtext('Storage/LogicalPortIP').strip()

View File

@ -596,6 +596,43 @@ class RestHelper(object):
share_name = "share_" + share_id
return share_name
def _get_share_name_by_export_location(self, export_location, share_proto):
export_location_split = None
share_name = None
share_ip = None
if export_location:
if share_proto == 'NFS':
export_location_split = export_location.split(':/')
if len(export_location_split) == 2:
share_name = export_location_split[1]
share_ip = export_location_split[0]
elif share_proto == 'CIFS':
export_location_split = export_location.split('\\')
if (len(export_location_split) == 4 and
export_location_split[0] == "" and
export_location_split[1] == ""):
share_ip = export_location_split[2]
share_name = export_location_split[3]
if share_name is None:
raise exception.InvalidInput(
reason=(_('No share with export location %s could be found.')
% export_location))
root = self._read_xml()
target_ip = root.findtext('Storage/LogicalPortIP')
if target_ip:
if share_ip != target_ip.strip():
raise exception.InvalidInput(
reason=(_('The share IP %s is not configured.')
% share_ip))
else:
raise exception.InvalidInput(
reason=(_('The config parameter LogicalPortIP is not set.')))
return share_name
def _get_snapshot_id(self, fs_id, snap_name):
snapshot_id = (fs_id + "@" + "share_snapshot_"
+ snap_name.replace("-", "_"))
@ -614,3 +651,14 @@ class RestHelper(object):
msg = "Change a share size error!"
self._assert_rest_result(result, msg)
self._assert_data_in_result(result, msg)
def _change_fs_name(self, fsid, name):
url = self.url + "/filesystem/%s" % fsid
fs_param = {
"NAME": name.replace("-", "_"),
}
data = jsonutils.dumps(fs_param)
result = self.call(url, data, "PUT")
msg = _("Change filesystem name error.")
self._assert_rest_result(result, msg)

View File

@ -27,6 +27,7 @@ import mock
from oslo_serialization import jsonutils
from manila import context
from manila import db
from manila import exception
from manila.share import configuration as conf
from manila.share.drivers.huawei import huawei_nas
@ -65,6 +66,10 @@ def filesystem(method, data, fs_status_flag):
"data":{"ID":"4",
"CAPACITY":"2097152"}}"""
shrink_share_flag = True
elif data == """{"NAME": "share_fake_manage_uuid"}""":
data = """{"error":{"code":0},
"data":{"ID":"4",
"CAPACITY":"8388608"}}"""
elif method == "DELETE":
data = """{"error":{"code":0}}"""
elif method == "GET":
@ -410,6 +415,43 @@ class HuaweiShareDriverTestCase(test.TestCase):
'share_network_id': 'fake_net_id',
'share_server_id': 'fake-share-srv-id',
'host': 'fake_host@fake_backend#OpenStack_Pool',
'export_locations': [
{'path': '100.115.10.68:/share_fake_uuid'},
],
'host': 'fake_host@fake_backend#OpenStack_Pool',
'share_type_id': 'fake_id',
}
self.share_manage_nfs = {
'id': 'fake_uuid',
'project_id': 'fake_tenant_id',
'display_name': 'fake',
'name': 'share-fake-manage-uuid',
'size': 1,
'share_proto': 'NFS',
'share_network_id': 'fake_net_id',
'share_server_id': 'fake-share-srv-id',
'export_locations': [
{'path': '100.115.10.68:/share_fake_uuid'},
],
'host': 'fake_host@fake_backend#OpenStack_Pool',
'share_type_id': 'fake_id',
}
self.share_pool_name_not_match = {
'id': 'fake_uuid',
'project_id': 'fake_tenant_id',
'display_name': 'fake',
'name': 'share-fake-manage-uuid',
'size': 1,
'share_proto': 'NFS',
'share_network_id': 'fake_net_id',
'share_server_id': 'fake-share-srv-id',
'export_locations': [
{'path': '100.115.10.68:/share_fake_uuid'},
],
'host': 'fake_host@fake_backend#OpenStack_Pool_not_match',
'share_type_id': 'fake_id',
}
self.share_proto_fail = {
@ -433,7 +475,27 @@ class HuaweiShareDriverTestCase(test.TestCase):
'share_proto': 'CIFS',
'share_network_id': 'fake_net_id',
'share_server_id': 'fake-share-srv-id',
'export_locations': [
{'path': 'share_fake_uuid'},
],
'host': 'fake_host@fake_backend#OpenStack_Pool',
'share_type_id': 'fake_id',
}
self.share_manage_cifs = {
'id': 'fake_uuid',
'project_id': 'fake_tenant_id',
'display_name': 'fake',
'name': 'share-fake-manage-uuid',
'size': 1,
'share_proto': 'CIFS',
'share_network_id': 'fake_net_id',
'share_server_id': 'fake-share-srv-id',
'export_locations': [
{'path': '\\\\100.115.10.68\\share_fake_uuid'},
],
'host': 'fake_host@fake_backend#OpenStack_Pool',
'share_type_id': 'fake_id',
}
self.nfs_snapshot = {
@ -478,6 +540,9 @@ class HuaweiShareDriverTestCase(test.TestCase):
'access_level': 'rw',
}
self.driver_options = {
'volume_id': 'fake',
}
self.share_server = None
self.driver._licenses = ['fake']
@ -516,6 +581,23 @@ class HuaweiShareDriverTestCase(test.TestCase):
'host': 'fake_host@fake_backend#OpenStack_Pool2',
}
fake_extra_specs = {
u'driver_handles_share_servers': u'False',
}
fake_share_type_id = u'fake_id'
self.fake_type_extra = {
'test_with_extra': {
'created_at': 'fake_time',
'deleted': '0',
'deleted_at': None,
'extra_specs': fake_extra_specs,
'required_extra_specs': {},
'id': fake_share_type_id,
'name': u'test_with_extra',
'updated_at': None
}
}
def test_conf_product_fail(self):
self.recreate_fake_conf_file(product_flag=False)
self.driver.plugin.configuration.manila_huawei_conf_file = (
@ -1088,6 +1170,132 @@ class HuaweiShareDriverTestCase(test.TestCase):
self.driver.delete_snapshot, self._context,
self.cifs_snapshot, self.share_server)
@ddt.data({"share_proto": "NFS",
"path": ["100.115.10.68:/share_fake_manage_uuid"]},
{"share_proto": "CIFS",
"path": ["\\\\100.115.10.68\\share_fake_manage_uuid"]})
@ddt.unpack
def test_manage_share_nfs_success(self, share_proto, path):
if share_proto == "NFS":
share = self.share_manage_nfs
elif share_proto == "CIFS":
share = self.share_manage_cifs
share_type = self.fake_type_extra['test_with_extra']
self.mock_object(db,
'share_type_get',
mock.Mock(return_value=share_type))
self.driver.plugin.helper.login()
share_info = self.driver.manage_existing(share,
self.driver_options)
self.assertEqual(4, share_info["size"])
self.assertEqual(path,
share_info["export_locations"])
@ddt.data({"flag": "share_not_exist", "exc": exception.InvalidShare},
{"flag": "fs_status_error", "exc": exception.InvalidShare},
{"flag": "poolname_not_match", "exc": exception.InvalidHost})
@ddt.unpack
def test_manage_share_fail(self, flag, exc):
share = None
if flag == "share_not_exist":
self.driver.plugin.helper.share_exist = False
share = self.share_nfs
elif flag == "fs_status_error":
self.driver.plugin.helper.fs_status_flag = False
share = self.share_nfs
elif flag == "poolname_not_match":
share = self.share_pool_name_not_match
self.driver.plugin.helper.login()
share_type = self.fake_type_extra['test_with_extra']
self.mock_object(db,
'share_type_get',
mock.Mock(return_value=share_type))
self.assertRaises(exc,
self.driver.manage_existing,
share,
self.driver_options)
@ddt.data({"share_proto": "NFS",
"export_path": "fake_ip:/share_fake_uuid"},
{"share_proto": "NFS", "export_path": "fake_ip:/"},
{"share_proto": "NFS",
"export_path": "100.112.0.1://share_fake_uuid"},
{"share_proto": "NFS", "export_path": None},
{"share_proto": "NFS", "export_path": "\\share_fake_uuid"},
{"share_proto": "CIFS",
"export_path": "\\\\fake_ip\\share_fake_uuid"},
{"share_proto": "CIFS",
"export_path": "\\dd\\100.115.10.68\\share_fake_uuid"})
@ddt.unpack
def test_manage_export_path_fail(self, share_proto, export_path):
share_manage_nfs_export_path_fail = {
'id': 'fake_uuid',
'project_id': 'fake_tenant_id',
'display_name': 'fake',
'name': 'share-fake-manage-uuid',
'size': 1,
'share_proto': share_proto,
'share_network_id': 'fake_net_id',
'share_server_id': 'fake-share-srv-id',
'export_locations': [
{'path': export_path},
],
'host': 'fake_host@fake_backend#OpenStack_Pool',
'share_type_id': 'fake_id'
}
share_type = self.fake_type_extra['test_with_extra']
self.mock_object(db,
'share_type_get',
mock.Mock(return_value=share_type))
self.driver.plugin.helper.login()
self.assertRaises(exception.InvalidInput,
self.driver.manage_existing,
share_manage_nfs_export_path_fail,
self.driver_options)
def test_manage_logical_port_ip_fail(self):
self.recreate_fake_conf_file(logical_port_ip="")
self.driver.plugin.configuration.manila_huawei_conf_file = (
self.fake_conf_file)
self.driver.plugin.helper.login()
share_type = self.fake_type_extra['test_with_extra']
self.mock_object(db,
'share_type_get',
mock.Mock(return_value=share_type))
self.assertRaises(exception.InvalidInput,
self.driver.manage_existing,
self.share_nfs,
self.driver_options)
def test_manage_existing_share_type_mismatch(self):
fake_extra_specs = {
u'driver_handles_share_servers': u'True',
}
fake_share_type_id = u'fake_id'
fake_type_mismatch_extra = {
'test_with_extra': {
'created_at': 'fake_time',
'deleted': '0',
'deleted_at': None,
'extra_specs': fake_extra_specs,
'required_extra_specs': {},
'id': fake_share_type_id,
'name': u'test_with_extra',
'updated_at': None
}
}
share_type = fake_type_mismatch_extra['test_with_extra']
self.mock_object(db,
'share_type_get',
mock.Mock(return_value=share_type))
self.driver.plugin.helper.login()
self.assertRaises(exception.ManageExistingShareTypeMismatch,
self.driver.manage_existing,
self.share_nfs,
self.driver_options)
def test_get_pool_success(self):
self.driver.plugin.helper.login()
pool_name = self.driver.get_pool(self.share_nfs_host_not_exist)
@ -1126,7 +1334,8 @@ class HuaweiShareDriverTestCase(test.TestCase):
pool_node_flag=True, timeout_flag=True,
wait_interval_flag=True,
alloctype_value='Thick',
multi_url=False):
multi_url=False,
logical_port_ip='100.115.10.68'):
doc = xml.dom.minidom.Document()
config = doc.createElement('Config')
doc.appendChild(config)
@ -1135,7 +1344,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
config.appendChild(storage)
controllerip0 = doc.createElement('LogicalPortIP')
controllerip0_text = doc.createTextNode('100.115.10.68')
controllerip0_text = doc.createTextNode(logical_port_ip)
controllerip0.appendChild(controllerip0_text)
storage.appendChild(controllerip0)
@ -1220,12 +1429,14 @@ class HuaweiShareDriverTestCase(test.TestCase):
pool_node_flag=True, timeout_flag=True,
wait_interval_flag=True,
alloctype_value='Thick',
multi_url=False):
multi_url=False,
logical_port_ip='100.115.10.68'):
self.tmp_dir = tempfile.mkdtemp()
self.fake_conf_file = self.tmp_dir + '/manila_huawei_conf.xml'
self.addCleanup(shutil.rmtree, self.tmp_dir)
self.create_fake_conf_file(self.fake_conf_file, product_flag,
username_flag, pool_node_flag,
timeout_flag, wait_interval_flag,
alloctype_value, multi_url)
alloctype_value, multi_url,
logical_port_ip)
self.addCleanup(os.remove, self.fake_conf_file)