Add cinder backend driver for Huawei FusionStorage

It will support the minimum set of features required in Cinder:
  - Volume Create/Delete
  - Volume Attach/Detach
  - Snapshot Create/Delete
  - Create Volume from Snapshot
  - Get Volume Stats
  - Copy Image to Volume
  - Copy Volume to Image
  - Clone Volume
  - Extend Volume

DocImpact
Implements: bp fusionstorage-cinder-driver

Co-Authored-By: wangxiyuan <wangxiyuan@huawei.com>
Change-Id: I26a809adb7bef4370eb53c634dc9bae0c74b4a8a
This commit is contained in:
ningwei 2016-06-13 02:57:51 -04:00
parent 6f2112caa8
commit ecfb70cfeb
8 changed files with 2347 additions and 0 deletions

View File

@ -87,6 +87,8 @@ from cinder.volume.drivers.emc import xtremio as \
from cinder.volume.drivers import eqlx as cinder_volume_drivers_eqlx
from cinder.volume.drivers.fujitsu import eternus_dx_common as \
cinder_volume_drivers_fujitsu_eternusdxcommon
from cinder.volume.drivers.fusionstorage import dsware as \
cinder_volume_drivers_fusionstorage_dsware
from cinder.volume.drivers import glusterfs as cinder_volume_drivers_glusterfs
from cinder.volume.drivers import hgst as cinder_volume_drivers_hgst
from cinder.volume.drivers.hitachi import hbsd_common as \
@ -312,6 +314,7 @@ def list_opts():
[cinder_scheduler_scheduleroptions.
scheduler_json_config_location_opt],
cinder_volume_drivers_zfssa_zfssanfs.ZFSSA_OPTS,
cinder_volume_drivers_fusionstorage_dsware.volume_opts,
cinder_volume_drivers_kaminario_kaminariocommon.
kaminario1_opts,
cinder_volume_drivers_disco_disco.disco_opts,

View File

