cinder/cinder/tests/unit/test_hitachi_hnas_nfs.py

302 lines
11 KiB
Python

# Copyright (c) 2014 Hitachi Data Systems, 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 os
import tempfile
import mock
import six
from cinder import exception
from cinder import test
from cinder.volume import configuration as conf
from cinder.volume.drivers.hitachi import hnas_nfs as nfs
from cinder.volume import volume_types
SHARESCONF = """172.17.39.132:/cinder
172.17.39.133:/cinder"""
HNASCONF = """<?xml version="1.0" encoding="UTF-8" ?>
<config>
<hnas_cmd>ssc</hnas_cmd>
<mgmt_ip0>172.17.44.15</mgmt_ip0>
<username>supervisor</username>
<password>supervisor</password>
<svc_0>
<volume_type>default</volume_type>
<hdp>172.17.39.132:/cinder</hdp>
</svc_0>
<svc_1>
<volume_type>silver</volume_type>
<hdp>172.17.39.133:/cinder</hdp>
</svc_1>
</config>
"""
HNAS_WRONG_CONF1 = """<?xml version="1.0" encoding="UTF-8" ?>
<config>
<hnas_cmd>ssc</hnas_cmd>
<mgmt_ip0>172.17.44.15</mgmt_ip0>
<username>supervisor</username>
<password>supervisor</password>
<volume_type>default</volume_type>
<hdp>172.17.39.132:/cinder</hdp>
</svc_0>
</config>
"""
HNAS_WRONG_CONF2 = """<?xml version="1.0" encoding="UTF-8" ?>
<config>
<hnas_cmd>ssc</hnas_cmd>
<mgmt_ip0>172.17.44.15</mgmt_ip0>
<username>supervisor</username>
<password>supervisor</password>
<svc_0>
<volume_type>default</volume_type>
</svc_0>
<svc_1>
<volume_type>silver</volume_type>
</svc_1>
</config>
"""
# The following information is passed on to tests, when creating a volume
_SERVICE = ('Test_hdp', 'Test_path', 'Test_label')
_SHARE = '172.17.39.132:/cinder'
_SHARE2 = '172.17.39.133:/cinder'
_EXPORT = '/cinder'
_VOLUME = {'name': 'volume-bcc48c61-9691-4e5f-897c-793686093190',
'volume_id': 'bcc48c61-9691-4e5f-897c-793686093190',
'size': 128,
'volume_type': 'silver',
'volume_type_id': 'test',
'metadata': [{'key': 'type',
'service_label': 'silver'}],
'provider_location': None,
'id': 'bcc48c61-9691-4e5f-897c-793686093190',
'status': 'available',
'host': 'host1@hnas-iscsi-backend#silver'}
_SNAPVOLUME = {'name': 'snapshot-51dd4-8d8a-4aa9-9176-086c9d89e7fc',
'id': '51dd4-8d8a-4aa9-9176-086c9d89e7fc',
'size': 128,
'volume_type': None,
'provider_location': None,
'volume_size': 128,
'volume_name': 'volume-bcc48c61-9691-4e5f-897c-793686093190',
'volume_id': 'bcc48c61-9691-4e5f-897c-793686093191',
'host': 'host1@hnas-iscsi-backend#silver'}
GET_ID_VOL = {
("bcc48c61-9691-4e5f-897c-793686093190"): [_VOLUME],
("bcc48c61-9691-4e5f-897c-793686093191"): [_SNAPVOLUME]
}
def id_to_vol(arg):
return GET_ID_VOL.get(arg)
class SimulatedHnasBackend(object):
"""Simulation Back end. Talks to HNAS."""
# these attributes are shared across object instances
start_lun = 0
def __init__(self):
self.type = 'HNAS'
self.out = ''
def file_clone(self, cmd, ip0, user, pw, fslabel, source_path,
target_path):
return ""
def get_version(self, ver, cmd, ip0, user, pw):
self.out = "Array_ID: 18-48-A5-A1-80-13 (3080-G2) " \
"version: 11.2.3319.09 LU: 256 " \
"RG: 0 RG_LU: 0 Utility_version: 11.1.3225.01"
return self.out
def get_hdp_info(self, ip0, user, pw):
self.out = "HDP: 1024 272384 MB 33792 MB 12 % LUs: 70 " \
"Normal fs1\n" \
"HDP: 1025 546816 MB 73728 MB 13 % LUs: 194 " \
"Normal fs2"
return self.out
def get_nfs_info(self, cmd, ip0, user, pw):
self.out = "Export: /cinder Path: /volumes HDP: fs1 FSID: 1024 " \
"EVS: 1 IPS: 172.17.39.132\n" \
"Export: /cinder Path: /volumes HDP: fs2 FSID: 1025 " \
"EVS: 1 IPS: 172.17.39.133"
return self.out
class HDSNFSDriverTest(test.TestCase):
"""Test HNAS NFS volume driver."""
def __init__(self, *args, **kwargs):
super(HDSNFSDriverTest, self).__init__(*args, **kwargs)
@mock.patch.object(nfs, 'factory_bend')
def setUp(self, m_factory_bend):
super(HDSNFSDriverTest, self).setUp()
self.backend = SimulatedHnasBackend()
m_factory_bend.return_value = self.backend
(handle, self.config_file) = tempfile.mkstemp('.xml')
os.write(handle, HNASCONF)
os.close(handle)
(handle, self.shares_file) = tempfile.mkstemp('')
os.write(handle, SHARESCONF)
os.close(handle)
self.configuration = mock.Mock(spec=conf.Configuration)
self.configuration.hds_hnas_nfs_config_file = self.config_file
self.configuration.nfs_shares_config = self.shares_file
self.configuration.nfs_mount_point_base = '/opt/stack/cinder/mnt'
self.configuration.nfs_mount_options = None
self.configuration.nas_ip = None
self.configuration.nas_share_path = None
self.configuration.nas_mount_options = None
self.driver = nfs.HDSNFSDriver(configuration=self.configuration)
self.driver.do_setup("")
self.addCleanup(self._clean)
def _clean(self):
os.remove(self.config_file)
os.remove(self.shares_file)
@mock.patch('__builtin__.open')
@mock.patch.object(os, 'access')
def test_read_config(self, m_access, m_open):
# Test exception when file is not found
m_access.return_value = False
m_open.return_value = six.StringIO(HNASCONF)
self.assertRaises(exception.NotFound, nfs._read_config, '')
# Test exception when config file has parsing errors
# due to missing <svc> tag
m_access.return_value = True
m_open.return_value = six.StringIO(HNAS_WRONG_CONF1)
self.assertRaises(exception.ConfigNotFound, nfs._read_config, '')
# Test exception when config file has parsing errors
# due to missing <hdp> tag
m_open.return_value = six.StringIO(HNAS_WRONG_CONF2)
self.configuration.hds_hnas_iscsi_config_file = ''
self.assertRaises(exception.ParameterNotFound, nfs._read_config, '')
@mock.patch.object(nfs.HDSNFSDriver, '_id_to_vol')
@mock.patch.object(nfs.HDSNFSDriver, '_get_provider_location')
@mock.patch.object(nfs.HDSNFSDriver, '_get_export_path')
@mock.patch.object(nfs.HDSNFSDriver, '_get_volume_location')
def test_create_snapshot(self, m_get_volume_location, m_get_export_path,
m_get_provider_location, m_id_to_vol):
svol = _SNAPVOLUME.copy()
m_id_to_vol.return_value = svol
m_get_provider_location.return_value = _SHARE
m_get_volume_location.return_value = _SHARE
m_get_export_path.return_value = _EXPORT
loc = self.driver.create_snapshot(svol)
out = "{'provider_location': \'" + _SHARE + "'}"
self.assertEqual(str(loc), out)
@mock.patch.object(nfs.HDSNFSDriver, '_get_service')
@mock.patch.object(nfs.HDSNFSDriver, '_id_to_vol', side_effect=id_to_vol)
@mock.patch.object(nfs.HDSNFSDriver, '_get_provider_location')
@mock.patch.object(nfs.HDSNFSDriver, '_get_volume_location')
def test_create_cloned_volume(self, m_get_volume_location,
m_get_provider_location, m_id_to_vol,
m_get_service):
vol = _VOLUME.copy()
svol = _SNAPVOLUME.copy()
m_get_service.return_value = _SERVICE
m_get_provider_location.return_value = _SHARE
m_get_volume_location.return_value = _SHARE
loc = self.driver.create_cloned_volume(vol, svol)
out = "{'provider_location': \'" + _SHARE + "'}"
self.assertEqual(str(loc), out)
@mock.patch.object(nfs.HDSNFSDriver, '_ensure_shares_mounted')
@mock.patch.object(nfs.HDSNFSDriver, '_do_create_volume')
@mock.patch.object(nfs.HDSNFSDriver, '_id_to_vol', side_effect=id_to_vol)
@mock.patch.object(nfs.HDSNFSDriver, '_get_provider_location')
@mock.patch.object(nfs.HDSNFSDriver, '_get_volume_location')
def test_create_volume(self, m_get_volume_location,
m_get_provider_location, m_id_to_vol,
m_do_create_volume, m_ensure_shares_mounted):
vol = _VOLUME.copy()
m_get_provider_location.return_value = _SHARE2
m_get_volume_location.return_value = _SHARE2
loc = self.driver.create_volume(vol)
out = "{'provider_location': \'" + _SHARE2 + "'}"
self.assertEqual(str(loc), out)
@mock.patch.object(nfs.HDSNFSDriver, '_id_to_vol')
@mock.patch.object(nfs.HDSNFSDriver, '_get_provider_location')
@mock.patch.object(nfs.HDSNFSDriver, '_volume_not_present')
def test_delete_snapshot(self, m_volume_not_present,
m_get_provider_location, m_id_to_vol):
svol = _SNAPVOLUME.copy()
m_id_to_vol.return_value = svol
m_get_provider_location.return_value = _SHARE
m_volume_not_present.return_value = True
self.driver.delete_snapshot(svol)
self.assertEqual(svol['provider_location'], None)
@mock.patch.object(nfs.HDSNFSDriver, '_get_service')
@mock.patch.object(nfs.HDSNFSDriver, '_id_to_vol', side_effect=id_to_vol)
@mock.patch.object(nfs.HDSNFSDriver, '_get_provider_location')
@mock.patch.object(nfs.HDSNFSDriver, '_get_export_path')
@mock.patch.object(nfs.HDSNFSDriver, '_get_volume_location')
def test_create_volume_from_snapshot(self, m_get_volume_location,
m_get_export_path,
m_get_provider_location, m_id_to_vol,
m_get_service):
vol = _VOLUME.copy()
svol = _SNAPVOLUME.copy()
m_get_service.return_value = _SERVICE
m_get_provider_location.return_value = _SHARE
m_get_export_path.return_value = _EXPORT
m_get_volume_location.return_value = _SHARE
loc = self.driver.create_volume_from_snapshot(vol, svol)
out = "{'provider_location': \'" + _SHARE + "'}"
self.assertEqual(str(loc), out)
@mock.patch.object(volume_types, 'get_volume_type_extra_specs',
return_value={'key': 'type', 'service_label': 'silver'})
def test_get_pool(self, m_ext_spec):
vol = _VOLUME.copy()
self.assertEqual(self.driver.get_pool(vol), 'silver')