Merge "Remove ProphetStor Flexvisor Driver"

This commit is contained in:
Zuul 2019-12-21 21:12:22 +00:00 committed by Gerrit Code Review
commit e45bb374ae
11 changed files with 8 additions and 3173 deletions

View File

@ -134,8 +134,6 @@ from cinder.volume.drivers.nexenta import options as \
cinder_volume_drivers_nexenta_options
from cinder.volume.drivers import nfs as cinder_volume_drivers_nfs
from cinder.volume.drivers import nimble as cinder_volume_drivers_nimble
from cinder.volume.drivers.prophetstor import options as \
cinder_volume_drivers_prophetstor_options
from cinder.volume.drivers import pure as cinder_volume_drivers_pure
from cinder.volume.drivers import qnap as cinder_volume_drivers_qnap
from cinder.volume.drivers import quobyte as cinder_volume_drivers_quobyte
@ -341,7 +339,6 @@ def list_opts():
cinder_volume_drivers_nexenta_options.NEXENTA_EDGE_OPTS,
cinder_volume_drivers_nfs.nfs_opts,
cinder_volume_drivers_nimble.nimble_opts,
cinder_volume_drivers_prophetstor_options.DPL_OPTS,
cinder_volume_drivers_pure.PURE_OPTS,
cinder_volume_drivers_qnap.qnap_opts,
cinder_volume_drivers_quobyte.volume_opts,

View File