@ -0,0 +1,771 @@
# Copyright (c) 2013 - 2016 Huawei Technologies Co., Ltd.
# 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.
"""
Unit Tests for Huawei FusionStorage drivers.
"""
import mock
from oslo_config import cfg
from oslo_service import loopingcall
from cinder import exception
from cinder.image import image_utils
from cinder import test
from cinder.volume import configuration as conf
from cinder.volume.drivers.fusionstorage import dsware
from cinder.volume.drivers.fusionstorage import fspythonapi
test_volume = {'name': 'test_vol1',
'size': 4,
'volume_metadata': '',
'host': 'host01@dsware',
'instance_uuid': None,
'provider_id': '127.0.0.1'}
test_src_volume = {'name': 'test_vol2',
'size': 4,
'status': 'available'}
test_snapshot = {
'name': 'test_snapshot1',
'volume_id': 'vol1',
'volume_size': '2'}
class FakeDSWAREDriver(dsware.DSWAREDriver):
def __init__(self):
configuration = conf.Configuration(
[
cfg.StrOpt('fake'),
],
None
)
super(FakeDSWAREDriver, self).__init__(configuration=configuration)
self.dsware_client = fspythonapi.FSPythonApi()
self.manage_ip = '127.0.0.1'
self.pool_type = '1'
class DSwareDriverTestCase(test.TestCase):
def setUp(self):
super(DSwareDriverTestCase, self).setUp()
self.driver = FakeDSWAREDriver()
def test_private_get_dsware_manage_ip(self):
retval = self.driver._get_dsware_manage_ip(test_volume)
self.assertEqual('127.0.0.1', retval)
test_volume_fail = {'name': 'test_vol',
'size': 4,
'volume_metadata': '',
'host': 'host01@dsware',
'provider_id': None}
self.assertRaises(exception.CinderException,
self.driver._get_dsware_manage_ip,
test_volume_fail)
def test_private_get_poolid_from_host(self):
retval = self.driver._get_poolid_from_host(
'abc@fusionstorage_sas2copy#0')
self.assertEqual('0', retval)
retval = self.driver._get_poolid_from_host(
'abc@fusionstorage_sas2copy@0')
self.assertEqual(self.driver.pool_type, retval)
retval = self.driver._get_poolid_from_host(None)
self.assertEqual(self.driver.pool_type, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'create_volume')
@mock.patch.object(fspythonapi.FSPythonApi, 'query_dsware_version')
@mock.patch.object(dsware.DSWAREDriver, '_get_poolid_from_host')
def test_private_create_volume_old_version(self, mock_get_poolid,
mock_query_dsware,
mock_create_volume):
# query_dsware_version return 1, old version
mock_query_dsware.return_value = 1
mock_create_volume.return_value = 0
self.driver._create_volume(test_volume['name'],
test_volume['size'],
True,
'abc@fusionstorage_sas2copy')
mock_create_volume.assert_called_with(test_volume['name'], 0,
test_volume['size'], 1)
self.driver._create_volume(test_volume['name'],
test_volume['size'],
False,
'abc@fusionstorage_sas2copy')
mock_create_volume.assert_called_with(test_volume['name'], 0,
test_volume['size'], 0)
@mock.patch.object(fspythonapi.FSPythonApi, 'create_volume')
@mock.patch.object(fspythonapi.FSPythonApi, 'query_dsware_version')
@mock.patch.object(dsware.DSWAREDriver, '_get_poolid_from_host')
def test_private_create_volume_new_version(self, mock_get_poolid,
mock_query_dsware,
mock_create_volume):
# query_dsware_version return 0, new version
mock_query_dsware.return_value = 0
mock_get_poolid.return_value = 0
mock_create_volume.return_value = 0
self.driver._create_volume(test_volume['name'],
test_volume['size'],
True,
'abcE@fusionstorage_sas2copy#0')
mock_create_volume.assert_called_with(test_volume['name'], 0,
test_volume['size'], 1)
self.driver._create_volume(test_volume['name'],
test_volume['size'],
False,
'abc@fusionstorage_sas2copy#0')
mock_create_volume.assert_called_with(test_volume['name'], 0,
test_volume['size'], 0)
mock_query_dsware.return_value = 0
mock_get_poolid.return_value = 1
mock_create_volume.return_value = 0
self.driver._create_volume(test_volume['name'],
test_volume['size'],
True,
'abc@fusionstorage_sas2copy#1')
mock_create_volume.assert_called_with(test_volume['name'], 1,
test_volume['size'], 1)
self.driver._create_volume(test_volume['name'],
test_volume['size'],
False,
'abc@fusionstorage_sas2copy#1')
mock_create_volume.assert_called_with(test_volume['name'], 1,
test_volume['size'], 0)
@mock.patch.object(fspythonapi.FSPythonApi, 'create_volume')
@mock.patch.object(fspythonapi.FSPythonApi, 'query_dsware_version')
@mock.patch.object(dsware.DSWAREDriver, '_get_poolid_from_host')
def test_private_create_volume_query_version_fail(self, mock_get_poolid,
mock_query_dsware,
mock_create_volume):
# query_dsware_version return 500015, query dsware version failed!
mock_query_dsware.return_value = 500015
self.assertRaises(exception.CinderException,
self.driver._create_volume,
test_volume['name'],
test_volume['size'],
True,
'abc@fusionstorage_sas2copy#0')
self.assertRaises(exception.CinderException,
self.driver._create_volume,
test_volume['name'],
test_volume['size'],
False,
'abc@fusionstorage_sas2copy#0')
@mock.patch.object(fspythonapi.FSPythonApi, 'create_volume')
@mock.patch.object(fspythonapi.FSPythonApi, 'query_dsware_version')
@mock.patch.object(dsware.DSWAREDriver, '_get_poolid_from_host')
def test_private_create_volume_fail(self, mock_get_poolid,
mock_query_dsware,
mock_create_volume):
mock_query_dsware.return_value = 1
# create_volume return 1, create volume failed
mock_create_volume.return_value = 1
self.assertRaises(exception.CinderException,
self.driver._create_volume,
test_volume['name'],
test_volume['size'],
True,
'abc@fusionstorage_sas2copy#0')
self.assertRaises(exception.CinderException,
self.driver._create_volume,
test_volume['name'],
test_volume['size'],
False,
'abc@fusionstorage_sas2copy#0')
@mock.patch.object(dsware.DSWAREDriver, '_create_volume')
@mock.patch.object(fspythonapi.FSPythonApi, 'get_manage_ip')
def test_create_volume(self, mock_get_manage_ip, mock_create_volume):
# success
mock_get_manage_ip.return_value = self.driver.manage_ip
retval = self.driver.create_volume(test_volume)
self.assertEqual({"provider_id": self.driver.manage_ip},
retval)
# failure
mock_create_volume.side_effect = exception.CinderException(
'DSWARE Create Volume failed!')
self.assertRaises(exception.CinderException,
self.driver.create_volume,
test_volume)
@mock.patch.object(fspythonapi.FSPythonApi, 'create_volume_from_snap')
def test_private_create_volume_from_snap(self, mock_create_volume):
mock_create_volume.side_effect = [0, 1]
self.driver._create_volume_from_snap(test_volume['name'],
test_volume['size'],
test_snapshot['name'])
# failure
self.assertRaises(exception.CinderException,
self.driver._create_volume_from_snap,
test_volume['name'], test_volume['size'],
test_snapshot['name'])
@mock.patch.object(fspythonapi.FSPythonApi, 'extend_volume')
def test_extend_volume(self, mock_extend_volume):
mock_extend_volume.return_value = 0
self.driver.extend_volume(test_volume, 5)
mock_extend_volume.return_value = 0
self.assertRaises(exception.CinderException,
self.driver.extend_volume,
test_volume,
3)
mock_extend_volume.return_value = 1
self.assertRaises(exception.CinderException,
self.driver.extend_volume,
test_volume,
5)
@mock.patch.object(dsware.DSWAREDriver, '_create_volume_from_snap')
@mock.patch.object(fspythonapi.FSPythonApi, 'get_manage_ip')
def test_create_volume_from_snap(self, mock_manage_ip, mock_create_vol):
# success
mock_manage_ip.return_value = self.driver.manage_ip
retval = self.driver.create_volume_from_snapshot(test_volume,
test_snapshot)
self.assertEqual({"provider_id": self.driver.manage_ip},
retval)
# failure
mock_create_vol.side_effect = exception.CinderException(
'DSWARE:create volume from snap failed')
self.assertRaises(exception.CinderException,
self.driver.create_volume_from_snapshot,
test_volume, test_snapshot)
@mock.patch.object(fspythonapi.FSPythonApi, 'create_volume_from_volume')
@mock.patch.object(fspythonapi.FSPythonApi, 'get_manage_ip')
@mock.patch.object(dsware.DSWAREDriver,
'_wait_for_create_cloned_volume_finish_timer')
def test_create_cloned_volume(self, mock_wait_finish,
mock_get_manage_ip, mock_create_volume):
# success
mock_create_volume.return_value = None
mock_get_manage_ip.return_value = self.driver.manage_ip
mock_wait_finish.return_value = True
retval = self.driver.create_cloned_volume(test_volume, test_src_volume)
self.assertEqual({"provider_id": "127.0.0.1"}, retval)
# failure:create exception
mock_create_volume.return_value = 500015
self.assertRaises(exception.CinderException,
self.driver.create_cloned_volume,
test_volume, test_src_volume)
# failure:wait exception
mock_create_volume.return_value = None
mock_wait_finish.return_value = False
self.assertRaises(exception.CinderException,
self.driver.create_cloned_volume,
test_volume, test_src_volume)
@mock.patch.object(fspythonapi.FSPythonApi, 'query_volume')
def test_private_check_create_cloned_volume_finish(self,
mock_query_volume):
query_result_done = {'result': 0, 'vol_name': 'vol1',
'father_name': 'vol1_father', 'status': '0',
'vol_size': '1024', 'real_size': '1024',
'pool_id': 'pool1', 'create_time': '01/01/2015'}
query_result_doing = {'result': 0, 'vol_name': 'vol1',
'father_name': 'vol1_father', 'status': '6',
'vol_size': '1024', 'real_size': '1024',
'pool_id': 'pool1', 'create_time': '01/01/2015'}
mock_query_volume.side_effect = [
query_result_done, query_result_doing, query_result_doing]
# success
self.assertRaises(loopingcall.LoopingCallDone,
self.driver._check_create_cloned_volume_finish,
test_volume['name'])
# in the process of creating volume
self.driver.count = self.driver.configuration.clone_volume_timeout - 1
self.driver._check_create_cloned_volume_finish(test_volume['name'])
self.assertEqual(self.driver.configuration.clone_volume_timeout,
self.driver.count)
# timeout
self.driver.count = self.driver.configuration.clone_volume_timeout
self.assertRaises(loopingcall.LoopingCallDone,
self.driver._check_create_cloned_volume_finish,
test_volume['name'])
@mock.patch.object(dsware.DSWAREDriver,
'_check_create_cloned_volume_finish')
def test_private_wait_for_create_cloned_volume_finish_timer(self,
mock_check):
mock_check.side_effect = [loopingcall.LoopingCallDone(retvalue=True),
loopingcall.LoopingCallDone(retvalue=False)]
retval = self.driver._wait_for_create_cloned_volume_finish_timer(
test_volume['name'])
self.assertTrue(retval)
retval = self.driver._wait_for_create_cloned_volume_finish_timer(
test_volume['name'])
self.assertFalse(retval)
def test_private_analyse_output(self):
out = 'ret_code=10\nret_desc=test\ndev_addr=/sda\n'
retval = self.driver._analyse_output(out)
self.assertEqual({'dev_addr': '/sda',
'ret_desc': 'test', 'ret_code': '10'},
retval)
out = 'abcdefg'
retval = self.driver._analyse_output(out)
self.assertEqual({}, retval)
def test_private_attach_volume(self):
success = ['ret_code=0\nret_desc=success\ndev_addr=/dev/sdb\n', '']
failure = ['ret_code=50510011\nret_desc=failed\ndev_addr=/dev/sdb\n',
'']
mock_execute = self.mock_object(self.driver, '_execute')
mock_execute.side_effect = [success, failure]
# attached successful
retval = self.driver._attach_volume(test_volume['name'],
self.driver.manage_ip)
self.assertEqual({'dev_addr': '/dev/sdb',
'ret_desc': 'success', 'ret_code': '0'},
retval)
# attached failure
retval = self.driver._attach_volume(test_volume['name'],
self.driver.manage_ip)
self.assertEqual({'dev_addr': '/dev/sdb',
'ret_desc': 'failed', 'ret_code': '50510011'},
retval)
def test_private_detach_volume(self):
success = ['ret_code=0\nret_desc=success\ndev_addr=/dev/sdb\n', '']
failure = ['ret_code=50510011\nret_desc=failed\ndev_addr=/dev/sdb\n',
'']
mock_execute = self.mock_object(self.driver, '_execute')
mock_execute.side_effect = [success, failure]
# detached successful
retval = self.driver._detach_volume(test_volume['name'],
self.driver.manage_ip)
self.assertEqual({'dev_addr': '/dev/sdb',
'ret_desc': 'success', 'ret_code': '0'},
retval)
# detached failure
retval = self.driver._detach_volume(test_volume['name'],
self.driver.manage_ip)
self.assertEqual({'dev_addr': '/dev/sdb',
'ret_desc': 'failed',
'ret_code': '50510011'},
retval)
def test_private_query_volume_attach(self):
success = ['ret_code=0\nret_desc=success\ndev_addr=/dev/sdb\n', '']
failure = ['ret_code=50510011\nret_desc=failed\ndev_addr=/dev/sdb\n',
'']
mock_execute = self.mock_object(self.driver, '_execute')
mock_execute.side_effect = [success, failure]
# query successful
retval = self.driver._query_volume_attach(test_volume['name'],
self.driver.manage_ip)
self.assertEqual({'dev_addr': '/dev/sdb',
'ret_desc': 'success',
'ret_code': '0'},
retval)
# query failure
retval = self.driver._query_volume_attach(test_volume['name'],
self.driver.manage_ip)
self.assertEqual({'dev_addr': '/dev/sdb',
'ret_desc': 'failed',
'ret_code': '50510011'},
retval)
@mock.patch.object(dsware.DSWAREDriver, '_get_dsware_manage_ip')
@mock.patch.object(dsware.DSWAREDriver, '_attach_volume')
@mock.patch.object(image_utils, 'fetch_to_raw')
@mock.patch.object(dsware.DSWAREDriver, '_detach_volume')
def test_copy_image_to_volume(self, mock_detach, mock_fetch,
mock_attach, mock_get_manage_ip):
success = {'ret_code': '0',
'ret_desc': 'success',
'dev_addr': '/dev/sdb'}
failure = {'ret_code': '50510011',
'ret_desc': 'failed',
'dev_addr': '/dev/sdb'}
context = ''
image_service = ''
image_id = ''
mock_get_manage_ip.return_value = '127.0.0.1'
mock_attach.side_effect = [success, failure, success]
mock_detach.side_effect = [success, failure, failure]
# success
self.driver.copy_image_to_volume(context, test_volume, image_service,
image_id)
# failure - attach failure
self.assertRaises(exception.CinderException,
self.driver.copy_image_to_volume,
context, test_volume, image_service, image_id)
# failure - detach failure
self.assertRaises(exception.CinderException,
self.driver.copy_image_to_volume,
context, test_volume, image_service, image_id)
@mock.patch.object(dsware.DSWAREDriver, '_get_dsware_manage_ip')
@mock.patch.object(dsware.DSWAREDriver, '_attach_volume')
@mock.patch.object(dsware.DSWAREDriver, '_query_volume_attach')
@mock.patch.object(image_utils, 'upload_volume')
@mock.patch.object(dsware.DSWAREDriver, '_detach_volume')
def test_copy_volume_to_image_success(self, mock_detach, mock_upload,
mock_query, mock_attach,
mock_get_manage_ip):
success = {'ret_code': '0',
'ret_desc': 'success',
'dev_addr': '/dev/sdb'}
already_attached = {'ret_code': '50151401',
'ret_desc': 'already_attached',
'dev_addr': '/dev/sdb'}
context = ''
image_service = ''
image_meta = ''
mock_get_manage_ip.return_value = '127.0.0.1'
mock_attach.return_value = success
mock_detach.return_value = success
self.driver.copy_volume_to_image(context, test_volume, image_service,
image_meta)
mock_upload.assert_called_with('', '', '', '/dev/sdb')
mock_attach.return_value = already_attached
mock_query.return_value = success
mock_detach.return_value = success
self.driver.copy_volume_to_image(context, test_volume, image_service,
image_meta)
mock_upload.assert_called_with('', '', '', '/dev/sdb')
@mock.patch.object(dsware.DSWAREDriver, '_get_dsware_manage_ip')
@mock.patch.object(dsware.DSWAREDriver, '_attach_volume')
@mock.patch.object(dsware.DSWAREDriver, '_query_volume_attach')
@mock.patch.object(image_utils, 'upload_volume')
@mock.patch.object(dsware.DSWAREDriver, '_detach_volume')
def test_copy_volume_to_image_attach_fail(self, mock_detach, mock_upload,
mock_query, mock_attach,
mock_get_manage_ip):
failure = {'ret_code': '50510011',
'ret_desc': 'failed',
'dev_addr': '/dev/sdb'}
context = ''
image_service = ''
image_meta = ''
mock_get_manage_ip.return_value = '127.0.0.1'
mock_attach.return_value = failure
self.assertRaises(exception.CinderException,
self.driver.copy_volume_to_image,
context, test_volume, image_service, image_meta)
mock_attach.return_value = None
self.assertRaises(exception.CinderException,
self.driver.copy_volume_to_image,
context, test_volume, image_service, image_meta)
@mock.patch.object(dsware.DSWAREDriver, '_get_dsware_manage_ip')
@mock.patch.object(dsware.DSWAREDriver, '_attach_volume')
@mock.patch.object(dsware.DSWAREDriver, '_query_volume_attach')
@mock.patch.object(image_utils, 'upload_volume')
@mock.patch.object(dsware.DSWAREDriver, '_detach_volume')
def test_copy_volume_to_image_query_attach_fail(self, mock_detach,
mock_upload, mock_query,
mock_attach,
mock_get_manage_ip):
already_attached = {'ret_code': '50151401',
'ret_desc': 'already_attached',
'dev_addr': '/dev/sdb'}
failure = {'ret_code': '50510011',
'ret_desc': 'failed',
'dev_addr': '/dev/sdb'}
context = ''
image_service = ''
image_meta = ''
mock_get_manage_ip.return_value = '127.0.0.1'
mock_attach.return_value = already_attached
mock_query.return_value = failure
self.assertRaises(exception.CinderException,
self.driver.copy_volume_to_image,
context, test_volume, image_service, image_meta)
mock_query.return_value = None
self.assertRaises(exception.CinderException,
self.driver.copy_volume_to_image,
context, test_volume, image_service, image_meta)
@mock.patch.object(dsware.DSWAREDriver, '_get_dsware_manage_ip')
@mock.patch.object(dsware.DSWAREDriver, '_attach_volume')
@mock.patch.object(dsware.DSWAREDriver, '_query_volume_attach')
@mock.patch.object(image_utils, 'upload_volume')
@mock.patch.object(dsware.DSWAREDriver, '_detach_volume')
def test_copy_volume_to_image_upload_fail(self, mock_detach, mock_upload,
mock_query, mock_attach,
mock_get_manage_ip):
success = {'ret_code': '0',
'ret_desc': 'success',
'dev_addr': '/dev/sdb'}
already_attached = {'ret_code': '50151401',
'ret_desc': 'already_attached',
'dev_addr': '/dev/sdb'}
context = ''
image_service = ''
image_meta = ''
mock_get_manage_ip.return_value = '127.0.0.1'
mock_attach.return_value = already_attached
mock_query.return_value = success
mock_upload.side_effect = exception.CinderException(
'upload_volume error')
self.assertRaises(exception.CinderException,
self.driver.copy_volume_to_image,
context, test_volume, image_service, image_meta)
@mock.patch.object(fspythonapi.FSPythonApi, 'query_volume')
def test_private_get_volume(self, mock_query):
result_success = {'result': 0}
result_not_exist = {'result': "50150005\n"}
result_exception = {'result': "50510006\n"}
mock_query.side_effect = [
result_success, result_not_exist, result_exception]
retval = self.driver._get_volume(test_volume['name'])
self.assertTrue(retval)
retval = self.driver._get_volume(test_volume['name'])
self.assertFalse(retval)
self.assertRaises(exception.CinderException,
self.driver._get_volume,
test_volume['name'])
@mock.patch.object(fspythonapi.FSPythonApi, 'delete_volume')
def test_private_delete_volume(self, mock_delete):
result_success = 0
result_not_exist = '50150005\n'
result_being_deleted = '50151002\n'
result_exception = '51050006\n'
mock_delete.side_effect = [result_success, result_not_exist,
result_being_deleted, result_exception]
retval = self.driver._delete_volume(test_volume['name'])
self.assertTrue(retval)
retval = self.driver._delete_volume(test_volume['name'])
self.assertTrue(retval)
retval = self.driver._delete_volume(test_volume['name'])
self.assertTrue(retval)
self.assertRaises(exception.CinderException,
self.driver._delete_volume, test_volume['name'])
@mock.patch.object(dsware.DSWAREDriver, '_get_volume')
@mock.patch.object(dsware.DSWAREDriver, '_delete_volume')
def test_delete_volume(self, mock_delete, mock_get):
mock_get.return_value = False
retval = self.driver.delete_volume(test_volume)
self.assertTrue(retval)
mock_get.return_value = True
mock_delete.return_value = True
retval = self.driver.delete_volume(test_volume)
self.assertTrue(retval)
mock_get.return_value = True
mock_delete.side_effect = exception.CinderException(
'delete volume exception')
self.assertRaises(exception.CinderException,
self.driver.delete_volume,
test_volume)
mock_get.side_effect = exception.CinderException(
'get volume exception')
self.assertRaises(exception.CinderException,
self.driver.delete_volume,
test_volume)
@mock.patch.object(fspythonapi.FSPythonApi, 'query_snap')
def test_private_get_snapshot(self, mock_query):
result_success = {'result': 0}
result_not_found = {'result': "50150006\n"}
result_exception = {'result': "51050007\n"}
mock_query.side_effect = [result_success, result_not_found,
result_exception]
retval = self.driver._get_snapshot(test_snapshot['name'])
self.assertTrue(retval)
retval = self.driver._get_snapshot(test_snapshot['name'])
self.assertFalse(retval)
self.assertRaises(exception.CinderException,
self.driver._get_snapshot,
test_snapshot['name'])
@mock.patch.object(fspythonapi.FSPythonApi, 'create_snapshot')
def test_private_create_snapshot(self, mock_create):
mock_create.side_effect = [0, 1]
self.driver._create_snapshot(test_snapshot['name'],
test_volume['name'])
self.assertRaises(exception.CinderException,
self.driver._create_snapshot,
test_snapshot['name'], test_volume['name'])
@mock.patch.object(fspythonapi.FSPythonApi, 'delete_snapshot')
def test_private_delete_snapshot(self, mock_delete):
mock_delete.side_effect = [0, 1]
self.driver._delete_snapshot(test_snapshot['name'])
self.assertRaises(exception.CinderException,
self.driver._delete_snapshot, test_snapshot['name'])
@mock.patch.object(dsware.DSWAREDriver, '_get_volume')
@mock.patch.object(dsware.DSWAREDriver, '_create_snapshot')
def test_create_snapshot(self, mock_create, mock_get):
mock_get.return_value = True
self.driver.create_snapshot(test_snapshot)
mock_create.side_effect = exception.CinderException(
'create snapshot failed')
self.assertRaises(exception.CinderException,
self.driver.create_snapshot, test_snapshot)
mock_get.side_effect = [
False, exception.CinderException('get volume failed')]
self.assertRaises(exception.CinderException,
self.driver.create_snapshot,
test_snapshot)
self.assertRaises(exception.CinderException,
self.driver.create_snapshot,
test_snapshot)
@mock.patch.object(dsware.DSWAREDriver, '_get_snapshot')
@mock.patch.object(dsware.DSWAREDriver, '_delete_snapshot')
def test_delete_snapshot(self, mock_delete, mock_get):
mock_get.side_effect = [True, False, exception.CinderException, True]
self.driver.delete_snapshot(test_snapshot)
self.driver.delete_snapshot(test_snapshot)
self.assertRaises(exception.CinderException,
self.driver.delete_snapshot,
test_snapshot)
mock_delete.side_effect = exception.CinderException(
'delete snapshot exception')
self.assertRaises(exception.CinderException,
self.driver.delete_snapshot,
test_snapshot)
@mock.patch.object(fspythonapi.FSPythonApi, 'query_pool_info')
def test_private_update_single_pool_info_status(self, mock_query):
pool_info = {'result': 0,
'pool_id': 10,
'total_capacity': 10240,
'used_capacity': 5120,
'alloc_capacity': 7168}
pool_info_none = {'result': 1}
mock_query.side_effect = [pool_info, pool_info_none]
self.driver._update_single_pool_info_status()
self.assertEqual({'total_capacity_gb': 10.0,
'free_capacity_gb': 5.0,
'volume_backend_name': None,
'vendor_name': 'Open Source',
'driver_version': '1.0',
'storage_protocol': 'dsware',
'reserved_percentage': 0,
'QoS_support': False},
self.driver._stats)
self.driver._update_single_pool_info_status()
self.assertIsNone(self.driver._stats)
@mock.patch.object(fspythonapi.FSPythonApi, 'query_pool_type')
def test_private_update_multi_pool_of_same_type_status(self, mock_query):
query_result = (0, [{'result': 0,
'pool_id': '0',
'total_capacity': '10240',
'used_capacity': '5120',
'alloc_capacity': '7168'}])
query_result_none = (0, [])
mock_query.side_effect = [query_result, query_result_none]
self.driver._update_multi_pool_of_same_type_status()
self.assertEqual({'volume_backend_name': None,
'vendor_name': 'Open Source',
'driver_version': '1.0',
'storage_protocol': 'dsware',
'pools': [{'pool_name': '0',
'total_capacity_gb': 10.0,
'allocated_capacity_gb': 5.0,
'free_capacity_gb': 5.0,
'QoS_support': False,
'reserved_percentage': 0}]},
self.driver._stats)
self.driver._update_multi_pool_of_same_type_status()
self.assertIsNone(self.driver._stats)
def test_private_calculate_pool_info(self):
pool_sets = [{'pool_id': 0,
'total_capacity': 10240,
'used_capacity': 5120,
'QoS_support': False,
'reserved_percentage': 0}]
retval = self.driver._calculate_pool_info(pool_sets)
self.assertEqual([{'pool_name': 0,
'total_capacity_gb': 10.0,
'allocated_capacity_gb': 5.0,
'free_capacity_gb': 5.0,
'QoS_support': False,
'reserved_percentage': 0}],
retval)
@mock.patch.object(dsware.DSWAREDriver, '_update_single_pool_info_status')
@mock.patch.object(dsware.DSWAREDriver,
'_update_multi_pool_of_same_type_status')
@mock.patch.object(fspythonapi.FSPythonApi, 'query_dsware_version')
def test_get_volume_stats(self, mock_query, mock_type, mock_info):
mock_query.return_value = 1
self.driver.get_volume_stats(False)
mock_query.assert_not_called()
self.driver.get_volume_stats(True)
mock_query.assert_called_once_with()

View File

@ -0,0 +1,447 @@
# Copyright (c) 2013 - 2016 Huawei Technologies Co., Ltd.
# 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.
"""
Unit Tests for Huawei FusionStorage drivers.
"""
import mock
from cinder import test
from cinder import utils
from cinder.volume.drivers.fusionstorage import fspythonapi
class FSPythonApiTestCase(test.TestCase):
def setUp(self):
super(FSPythonApiTestCase, self).setUp()
self.api = fspythonapi.FSPythonApi()
@mock.patch.object(fspythonapi.FSPythonApi, 'get_ip_port')
@mock.patch.object(fspythonapi.FSPythonApi, 'get_manage_ip')
@mock.patch.object(utils, 'execute')
def test_start_execute_cmd(self, mock_execute,
mock_get_manage_ip, mock_get_ip_port):
result1 = ['result=0\ndesc=success\n', '']
result2 = ['result=50150007\ndesc=volume does not exist\n', '']
result3 = ['result=50150008\ndesc=volume is being deleted\n', '']
result4 = ['result=50\ndesc=exception\n', '']
cmd = 'abcdef'
mock_get_ip_port.return_value = ['127.0.0.1', '128.0.0.1']
mock_get_manage_ip.return_value = '127.0.0.1'
mock_execute.return_value = result1
retval = self.api.start_execute_cmd(cmd, 0)
self.assertEqual('result=0', retval)
mock_execute.return_value = result2
retval = self.api.start_execute_cmd(cmd, 0)
self.assertEqual('result=0', retval)
mock_execute.return_value = result3
retval = self.api.start_execute_cmd(cmd, 0)
self.assertEqual('result=0', retval)
mock_execute.return_value = result4
retval = self.api.start_execute_cmd(cmd, 0)
self.assertEqual('result=50', retval)
mock_execute.return_value = result1
retval = self.api.start_execute_cmd(cmd, 1)
self.assertEqual(['result=0', 'desc=success', ''], retval)
mock_execute.return_value = result2
retval = self.api.start_execute_cmd(cmd, 1)
self.assertEqual('result=0', retval)
mock_execute.return_value = result3
retval = self.api.start_execute_cmd(cmd, 1)
self.assertEqual('result=0', retval)
mock_execute.return_value = result4
retval = self.api.start_execute_cmd(cmd, 1)
self.assertEqual(['result=50', 'desc=exception', ''], retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'start_execute_cmd')
def test_create_volume(self, mock_start_execute):
mock_start_execute.side_effect = ['result=0\n',
'result=50150007\n', None]
retval = self.api.create_volume('volume_name', 'pool_id-123', 1024, 0)
self.assertEqual(0, retval)
retval = self.api.create_volume('volume_name', 'pool_id-123', 1024, 0)
self.assertEqual('50150007\n', retval)
retval = self.api.create_volume('volume_name', 'pool_id-123', 1024, 0)
self.assertEqual(1, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'start_execute_cmd')
def test_extend_volume(self, mock_start_execute):
mock_start_execute.side_effect = ['result=0\n',
'result=50150007\n', None]
retval = self.api.extend_volume('volume_name', 1024)
self.assertEqual(0, retval)
retval = self.api.extend_volume('volume_name', 1024)
self.assertEqual('50150007\n', retval)
retval = self.api.extend_volume('volume_name', 1024)
self.assertEqual(1, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'start_execute_cmd')
def test_create_volume_from_snap(self, mock_start_execute):
mock_start_execute.side_effect = ['result=0\n',
'result=50150007\n', None]
retval = self.api.create_volume_from_snap('volume_name', 1024,
'snap_name')
self.assertEqual(0, retval)
retval = self.api.create_volume_from_snap('volume_name', 1024,
'snap_name')
self.assertEqual('50150007\n', retval)
retval = self.api.create_volume_from_snap('volume_name', 1024,
'snap_name')
self.assertEqual(1, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'start_execute_cmd')
def test_create_fullvol_from_snap(self, mock_start_execute):
mock_start_execute.side_effect = ['result=0\n',
'result=50150007\n', None]
retval = self.api.create_fullvol_from_snap('volume_name', 'snap_name')
self.assertEqual(0, retval)
retval = self.api.create_fullvol_from_snap('volume_name', 'snap_name')
self.assertEqual('50150007\n', retval)
retval = self.api.create_fullvol_from_snap('volume_name', 'snap_name')
self.assertEqual(1, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'create_snapshot')
@mock.patch.object(fspythonapi.FSPythonApi, 'create_volume')
@mock.patch.object(fspythonapi.FSPythonApi, 'delete_snapshot')
@mock.patch.object(fspythonapi.FSPythonApi, 'delete_volume')
@mock.patch.object(fspythonapi.FSPythonApi, 'create_fullvol_from_snap')
def test_create_volume_from_volume(self, mock_create_fullvol,
mock_delete_volume, mock_delete_snap,
mock_create_volume, mock_create_snap):
mock_create_snap.return_value = 0
mock_create_volume.return_value = 0
mock_create_fullvol.return_value = 0
retval = self.api.create_volume_from_volume('vol_name', 1024,
'src_vol_name')
self.assertEqual(0, retval)
mock_create_snap.return_value = 1
retval = self.api.create_volume_from_volume('vol_name', 1024,
'src_vol_name')
self.assertEqual(1, retval)
mock_create_snap.return_value = 0
mock_create_volume.return_value = 1
retval = self.api.create_volume_from_volume('vol_name', 1024,
'src_vol_name')
self.assertEqual(1, retval)
mock_create_volume.return_value = 0
self.api.create_fullvol_from_snap.return_value = 1
retval = self.api.create_volume_from_volume('vol_name', 1024,
'src_vol_name')
self.assertEqual(1, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'create_snapshot')
@mock.patch.object(fspythonapi.FSPythonApi, 'create_volume_from_snap')
def test_create_clone_volume_from_volume(self, mock_volume, mock_snap):
mock_snap.side_effect = [0, 1]
mock_volume.side_effect = [0, 1]
retval = self.api.create_clone_volume_from_volume('vol_name', 1024,
'src_vol_name')
self.assertEqual(0, retval)
retval = self.api.create_clone_volume_from_volume('vol_name', 1024,
'src_vol_name')
self.assertEqual(1, retval)
def test_volume_info_analyze_success(self):
vol_info = ('vol_name=vol1,father_name=vol1_father,'
'status=available,vol_size=1024,real_size=1024,'
'pool_id=pool1,create_time=01/01/2015')
vol_info_res = {'result': 0, 'vol_name': 'vol1',
'father_name': 'vol1_father',
'status': 'available', 'vol_size': '1024',
'real_size': '1024', 'pool_id': 'pool1',
'create_time': '01/01/2015'}
retval = self.api.volume_info_analyze(vol_info)
self.assertEqual(vol_info_res, retval)
def test_volume_info_analyze_fail(self):
vol_info = ''
vol_info_res = {'result': 1, 'vol_name': '', 'father_name': '',
'status': '', 'vol_size': '', 'real_size': '',
'pool_id': '', 'create_time': ''}
retval = self.api.volume_info_analyze(vol_info)
self.assertEqual(vol_info_res, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'start_execute_cmd')
@mock.patch.object(fspythonapi.FSPythonApi, 'volume_info_analyze')
@mock.patch.object(fspythonapi.FSPythonApi, 'delete_snapshot')
def test_query_volume(self, mock_delete, mock_analyze, mock_execute):
exec_result = ['result=0\n',
'vol_name=vol1,father_name=vol1_father,status=0,' +
'vol_size=1024,real_size=1024,pool_id=pool1,' +
'create_time=01/01/2015']
query_result = {'result': 0, 'vol_name': 'vol1',
'father_name': 'vol1_father', 'status': '0',
'vol_size': '1024', 'real_size': '1024',
'pool_id': 'pool1', 'create_time': '01/01/2015'}
mock_delete.return_value = 0
mock_execute.return_value = exec_result
mock_analyze.return_value = query_result
retval = self.api.query_volume('vol1')
self.assertEqual(query_result, retval)
exec_result = ['result=0\n',
'vol_name=vol1,father_name=vol1_father,status=1,' +
'vol_size=1024,real_size=1024,pool_id=pool1,' +
'create_time=01/01/2015']
query_result = {'result': 0, 'vol_name': 'vol1',
'father_name': 'vol1_father', 'status': '1',
'vol_size': '1024', 'real_size': '1024',
'pool_id': 'pool1', 'create_time': '01/01/2015'}
mock_delete.return_value = 0
mock_execute.return_value = exec_result
mock_analyze.return_value = query_result
retval = self.api.query_volume('vol1')
self.assertEqual(query_result, retval)
vol_info_failure = 'result=32500000\n'
failure_res = {'result': 1, 'vol_name': '', 'father_name': '',
'status': '', 'vol_size': '', 'real_size': '',
'pool_id': '', 'create_time': ''}
mock_execute.return_value = vol_info_failure
retval = self.api.query_volume('vol1')
self.assertEqual(failure_res, retval)
vol_info_failure = None
failure_res = {'result': 1, 'vol_name': '', 'father_name': '',
'status': '', 'vol_size': '', 'real_size': '',
'pool_id': '', 'create_time': ''}
mock_execute.return_value = vol_info_failure
retval = self.api.query_volume('vol1')
self.assertEqual(failure_res, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'start_execute_cmd')
def test_delete_volume(self, mock_execute):
mock_execute.side_effect = ['result=0\n',
'result=50150007\n', None]
retval = self.api.delete_volume('volume_name')
self.assertEqual(0, retval)
retval = self.api.delete_volume('volume_name')
self.assertEqual('50150007\n', retval)
retval = self.api.delete_volume('volume_name')
self.assertEqual(1, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'start_execute_cmd')
def test_create_snapshot(self, mock_execute):
mock_execute.side_effect = ['result=0\n',
'result=50150007\n', None]
retval = self.api.create_snapshot('snap_name', 'vol_name', 0)
self.assertEqual(0, retval)
retval = self.api.create_snapshot('snap_name', 'vol_name', 0)
self.assertEqual('50150007\n', retval)
retval = self.api.create_snapshot('snap_name', 'vol_name', 0)
self.assertEqual(1, retval)
def test_snap_info_analyze_success(self):
snap_info = ('snap_name=snap1,father_name=snap1_father,status=0,'
'snap_size=1024,real_size=1024,pool_id=pool1,'
'delete_priority=1,create_time=01/01/2015')
snap_info_res = {'result': 0, 'snap_name': 'snap1',
'father_name': 'snap1_father', 'status': '0',
'snap_size': '1024', 'real_size': '1024',
'pool_id': 'pool1', 'delete_priority': '1',
'create_time': '01/01/2015'}
retval = self.api.snap_info_analyze(snap_info)
self.assertEqual(snap_info_res, retval)
def test_snap_info_analyze_fail(self):
snap_info = ''
snap_info_res = {'result': 1, 'snap_name': '', 'father_name': '',
'status': '', 'snap_size': '', 'real_size': '',
'pool_id': '', 'delete_priority': '',
'create_time': ''}
retval = self.api.snap_info_analyze(snap_info)
self.assertEqual(snap_info_res, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'start_execute_cmd')
def test_query_snap(self, mock_execute):
exec_result = ['result=0\n',
'snap_name=snap1,father_name=snap1_father,status=0,' +
'snap_size=1024,real_size=1024,pool_id=pool1,' +
'delete_priority=1,create_time=01/01/2015']
query_result = {'result': 0, 'snap_name': 'snap1',
'father_name': 'snap1_father', 'status': '0',
'snap_size': '1024', 'real_size': '1024',
'pool_id': 'pool1', 'delete_priority': '1',
'create_time': '01/01/2015'}
mock_execute.return_value = exec_result
retval = self.api.query_snap('snap1')
self.assertEqual(query_result, retval)
exec_result = ['result=50150007\n']
qurey_result = {'result': '50150007\n', 'snap_name': '',
'father_name': '', 'status': '', 'snap_size': '',
'real_size': '', 'pool_id': '',
'delete_priority': '', 'create_time': ''}
mock_execute.return_value = exec_result
retval = self.api.query_snap('snap1')
self.assertEqual(qurey_result, retval)
exec_result = ''
query_result = {'result': 1, 'snap_name': '', 'father_name': '',
'status': '', 'snap_size': '', 'real_size': '',
'pool_id': '', 'delete_priority': '',
'create_time': ''}
mock_execute.return_value = exec_result
retval = self.api.query_snap('snap1')
self.assertEqual(query_result, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'start_execute_cmd')
def test_delete_snapshot(self, mock_execute):
mock_execute.side_effect = ['result=0\n',
'result=50150007\n', None]
retval = self.api.delete_snapshot('snap_name')
self.assertEqual(0, retval)
retval = self.api.delete_snapshot('snap_name')
self.assertEqual('50150007\n', retval)
retval = self.api.delete_snapshot('snap_name')
self.assertEqual(1, retval)
def test_pool_info_analyze(self):
pool_info = 'pool_id=pool100,total_capacity=1024,' + \
'used_capacity=500,alloc_capacity=500'
analyze_res = {'result': 0, 'pool_id': 'pool100',
'total_capacity': '1024', 'used_capacity': '500',
'alloc_capacity': '500'}
retval = self.api.pool_info_analyze(pool_info)
self.assertEqual(analyze_res, retval)
pool_info = ''
analyze_res = {'result': 1, 'pool_id': '', 'total_capacity': '',
'used_capacity': '', 'alloc_capacity': ''}
retval = self.api.pool_info_analyze(pool_info)
self.assertEqual(analyze_res, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'start_execute_cmd')
def test_query_pool_info(self, mock_execute):
exec_result = ['result=0\n',
'pool_id=0,total_capacity=1024,' +
'used_capacity=500,alloc_capacity=500\n']
query_result = {'result': 0, 'pool_id': '0',
'total_capacity': '1024', 'used_capacity': '500',
'alloc_capacity': '500'}
mock_execute.return_value = exec_result
retval = self.api.query_pool_info('0')
self.assertEqual(query_result, retval)
exec_result = ['result=51050008\n']
query_result = {'result': '51050008\n', 'pool_id': '',
'total_capacity': '', 'used_capacity': '',
'alloc_capacity': ''}
mock_execute.return_value = exec_result
retval = self.api.query_pool_info('0')
self.assertEqual(query_result, retval)
exec_result = ''
query_result = {'result': 1, 'pool_id': '', 'total_capacity': '',
'used_capacity': '', 'alloc_capacity': ''}
mock_execute.return_value = exec_result
retval = self.api.query_pool_info('0')
self.assertEqual(query_result, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'start_execute_cmd')
def test_query_pool_type(self, mock_execute):
exec_result = ['result=0\n',
'pool_id=0,total_capacity=1024,' +
'used_capacity=500,alloc_capacity=500\n']
query_result = (0, [{'result': 0,
'pool_id': '0', 'total_capacity': '1024',
'used_capacity': '500', 'alloc_capacity': '500'}])
mock_execute.return_value = exec_result
retval = self.api.query_pool_type('sata2copy')
self.assertEqual(query_result, retval)
exec_result = ['result=0\n',
'pool_id=0,total_capacity=1024,' +
'used_capacity=500,alloc_capacity=500\n',
'pool_id=1,total_capacity=2048,' +
'used_capacity=500,alloc_capacity=500\n']
query_result = (0, [{'result': 0, 'pool_id': '0',
'total_capacity': '1024', 'used_capacity': '500',
'alloc_capacity': '500'},
{'result': 0, 'pool_id': '1',
'total_capacity': '2048', 'used_capacity': '500',
'alloc_capacity': '500'}])
mock_execute.return_value = exec_result
retval = self.api.query_pool_type('sata2copy')
self.assertEqual(query_result, retval)
exec_result = ['result=51010015\n']
query_result = (51010015, [])
mock_execute.return_value = exec_result
retval = self.api.query_pool_type('sata2copy')
self.assertEqual(query_result, retval)
exec_result = ''
query_result = (0, [])
mock_execute.return_value = exec_result
retval = self.api.query_pool_type('sata2copy')
self.assertEqual(query_result, retval)
@mock.patch.object(fspythonapi.FSPythonApi, 'start_execute_cmd')
def test_query_dsware_version(self, mock_execute):
mock_execute.side_effect = ['result=0\n', 'result=50500001\n',
'result=50150007\n', None]
retval = self.api.query_dsware_version()
self.assertEqual(0, retval)
retval = self.api.query_dsware_version()
self.assertEqual(1, retval)
retval = self.api.query_dsware_version()
self.assertEqual('50150007\n', retval)
retval = self.api.query_dsware_version()
self.assertEqual(2, retval)