@ -1,931 +0,0 @@
# Copyright (c) 2014 ProphetStor, Inc.
# All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import errno
import re
import mock
from oslo_utils import units
from six.moves import http_client
from cinder import context
from cinder import exception
from cinder.objects import fields
from cinder import test
from cinder.tests.unit import fake_constants
from cinder.tests.unit import fake_snapshot
from cinder.tests.unit import utils as test_utils
from cinder.volume import configuration as conf
from cinder.volume.drivers.prophetstor import dpl_iscsi as DPLDRIVER
from cinder.volume.drivers.prophetstor import dplcommon as DPLCOMMON
from cinder.volume import group_types
POOLUUID = 'ac33fc6e417440d5a1ef27d7231e1cc4'
VOLUMEUUID = 'a000000000000000000000000000001'
INITIATOR = 'iqn.2013-08.org.debian:01:aaaaaaaa'
DATA_IN_CONNECTOR = {'initiator': INITIATOR}
DATA_SERVER_INFO = 0, {
'metadata': {'vendor': 'ProphetStor',
'version': '1.5'}}
DATA_POOLS = 0, {
'children': [POOLUUID]
}
DATA_POOLINFO = 0, {
'capabilitiesURI': '',
'children': [],
'childrenrange': '',
'completionStatus': 'Complete',
'metadata': {'available_capacity': 4294967296,
'ctime': 1390551362349,
'vendor': 'prophetstor',
'version': '1.5',
'display_description': 'Default Pool',
'display_name': 'default_pool',
'event_uuid': '4f7c4d679a664857afa4d51f282a516a',
'physical_device': {'cache': [],
'data': ['disk_uuid_0',
'disk_uuid_1',
'disk_uuid_2'],
'log': [],
'spare': []},
'pool_uuid': POOLUUID,
'properties': {'raid_level': 'raid0'},
'state': 'Online',
'used_capacity': 0,
'total_capacity': 4294967296,
'zpool_guid': '8173612007304181810'},
'objectType': 'application/cdmi-container',
'percentComplete': 100}
DATA_ASSIGNVDEV = 0, {
'children': [],
'childrenrange': '',
'completionStatus': 'Complete',
'domainURI': '',
'exports': {'Network/iSCSI': [
{'logical_unit_name': '',
'logical_unit_number': '101',
'permissions': [INITIATOR],
'portals': ['172.31.1.210:3260'],
'target_identifier':
'iqn.2013-09.com.prophetstor:hypervisor.886423051816'
}]},
'metadata': {'ctime': 0,
'event_uuid': 'c11e90287e9348d0b4889695f1ec4be5',
'type': 'volume'},
'objectID': '',
'objectName': 'd827e23d403f4f12bb208a6fec208fd8',
'objectType': 'application/cdmi-container',
'parentID': '8daa374670af447e8efea27e16bf84cd',
'parentURI': '/dpl_volume',
'snapshots': []
}
DATA_OUTPUT = 0, None
MOD_OUTPUT = {'status': 'available'}
DATA_IN_GROUP = {'id': 'fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
'name': 'group123',
'description': 'des123',
'status': ''}
DATA_IN_VOLUME = {'id': 'c11e902-87e9-348d-0b48-89695f1ec4be5',
'display_name': 'abc123',
'display_description': '',
'size': 10,
'host': "hostname@backend#%s" % POOLUUID}
DATA_IN_VOLUME_VG = {'id': 'fe2dbc5-1581-0451-dab2-f8c8a48d15bee',
'display_name': 'abc123',
'display_description': '',
'size': 10,
'group_id':
'fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
'status': 'available',
'host': "hostname@backend#%s" % POOLUUID}
DATA_IN_REMOVE_VOLUME_VG = {
'id': 'fe2dbc515810451dab2f8c8a48d15bee',
'display_name': 'fe2dbc515810451dab2f8c8a48d15bee',
'display_description': '',
'size': 10,
'group_id': 'fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
'status': 'available',
'host': "hostname@backend#%s" % POOLUUID}
DATA_IN_VOLUME1 = {'id': 'c11e902-87e9-348d-0b48-89695f1ec4bef',
'display_name': 'abc456',
'display_description': '',
'size': 10,
'host': "hostname@backend#%s" % POOLUUID}
DATA_IN_CG_SNAPSHOT = {
'group_id': 'fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
'id': 'cgsnapshot1',
'name': 'cgsnapshot1',
'description': 'cgsnapshot1',
'status': ''}
DATA_IN_SNAPSHOT = {'id': 'fe2dbc5-1581-0451-dab2-f8c8a48d15bee',
'volume_id': 'c11e902-87e9-348d-0b48-89695f1ec4be5',
'display_name': 'snapshot1',
'display_description': '',
'volume_size': 5}
DATA_OUT_SNAPSHOT_CG = {
'id': 'snapshot1',
'volume_id': 'c11e902-87e9-348d-0b48-89695f1ec4be5',
'display_name': 'snapshot1',
'display_description': '',
'group_snapshot_id': 'fe2dbc51-5810-451d-ab2f-8c8a48d15bee'}
DATA_OUT_CG = {
"objectType": "application/cdmi-container",
"objectID": "fe2dbc515810451dab2f8c8a48d15bee",
"objectName": "<new_volume_group_uuid>",
"parentURI": "/dpl_volgroup",
"parentID": "fe2dbc515810451dab2f8c8a48d15bee",
"domainURI": "",
"capabilitiesURI": "",
"completionStatus": "Complete",
"percentComplete": 100,
"metadata":
{
"type": "volume|snapshot|replica",
"volume_group_uuid": "<volume_group_uuid>",
"origin_uuid": "<origin_uuid>",
"snapshot_uuid": "<snapshot_uuid>",
"display_name": "<display name>",
"display_description": "<display description>",
"ctime": 12345678,
"total_capacity": 1024,
"snapshot_used_capacity": 0,
"maximum_snapshot": 1024,
"snapshot_quota": 0,
"state": "<state>",
"properties":
{
"snapshot_rotation": True,
}
},
"childrenrange": "<range>",
"children":
[
'fe2dbc515810451dab2f8c8a48d15bee',
],
}
class TestProphetStorDPLVolume(test.TestCase):
def _gen_snapshot_url(self, vdevid, snapshotid):
snapshot_url = '/%s/%s/%s' % (vdevid, DPLCOMMON.DPL_OBJ_SNAPSHOT,
snapshotid)
return snapshot_url
def setUp(self):
super(TestProphetStorDPLVolume, self).setUp()
self.dplcmd = DPLCOMMON.DPLVolume('1.1.1.1', 8356, 'admin', 'password')
self.DPL_MOCK = mock.MagicMock()
self.dplcmd.objCmd = self.DPL_MOCK
self.DPL_MOCK.send_cmd.return_value = DATA_OUTPUT
def test_getserverinfo(self):
self.dplcmd.get_server_info()
self.DPL_MOCK.send_cmd.assert_called_once_with(
'GET',
'/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_SYSTEM),
None,
[http_client.OK, http_client.ACCEPTED])
def test_createvdev(self):
self.dplcmd.create_vdev(DATA_IN_VOLUME['id'],
DATA_IN_VOLUME['display_name'],
DATA_IN_VOLUME['display_description'],
POOLUUID,
int(DATA_IN_VOLUME['size']) * units.Gi)
metadata = {}
metadata['display_name'] = DATA_IN_VOLUME['display_name']
metadata['display_description'] = DATA_IN_VOLUME['display_description']
metadata['pool_uuid'] = POOLUUID
metadata['total_capacity'] = int(DATA_IN_VOLUME['size']) * units.Gi
metadata['maximum_snapshot'] = 1024
metadata['properties'] = dict(thin_provision=True)
params = {}
params['metadata'] = metadata
self.DPL_MOCK.send_cmd.assert_called_once_with(
'PUT',
'/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME,
DATA_IN_VOLUME['id']),
params,
[http_client.OK, http_client.ACCEPTED, http_client.CREATED])
def test_extendvdev(self):
self.dplcmd.extend_vdev(DATA_IN_VOLUME['id'],
DATA_IN_VOLUME['display_name'],
DATA_IN_VOLUME['display_description'],
int(DATA_IN_VOLUME['size']) * units.Gi)
metadata = {}
metadata['display_name'] = DATA_IN_VOLUME['display_name']
metadata['display_description'] = DATA_IN_VOLUME['display_description']
metadata['total_capacity'] = int(DATA_IN_VOLUME['size']) * units.Gi
metadata['maximum_snapshot'] = 1024
params = {}
params['metadata'] = metadata
self.DPL_MOCK.send_cmd.assert_called_once_with(
'PUT',
'/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME,
DATA_IN_VOLUME['id']),
params,
[http_client.OK, http_client.ACCEPTED, http_client.CREATED])
def test_deletevdev(self):
self.dplcmd.delete_vdev(DATA_IN_VOLUME['id'], True)
metadata = {}
params = {}
metadata['force'] = True
params['metadata'] = metadata
self.DPL_MOCK.send_cmd.assert_called_once_with(
'DELETE',
'/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME,
DATA_IN_VOLUME['id']),
params,
[http_client.OK, http_client.ACCEPTED, http_client.NOT_FOUND,
http_client.NO_CONTENT])
def test_createvdevfromsnapshot(self):
self.dplcmd.create_vdev_from_snapshot(
DATA_IN_VOLUME['id'],
DATA_IN_VOLUME['display_name'],
DATA_IN_VOLUME['display_description'],
DATA_IN_SNAPSHOT['id'],
POOLUUID)
metadata = {}
params = {}
metadata['snapshot_operation'] = 'copy'
metadata['display_name'] = DATA_IN_VOLUME['display_name']
metadata['display_description'] = DATA_IN_VOLUME['display_description']
metadata['pool_uuid'] = POOLUUID
metadata['maximum_snapshot'] = 1024
metadata['properties'] = dict(thin_provision=True)
params['metadata'] = metadata
params['copy'] = self._gen_snapshot_url(DATA_IN_VOLUME['id'],
DATA_IN_SNAPSHOT['id'])
self.DPL_MOCK.send_cmd.assert_called_once_with(
'PUT',
'/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME,
DATA_IN_VOLUME['id']),
params,
[http_client.OK, http_client.ACCEPTED, http_client.CREATED])
def test_getpool(self):
self.dplcmd.get_pool(POOLUUID)
self.DPL_MOCK.send_cmd.assert_called_once_with(
'GET',
'/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_POOL,
POOLUUID),
None,
[http_client.OK, http_client.ACCEPTED])
def test_clonevdev(self):
self.dplcmd.clone_vdev(
DATA_IN_VOLUME['id'],
DATA_IN_VOLUME1['id'],
POOLUUID,
DATA_IN_VOLUME['display_name'],
DATA_IN_VOLUME['display_description'],
int(DATA_IN_VOLUME['size']) * units.Gi
)
metadata = {}
params = {}
metadata["snapshot_operation"] = "clone"
metadata["display_name"] = DATA_IN_VOLUME['display_name']
metadata["display_description"] = DATA_IN_VOLUME['display_description']
metadata["pool_uuid"] = POOLUUID
metadata["total_capacity"] = int(DATA_IN_VOLUME['size']) * units.Gi
metadata['maximum_snapshot'] = 1024
metadata['properties'] = dict(thin_provision=True)
params["metadata"] = metadata
params["copy"] = DATA_IN_VOLUME['id']
self.DPL_MOCK.send_cmd.assert_called_once_with(
'PUT',
'/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME,
DATA_IN_VOLUME1['id']),
params,
[http_client.OK, http_client.CREATED, http_client.ACCEPTED])
def test_createvdevsnapshot(self):
self.dplcmd.create_vdev_snapshot(
DATA_IN_VOLUME['id'],
DATA_IN_SNAPSHOT['id'],
DATA_IN_SNAPSHOT['display_name'],
DATA_IN_SNAPSHOT['display_description']
)
metadata = {}
params = {}
metadata['display_name'] = DATA_IN_SNAPSHOT['display_name']
metadata['display_description'] = (
DATA_IN_SNAPSHOT['display_description'])
params['metadata'] = metadata
params['snapshot'] = DATA_IN_SNAPSHOT['id']
self.DPL_MOCK.send_cmd.assert_called_once_with(
'PUT',
'/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME,
DATA_IN_VOLUME['id']),
params,
[http_client.OK, http_client.CREATED, http_client.ACCEPTED])
def test_getvdev(self):
self.dplcmd.get_vdev(DATA_IN_VOLUME['id'])
self.DPL_MOCK.send_cmd.assert_called_once_with(
'GET',
'/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1, DPLCOMMON.DPL_OBJ_VOLUME,
DATA_IN_VOLUME['id']),
None,
[http_client.OK, http_client.ACCEPTED, http_client.NOT_FOUND])
def test_getvdevstatus(self):
self.dplcmd.get_vdev_status(DATA_IN_VOLUME['id'], '123456')
self.DPL_MOCK.send_cmd.assert_called_once_with(
'GET',
'/%s/%s/%s/?event_uuid=%s' % (DPLCOMMON.DPL_VER_V1,
DPLCOMMON.DPL_OBJ_VOLUME,
DATA_IN_VOLUME['id'],
'123456'),
None,
[http_client.OK, http_client.NOT_FOUND])
def test_getpoolstatus(self):
self.dplcmd.get_pool_status(POOLUUID, '123456')
self.DPL_MOCK.send_cmd.assert_called_once_with(
'GET',
'/%s/%s/%s/?event_uuid=%s' % (DPLCOMMON.DPL_VER_V1,
DPLCOMMON.DPL_OBJ_POOL,
POOLUUID,
'123456'),
None,
[http_client.OK, http_client.NOT_FOUND])
def test_assignvdev(self):
self.dplcmd.assign_vdev(
DATA_IN_VOLUME['id'],
'iqn.1993-08.org.debian:01:test1',
'',
'1.1.1.1:3260',
0
)
params = {}
metadata = {}
exports = {}
metadata['export_operation'] = 'assign'
exports['Network/iSCSI'] = {}
target_info = {}
target_info['logical_unit_number'] = 0
target_info['logical_unit_name'] = ''
permissions = []
portals = []
portals.append('1.1.1.1:3260')
permissions.append('iqn.1993-08.org.debian:01:test1')
target_info['permissions'] = permissions
target_info['portals'] = portals
exports['Network/iSCSI'] = target_info
params['metadata'] = metadata
params['exports'] = exports
self.DPL_MOCK.send_cmd.assert_called_once_with(
'PUT',
'/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1,
DPLCOMMON.DPL_OBJ_VOLUME,
DATA_IN_VOLUME['id']),
params,
[http_client.OK, http_client.ACCEPTED, http_client.CREATED])
def test_unassignvdev(self):
self.dplcmd.unassign_vdev(DATA_IN_VOLUME['id'],
'iqn.1993-08.org.debian:01:test1',
'')
params = {}
metadata = {}
exports = {}
metadata['export_operation'] = 'unassign'
params['metadata'] = metadata
exports['Network/iSCSI'] = {}
exports['Network/iSCSI']['target_identifier'] = ''
permissions = []
permissions.append('iqn.1993-08.org.debian:01:test1')
exports['Network/iSCSI']['permissions'] = permissions
params['exports'] = exports
self.DPL_MOCK.send_cmd.assert_called_once_with(
'PUT',
'/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1,
DPLCOMMON.DPL_OBJ_VOLUME,
DATA_IN_VOLUME['id']),
params,
[http_client.OK, http_client.ACCEPTED,
http_client.NO_CONTENT, http_client.NOT_FOUND])
def test_deletevdevsnapshot(self):
self.dplcmd.delete_vdev_snapshot(DATA_IN_VOLUME['id'],
DATA_IN_SNAPSHOT['id'])
params = {}
params['copy'] = self._gen_snapshot_url(DATA_IN_VOLUME['id'],
DATA_IN_SNAPSHOT['id'])
self.DPL_MOCK.send_cmd.assert_called_once_with(
'DELETE',
'/%s/%s/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1,
DPLCOMMON.DPL_OBJ_VOLUME,
DATA_IN_VOLUME['id'],
DPLCOMMON.DPL_OBJ_SNAPSHOT,
DATA_IN_SNAPSHOT['id']),
None,
[http_client.OK, http_client.ACCEPTED, http_client.NO_CONTENT,
http_client.NOT_FOUND])
def test_listvdevsnapshots(self):
self.dplcmd.list_vdev_snapshots(DATA_IN_VOLUME['id'])
self.DPL_MOCK.send_cmd.assert_called_once_with(
'GET',
'/%s/%s/%s/%s/' % (DPLCOMMON.DPL_VER_V1,
DPLCOMMON.DPL_OBJ_VOLUME,
DATA_IN_VOLUME['id'],
DPLCOMMON.DPL_OBJ_SNAPSHOT),
None,
[http_client.OK])
class TestProphetStorDPLDriver(test.TestCase):
def __init__(self, method):
super(TestProphetStorDPLDriver, self).__init__(method)
def _conver_uuid2hex(self, strID):
return strID.replace('-', '')
def setUp(self):
super(TestProphetStorDPLDriver, self).setUp()
self.configuration = mock.Mock(conf.Configuration)
self.configuration.san_ip = '1.1.1.1'
self.configuration.dpl_port = 8356
self.configuration.san_login = 'admin'
self.configuration.san_password = 'password'
self.configuration.dpl_pool = POOLUUID
self.configuration.target_port = 3260
self.configuration.san_is_local = False
self.configuration.san_thin_provision = True
self.configuration.driver_ssl_cert_verify = False
self.configuration.driver_ssl_cert_path = None
self.context = context.get_admin_context()
self.DPL_MOCK = mock.MagicMock()
self.DB_MOCK = mock.MagicMock()
self.dpldriver = DPLDRIVER.DPLISCSIDriver(
configuration=self.configuration)
self.dpldriver.dpl = self.DPL_MOCK
self.dpldriver.db = self.DB_MOCK
self.dpldriver.do_setup(self.context)
def test_get_volume_stats(self):
self.DPL_MOCK.get_pool.return_value = DATA_POOLINFO
self.DPL_MOCK.get_server_info.return_value = DATA_SERVER_INFO
res = self.dpldriver.get_volume_stats(True)
self.assertEqual('ProphetStor', res['vendor_name'])
self.assertEqual('1.5', res['driver_version'])
pool = res["pools"][0]
self.assertEqual(4, pool['total_capacity_gb'])
self.assertEqual(4, pool['free_capacity_gb'])
self.assertEqual(0, pool['reserved_percentage'])
self.assertFalse(pool['QoS_support'])
def test_create_volume(self):
volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME['id'],
display_name=DATA_IN_VOLUME['display_name'],
size=DATA_IN_VOLUME['size'],
host=DATA_IN_VOLUME['host'])
self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT
self.dpldriver.create_volume(volume)
self.DPL_MOCK.create_vdev.assert_called_once_with(
self._conver_uuid2hex(volume.id),
volume.display_name,
volume.display_description,
self.configuration.dpl_pool,
int(volume.size) * units.Gi,
True)
def test_create_volume_without_pool(self):
volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME['id'],
display_name=DATA_IN_VOLUME['display_name'],
size=DATA_IN_VOLUME['size'],
host=DATA_IN_VOLUME['host'])
self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT
self.configuration.dpl_pool = ""
volume.host = "host@backend" # missing pool
self.assertRaises(exception.InvalidHost, self.dpldriver.create_volume,
volume=volume)
def test_create_volume_with_configuration_pool(self):
volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME['id'],
display_name=DATA_IN_VOLUME['display_name'],
size=DATA_IN_VOLUME['size'],
host="host@backend")
self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT
self.dpldriver.create_volume(volume)
self.DPL_MOCK.create_vdev.assert_called_once_with(
self._conver_uuid2hex(volume.id),
volume.display_name, volume.display_description,
self.configuration.dpl_pool, int(volume.size) * units.Gi, True)
def test_create_volume_of_group(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
group = test_utils.create_group(
self.context,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
self.DPL_MOCK.create_vdev.return_value = DATA_OUTPUT
self.DPL_MOCK.join_vg.return_value = DATA_OUTPUT
volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME_VG['id'],
display_name=DATA_IN_VOLUME_VG['display_name'],
size=DATA_IN_VOLUME_VG['size'],
group_id=group.id,
host=DATA_IN_VOLUME_VG['host'])
self.dpldriver.create_volume(volume)
self.DPL_MOCK.create_vdev.assert_called_once_with(
self._conver_uuid2hex(volume.id),
volume.display_name,
volume.display_description,
self.configuration.dpl_pool,
int(volume.size) * units.Gi,
True)
self.DPL_MOCK.join_vg.assert_called_once_with(
self._conver_uuid2hex(volume.id),
self._conver_uuid2hex(volume.group_id))
def test_delete_volume(self):
volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME['id'],
display_name=DATA_IN_VOLUME['display_name'],
size=DATA_IN_VOLUME['size'],
host=DATA_IN_VOLUME['host'])
self.DPL_MOCK.delete_vdev.return_value = DATA_OUTPUT
self.dpldriver.delete_volume(volume)
self.DPL_MOCK.delete_vdev.assert_called_once_with(
self._conver_uuid2hex(volume.id))
def test_delete_volume_of_group(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
group = test_utils.create_group(
self.context,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME_VG['id'],
display_name=DATA_IN_VOLUME_VG['display_name'],
size=DATA_IN_VOLUME_VG['size'],
group_id=group.id,
host=DATA_IN_VOLUME_VG['host'])
self.DPL_MOCK.delete_vdev.return_value = DATA_OUTPUT
self.DPL_MOCK.leave_vg.return_volume = DATA_OUTPUT
self.dpldriver.delete_volume(volume)
self.DPL_MOCK.leave_vg.assert_called_once_with(
self._conver_uuid2hex(volume.id),
self._conver_uuid2hex(volume.group_id)
)
self.DPL_MOCK.delete_vdev.assert_called_once_with(
self._conver_uuid2hex(volume.id))
def test_create_volume_from_snapshot(self):
self.DPL_MOCK.create_vdev_from_snapshot.return_value = DATA_OUTPUT
self.DPL_MOCK.extend_vdev.return_value = DATA_OUTPUT
volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME_VG['id'],
display_name=DATA_IN_VOLUME_VG['display_name'],
size=DATA_IN_VOLUME_VG['size'],
host=DATA_IN_VOLUME_VG['host'])
self.dpldriver.create_volume_from_snapshot(
volume, DATA_IN_SNAPSHOT)
self.DPL_MOCK.create_vdev_from_snapshot.assert_called_once_with(
self._conver_uuid2hex(volume.id),
volume.display_name,
volume.display_description,
self._conver_uuid2hex(volume.id),
self.configuration.dpl_pool,
True)
self.DPL_MOCK.extend_vdev.assert_called_once_with(
self._conver_uuid2hex(volume.id),
volume.display_name,
volume.display_description,
volume.size * units.Gi)
def test_create_cloned_volume(self):
new_volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME1['id'],
display_name=DATA_IN_VOLUME1['display_name'],
size=DATA_IN_VOLUME1['size'],
host=DATA_IN_VOLUME1['host'])
src_volume = test_utils.create_volume(
self.context,
id=DATA_IN_VOLUME['id'])
self.DPL_MOCK.clone_vdev.return_value = DATA_OUTPUT
self.dpldriver.create_cloned_volume(new_volume, src_volume)
self.DPL_MOCK.clone_vdev.assert_called_once_with(
self._conver_uuid2hex(src_volume.id),
self._conver_uuid2hex(new_volume.id),
self.configuration.dpl_pool,
new_volume.display_name,
new_volume.display_description,
int(new_volume.size) *
units.Gi,
True)
def test_create_snapshot(self):
self.DPL_MOCK.create_vdev_snapshot.return_value = DATA_OUTPUT
self.dpldriver.create_snapshot(DATA_IN_SNAPSHOT)
self.DPL_MOCK.create_vdev_snapshot.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_SNAPSHOT['volume_id']),
self._conver_uuid2hex(DATA_IN_SNAPSHOT['id']),
DATA_IN_SNAPSHOT['display_name'],
DATA_IN_SNAPSHOT['display_description'])
def test_delete_snapshot(self):
self.DPL_MOCK.delete_vdev_snapshot.return_value = DATA_OUTPUT
self.dpldriver.delete_snapshot(DATA_IN_SNAPSHOT)
self.DPL_MOCK.delete_vdev_snapshot.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_SNAPSHOT['volume_id']),
self._conver_uuid2hex(DATA_IN_SNAPSHOT['id']))
def test_initialize_connection(self):
self.DPL_MOCK.assign_vdev.return_value = DATA_ASSIGNVDEV
self.DPL_MOCK.get_vdev.return_value = DATA_ASSIGNVDEV
res = self.dpldriver.initialize_connection(DATA_IN_VOLUME,
DATA_IN_CONNECTOR)
self.assertEqual('iscsi', res['driver_volume_type'])
self.assertEqual(101, res['data']['target_lun'])
self.assertTrue(res['data']['target_discovered'])
self.assertEqual('172.31.1.210:3260', res['data']['target_portal'])
self.assertEqual(
'iqn.2013-09.com.prophetstor:hypervisor.886423051816',
res['data']['target_iqn'])
def test_terminate_connection(self):
self.DPL_MOCK.unassign_vdev.return_value = DATA_OUTPUT
self.dpldriver.terminate_connection(DATA_IN_VOLUME, DATA_IN_CONNECTOR)
self.DPL_MOCK.unassign_vdev.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_VOLUME['id']),
DATA_IN_CONNECTOR['initiator'])
def test_terminate_connection_volume_detached(self):
self.DPL_MOCK.unassign_vdev.return_value = errno.ENODATA, None
self.dpldriver.terminate_connection(DATA_IN_VOLUME, DATA_IN_CONNECTOR)
self.DPL_MOCK.unassign_vdev.assert_called_once_with(
self._conver_uuid2hex(DATA_IN_VOLUME['id']),
DATA_IN_CONNECTOR['initiator'])
def test_terminate_connection_failed(self):
self.DPL_MOCK.unassign_vdev.return_value = errno.EFAULT, None
ex = self.assertRaises(
exception.VolumeBackendAPIException,
self.dpldriver.terminate_connection,
volume=DATA_IN_VOLUME, connector=DATA_IN_CONNECTOR)
self.assertIsNotNone(
re.match(r".*Flexvisor failed", ex.msg))
def test_get_pool_info(self):
self.DPL_MOCK.get_pool.return_value = DATA_POOLINFO
_, res = self.dpldriver._get_pool_info(POOLUUID)
self.assertEqual(4294967296, res['metadata']['available_capacity'])
self.assertEqual(1390551362349, res['metadata']['ctime'])
self.assertEqual('Default Pool',
res['metadata']['display_description'])
self.assertEqual('default_pool',
res['metadata']['display_name'])
self.assertEqual('4f7c4d679a664857afa4d51f282a516a',
res['metadata']['event_uuid'])
self.assertEqual(
{'cache': [],
'data': ['disk_uuid_0', 'disk_uuid_1', 'disk_uuid_2'],
'log': [],
'spare': []},
res['metadata']['physical_device'])
self.assertEqual(POOLUUID, res['metadata']['pool_uuid'])
self.assertEqual(
{'raid_level': 'raid0'},
res['metadata']['properties'])
self.assertEqual('Online', res['metadata']['state'])
self.assertEqual(4294967296, res['metadata']['total_capacity'])
self.assertEqual('8173612007304181810', res['metadata']['zpool_guid'])
def test_create_group(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
group = test_utils.create_group(
self.context,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
self.DPL_MOCK.create_vg.return_value = DATA_OUTPUT
model_update = self.dpldriver.create_group(self.context, group)
self.DPL_MOCK.create_vg.assert_called_once_with(
self._conver_uuid2hex(fake_constants.CONSISTENCY_GROUP_ID),
'test_group',
'this is a test group')
self.assertDictEqual({'status': (
fields.ConsistencyGroupStatus.AVAILABLE)}, model_update)
def test_delete_group(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
group = test_utils.create_group(
self.context,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
self.DB_MOCK.volume_get_all_by_group.return_value = (
[DATA_IN_VOLUME_VG])
self.DPL_MOCK.delete_vdev.return_value = DATA_OUTPUT
self.DPL_MOCK.delete_cg.return_value = DATA_OUTPUT
model_update, volumes = self.dpldriver.delete_group(
self.context, group, [])
self.DPL_MOCK.delete_vg.assert_called_once_with(
self._conver_uuid2hex(fake_constants.CONSISTENCY_GROUP_ID))
self.DPL_MOCK.delete_vdev.assert_called_once_with(
self._conver_uuid2hex((DATA_IN_VOLUME_VG['id'])))
self.assertDictEqual({'status': (
fields.ConsistencyGroupStatus.DELETED)}, model_update)
def test_update_group(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
self.DPL_MOCK.get_vg.return_value = (0, DATA_OUT_CG)
self.DPL_MOCK.join_vg.return_value = DATA_OUTPUT
self.DPL_MOCK.leave_vg.return_value = DATA_OUTPUT
group = test_utils.create_group(
self.context,
id='fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
vol_add = test_utils.create_volume(
self.context,
id=fake_constants.VOLUME2_ID,
display_name=DATA_IN_VOLUME_VG['display_name'],
size=DATA_IN_VOLUME_VG['size'],
group_id='fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
host=DATA_IN_VOLUME_VG['host'])
vol_del = test_utils.create_volume(
self.context,
id=DATA_IN_REMOVE_VOLUME_VG['id'],
display_name=DATA_IN_REMOVE_VOLUME_VG['display_name'],
size=DATA_IN_REMOVE_VOLUME_VG['size'],
group_id='fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
host=DATA_IN_REMOVE_VOLUME_VG['host'])
(model_update, add_vols, remove_vols) = (
self.dpldriver.update_group(
self.context, group, [vol_add], [vol_del]))
self.DPL_MOCK.join_vg.assert_called_once_with(
self._conver_uuid2hex(vol_add.id),
self._conver_uuid2hex(group.id))
self.DPL_MOCK.leave_vg.assert_called_once_with(
self._conver_uuid2hex(vol_del.id),
self._conver_uuid2hex(group.id))
self.assertDictEqual({'status': (
fields.ConsistencyGroupStatus.AVAILABLE)}, model_update)
def test_update_group_exception_join(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
self.DPL_MOCK.get_vg.return_value = (0, DATA_OUT_CG)
self.DPL_MOCK.join_vg.return_value = -1, None
self.DPL_MOCK.leave_vg.return_value = DATA_OUTPUT
volume = test_utils.create_volume(
self.context,
id=fake_constants.VOLUME2_ID,
display_name=DATA_IN_VOLUME_VG['display_name'],
size=DATA_IN_VOLUME_VG['size'],
host=DATA_IN_VOLUME_VG['host'])
group = test_utils.create_group(
self.context,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
self.assertRaises(exception.VolumeBackendAPIException,
self.dpldriver.update_group,
context=None,
group=group,
add_volumes=[volume],
remove_volumes=None)
def test_update_group_exception_leave(self):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
self.DPL_MOCK.get_vg.return_value = (0, DATA_OUT_CG)
self.DPL_MOCK.leave_vg.return_value = -1, None
volume = test_utils.create_volume(
self.context,
id='fe2dbc51-5810-451d-ab2f-8c8a48d15bee',
display_name=DATA_IN_VOLUME_VG['display_name'],
size=DATA_IN_VOLUME_VG['size'],
host=DATA_IN_VOLUME_VG['host'])
group = test_utils.create_group(
self.context,
id=fake_constants.CONSISTENCY_GROUP_ID,
host='host@backend#unit_test_pool',
group_type_id=group_type.id)
self.assertRaises(exception.VolumeBackendAPIException,
self.dpldriver.update_group,
context=None,
group=group,
add_volumes=None,
remove_volumes=[volume])
@mock.patch(
'cinder.objects.snapshot.SnapshotList.get_all_for_group_snapshot')
def test_create_group_snapshot(self, get_all_for_group_snapshot):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
snapshot_obj = fake_snapshot.fake_snapshot_obj(self.context)
snapshot_obj.group_id = \
DATA_IN_CG_SNAPSHOT['group_id']
snapshot_obj.group_type_id = group_type.id
get_all_for_group_snapshot.return_value = [snapshot_obj]
self.DPL_MOCK.create_vdev_snapshot.return_value = DATA_OUTPUT
model_update, snapshots = self.dpldriver.create_group_snapshot(
self.context, snapshot_obj, [])
self.assertDictEqual({'status': 'available'}, model_update)
@mock.patch(
'cinder.objects.snapshot.SnapshotList.get_all_for_group_snapshot')
def test_delete_group_snapshot(self, get_all_for_group_snapshot):
group_type = group_types.create(
self.context,
'group',
{'consistent_group_snapshot_enabled': '<is> True'}
)
snapshot_obj = fake_snapshot.fake_snapshot_obj(self.context)
snapshot_obj.group_id = \
DATA_IN_CG_SNAPSHOT['group_id']
snapshot_obj.group_type_id = group_type.id
get_all_for_group_snapshot.return_value = [snapshot_obj]
self.DPL_MOCK.delete_group_snapshot.return_value = DATA_OUTPUT
model_update, snapshots = self.dpldriver.delete_group_snapshot(
self.context, snapshot_obj, [])
self.DPL_MOCK.delete_vdev_snapshot.assert_called_once_with(
self._conver_uuid2hex(snapshot_obj.group_id),
self._conver_uuid2hex(snapshot_obj.id),
True)
self.assertDictEqual({'status': 'deleted'}, model_update)

View File

@ -1,413 +0,0 @@
# Copyright (c) 2014 ProphetStor, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import errno
from oslo_log import log as logging
from cinder import exception
from cinder.i18n import _
from cinder import interface
from cinder.volume import driver
from cinder.volume.drivers.prophetstor import dplcommon
from cinder.zonemanager import utils as fczm_utils
LOG = logging.getLogger(__name__)
@interface.volumedriver
class DPLFCDriver(dplcommon.DPLCOMMONDriver,
driver.FibreChannelDriver):
def __init__(self, *args, **kwargs):
super(DPLFCDriver, self).__init__(*args, **kwargs)
def _get_fc_channel(self):
"""Get FibreChannel info.
:returns: fcInfos[uuid]
fcInfo[uuid]['display_name']
fcInfo[uuid]['display_description']
fcInfo[uuid]['hardware_address']
fcInfo[uuid]['type']
fcInfo[uuid]['speed']
fcInfo[uuid]['state']
"""
output = None
fcInfos = {}
try:
retCode, output = self.dpl.get_server_info()
if retCode == 0 and output:
fcUuids = output.get('metadata',
{}).get('storage_adapter', {}).keys()
for fcUuid in fcUuids:
fcInfo = output.get('metadata',
{}).get('storage_adapter',
{}).get(fcUuid)
if fcInfo['type'] == 'fc':
fcInfos[fcUuid] = fcInfo
except Exception as e:
LOG.error("Failed to get fiber channel info from storage "
"due to %(stat)s", {'stat': e})
return fcInfos
def _get_targets(self):
"""Get targets.
:returns: targetInfos[uuid] = targetInfo
targetInfo['targetUuid']
targetInfo['targetName']
targetInfo['targetAddr']
"""
output = None
targetInfos = {}
try:
retCode, output = self.dpl.get_target_list('target')
if retCode == 0 and output:
for targetInfo in output.get('children', []):
targetI = {}
targetI['targetUuid'] = targetInfo[0]
targetI['targetName'] = targetInfo[1]
targetI['targetAddr'] = targetInfo[2]
targetInfos[str(targetInfo[0])] = targetI
except Exception as e:
targetInfos = {}
LOG.error("Failed to get fiber channel target from "
"storage server due to %(stat)s",
{'stat': e})
return targetInfos
def _get_targetwpns(self, volumeid, initiatorWwpns):
lstargetWwpns = []
try:
ret, output = self.dpl.get_vdev(volumeid)
if ret == 0 and output:
exports = output.get('exports', {})
fc_infos = exports.get('Network/FC', {})
for fc_info in fc_infos:
for p in fc_info.get('permissions', []):
if p.get(initiatorWwpns, None):
targetWwpns = fc_info.get('target_identifier', '')
lstargetWwpns.append(targetWwpns)
except Exception as e:
LOG.error("Failed to get target wwpns from storage due "
"to %(stat)s", {'stat': e})
lstargetWwpns = []
return lstargetWwpns
def _is_initiator_wwpn_active(self, targetWwpn, initiatorWwpn):
fActive = False
output = None
try:
retCode, output = self.dpl.get_sns_table(targetWwpn)
if retCode == 0 and output:
for fdwwpn, fcport in output.get('metadata',
{}).get('sns_table',
[]):
if fdwwpn == initiatorWwpn:
fActive = True
break
except Exception:
LOG.error('Failed to get sns table')
return fActive
def _convertHex2String(self, wwpns):
szwwpns = ''
if len(str(wwpns)) == 16:
szwwpns = '%2s:%2s:%2s:%2s:%2s:%2s:%2s:%2s' % (
str(wwpns)[0:2],
str(wwpns)[2:4],
str(wwpns)[4:6],
str(wwpns)[6:8],
str(wwpns)[8:10],
str(wwpns)[10:12],
str(wwpns)[12:14],
str(wwpns)[14:16])
return szwwpns
def _export_fc(self, volumeid, targetwwpns, initiatorwwpns, volumename):
ret = 0
output = ''
LOG.debug('Export fc: %(volume)s, %(wwpns)s, %(iqn)s, %(volumename)s',
{'volume': volumeid, 'wwpns': targetwwpns,
'iqn': initiatorwwpns, 'volumename': volumename})
try:
ret, output = self.dpl.assign_vdev_fc(
self._conver_uuid2hex(volumeid), targetwwpns,
initiatorwwpns, volumename)
except Exception:
LOG.error('Volume %(volumeid)s failed to send assign command, '
'ret: %(status)s output: %(output)s',
{'volumeid': volumeid, 'status': ret, 'output': output})
ret = errno.EFAULT
if ret == errno.EAGAIN:
ret, event_uuid = self._get_event_uuid(output)
if len(event_uuid):
ret = 0
status = self._wait_event(
self.dpl.get_vdev_status,
self._conver_uuid2hex(volumeid), event_uuid)
if status['state'] == 'error':
ret = errno.EFAULT
msg = _('Flexvisor failed to assign volume %(id)s: '
'%(status)s.') % {'id': volumeid,
'status': status}
raise exception.VolumeBackendAPIException(data=msg)
else:
ret = errno.EFAULT
msg = _('Flexvisor failed to assign volume %(id)s due to '
'unable to query status by event '
'id.') % {'id': volumeid}
raise exception.VolumeBackendAPIException(data=msg)
elif ret != 0:
msg = _('Flexvisor assign volume failed:%(id)s:'
'%(status)s.') % {'id': volumeid, 'status': ret}
raise exception.VolumeBackendAPIException(data=msg)
return ret
def _delete_export_fc(self, volumeid, targetwwpns, initiatorwwpns):
ret = 0
output = ''
ret, output = self.dpl.unassign_vdev_fc(
self._conver_uuid2hex(volumeid),
targetwwpns, initiatorwwpns)
if ret == errno.EAGAIN:
ret, event_uuid = self._get_event_uuid(output)
if ret == 0 and len(event_uuid):
status = self._wait_event(
self.dpl.get_vdev_status, volumeid, event_uuid)
if status['state'] == 'error':
ret = errno.EFAULT
msg = _('Flexvisor failed to unassign volume %(id)s:'
' %(status)s.') % {'id': volumeid,
'status': status}
raise exception.VolumeBackendAPIException(data=msg)
else:
msg = _('Flexvisor failed to unassign volume (get event) '
'%(id)s.') % {'id': volumeid}
raise exception.VolumeBackendAPIException(data=msg)
elif ret != 0:
msg = _('Flexvisor unassign volume failed:%(id)s:'
'%(status)s.') % {'id': volumeid, 'status': ret}
raise exception.VolumeBackendAPIException(data=msg)
else:
LOG.info('Flexvisor succeeded to unassign volume %(id)s.',
{'id': volumeid})
return ret
def _build_initiator_target_map(self, connector, tgtwwns):
"""Build the target_wwns and the initiator target map."""
init_targ_map = {}
initiator_wwns = connector['wwpns']
for initiator in initiator_wwns:
init_targ_map[initiator] = tgtwwns
return init_targ_map
def initialize_connection(self, volume, connector):
"""Allow connection to connector and return connection info."""
"""
connector = {'ip': CONF.my_ip,
'host': CONF.host,
'initiator': self._initiator,
'wwnns': self._fc_wwnns,
'wwpns': self._fc_wwpns}
"""
dc_fc = {}
dc_target = {}
lsTargetWwpn = []
output = None
properties = {}
preferTargets = {}
ret = 0
targetIdentifier = []
szwwpns = []
LOG.info('initialize_connection volume: %(volume)s, connector:'
' %(connector)s',
{"volume": volume, "connector": connector})
# Get Storage Fiber channel controller
dc_fc = self._get_fc_channel()
# Get existed FC target list to decide target wwpn
dc_target = self._get_targets()
if len(dc_target) == 0:
msg = _('Backend storage did not configure fiber channel '
'target.')
raise exception.VolumeBackendAPIException(data=msg)
for keyFc in dc_fc:
for targetuuid in dc_target:
if dc_fc[keyFc]['hardware_address'] == \
dc_target[targetuuid]['targetAddr']:
preferTargets[targetuuid] = dc_target[targetuuid]
break
# Confirm client wwpn is existed in sns table
# Covert wwwpns to 'xx:xx:xx:xx:xx:xx:xx:xx' format
for dwwpn in connector['wwpns']:
szwwpn = self._convertHex2String(dwwpn)
if len(szwwpn) == 0:
msg = _('Invalid wwpns format %(wwpns)s') % \
{'wwpns': connector['wwpns']}
raise exception.VolumeBackendAPIException(data=msg)
szwwpns.append(szwwpn)
if len(szwwpns):
for targetUuid in preferTargets:
targetWwpn = ''
targetWwpn = preferTargets.get(targetUuid,
{}).get('targetAddr', '')
lsTargetWwpn.append(targetWwpn)
# Use wwpns to assign volume.
LOG.info('Prefer use target wwpn %(wwpn)s',
{'wwpn': lsTargetWwpn})
# Start to create export in all FC target node.
assignedTarget = []
for pTarget in lsTargetWwpn:
try:
ret = self._export_fc(volume['id'], str(pTarget), szwwpns,
volume['name'])
if ret:
break
else:
assignedTarget.append(pTarget)
except Exception as e:
LOG.error('Failed to export fiber channel target '
'due to %s', e)
ret = errno.EFAULT
break
if ret == 0:
ret, output = self.dpl.get_vdev(self._conver_uuid2hex(
volume['id']))
nLun = -1
if ret == 0:
try:
for p in output['exports']['Network/FC']:
# check initiator wwpn existed in target initiator list
for initI in p.get('permissions', []):
for szwpn in szwwpns:
if initI.get(szwpn, None):
nLun = initI[szwpn]
break
if nLun != -1:
break
if nLun != -1:
targetIdentifier.append(
str(p['target_identifier']).replace(':', ''))
except Exception:
msg = _('Invalid connection initialization response of '
'volume %(name)s: '
'%(output)s') % {'name': volume['name'],
'output': output}
raise exception.VolumeBackendAPIException(data=msg)
if nLun != -1:
init_targ_map = self._build_initiator_target_map(connector,
targetIdentifier)
properties['target_discovered'] = True
properties['target_wwn'] = targetIdentifier
properties['target_lun'] = int(nLun)
properties['volume_id'] = volume['id']
properties['initiator_target_map'] = init_targ_map
LOG.info('%(volume)s assign type fibre_channel, properties '
'%(properties)s',
{'volume': volume['id'], 'properties': properties})
else:
msg = _('Invalid connection initialization response of '
'volume %(name)s') % {'name': volume['name']}
raise exception.VolumeBackendAPIException(data=msg)
LOG.info('Connect initialization info: '
'{driver_volume_type: fibre_channel, '
'data: %(properties)s', {'properties': properties})
conn_info = {'driver_volume_type': 'fibre_channel',
'data': properties}
fczm_utils.add_fc_zone(conn_info)
return conn_info
def terminate_connection(self, volume, connector, **kwargs):
"""Disallow connection from connector."""
"""
connector = {'ip': CONF.my_ip,
'host': CONF.host,
'initiator': self._initiator,
'wwnns': self._fc_wwnns,
'wwpns': self._fc_wwpns}
"""
lstargetWwpns = []
lsTargets = []
szwwpns = []
ret = 0
info = {'driver_volume_type': 'fibre_channel', 'data': {}}
LOG.info('terminate_connection volume: %(volume)s, '
'connector: %(con)s',
{'volume': volume, 'con': connector})
# Query targetwwpns.
# Get all target list of volume.
for dwwpn in connector['wwpns']:
szwwpn = self._convertHex2String(dwwpn)
if len(szwwpn) == 0:
msg = _('Invalid wwpns format %(wwpns)s') % \
{'wwpns': connector['wwpns']}
raise exception.VolumeBackendAPIException(data=msg)
szwwpns.append(szwwpn)
if len(szwwpns) == 0:
ret = errno.EFAULT
msg = _('Invalid wwpns format %(wwpns)s') % \
{'wwpns': connector['wwpns']}
raise exception.VolumeBackendAPIException(data=msg)
else:
for szwwpn in szwwpns:
lstargetWwpns = self._get_targetwpns(
self._conver_uuid2hex(volume['id']), szwwpn)
lsTargets = list(set(lsTargets + lstargetWwpns))
# Remove all export target
try:
for ptarget in lsTargets:
ret = self._delete_export_fc(volume['id'], ptarget, szwwpns)
if ret:
break
except Exception:
ret = errno.EFAULT
finally:
if ret:
msg = _('Faield to unassign %(volume)s') % (volume['id'])
raise exception.VolumeBackendAPIException(data=msg)
# Failed to delete export with fibre channel
if ret:
init_targ_map = self._build_initiator_target_map(connector,
lsTargets)
info['data'] = {'target_wwn': lsTargets,
'initiator_target_map': init_targ_map}
fczm_utils.remove_fc_zone(info)
return info
def get_volume_stats(self, refresh=False):
if refresh:
data = super(DPLFCDriver, self).get_volume_stats(refresh)
if data:
data['storage_protocol'] = 'FC'
backend_name = \
self.configuration.safe_get('volume_backend_name')
data['volume_backend_name'] = (backend_name or 'DPLFCDriver')
self._stats = data
return self._stats

View File

@ -1,155 +0,0 @@
# Copyright (c) 2014 ProphetStor, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import errno
from oslo_log import log as logging
from cinder import exception
from cinder.i18n import _
from cinder import interface
import cinder.volume.driver
from cinder.volume.drivers.prophetstor import dplcommon
LOG = logging.getLogger(__name__)
@interface.volumedriver
class DPLISCSIDriver(dplcommon.DPLCOMMONDriver,
cinder.volume.driver.ISCSIDriver):
def __init__(self, *args, **kwargs):
super(DPLISCSIDriver, self).__init__(*args, **kwargs)
def initialize_connection(self, volume, connector):
"""Allow connection to connector and return connection info."""
properties = {}
properties['target_lun'] = None
properties['target_discovered'] = True
properties['target_portal'] = ''
properties['target_iqn'] = None
properties['volume_id'] = volume['id']
dpl_server = self.configuration.san_ip
dpl_iscsi_port = self.configuration.target_port
ret, output = self.dpl.assign_vdev(self._conver_uuid2hex(
volume['id']), connector['initiator'].lower(), volume['id'],
'%s:%d' % (dpl_server, dpl_iscsi_port), 0)
if ret == errno.EAGAIN:
ret, event_uuid = self._get_event_uuid(output)
if len(event_uuid):
ret = 0
status = self._wait_event(
self.dpl.get_vdev_status, self._conver_uuid2hex(
volume['id']), event_uuid)
if status['state'] == 'error':
ret = errno.EFAULT
msg = _('Flexvisor failed to assign volume %(id)s: '
'%(status)s.') % {'id': volume['id'],
'status': status}
raise exception.VolumeBackendAPIException(data=msg)
else:
ret = errno.EFAULT
msg = _('Flexvisor failed to assign volume %(id)s due to '
'unable to query status by event '
'id.') % {'id': volume['id']}
raise exception.VolumeBackendAPIException(data=msg)
elif ret != 0:
msg = _('Flexvisor assign volume failed.:%(id)s:'
'%(status)s.') % {'id': volume['id'], 'status': ret}
raise exception.VolumeBackendAPIException(data=msg)
if ret == 0:
ret, output = self.dpl.get_vdev(
self._conver_uuid2hex(volume['id']))
if ret == 0:
for tgInfo in output['exports']['Network/iSCSI']:
if tgInfo['permissions'] and \
isinstance(tgInfo['permissions'][0], dict):
for assign in tgInfo['permissions']:
if connector['initiator'].lower() in assign.keys():
for tgportal in tgInfo.get('portals', {}):
properties['target_portal'] = tgportal
break
properties['target_lun'] = \
int(assign[connector['initiator'].lower()])
break
if properties['target_portal'] != '':
properties['target_iqn'] = tgInfo['target_identifier']
break
else:
if connector['initiator'].lower() in tgInfo['permissions']:
for tgportal in tgInfo.get('portals', {}):
properties['target_portal'] = tgportal
break
if properties['target_portal'] != '':
properties['target_lun'] = int(
tgInfo['logical_unit_number'])
properties['target_iqn'] = tgInfo['target_identifier']
break
if not (ret == 0 or properties['target_portal']):
msg = _('Flexvisor failed to assign volume %(volume)s '
'iqn %(iqn)s.') % {'volume': volume['id'],
'iqn': connector['initiator']}
raise exception.VolumeBackendAPIException(data=msg)
return {'driver_volume_type': 'iscsi', 'data': properties}
def terminate_connection(self, volume, connector, **kwargs):
"""Disallow connection from connector."""
ret, output = self.dpl.unassign_vdev(
self._conver_uuid2hex(volume['id']),
connector['initiator'])
if ret == errno.EAGAIN:
ret, event_uuid = self._get_event_uuid(output)
if ret == 0:
status = self._wait_event(
self.dpl.get_vdev_status, volume['id'], event_uuid)
if status['state'] == 'error':
ret = errno.EFAULT
msg = _('Flexvisor failed to unassign volume %(id)s:'
' %(status)s.') % {'id': volume['id'],
'status': status}
raise exception.VolumeBackendAPIException(data=msg)
else:
msg = _('Flexvisor failed to unassign volume (get event) '
'%(id)s.') % {'id': volume['id']}
raise exception.VolumeBackendAPIException(data=msg)
elif ret == errno.ENODATA:
LOG.info('Flexvisor already unassigned volume %(id)s.',
{'id': volume['id']})
elif ret != 0:
msg = _('Flexvisor failed to unassign volume:%(id)s:'
'%(status)s.') % {'id': volume['id'], 'status': ret}
raise exception.VolumeBackendAPIException(data=msg)
def get_volume_stats(self, refresh=False):
if refresh:
try:
data = super(DPLISCSIDriver, self).get_volume_stats(refresh)
if data:
data['storage_protocol'] = 'iSCSI'
backend_name = \
self.configuration.safe_get('volume_backend_name')
data['volume_backend_name'] = \
(backend_name or 'DPLISCSIDriver')
self._stats = data
except Exception as exc:
LOG.warning('Cannot get volume status %(exc)s.', {'exc': exc})
return self._stats

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +0,0 @@
# Copyright (c) 2014 ProphetStor, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from cinder.volume import configuration
DPL_OPTS = [
cfg.StrOpt('dpl_pool',
default='',
help='DPL pool uuid in which DPL volumes are stored.'),
cfg.PortOpt('dpl_port',
default=8357,
help='DPL port number.'),
]
CONF = cfg.CONF
CONF.register_opts(DPL_OPTS, group=configuration.SHARED_CONF_GROUP)

View File

@ -1,104 +0,0 @@
===========================================
ProphetStor Fibre Channel and iSCSI drivers
===========================================
ProhetStor Fibre Channel and iSCSI drivers add support for
ProphetStor Flexvisor through the Block Storage service.
ProphetStor Flexvisor enables commodity x86 hardware as software-defined
storage leveraging well-proven ZFS for disk management to provide
enterprise grade storage services such as snapshots, data protection
with different RAID levels, replication, and deduplication.
The ``DPLFCDriver`` and ``DPLISCSIDriver`` drivers run volume operations
by communicating with the ProphetStor storage system over HTTPS.
Supported operations
~~~~~~~~~~~~~~~~~~~~
* Create, delete, attach, and detach volumes.
* Create, list, and delete volume snapshots.
* Create a volume from a snapshot.
* Copy an image to a volume.
* Copy a volume to an image.
* Clone a volume.
* Extend a volume.
Enable the Fibre Channel or iSCSI drivers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``DPLFCDriver`` and ``DPLISCSIDriver`` are installed with the OpenStack
software.
#. Query storage pool id to configure ``dpl_pool`` of the ``cinder.conf``
file.
a. Log on to the storage system with administrator access.
.. code-block:: console
$ ssh root@STORAGE_IP_ADDRESS
b. View the current usable pool id.
.. code-block:: console
$ flvcli show pool list
- d5bd40b58ea84e9da09dcf25a01fdc07 : default_pool_dc07
c. Use ``d5bd40b58ea84e9da09dcf25a01fdc07`` to configure the ``dpl_pool`` of
``/etc/cinder/cinder.conf`` file.
.. note::
Other management commands can be referenced with the help command
:command:`flvcli -h`.
#. Make the following changes on the volume node ``/etc/cinder/cinder.conf``
file.
.. code-block:: ini
# IP address of SAN controller (string value)
san_ip=STORAGE IP ADDRESS
# Username for SAN controller (string value)
san_login=USERNAME
# Password for SAN controller (string value)
san_password=PASSWORD
# Use thin provisioning for SAN volumes? (boolean value)
san_thin_provision=true
# The port that the iSCSI daemon is listening on. (integer value)
iscsi_port=3260
# DPL pool uuid in which DPL volumes are stored. (string value)
dpl_pool=d5bd40b58ea84e9da09dcf25a01fdc07
# DPL port number. (integer value)
dpl_port=8357
# Uncomment one of the next two option to enable Fibre channel or iSCSI
# FIBRE CHANNEL(uncomment the next line to enable the FC driver)
#volume_driver=cinder.volume.drivers.prophetstor.dpl_fc.DPLFCDriver
# iSCSI (uncomment the next line to enable the iSCSI driver)
#volume_driver=cinder.volume.drivers.prophetstor.dpl_iscsi.DPLISCSIDriver
#. Save the changes to the ``/etc/cinder/cinder.conf`` file and
restart the ``cinder-volume`` service.
The ProphetStor Fibre Channel or iSCSI drivers are now enabled on your
OpenStack system. If you experience problems, review the Block Storage
service log files for errors.
The following table contains the options supported by the ProphetStor
storage driver.
.. include:: ../../tables/cinder-prophetstor_dpl.inc

View File

@ -141,9 +141,6 @@ title=Generic NFS Reference Driver (NFS)
[driver.nimble]
title=Nimble Storage Driver (iSCSI)
[driver.prophetstor]
title=ProphetStor Flexvisor Driver (iSCSI, NFS)
[driver.pure]
title=Pure Storage Driver (iSCSI, FC)
@ -241,7 +238,6 @@ driver.netapp_solidfire=complete
driver.nexenta=complete
driver.nfs=complete
driver.nimble=missing
driver.prophetstor=missing
driver.pure=complete
driver.qnap=complete
driver.quobyte=complete
@ -305,7 +301,6 @@ driver.netapp_solidfire=complete
driver.nexenta=complete
driver.nfs=complete
driver.nimble=complete
driver.prophetstor=complete
driver.pure=complete
driver.qnap=complete
driver.quobyte=complete
@ -369,7 +364,6 @@ driver.netapp_solidfire=missing
driver.nexenta=missing
driver.nfs=missing
driver.nimble=missing
driver.prophetstor=missing
driver.pure=missing
driver.qnap=missing
driver.quobyte=missing
@ -436,7 +430,6 @@ driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=missing
driver.nimble=missing
driver.prophetstor=missing
driver.pure=missing
driver.qnap=missing
driver.quobyte=missing
@ -502,7 +495,6 @@ driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=missing
driver.nimble=missing
driver.prophetstor=missing
driver.pure=complete
driver.qnap=missing
driver.quobyte=missing
@ -569,7 +561,6 @@ driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=missing
driver.nimble=missing
driver.prophetstor=complete
driver.pure=complete
driver.qnap=missing
driver.quobyte=missing
@ -635,7 +626,6 @@ driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=complete
driver.nimble=missing
driver.prophetstor=missing
driver.pure=complete
driver.qnap=missing
driver.quobyte=missing
@ -702,7 +692,6 @@ driver.netapp_solidfire=missing
driver.nexenta=missing
driver.nfs=missing
driver.nimble=missing
driver.prophetstor=missing
driver.pure=missing
driver.qnap=missing
driver.quobyte=missing
@ -769,7 +758,6 @@ driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=missing
driver.nimble=missing
driver.prophetstor=missing
driver.pure=complete
driver.qnap=missing
driver.quobyte=missing
@ -833,7 +821,6 @@ driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=missing
driver.nimble=missing
driver.prophetstor=missing
driver.pure=missing
driver.qnap=missing
driver.quobyte=missing
@ -901,7 +888,6 @@ driver.netapp_solidfire=missing
driver.nexenta=missing
driver.nfs=missing
driver.nimble=missing
driver.prophetstor=missing
driver.pure=missing
driver.qnap=missing
driver.quobyte=missing

View File

@ -88,3 +88,4 @@ release.
* Ussuri
* Huawei FusionStorage Driver
* ProphetStor Flexvisor Driver

View File

@ -0,0 +1,7 @@
---
upgrade:
- |
The ProphetStor Flexvisor driver was marked unsupported in the
Train release and has now been removed. All data on
ProphetStor Flexvisor backends should be migrated to a supported
storage backend before upgrading your Cinder installation.