View File

@ -0,0 +1,624 @@
# Copyright (c) 2013 - 2016 Huawei Technologies Co., Ltd.
# 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.
"""
Driver for Huawei FusionStorage.
"""
import os
import re
from oslo_config import cfg
from oslo_log import log as logging
from oslo_service import loopingcall
from cinder import exception
from cinder.i18n import _, _LE, _LW
from cinder.image import image_utils
from cinder import interface
from cinder.volume import driver
from cinder.volume.drivers.fusionstorage import fspythonapi
LOG = logging.getLogger(__name__)
volume_opts = [
cfg.BoolOpt('dsware_isthin',
default = False,
help = 'The flag of thin storage allocation.'),
cfg.StrOpt('dsware_manager',
default = '',
help = 'Fusionstorage manager ip addr for cinder-volume.'),
cfg.StrOpt('fusionstorageagent',
default = '',
help = 'Fusionstorage agent ip addr range.'),
cfg.StrOpt('pool_type',
default = 'default',
help = 'Pool type, like sata-2copy.'),
cfg.ListOpt('pool_id_filter',
default = [],
help = 'Pool id permit to use.'),
cfg.IntOpt('clone_volume_timeout',
default = 680,
help = 'Create clone volume timeout.'),
]
CONF = cfg.CONF
CONF.register_opts(volume_opts)
OLD_VERSION = 1
NEW_VERSION = 0
VOLUME_ALREADY_ATTACHED = 50151401
VOLUME_NOT_EXIST = '50150005\n'
VOLUME_BEING_DELETED = '50151002\n'
SNAP_NOT_EXIST = '50150006\n'
@interface.volumedriver
class DSWAREDriver(driver.VolumeDriver):
"""Huawei FusionStorage Driver."""
VERSION = '1.0'
DSWARE_VOLUME_CREATE_SUCCESS_STATUS = 0
DSWARE_VOLUME_DUPLICATE_VOLUME = 6
DSWARE_VOLUME_CREATING_STATUS = 7
def __init__(self, *args, **kwargs):
super(DSWAREDriver, self).__init__(*args, **kwargs)
self.dsware_client = fspythonapi.FSPythonApi()
self.check_cloned_interval = 2
self.configuration.append_config_values(volume_opts)
def check_for_setup_error(self):
# lrk: check config file here.
if not os.path.exists(fspythonapi.fsc_conf_file):
msg = _("Dsware config file not exists!")
LOG.error(_LE("Dsware config file: %s not exists!"),
fspythonapi.fsc_conf_file)
raise exception.VolumeBackendAPIException(data=msg)
def do_setup(self, context):
# lrk: create fsc_conf_file here.
conf_info = ["manage_ip=%s" % self.configuration.dsware_manager,
"\n",
"vbs_url=%s" % self.configuration.fusionstorageagent]
fsc_dir = os.path.dirname(fspythonapi.fsc_conf_file)
if not os.path.exists(fsc_dir):
os.makedirs(fsc_dir)
with open(fspythonapi.fsc_conf_file, 'w') as f:
f.writelines(conf_info)
# Get pool type.
self.pool_type = self.configuration.pool_type
LOG.debug("Dsware Driver do_setup finish.")
def _get_dsware_manage_ip(self, volume):
dsw_manager_ip = volume['provider_id']
if dsw_manager_ip is not None:
return dsw_manager_ip
else:
msg = _("Dsware get manager ip failed, "
"volume provider_id is None!")
raise exception.VolumeBackendAPIException(data=msg)
def _get_poolid_from_host(self, host):
# Host format: 'hostid@backend#poolid'.
# Other formats: return 'default', and the pool id would be zero.
if host:
if len(host.split('#', 1)) == 2:
return host.split('#')[1]
return self.pool_type
def _create_volume(self, volume_id, volume_size, isThin, volume_host):
pool_id = 0
result = 1
# Query Dsware version.
retcode = self.dsware_client.query_dsware_version()
# Old version.
if retcode == OLD_VERSION:
pool_id = 0
# New version.
elif retcode == NEW_VERSION:
pool_info = self._get_poolid_from_host(volume_host)
if pool_info != self.pool_type:
pool_id = int(pool_info)
# Query Dsware version failed!
else:
LOG.error(_LE("Query Dsware version fail!"))
msg = (_("Query Dsware version failed! Retcode is %s.") %
retcode)
raise exception.VolumeBackendAPIException(data=msg)
try:
result = self.dsware_client.create_volume(
volume_id, pool_id, volume_size, int(isThin))
except Exception as e:
LOG.exception(_LE("Create volume error, details is: %s."), e)
raise
if result != 0:
msg = _("Dsware create volume failed! Result is: %s.") % result
raise exception.VolumeBackendAPIException(data=msg)
def create_volume(self, volume):
# Creates a volume in Dsware.
LOG.debug("Begin to create volume %s in Dsware.", volume['name'])
volume_id = volume['name']
volume_size = volume['size']
volume_host = volume['host']
is_thin = self.configuration.dsware_isthin
# Change GB to MB.
volume_size = volume_size * 1024
self._create_volume(volume_id, volume_size, is_thin, volume_host)
dsw_manager_ip = self.dsware_client.get_manage_ip()
return {"provider_id": dsw_manager_ip}
def _create_volume_from_snap(self, volume_id, volume_size, snapshot_name):
result = self.dsware_client.create_volume_from_snap(
volume_id, volume_size, snapshot_name)
if result != 0:
msg = (_("Dsware: create volume from snap failed. Result: %s.") %
result)
raise exception.VolumeBackendAPIException(data=msg)
def create_volume_from_snapshot(self, volume, snapshot):
# Creates a volume from snapshot.
volume_id = volume['name']
volume_size = volume['size']
snapshot_name = snapshot['name']
if volume_size < int(snapshot['volume_size']):
msg = _("Dsware: volume size can not be less than snapshot size.")
raise exception.VolumeBackendAPIException(data=msg)
volume_size = volume_size * 1024
self._create_volume_from_snap(volume_id, volume_size, snapshot_name)
dsw_manager_ip = self.dsware_client.get_manage_ip()
return {"provider_id": dsw_manager_ip}
def create_cloned_volume(self, volume, src_volume):
"""Dispatcher to Dsware client to create volume from volume.
Wait volume create finished.
"""
volume_name = volume['name']
volume_size = volume['size']
src_volume_name = src_volume['name']
volume_size = volume_size * 1024
result = self.dsware_client.create_volume_from_volume(
volume_name, volume_size, src_volume_name)
if result:
msg = _('Dsware fails to start cloning volume %s.') % volume_name
raise exception.VolumeBackendAPIException(data=msg)
LOG.debug('Dsware create volume %(volume_name)s of size '
'%(volume_size)s from src volume %(src_volume_name)s start.',
{"volume_name": volume_name,
"volume_size": volume_size,
"src_volume_name": src_volume_name})
ret = self._wait_for_create_cloned_volume_finish_timer(volume_name)
if not ret:
msg = (_('Clone volume %s failed while waiting for success.') %
volume_name)
raise exception.VolumeBackendAPIException(data=msg)
LOG.debug('Dsware create volume from volume ends.')
dsw_manager_ip = self.dsware_client.get_manage_ip()
return {"provider_id": dsw_manager_ip}
def _check_create_cloned_volume_finish(self, new_volume_name):
LOG.debug('Loopcall: _check_create_cloned_volume_finish(), '
'volume-name: %s.', new_volume_name)
current_volume = self.dsware_client.query_volume(new_volume_name)
if current_volume:
status = current_volume['status']
LOG.debug('Wait clone volume %(volume_name)s, status: %(status)s.',
{"volume_name": new_volume_name,
"status": status})
if int(status) == self.DSWARE_VOLUME_CREATING_STATUS or int(
status) == self.DSWARE_VOLUME_DUPLICATE_VOLUME:
self.count += 1
elif int(status) == self.DSWARE_VOLUME_CREATE_SUCCESS_STATUS:
raise loopingcall.LoopingCallDone(retvalue=True)
else:
msg = _('Clone volume %(new_volume_name)s failed, '
'volume status is: %(status)s.')
LOG.error(msg, {'new_volume_name': new_volume_name,
'status': status})
raise loopingcall.LoopingCallDone(retvalue=False)
if self.count > self.configuration.clone_volume_timeout:
msg = _('Dsware clone volume time out. '
'Volume: %(new_volume_name)s, status: %(status)s')
LOG.error(msg, {'new_volume_name': new_volume_name,
'status': current_volume['status']})
raise loopingcall.LoopingCallDone(retvalue=False)
else:
LOG.warning(_LW('Can not find volume %s from Dsware.'),
new_volume_name)
self.count += 1
if self.count > 10:
msg = _("Dsware clone volume failed: volume "
"can not be found from Dsware.")
LOG.error(msg)
raise loopingcall.LoopingCallDone(retvalue=False)
def _wait_for_create_cloned_volume_finish_timer(self, new_volume_name):
timer = loopingcall.FixedIntervalLoopingCall(
self._check_create_cloned_volume_finish, new_volume_name)
LOG.debug('Call _check_create_cloned_volume_finish: volume-name %s.',
new_volume_name)
self.count = 0
ret = timer.start(interval=self.check_cloned_interval).wait()
timer.stop()
return ret
def _analyse_output(self, out):
if out is not None:
analyse_result = {}
out_temp = out.split('\n')
for line in out_temp:
if re.search('^ret_code=', line):
analyse_result['ret_code'] = line[9:]
elif re.search('^ret_desc=', line):
analyse_result['ret_desc'] = line[9:]
elif re.search('^dev_addr=', line):
analyse_result['dev_addr'] = line[9:]
return analyse_result
else:
return None
def _attach_volume(self, volume_name, dsw_manager_ip):
cmd = ['vbs_cli', '-c', 'attachwithip', '-v', volume_name, '-i',
dsw_manager_ip.replace('\n', ''), '-p', 0]
out, err = self._execute(*cmd, run_as_root=True)
analyse_result = self._analyse_output(out)
LOG.debug("Attach volume result is %s.", analyse_result)
return analyse_result
def _detach_volume(self, volume_name, dsw_manager_ip):
cmd = ['vbs_cli', '-c', 'detachwithip', '-v', volume_name, '-i',
dsw_manager_ip.replace('\n', ''), '-p', 0]
out, err = self._execute(*cmd, run_as_root=True)
analyse_result = self._analyse_output(out)
LOG.debug("Detach volume result is %s.", analyse_result)
return analyse_result
def _query_volume_attach(self, volume_name, dsw_manager_ip):
cmd = ['vbs_cli', '-c', 'querydevwithip', '-v', volume_name, '-i',
dsw_manager_ip.replace('\n', ''), '-p', 0]
out, err = self._execute(*cmd, run_as_root=True)
analyse_result = self._analyse_output(out)
LOG.debug("Query volume attach result is %s.", analyse_result)
return analyse_result
def copy_image_to_volume(self, context, volume, image_service, image_id):
# Copy image to volume.
# Step1: attach volume to host.
LOG.debug("Begin to copy image to volume.")
dsw_manager_ip = self._get_dsware_manage_ip(volume)
volume_attach_result = self._attach_volume(volume['name'],
dsw_manager_ip)
volume_attach_path = ''
if volume_attach_result is not None and int(
volume_attach_result['ret_code']) == 0:
volume_attach_path = volume_attach_result['dev_addr']
LOG.debug("Volume attach path is %s.", volume_attach_path)
if volume_attach_path == '':
msg = _("Host attach volume failed!")
raise exception.VolumeBackendAPIException(data=msg)
# Step2: fetch the image from image_service and write it to the
# volume.
try:
image_utils.fetch_to_raw(context,
image_service,
image_id,
volume_attach_path,
self.configuration.volume_dd_blocksize)
finally:
# Step3: detach volume from host.
dsw_manager_ip = self._get_dsware_manage_ip(volume)
volume_detach_result = self._detach_volume(volume['name'],
dsw_manager_ip)
if volume_detach_result is not None and int(
volume_detach_result['ret_code']) != 0:
msg = (_("Dsware detach volume from host failed: %s!") %
volume_detach_result)
raise exception.VolumeBackendAPIException(data=msg)
def copy_volume_to_image(self, context, volume, image_service, image_meta):
# Copy volume to image.
# If volume was not attached, then attach it.
dsw_manager_ip = self._get_dsware_manage_ip(volume)
already_attached = False
_attach_result = self._attach_volume(volume['name'], dsw_manager_ip)
if _attach_result:
retcode = _attach_result['ret_code']
if int(retcode) == VOLUME_ALREADY_ATTACHED:
already_attached = True
result = self._query_volume_attach(volume['name'],
dsw_manager_ip)
if not result or int(result['ret_code']) != 0:
msg = (_("Query volume attach failed, result=%s.") %
result)
raise exception.VolumeBackendAPIException(data=msg)
elif int(retcode) == 0:
result = _attach_result
else:
msg = (_("Attach volume to host failed "
"in copy volume to image, retcode: %s.") %
retcode)
raise exception.VolumeBackendAPIException(data=msg)
volume_attach_path = result['dev_addr']
else:
msg = _("Attach_volume failed.")
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
try:
image_utils.upload_volume(context,
image_service,
image_meta,
volume_attach_path)
except Exception as e:
LOG.error(_LE("Upload volume error, details: %s."), e)
raise e
finally:
if not already_attached:
self._detach_volume(volume['name'], dsw_manager_ip)
def _get_volume(self, volume_name):
result = self.dsware_client.query_volume(volume_name)
LOG.debug("Dsware query volume result is %s.", result['result'])
if result['result'] == VOLUME_NOT_EXIST:
LOG.debug("Dsware volume %s does not exist.", volume_name)
return False
elif result['result'] == 0:
return True
else:
msg = _("Dsware query volume %s failed!") % volume_name
raise exception.VolumeBackendAPIException(data=msg)
def _delete_volume(self, volume_name):
# Delete volume in Dsware.
result = self.dsware_client.delete_volume(volume_name)
LOG.debug("Dsware delete volume, result is %s.", result)
if result == VOLUME_NOT_EXIST:
LOG.debug("Dsware delete volume, volume does not exist.")
return True
elif result == VOLUME_BEING_DELETED:
LOG.debug("Dsware delete volume, volume is being deleted.")
return True
elif result == 0:
return True
else:
msg = _("Dsware delete volume failed: %s!") % result
raise exception.VolumeBackendAPIException(data=msg)
def delete_volume(self, volume):
# Delete volume.
# If volume does not exist, then return.
LOG.debug("Begin to delete volume in Dsware: %s.", volume['name'])
if not self._get_volume(volume['name']):
return True
return self._delete_volume(volume['name'])
def _get_snapshot(self, snapshot_name):
snapshot_info = self.dsware_client.query_snap(snapshot_name)
LOG.debug("Get snapshot, snapshot_info is : %s.", snapshot_info)
if snapshot_info['result'] == SNAP_NOT_EXIST:
LOG.error(_LE('Snapshot: %s not found!'), snapshot_name)
return False
elif snapshot_info['result'] == 0:
return True
else:
msg = _("Dsware get snapshot failed!")
raise exception.VolumeBackendAPIException(data=msg)
def _create_snapshot(self, snapshot_id, volume_id):
LOG.debug("Create snapshot %s to Dsware.", snapshot_id)
smart_flag = 0
res = self.dsware_client.create_snapshot(snapshot_id,
volume_id,
smart_flag)
if res != 0:
msg = _("Dsware Create Snapshot failed! Result: %s.") % res
raise exception.VolumeBackendAPIException(data=msg)
def _delete_snapshot(self, snapshot_id):
LOG.debug("Delete snapshot %s to Dsware.", snapshot_id)
res = self.dsware_client.delete_snapshot(snapshot_id)
LOG.debug("Ddelete snapshot result is: %s.", res)
if res != 0:
raise exception.SnapshotIsBusy(snapshot_name=snapshot_id)
def create_snapshot(self, snapshot):
vol_id = 'volume-%s' % snapshot['volume_id']
snapshot_id = snapshot['name']
if not self._get_volume(vol_id):
msg = _LE('Create Snapshot, but volume: %s not found!')
LOG.error(msg, vol_id)
raise exception.VolumeNotFound(volume_id=vol_id)
else:
self._create_snapshot(snapshot_id, vol_id)
def delete_snapshot(self, snapshot):
LOG.debug("Delete snapshot %s.", snapshot['name'])
snapshot_id = snapshot['name']
if self._get_snapshot(snapshot_id):
self._delete_snapshot(snapshot_id)
def _calculate_pool_info(self, pool_sets):
filter = False
pools_status = []
reserved_percentage = self.configuration.reserved_percentage
pool_id_filter = self.configuration.pool_id_filter
LOG.debug("Filtered pool id is %s.", pool_id_filter)
if pool_id_filter == []:
for pool_info in pool_sets:
pool = {}
pool['pool_name'] = pool_info['pool_id']
pool['total_capacity_gb'] = float(
pool_info['total_capacity']) / 1024
pool['allocated_capacity_gb'] = float(
pool_info['used_capacity']) / 1024
pool['free_capacity_gb'] = pool['total_capacity_gb'] - pool[
'allocated_capacity_gb']
pool['QoS_support'] = False
pool['reserved_percentage'] = reserved_percentage
pools_status.append(pool)
else:
for pool_info in pool_sets:
for pool_id in pool_id_filter:
if pool_id == pool_info['pool_id']:
filter = True
break
if filter:
pool = {}
pool['pool_name'] = pool_info['pool_id']
pool['total_capacity_gb'] = float(
pool_info['total_capacity']) / 1024
pool['allocated_capacity_gb'] = float(
pool_info['used_capacity']) / 1024
pool['free_capacity_gb'] = float(
pool['total_capacity_gb'] - pool[
'allocated_capacity_gb'])
pool['QoS_support'] = False
pool['reserved_percentage'] = reserved_percentage
pools_status.append(pool)
filter = False
return pools_status
def _update_single_pool_info_status(self):
"""Query pool info when Dsware is single-pool version."""
status = {}
status['volume_backend_name'] = self.configuration.volume_backend_name
status['vendor_name'] = 'Open Source'
status['driver_version'] = self.VERSION
status['storage_protocol'] = 'dsware'
status['total_capacity_gb'] = 0
status['free_capacity_gb'] = 0
status['reserved_percentage'] = self.configuration.reserved_percentage
status['QoS_support'] = False
pool_id = 0
pool_info = self.dsware_client.query_pool_info(pool_id)
result = pool_info['result']
if result == 0:
status['total_capacity_gb'] = float(
pool_info['total_capacity']) / 1024
status['free_capacity_gb'] = (float(
pool_info['total_capacity']) - float(
pool_info['used_capacity'])) / 1024
LOG.debug("total_capacity_gb is %s, free_capacity_gb is %s.",
status['total_capacity_gb'],
status['free_capacity_gb'])
self._stats = status
else:
self._stats = None
def _update_multi_pool_of_same_type_status(self):
"""Query info of multiple pools when Dsware is multi-pool version.
These pools have the same pool type.
"""
status = {}
status['volume_backend_name'] = self.configuration.volume_backend_name
status['vendor_name'] = 'Open Source'
status['driver_version'] = self.VERSION
status['storage_protocol'] = 'dsware'
(result, pool_sets) = self.dsware_client.query_pool_type(
self.pool_type)
if pool_sets == []:
self._stats = None
else:
pools_status = self._calculate_pool_info(pool_sets)
status['pools'] = pools_status
self._stats = status
def get_volume_stats(self, refresh=False):
if refresh:
dsware_version = self.dsware_client.query_dsware_version()
# Old version.
if dsware_version == OLD_VERSION:
self._update_single_pool_info_status()
# New version.
elif dsware_version == NEW_VERSION:
self._update_multi_pool_of_same_type_status()
else:
msg = _("Dsware query Dsware version failed!")
LOG.error(msg)
raise exception.VolumeBackendAPIException(data=msg)
return self._stats
def extend_volume(self, volume, new_size):
# Extend volume in Dsware.
LOG.debug("Begin to extend volume in Dsware: %s.", volume['name'])
volume_id = volume['name']
if volume['size'] > new_size:
msg = (_("Dsware extend Volume failed! "
"New size %(new_size)s should be greater than "
"old size %(old_size)s!")
% {'new_size': new_size,
'old_size': volume['size']})
raise exception.VolumeBackendAPIException(data=msg)
# Change GB to MB.
volume_size = new_size * 1024
result = self.dsware_client.extend_volume(volume_id, volume_size)
if result != 0:
msg = _("Dsware extend Volume failed! Result:%s.") % result
raise exception.VolumeBackendAPIException(data=msg)
def initialize_connection(self, volume, connector):
"""Initializes the connection and returns connection info."""
LOG.debug("Begin initialize connection.")
properties = {}
properties['volume_name'] = volume['name']
properties['volume'] = volume
properties['dsw_manager_ip'] = self._get_dsware_manage_ip(volume)
LOG.debug("End initialize connection with properties:%s.", properties)
return {'driver_volume_type': 'dsware',
'data': properties}
def terminate_connection(self, volume, connector, force=False, **kwargs):
pass
def create_export(self, context, volume, connector):
pass
def ensure_export(self, context, volume):
pass
def remove_export(self, context, volume):
pass

View File

@ -0,0 +1,499 @@
# Copyright (c) 2013 - 2016 Huawei Technologies Co., Ltd.
# 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.
"""
Volume api for FusionStorage systems.
"""
import os
import re
import six
from oslo_log import log as logging
from cinder.i18n import _LE
from cinder import utils
LOG = logging.getLogger(__name__)
fsc_conf_file = "/etc/cinder/volumes/fsc_conf"
fsc_cli = "fsc_cli"
fsc_ip = []
fsc_port = '10519'
manage_ip = "127.0.0.1"
CMD_BIN = fsc_cli
volume_info = {
'result': '',
'vol_name': '',
'father_name': '',
'status': '',
'vol_size': '',
'real_size': '',
'pool_id': '',
'create_time': ''}
snap_info = {
'result': '',
'snap_name': '',
'father_name': '',
'status': '',
'snap_size': '',
'real_size': '',
'pool_id': '',
'delete_priority': '',
'create_time': ''}
pool_info = {
'result': '',
'pool_id': '',
'total_capacity': '',
'used_capacity': '',
'alloc_capacity': ''}
class FSPythonApi(object):
def __init__(self):
LOG.debug("FSPythonApi init.")
self.get_ip_port()
self.res_idx = len('result=')
def get_ip_port(self):
LOG.debug("File fsc_conf_file is %s.", fsc_conf_file)
if os.path.exists(fsc_conf_file):
try:
fsc_file = open(fsc_conf_file, 'r')
full_txt = fsc_file.readlines()
LOG.debug("Full_txt is %s.", full_txt)
for line in full_txt:
if re.search('^vbs_url=', line):
tmp_vbs_url = line[8:]
return re.split(',', tmp_vbs_url)
except Exception as e:
LOG.debug("Get fsc ip failed, error=%s.", e)
finally:
fsc_file.close()
else:
LOG.debug("Fsc conf file not exist, file_name=%s.", fsc_conf_file)
def get_manage_ip(self):
LOG.debug("File fsc_conf_file is %s.", fsc_conf_file)
if os.path.exists(fsc_conf_file):
try:
fsc_file = open(fsc_conf_file, 'r')
full_txt = fsc_file.readlines()
for line in full_txt:
if re.search('^manage_ip=', line):
manage_ip = line[len('manage_ip='):]
manage_ip = manage_ip.strip('\n')
return manage_ip
except Exception as e:
LOG.debug("Get manage ip failed, error=%s.", e)
finally:
fsc_file.close()
else:
LOG.debug("Fsc conf file not exist, file_name=%s.", fsc_conf_file)
def get_dsw_manage_ip(self):
return manage_ip
def start_execute_cmd(self, cmd, full_result_flag):
fsc_ip = self.get_ip_port()
manage_ip = self.get_manage_ip()
ip_num = len(fsc_ip)
LOG.debug("fsc_ip is %s", fsc_ip)
if ip_num <= 0:
return None
if ip_num > 3:
ip_num = 3
exec_result = ''
result = ''
if full_result_flag:
for ip in fsc_ip:
cmd_args = [CMD_BIN, '--manage_ip', manage_ip.replace(
'\n', ''), '--ip', ip.replace('\n', '')] + cmd.split()
LOG.debug("Dsware cmd_args is %s.", cmd_args)
exec_result, err = utils.execute(*cmd_args, run_as_root=True)
exec_result = exec_result.split('\n')
LOG.debug("Result is %s.", exec_result)
if exec_result:
for line in exec_result:
if re.search('^result=0', line):
return exec_result
elif re.search('^result=50150007', line):
return 'result=0'
elif re.search('^result=50150008', line):
return 'result=0'
elif re.search('^result=50', line):
return exec_result
return exec_result
else:
for ip in fsc_ip:
cmd_args = [CMD_BIN, '--manage_ip', manage_ip.replace(
'\n', ''), '--ip', ip.replace('\n', '')] + cmd.split()
LOG.debug("Dsware cmd_args is %s.", cmd_args)
exec_result, err = utils.execute(*cmd_args, run_as_root=True)
LOG.debug("Result is %s.", exec_result)
exec_result = exec_result.split('\n')
if exec_result:
for line in exec_result:
if re.search('^result=', line):
result = line
if re.search('^result=0', line):
return line
elif re.search('^result=50150007', line):
return 'result=0'
elif re.search('^result=50150008', line):
return 'result=0'
elif re.search('^result=50', line):
return line
return result
def create_volume(self, vol_name, pool_id, vol_size, thin_flag):
cmd = '--op createVolume' + ' ' + '--volName' + ' ' + six.text_type(
vol_name) + ' ' + '--poolId' + ' ' + six.text_type(
pool_id) + ' ' + '--volSize' + ' ' + six.text_type(
vol_size) + ' ' + '--thinFlag' + ' ' + six.text_type(thin_flag)
exec_result = self.start_execute_cmd(cmd, 0)
if exec_result:
if re.search('^result=0', exec_result):
return 0
else:
return exec_result[self.res_idx:]
else:
return 1
def extend_volume(self, vol_name, new_vol_size):
cmd = ''
cmd = '--op expandVolume' + ' ' + '--volName' + ' ' + six.text_type(
vol_name) + ' ' + '--volSize' + ' ' + six.text_type(new_vol_size)
exec_result = self.start_execute_cmd(cmd, 0)
if exec_result:
if re.search('^result=0', exec_result):
return 0
else:
return exec_result[self.res_idx:]
else:
return 1
def create_volume_from_snap(self, vol_name, vol_size, snap_name):
cmd = ('--op createVolumeFromSnap' + ' ') + (
'--volName' + ' ') + six.text_type(
vol_name) + ' ' + '--snapNameSrc' + ' ' + six.text_type(
snap_name) + ' ' + '--volSize' + ' ' + six.text_type(vol_size)
exec_result = self.start_execute_cmd(cmd, 0)
if exec_result:
if re.search('^result=0', exec_result):
return 0
else:
return exec_result[self.res_idx:]
else:
return 1
def create_fullvol_from_snap(self, vol_name, snap_name):
cmd = ('--op createFullVolumeFromSnap' + ' ') + (
'--volName' + ' ') + six.text_type(
vol_name) + ' ' + '--snapName' + ' ' + six.text_type(snap_name)
exec_result = self.start_execute_cmd(cmd, 0)
if exec_result:
if re.search('^result=0', exec_result):
return 0
else:
return exec_result[self.res_idx:]
else:
return 1
def create_volume_from_volume(self, vol_name, vol_size, src_vol_name):
retcode = 1
tmp_snap_name = six.text_type(vol_name) + '_tmp_snap'
retcode = self.create_snapshot(tmp_snap_name, src_vol_name, 0)
if 0 != retcode:
return retcode
retcode = self.create_volume(vol_name, 0, vol_size, 0)
if 0 != retcode:
self.delete_snapshot(tmp_snap_name)
return retcode
retcode = self.create_fullvol_from_snap(vol_name, tmp_snap_name)
if 0 != retcode:
self.delete_snapshot(tmp_snap_name)
self.delete_volume(vol_name)
return retcode
return 0
def create_clone_volume_from_volume(self, vol_name,
vol_size, src_vol_name):
retcode = 1
tmp_snap_name = six.text_type(src_vol_name) + '_DT_clnoe_snap'
retcode = self.create_snapshot(tmp_snap_name, src_vol_name, 0)
if 0 != retcode:
return retcode
retcode = self.create_volume_from_snap(
vol_name, vol_size, tmp_snap_name)
if 0 != retcode:
return retcode
return 0
def volume_info_analyze(self, vol_info):
local_volume_info = volume_info
if not vol_info:
local_volume_info['result'] = 1
return local_volume_info
local_volume_info['result'] = 0
vol_info_list = []
vol_info_list = re.split(',', vol_info)
for line in vol_info_list:
line = line.replace('\n', '')
if re.search('^vol_name=', line):
local_volume_info['vol_name'] = line[len('vol_name='):]
elif re.search('^father_name=', line):
local_volume_info['father_name'] = line[len('father_name='):]
elif re.search('^status=', line):
local_volume_info['status'] = line[len('status='):]
elif re.search('^vol_size=', line):
local_volume_info['vol_size'] = line[len('vol_size='):]
elif re.search('^real_size=', line):
local_volume_info['real_size'] = line[len('real_size='):]
elif re.search('^pool_id=', line):
local_volume_info['pool_id'] = line[len('pool_id='):]
elif re.search('^create_time=', line):
local_volume_info['create_time'] = line[len('create_time='):]
else:
LOG.error(_LE("Analyze key not exist, key=%s."),
six.text_type(line))
return local_volume_info
def query_volume(self, vol_name):
tmp_volume_info = volume_info
cmd = '--op queryVolume' + ' ' + '--volName' + ' ' + vol_name
exec_result = self.start_execute_cmd(cmd, 1)
if exec_result:
for line in exec_result:
if re.search('^result=', line):
if not re.search('^result=0', line):
tmp_volume_info['result'] = line[self.res_idx:]
return tmp_volume_info
for line in exec_result:
if re.search('^vol_name=' + vol_name, line):
tmp_volume_info = self.volume_info_analyze(line)
if six.text_type(0) == tmp_volume_info['status']:
tmp_snap_name = six.text_type(
vol_name) + '_tmp_snap'
self.delete_snapshot(tmp_snap_name)
return tmp_volume_info
tmp_volume_info['result'] = 1
return tmp_volume_info
def delete_volume(self, vol_name):
cmd = '--op deleteVolume' + ' ' + '--volName' + ' ' + vol_name
exec_result = self.start_execute_cmd(cmd, 0)
if exec_result:
if re.search('^result=0', exec_result):
return 0
else:
return exec_result[self.res_idx:]
else:
return 1
def create_snapshot(self, snap_name, vol_name, smart_flag):
cmd = '--op createSnapshot' + ' ' + '--volName' + ' ' + six.text_type(
vol_name) + ' ' + '--snapName' + ' ' + six.text_type(
snap_name) + ' ' + '--smartFlag' + ' ' + six.text_type(smart_flag)
exec_result = self.start_execute_cmd(cmd, 0)
if exec_result:
if re.search('^result=0', exec_result):
return 0
else:
return exec_result[self.res_idx:]
else:
return 1
def snap_info_analyze(self, info):
local_snap_info = snap_info.copy()
if not info:
local_snap_info['result'] = 1
return local_snap_info
local_snap_info['result'] = 0
snap_info_list = []
snap_info_list = re.split(',', info)
for line in snap_info_list:
line = line.replace('\n', '')
if re.search('^snap_name=', line):
local_snap_info['snap_name'] = line[len('snap_name='):]
elif re.search('^father_name=', line):
local_snap_info['father_name'] = line[len('father_name='):]
elif re.search('^status=', line):
local_snap_info['status'] = line[len('status='):]
elif re.search('^snap_size=', line):
local_snap_info['snap_size'] = line[len('snap_size='):]
elif re.search('^real_size=', line):
local_snap_info['real_size'] = line[len('real_size='):]
elif re.search('^pool_id=', line):
local_snap_info['pool_id'] = line[len('pool_id='):]
elif re.search('^delete_priority=', line):
local_snap_info['delete_priority'] = line[
len('delete_priority='):]
elif re.search('^create_time=', line):
local_snap_info['create_time'] = line[len('create_time='):]
else:
LOG.error(_LE("Analyze key not exist, key=%s."),
line)
return local_snap_info
def query_snap(self, snap_name):
tmp_snap_info = snap_info.copy()
cmd = '--op querySnapshot' + ' ' + '--snapName' + ' ' + snap_name
exec_result = self.start_execute_cmd(cmd, 1)
if exec_result:
for line in exec_result:
if re.search('^result=', line):
if not re.search('^result=0', line):
tmp_snap_info['result'] = line[self.res_idx:]
return tmp_snap_info
for line in exec_result:
if re.search('^snap_name=' + snap_name, line):
tmp_snap_info = self.snap_info_analyze(line)
return tmp_snap_info
tmp_snap_info['result'] = 1
return tmp_snap_info
def delete_snapshot(self, snap_name):
cmd = '--op deleteSnapshot' + ' ' + '--snapName' + ' ' + snap_name
exec_result = self.start_execute_cmd(cmd, 0)
if exec_result:
if re.search('^result=0', exec_result):
return 0
else:
return exec_result[self.res_idx:]
else:
return 1
def pool_info_analyze(self, info):
local_pool_info = pool_info.copy()
if not info:
local_pool_info['result'] = 1
return local_pool_info
local_pool_info['result'] = 0
pool_info_list = []
pool_info_list = re.split(',', info)
for line in pool_info_list:
line = line.replace('\n', '')
if re.search('^pool_id=', line):
local_pool_info['pool_id'] = line[len('pool_id='):]
elif re.search('^total_capacity=', line):
local_pool_info['total_capacity'] = line[
len('total_capacity='):]
elif re.search('^used_capacity=', line):
local_pool_info['used_capacity'] = line[len('used_capacity='):]
elif re.search('^alloc_capacity=', line):
local_pool_info['alloc_capacity'] = line[
len('alloc_capacity='):]
else:
LOG.error(_LE("Analyze key not exist, key=%s."),
six.text_type(line))
return local_pool_info
def query_pool_info(self, pool_id):
tmp_pool_info = pool_info.copy()
cmd = '--op queryPoolInfo' + ' ' + '--poolId' + ' ' + six.text_type(
pool_id)
LOG.debug("Pool id is %s.", pool_id)
exec_result = self.start_execute_cmd(cmd, 1)
if exec_result:
for line in exec_result:
if re.search('^result=', line):
if not re.search('^result=0', line):
tmp_pool_info['result'] = line[self.res_idx:]
return tmp_pool_info
for line in exec_result:
if re.search('^pool_id=' + six.text_type(pool_id),
line):
tmp_pool_info = self.pool_info_analyze(line)
return tmp_pool_info
tmp_pool_info['result'] = 1
return tmp_pool_info
def query_pool_type(self, pool_type):
pool_list = []
tmp_pool_info = {}
result = 0
cmd = ''
cmd = '--op queryPoolType --poolType' + ' ' + pool_type
LOG.debug("Query poolType: %s.", pool_type)
exec_result = self.start_execute_cmd(cmd, 1)
if exec_result:
for line in exec_result:
line = line.replace('\n', '')
if re.search('^result=', line):
if not re.search('^result=0', line):
result = int(line[self.res_idx:])
break
for one_line in exec_result:
if re.search('^pool_id=', one_line):
tmp_pool_info = self.pool_info_analyze(one_line)
pool_list.append(tmp_pool_info)
break
return (result, pool_list)
def query_dsware_version(self):
retcode = 2
cmd = '--op getDSwareIdentifier'
exec_result = self.start_execute_cmd(cmd, 0)
if exec_result:
# New version.
if re.search('^result=0', exec_result):
retcode = 0
# Old version.
elif re.search('^result=50500001', exec_result):
retcode = 1
# Failed!
else:
retcode = exec_result[self.res_idx:]
return retcode

View File

@ -0,0 +1,3 @@
---
features:
- Added backend driver for Huawei FusionStorage.