cinder/cinder/tests/unit/volume/drivers/test_xio.py

1479 lines
58 KiB
Python

# Copyright (c) 2014 X-IO Technologies.
# 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 mock
from six.moves import http_client
from cinder import context
from cinder import exception
from cinder import test
from cinder.tests.unit import utils
from cinder.volume.drivers import xio
from cinder.volume import qos_specs
from cinder.volume import volume_types
ISE_IP1 = '10.12.12.1'
ISE_IP2 = '10.11.12.2'
ISE_ISCSI_IP1 = '1.2.3.4'
ISE_ISCSI_IP2 = '1.2.3.5'
ISE_GID = 'isegid'
ISE_IQN = ISE_GID
ISE_WWN1 = ISE_GID + '1'
ISE_WWN2 = ISE_GID + '2'
ISE_WWN3 = ISE_GID + '3'
ISE_WWN4 = ISE_GID + '4'
ISE_TARGETS = [ISE_WWN1, ISE_WWN2, ISE_WWN3, ISE_WWN4]
ISE_INIT_TARGET_MAP = {'init_wwn1': ISE_TARGETS,
'init_wwn2': ISE_TARGETS}
VOLUME_SIZE = 10
NEW_VOLUME_SIZE = 20
VOLUME1 = {'id': '1', 'name': 'volume1',
'size': VOLUME_SIZE, 'volume_type_id': 'type1'}
VOLUME2 = {'id': '2', 'name': 'volume2',
'size': VOLUME_SIZE, 'volume_type_id': 'type2',
'provider_auth': 'CHAP abc abc'}
VOLUME3 = {'id': '3', 'name': 'volume3',
'size': VOLUME_SIZE, 'volume_type_id': None}
SNAPSHOT1 = {'name': 'snapshot1',
'volume_name': VOLUME1['name'],
'volume_type_id': 'type3'}
CLONE1 = {'id': '3', 'name': 'clone1',
'size': VOLUME_SIZE, 'volume_type_id': 'type4'}
HOST1 = 'host1'
HOST2 = 'host2'
ISCSI_CONN1 = {'initiator': 'init_iqn1',
'host': HOST1}
ISCSI_CONN2 = {'initiator': 'init_iqn2',
'host': HOST2}
FC_CONN1 = {'wwpns': ['init_wwn1', 'init_wwn2'],
'host': HOST1}
FC_CONN2 = {'wwpns': ['init_wwn3', 'init_wwn4'],
'host': HOST2}
ISE_HTTP_IP = 'http://' + ISE_IP1
ISE_HOST_LOCATION = '/storage/hosts/1'
ISE_HOST_LOCATION_URL = ISE_HTTP_IP + ISE_HOST_LOCATION
ISE_VOLUME1_LOCATION = '/storage/volumes/volume1'
ISE_VOLUME1_LOCATION_URL = ISE_HTTP_IP + ISE_VOLUME1_LOCATION
ISE_VOLUME2_LOCATION = '/storage/volumes/volume2'
ISE_VOLUME2_LOCATION_URL = ISE_HTTP_IP + ISE_VOLUME2_LOCATION
ISE_VOLUME3_LOCATION = '/storage/volumes/volume3'
ISE_VOLUME3_LOCATION_URL = ISE_HTTP_IP + ISE_VOLUME3_LOCATION
ISE_SNAPSHOT_LOCATION = '/storage/volumes/snapshot1'
ISE_SNAPSHOT_LOCATION_URL = ISE_HTTP_IP + ISE_SNAPSHOT_LOCATION
ISE_CLONE_LOCATION = '/storage/volumes/clone1'
ISE_CLONE_LOCATION_URL = ISE_HTTP_IP + ISE_CLONE_LOCATION
ISE_ALLOCATION_LOCATION = '/storage/allocations/a1'
ISE_ALLOCATION_LOCATION_URL = ISE_HTTP_IP + ISE_ALLOCATION_LOCATION
ISE_GET_QUERY_XML =\
"""<array>
<globalid>ABC12345</globalid>
<capabilities>
<capability value="3" string="Storage" type="source"/>
<capability value="49003" string="Volume Affinity"/>
<capability value="49004" string="Volume Quality of Service IOPS"/>
<capability value="49005" string="Thin Provisioning"/>
<capability value="49006" string="Clones" type="source"/>
</capabilities>
<controllers>
<controller>
<ipaddress>%s</ipaddress>
<rank value="1"/>
</controller>
<controller>
<ipaddress>%s</ipaddress>
<rank value="0"/>
</controller>
</controllers>
</array>""" % (ISE_IP1, ISE_IP2)
ISE_GET_QUERY_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_QUERY_XML.split())}
ISE_GET_QUERY_NO_CAP_XML =\
"""<array>
<globalid>ABC12345</globalid>
<controllers>
<controller>
<ipaddress>%s</ipaddress>
<rank value="1"/>
</controller>
<controller>
<ipaddress>%s</ipaddress>
<rank value="0"/>
</controller>
</controllers>
</array>""" % (ISE_IP1, ISE_IP2)
ISE_GET_QUERY_NO_CAP_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_QUERY_NO_CAP_XML.split())}
ISE_GET_QUERY_NO_CTRL_XML =\
"""<array>
<globalid>ABC12345</globalid>
<capabilities>
<capability value="3" string="Storage" type="source"/>
<capability value="49003" string="Volume Affinity"/>
<capability value="49004" string="Volume Quality of Service IOPS"/>
<capability value="49005" string="Thin Provisioning"/>
<capability value="49006" string="Clones" type="source"/>
<capability value="49007" string="Thin clones" type="source"/>
<capability value="49007" string="Thin clones" type="source"/>
</capabilities>
</array>"""
ISE_GET_QUERY_NO_CTRL_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_QUERY_NO_CTRL_XML.split())}
ISE_GET_QUERY_NO_IP_XML =\
"""<array>
<globalid>ABC12345</globalid>
<capabilities>
<test value="1"/>
<capability value="3" string="Storage" type="source"/>
<capability value="49003" string="Volume Affinity"/>
<capability value="49004" string="Volume Quality of Service IOPS"/>
<capability value="49005" string="Thin Provisioning"/>
<capability value="49006" string="Clones" type="source"/>
<capability value="49007" string="Thin clones" type="source"/>
<capability value="49007" string="Thin clones" type="source"/>
</capabilities>
<controllers>
<test value="2"/>
<controller>
<rank value="1"/>
</controller>
<controller>
<rank value="0"/>
</controller>
</controllers>
</array>"""
ISE_GET_QUERY_NO_IP_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_QUERY_NO_IP_XML.split())}
ISE_GET_QUERY_NO_GID_XML =\
"""<array>
<capabilities>
<capability value="3" string="Storage" type="source"/>
<capability value="49003" string="Volume Affinity"/>
<capability value="49004" string="Volume Quality of Service IOPS"/>
<capability value="49005" string="Thin Provisioning"/>
<capability value="49006" string="Clones" type="source"/>
</capabilities>
<controllers>
<controller>
<ipaddress>%s</ipaddress>
<rank value="1"/>
</controller>
<controller>
<ipaddress>%s</ipaddress>
<rank value="0"/>
</controller>
</controllers>
</array>""" % (ISE_IP1, ISE_IP2)
ISE_GET_QUERY_NO_GID_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_QUERY_NO_GID_XML.split())}
ISE_GET_QUERY_NO_CLONE_XML =\
"""<array>
<globalid>ABC12345</globalid>
<capabilities>
<capability value="3" string="Storage" type="source"/>
<capability value="49003" string="Volume Affinity"/>
<capability value="49004" string="Volume Quality of Service IOPS"/>
<capability value="49005" string="Thin Provisioning"/>
</capabilities>
<controllers>
<controller>
<ipaddress>%s</ipaddress>
<rank value="1"/>
</controller>
<controller>
<ipaddress>%s</ipaddress>
<rank value="0"/>
</controller>
</controllers>
</array>""" % (ISE_IP1, ISE_IP2)
ISE_GET_QUERY_NO_CLONE_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_QUERY_NO_CLONE_XML.split())}
ISE_GET_STORAGE_POOLS_XML =\
"""
<pools>
<pool>
<name>Pool 1</name>
<id>1</id>
<status value="0" string="Operational">
<details value="0x00000000">
<detail>None</detail>
</details>
</status>
<available total="60">
<byredundancy>
<raid-0>60</raid-0>
<raid-1>30</raid-1>
<raid-5>45</raid-5>
</byredundancy>
</available>
<used total="40">
<byredundancy>
<raid-0>0</raid-0>
<raid-1>40</raid-1>
<raid-5>0</raid-5>
</byredundancy>
</used>
<media>
<medium>
<health>100</health>
<tier value="4" string="Hybrid"/>
</medium>
</media>
<volumes>
<volume>
<globalid>volgid</globalid>
</volume>
<volume>
<globalid>volgid2</globalid>
</volume>
</volumes>
</pool>
</pools>
"""
ISE_GET_STORAGE_POOLS_RESP =\
{'status': http_client.OK,
'location': 'Pool location',
'content': " ".join(ISE_GET_STORAGE_POOLS_XML.split())}
ISE_GET_VOL_STATUS_NO_VOL_NODE_XML =\
"""<volumes></volumes>"""
ISE_GET_VOL_STATUS_NO_VOL_NODE_RESP =\
{'status': http_client.OK,
'location': 'u%s' % ISE_VOLUME1_LOCATION_URL,
'content': " ".join(ISE_GET_VOL_STATUS_NO_VOL_NODE_XML.split())}
ISE_GET_VOL_STATUS_NO_STATUS_XML =\
"""<volumes>
<volume self="%s">
</volume>
</volumes>""" % (ISE_VOLUME1_LOCATION_URL)
ISE_GET_VOL_STATUS_NO_STATUS_RESP =\
{'status': http_client.OK,
'location': 'u%s' % ISE_VOLUME1_LOCATION_URL,
'content': " ".join(ISE_GET_VOL_STATUS_NO_STATUS_XML.split())}
ISE_GET_VOL1_STATUS_XML =\
"""<volumes>
<volume self="%s">
<status value="0" string="Operational">
<details>
<detail>Prepared</detail>
</details>
</status>
<size>10</size>
</volume>
</volumes>""" % (ISE_VOLUME1_LOCATION_URL)
ISE_GET_VOL1_STATUS_RESP =\
{'status': http_client.OK,
'location': 'u%s' % ISE_VOLUME1_LOCATION_URL,
'content': " ".join(ISE_GET_VOL1_STATUS_XML.split())}
ISE_GET_VOL2_STATUS_XML =\
"""<volumes>
<volume self="%s">
<status value="0" string="Operational">
<details>
<detail>Prepared</detail>
</details>
</status>
</volume>
</volumes>""" % (ISE_VOLUME2_LOCATION_URL)
ISE_GET_VOL2_STATUS_RESP =\
{'status': http_client.OK,
'location': 'u%s' % ISE_VOLUME2_LOCATION_URL,
'content': " ".join(ISE_GET_VOL2_STATUS_XML.split())}
ISE_GET_VOL3_STATUS_XML =\
"""<volumes>
<volume self="%s">
<status value="0" string="Operational">
<details>
<detail>Prepared</detail>
</details>
</status>
</volume>
</volumes>""" % (ISE_VOLUME3_LOCATION_URL)
ISE_GET_VOL3_STATUS_RESP =\
{'status': http_client.OK,
'location': 'u%s' % ISE_VOLUME3_LOCATION_URL,
'content': " ".join(ISE_GET_VOL3_STATUS_XML.split())}
ISE_GET_SNAP1_STATUS_XML =\
"""<volumes>
<volume self="%s">
<status value="0" string="Operational">
<details>
<detail>Prepared</detail>
</details>
</status>
</volume>
</volumes>""" % (ISE_SNAPSHOT_LOCATION_URL)
ISE_GET_SNAP1_STATUS_RESP =\
{'status': http_client.OK,
'location': 'u%s' % ISE_SNAPSHOT_LOCATION_URL,
'content': " ".join(ISE_GET_SNAP1_STATUS_XML.split())}
ISE_GET_CLONE1_STATUS_XML =\
"""<volumes>
<volume self="%s">
<status value="0" string="Operational">
<details>
<detail>Prepared</detail>
</details>
</status>
</volume>
</volumes>""" % (ISE_CLONE_LOCATION_URL)
ISE_GET_CLONE1_STATUS_RESP =\
{'status': http_client.OK,
'location': 'u%s' % ISE_CLONE_LOCATION_URL,
'content': " ".join(ISE_GET_CLONE1_STATUS_XML.split())}
ISE_CREATE_VOLUME_XML = """<volume/>"""
ISE_CREATE_VOLUME_RESP =\
{'status': http_client.CREATED,
'location': ISE_VOLUME1_LOCATION_URL,
'content': " ".join(ISE_CREATE_VOLUME_XML.split())}
ISE_GET_IONETWORKS_XML =\
"""<chap>
<chapin value="0" string="disabled">
<username/>
<password/>
</chapin>
<chapout value="0" string="disabled">
<username/>
<password/>
</chapout>
</chap>"""
ISE_GET_IONETWORKS_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_IONETWORKS_XML.split())}
ISE_GET_IONETWORKS_CHAP_XML =\
"""<chap>
<chapin value="1" string="disabled">
<username>abc</username>
<password>abc</password>
</chapin>
<chapout value="0" string="disabled">
<username/>
<password/>
</chapout>
</chap>"""
ISE_GET_IONETWORKS_CHAP_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_IONETWORKS_CHAP_XML.split())}
ISE_DELETE_VOLUME_XML = """<volumes/>"""
ISE_DELETE_VOLUME_RESP =\
{'status': http_client.NO_CONTENT,
'location': '',
'content': " ".join(ISE_DELETE_VOLUME_XML.split())}
ISE_GET_ALLOC_WITH_EP_XML =\
"""<allocations>
<allocation self="%s">
<volume>
<volumename>%s</volumename>
</volume>
<endpoints>
<hostname>%s</hostname>
</endpoints>
<lun>1</lun>
</allocation>
</allocations>""" %\
(ISE_ALLOCATION_LOCATION_URL, VOLUME1['name'], HOST1)
ISE_GET_ALLOC_WITH_EP_RESP =\
{'status': http_client.OK,
'location': ISE_ALLOCATION_LOCATION_URL,
'content': " ".join(ISE_GET_ALLOC_WITH_EP_XML.split())}
ISE_GET_ALLOC_WITH_NO_ALLOC_XML =\
"""<allocations self="%s"/>""" % ISE_ALLOCATION_LOCATION_URL
ISE_GET_ALLOC_WITH_NO_ALLOC_RESP =\
{'status': http_client.OK,
'location': ISE_ALLOCATION_LOCATION_URL,
'content': " ".join(ISE_GET_ALLOC_WITH_NO_ALLOC_XML.split())}
ISE_DELETE_ALLOC_XML = """<allocations/>"""
ISE_DELETE_ALLOC_RESP =\
{'status': http_client.NO_CONTENT,
'location': '',
'content': " ".join(ISE_DELETE_ALLOC_XML.split())}
ISE_GET_HOSTS_NOHOST_XML =\
"""<hosts self="http://ip/storage/hosts"/>"""
ISE_GET_HOSTS_NOHOST_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_HOSTS_NOHOST_XML.split())}
ISE_GET_HOSTS_HOST1_XML =\
"""<hosts self="http://ip/storage/hosts">
<host self="http://ip/storage/hosts/1">
<type>"OPENSTACK"</type>
<name>%s</name>
<id>1</id>
<endpoints self="http://ip/storage/endpoints">
<endpoint self="http://ip/storage/endpoints/ep1">
<globalid>init_wwn1</globalid>
</endpoint>
<endpoint self="http://ip/storage/endpoints/ep2">
<globalid>init_wwn2</globalid>
</endpoint>
<endpoint self="http://ip/storage/endpoints/ep1">
<globalid>init_iqn1</globalid>
</endpoint>
</endpoints>
</host>
</hosts>""" % HOST1
ISE_GET_HOSTS_HOST1_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_HOSTS_HOST1_XML.split())}
ISE_GET_HOSTS_HOST1_HOST_TYPE_XML =\
"""<hosts self="http://ip/storage/hosts">
<host self="http://ip/storage/hosts/1">
<type>"WINDOWS"</type>
<name>%s</name>
<id>1</id>
<endpoints self="http://ip/storage/endpoints">
<endpoint self="http://ip/storage/endpoints/ep1">
<globalid>init_wwn1</globalid>
</endpoint>
<endpoint self="http://ip/storage/endpoints/ep2">
<globalid>init_wwn2</globalid>
</endpoint>
<endpoint self="http://ip/storage/endpoints/ep1">
<globalid>init_iqn1</globalid>
</endpoint>
</endpoints>
</host>
</hosts>""" % HOST1
ISE_GET_HOSTS_HOST1_HOST_TYPE_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_HOSTS_HOST1_HOST_TYPE_XML.split())}
ISE_GET_HOSTS_HOST2_XML =\
"""<hosts self="http://ip/storage/hosts">
<host self="http://ip/storage/hosts/2">
<name>%s</name>
<id>2</id>
<endpoints self="http://ip/storage/endpoints">
<endpoint self="http://ip/storage/endpoints/ep3">
<globalid>init_wwn3</globalid>
</endpoint>
<endpoint self="http://ip/storage/endpoints/ep4">
<globalid>init_wwn4</globalid>
</endpoint>
<endpoint self="http://ip/storage/endpoints/ep3">
<globalid>init_iqn2</globalid>
</endpoint>
</endpoints>
</host>
</hosts>""" % HOST2
ISE_GET_HOSTS_HOST2_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_HOSTS_HOST2_XML.split())}
ISE_CREATE_HOST_XML =\
"""<hosts self="http://ip/storage/hosts"/>"""
ISE_CREATE_HOST_RESP =\
{'status': http_client.CREATED,
'location': 'http://ip/storage/hosts/host1',
'content': " ".join(ISE_CREATE_HOST_XML.split())}
ISE_CREATE_ALLOC_XML =\
"""<allocations self="http://ip/storage/allocations"/>"""
ISE_CREATE_ALLOC_RESP =\
{'status': http_client.CREATED,
'location': ISE_ALLOCATION_LOCATION_URL,
'content': " ".join(ISE_CREATE_ALLOC_XML.split())}
ISE_GET_ENDPOINTS_XML =\
"""<endpoints self="http://ip/storage/endpoints">
<endpoint type="array" self="http://ip/storage/endpoints/isegid">
<globalid>isegid</globalid>
<protocol>iSCSI</protocol>
<array self="http://ip/storage/arrays/ise1">
<globalid>ise1</globalid>
</array>
<host/>
<allocations self="http://ip/storage/allocations">
<allocation self="%s">
<globalid>
a1
</globalid>
</allocation>
</allocations>
</endpoint>
<endpoint type="array" self="http://ip/storage/endpoints/isegid">
<globalid>isegid</globalid>
<protocol>Fibre Channel</protocol>
<array self="http://ip/storage/arrays/ise1">
<globalid>ise1</globalid>
</array>
<host/>
<allocations self="http://ip/storage/allocations">
<allocation self="%s">
<globalid>
a1
</globalid>
</allocation>
</allocations>
</endpoint>
</endpoints>""" % (ISE_ALLOCATION_LOCATION_URL,
ISE_ALLOCATION_LOCATION_URL)
ISE_GET_ENDPOINTS_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_ENDPOINTS_XML.split())}
ISE_GET_CONTROLLERS_XML =\
"""<controllers self="http://ip/storage/arrays/controllers">
<controller>
<status/>
<ioports>
<ioport>
<ipaddresses>
<ipaddress>%s</ipaddress>
</ipaddresses>
<endpoint>
<globalid>isegid</globalid>
</endpoint>
</ioport>
</ioports>
<fcports>
<fcport>
<wwn>%s</wwn>
</fcport>
<fcport>
<wwn>%s</wwn>
</fcport>
</fcports>
</controller>
<controller>
<status/>
<ioports>
<ioport>
<ipaddresses>
<ipaddress>%s</ipaddress>
</ipaddresses>
<endpoint>
<globalid>isegid</globalid>
</endpoint>
</ioport>
</ioports>
<fcports>
<fcport>
<wwn>%s</wwn>
</fcport>
<fcport>
<wwn>%s</wwn>
</fcport>
</fcports>
</controller>
</controllers>""" % (ISE_ISCSI_IP1, ISE_WWN1, ISE_WWN2,
ISE_ISCSI_IP2, ISE_WWN3, ISE_WWN4)
ISE_GET_CONTROLLERS_RESP =\
{'status': http_client.OK,
'location': '',
'content': " ".join(ISE_GET_CONTROLLERS_XML.split())}
ISE_CREATE_SNAPSHOT_XML = """<snapshot/>"""
ISE_CREATE_SNAPSHOT_RESP =\
{'status': http_client.CREATED,
'location': ISE_SNAPSHOT_LOCATION_URL,
'content': " ".join(ISE_CREATE_SNAPSHOT_XML.split())}
ISE_PREP_SNAPSHOT_XML = """<snapshot/>"""
ISE_PREP_SNAPSHOT_RESP =\
{'status': http_client.ACCEPTED,
'location': ISE_SNAPSHOT_LOCATION_URL,
'content': " ".join(ISE_PREP_SNAPSHOT_XML.split())}
ISE_MODIFY_VOLUME_XML = """<volume/>"""
ISE_MODIFY_VOLUME_RESP =\
{'status': http_client.CREATED,
'location': ISE_VOLUME1_LOCATION_URL,
'content': " ".join(ISE_MODIFY_VOLUME_XML.split())}
ISE_MODIFY_HOST_XML = """<host/>"""
ISE_MODIFY_HOST_RESP =\
{'status': http_client.CREATED,
'location': ISE_HOST_LOCATION_URL,
'content': " ".join(ISE_MODIFY_HOST_XML.split())}
ISE_BAD_CONNECTION_RESP =\
{'status': 0,
'location': '',
'content': " "}
ISE_400_RESP =\
{'status': http_client.BAD_REQUEST,
'location': '',
'content': ""}
ISE_GET_VOL_STATUS_404_XML = \
"""<response value="404" index="3">VOLUME not found.</response>"""
ISE_GET_VOL_STATUS_404_RESP =\
{'status': http_client.NOT_FOUND,
'location': '',
'content': " ".join(ISE_GET_VOL_STATUS_404_XML.split())}
ISE_400_INVALID_STATE_XML = \
"""<response value="400">Not in a valid state.</response>"""
ISE_400_INVALID_STATE_RESP =\
{'status': http_client.BAD_REQUEST,
'location': '',
'content': " ".join(ISE_400_INVALID_STATE_XML.split())}
ISE_409_CONFLICT_XML = \
"""<response value="409">Conflict</response>"""
ISE_409_CONFLICT_RESP =\
{'status': http_client.CONFLICT,
'location': '',
'content': " ".join(ISE_409_CONFLICT_XML.split())}
DRIVER = "cinder.volume.drivers.xio.XIOISEDriver"
@mock.patch(DRIVER + "._opener", autospec=True)
class XIOISEDriverTestCase(object):
# Test cases for X-IO volume driver
def setUp(self):
super(XIOISEDriverTestCase, self).setUp()
# set good default values
self.configuration = mock.Mock()
self.configuration.san_ip = ISE_IP1
self.configuration.san_user = 'fakeuser'
self.configuration.san_password = 'fakepass'
self.configuration.iscsi_ip_address = ISE_ISCSI_IP1
self.configuration.driver_use_ssl = False
self.configuration.ise_completion_retries = 30
self.configuration.ise_connection_retries = 5
self.configuration.ise_retry_interval = 1
self.configuration.volume_backend_name = 'ise1'
self.driver = None
self.protocol = ''
self.connector = None
self.connection_failures = 0
self.hostgid = ''
self.use_response_table = 1
def setup_test(self, protocol):
self.protocol = protocol
# set good default values
if self.protocol == 'iscsi':
self.configuration.ise_protocol = protocol
self.connector = ISCSI_CONN1
self.hostgid = self.connector['initiator']
elif self.protocol == 'fibre_channel':
self.configuration.ise_protocol = protocol
self.connector = FC_CONN1
self.hostgid = self.connector['wwpns'][0]
def setup_driver(self):
# this setups up driver object with previously set configuration values
if self.configuration.ise_protocol == 'iscsi':
self.driver =\
xio.XIOISEISCSIDriver(configuration=self.configuration)
elif self.configuration.ise_protocol == 'fibre_channel':
self.driver =\
xio.XIOISEFCDriver(configuration=self.configuration)
elif self.configuration.ise_protocol == 'test_prot':
# if test_prot specified override with correct protocol
# used to bypass protocol specific driver
self.configuration.ise_protocol = self.protocol
self.driver = xio.XIOISEDriver(configuration=self.configuration)
else:
# Invalid protocol type
raise exception.Invalid()
#################################
# UNIT TESTS #
#################################
def test_do_setup(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_RESP])
self.driver.do_setup(None)
def test_negative_do_setup_no_clone_support(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_NO_CLONE_RESP])
self.assertRaises(exception.XIODriverException,
self.driver.do_setup, None)
def test_negative_do_setup_no_capabilities(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_NO_CAP_RESP])
self.assertRaises(exception.XIODriverException,
self.driver.do_setup, None)
def test_negative_do_setup_no_ctrl(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_NO_CTRL_RESP])
self.assertRaises(exception.XIODriverException,
self.driver.do_setup, None)
def test_negative_do_setup_no_ipaddress(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_NO_IP_RESP])
self.driver.do_setup(None)
def test_negative_do_setup_bad_globalid_none(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_NO_GID_RESP])
self.assertRaises(exception.XIODriverException,
self.driver.do_setup, None)
def test_check_for_setup_error(self, mock_req):
mock_req.side_effect = iter([ISE_GET_QUERY_RESP])
self.setup_driver()
self.driver.check_for_setup_error()
def test_negative_do_setup_bad_ip(self, mock_req):
# set san_ip to bad value
self.configuration.san_ip = ''
mock_req.side_effect = iter([ISE_GET_QUERY_RESP])
self.setup_driver()
self.assertRaises(exception.XIODriverException,
self.driver.check_for_setup_error)
def test_negative_do_setup_bad_user_blank(self, mock_req):
# set san_user to bad value
self.configuration.san_login = ''
mock_req.side_effect = iter([ISE_GET_QUERY_RESP])
self.setup_driver()
self.assertRaises(exception.XIODriverException,
self.driver.check_for_setup_error)
def test_negative_do_setup_bad_password_blank(self, mock_req):
# set san_password to bad value
self.configuration.san_password = ''
mock_req.side_effect = iter([ISE_GET_QUERY_RESP])
self.setup_driver()
self.assertRaises(exception.XIODriverException,
self.driver.check_for_setup_error)
def test_get_volume_stats(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_STORAGE_POOLS_RESP])
backend_name = self.configuration.volume_backend_name
if self.configuration.ise_protocol == 'iscsi':
protocol = 'iSCSI'
else:
protocol = 'fibre_channel'
exp_result = {'vendor_name': "X-IO",
'driver_version': "1.1.4",
'volume_backend_name': backend_name,
'reserved_percentage': 0,
'total_capacity_gb': 100,
'free_capacity_gb': 60,
'QoS_support': True,
'affinity': True,
'thin': False,
'pools': [{'pool_ise_name': "Pool 1",
'pool_name': "1",
'status': "Operational",
'status_details': "None",
'free_capacity_gb': 60,
'free_capacity_gb_raid_0': 60,
'free_capacity_gb_raid_1': 30,
'free_capacity_gb_raid_5': 45,
'allocated_capacity_gb': 40,
'allocated_capacity_gb_raid_0': 0,
'allocated_capacity_gb_raid_1': 40,
'allocated_capacity_gb_raid_5': 0,
'health': 100,
'media': "Hybrid",
'total_capacity_gb': 100,
'QoS_support': True,
'reserved_percentage': 0}],
'active_volumes': 2,
'storage_protocol': protocol}
act_result = self.driver.get_volume_stats(True)
self.assertDictEqual(exp_result, act_result)
def test_get_volume_stats_ssl(self, mock_req):
self.configuration.driver_use_ssl = True
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_STORAGE_POOLS_RESP])
self.driver.get_volume_stats(True)
def test_negative_get_volume_stats_bad_primary(self, mock_req):
self.configuration.ise_connection_retries = 1
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_BAD_CONNECTION_RESP,
ISE_GET_STORAGE_POOLS_RESP])
self.driver.get_volume_stats(True)
def test_create_volume(self, mock_req):
ctxt = context.get_admin_context()
extra_specs = {"Feature:Pool": "1",
"Feature:Raid": "1",
"Affinity:Type": "flash",
"Alloc:Type": "thick"}
type_ref = volume_types.create(ctxt, 'VT1', extra_specs)
specs = {'qos:minIOPS': '20',
'qos:maxIOPS': '2000',
'qos:burstIOPS': '5000'}
qos = qos_specs.create(ctxt, 'fake-qos', specs)
qos_specs.associate_qos_with_type(ctxt, qos['id'], type_ref['id'])
VOLUME1['volume_type_id'] = type_ref['id']
self.setup_driver()
if self.configuration.ise_protocol == 'iscsi':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_CREATE_VOLUME_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_GET_IONETWORKS_RESP])
exp_result = {}
exp_result = {"provider_auth": ""}
act_result = self.driver.create_volume(VOLUME1)
self.assertDictEqual(exp_result, act_result)
elif self.configuration.ise_protocol == 'fibre_channel':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_CREATE_VOLUME_RESP,
ISE_GET_VOL1_STATUS_RESP])
self.driver.create_volume(VOLUME1)
def test_create_volume_chap(self, mock_req):
ctxt = context.get_admin_context()
extra_specs = {"Feature:Pool": "1",
"Feature:Raid": "1",
"Affinity:Type": "flash",
"Alloc:Type": "thick"}
type_ref = volume_types.create(ctxt, 'VT1', extra_specs)
specs = {'qos:minIOPS': '20',
'qos:maxIOPS': '2000',
'qos:burstIOPS': '5000'}
qos = qos_specs.create(ctxt, 'fake-qos', specs)
qos_specs.associate_qos_with_type(ctxt, qos['id'], type_ref['id'])
VOLUME1['volume_type_id'] = type_ref['id']
self.setup_driver()
if self.configuration.ise_protocol == 'iscsi':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_CREATE_VOLUME_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_GET_IONETWORKS_CHAP_RESP])
exp_result = {}
exp_result = {"provider_auth": "CHAP abc abc"}
act_result = self.driver.create_volume(VOLUME1)
self.assertDictEqual(exp_result, act_result)
elif self.configuration.ise_protocol == 'fibre_channel':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_CREATE_VOLUME_RESP,
ISE_GET_VOL1_STATUS_RESP])
self.driver.create_volume(VOLUME1)
def test_create_volume_type_none(self, mock_req):
self.setup_driver()
if self.configuration.ise_protocol == 'iscsi':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_CREATE_VOLUME_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_GET_IONETWORKS_RESP])
elif self.configuration.ise_protocol == 'fibre_channel':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_CREATE_VOLUME_RESP,
ISE_GET_VOL1_STATUS_RESP])
self.driver.create_volume(VOLUME3)
def test_delete_volume(self, mock_req):
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_ALLOC_WITH_EP_RESP,
ISE_DELETE_ALLOC_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_DELETE_VOLUME_RESP,
ISE_GET_VOL_STATUS_404_RESP])
self.setup_driver()
self.driver.delete_volume(VOLUME1)
def test_delete_volume_delayed(self, mock_req):
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_ALLOC_WITH_EP_RESP,
ISE_DELETE_ALLOC_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_DELETE_VOLUME_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_GET_VOL_STATUS_404_RESP])
self.setup_driver()
self.driver.delete_volume(VOLUME1)
def test_delete_volume_timeout(self, mock_req):
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_ALLOC_WITH_EP_RESP,
ISE_DELETE_ALLOC_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_DELETE_VOLUME_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_GET_VOL1_STATUS_RESP])
self.configuration.ise_completion_retries = 3
self.setup_driver()
self.driver.delete_volume(VOLUME1)
def test_delete_volume_none_existing(self, mock_req):
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_ALLOC_WITH_EP_RESP,
ISE_DELETE_ALLOC_RESP,
ISE_GET_VOL1_STATUS_RESP])
self.setup_driver()
self.driver.delete_volume(VOLUME2)
def test_initialize_connection_positive(self, mock_req):
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_HOSTS_HOST2_RESP,
ISE_CREATE_HOST_RESP,
ISE_GET_HOSTS_HOST1_RESP,
ISE_CREATE_ALLOC_RESP,
ISE_GET_ALLOC_WITH_EP_RESP,
ISE_GET_CONTROLLERS_RESP])
self.setup_driver()
exp_result = {}
if self.configuration.ise_protocol == 'iscsi':
exp_result = {"driver_volume_type": "iscsi",
"data": {"target_lun": 1,
"volume_id": '1',
"target_discovered": False,
"target_iqn": ISE_IQN,
"target_portal": ISE_ISCSI_IP1 + ":3260"}}
elif self.configuration.ise_protocol == 'fibre_channel':
exp_result = {"driver_volume_type": "fibre_channel",
"data": {"target_lun": 1,
"volume_id": '1',
"target_discovered": True,
"initiator_target_map": ISE_INIT_TARGET_MAP,
"target_wwn": ISE_TARGETS}}
act_result =\
self.driver.initialize_connection(VOLUME1, self.connector)
self.assertDictEqual(exp_result, act_result)
def test_initialize_connection_positive_host_type(self, mock_req):
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_HOSTS_HOST1_HOST_TYPE_RESP,
ISE_MODIFY_HOST_RESP,
ISE_CREATE_ALLOC_RESP,
ISE_GET_ALLOC_WITH_EP_RESP,
ISE_GET_CONTROLLERS_RESP])
self.setup_driver()
exp_result = {}
if self.configuration.ise_protocol == 'iscsi':
exp_result = {"driver_volume_type": "iscsi",
"data": {"target_lun": 1,
"volume_id": '1',
"target_discovered": False,
"target_iqn": ISE_IQN,
"target_portal": ISE_ISCSI_IP1 + ":3260"}}
elif self.configuration.ise_protocol == 'fibre_channel':
exp_result = {"driver_volume_type": "fibre_channel",
"data": {"target_lun": 1,
"volume_id": '1',
"target_discovered": True,
"initiator_target_map": ISE_INIT_TARGET_MAP,
"target_wwn": ISE_TARGETS}}
act_result =\
self.driver.initialize_connection(VOLUME1, self.connector)
self.assertDictEqual(exp_result, act_result)
def test_initialize_connection_positive_chap(self, mock_req):
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_HOSTS_HOST2_RESP,
ISE_CREATE_HOST_RESP,
ISE_GET_HOSTS_HOST1_RESP,
ISE_CREATE_ALLOC_RESP,
ISE_GET_ALLOC_WITH_EP_RESP,
ISE_GET_CONTROLLERS_RESP])
self.setup_driver()
exp_result = {}
if self.configuration.ise_protocol == 'iscsi':
exp_result = {"driver_volume_type": "iscsi",
"data": {"target_lun": 1,
"volume_id": '2',
"target_discovered": False,
"target_iqn": ISE_IQN,
"target_portal": ISE_ISCSI_IP1 + ":3260",
'auth_method': 'CHAP',
'auth_username': 'abc',
'auth_password': 'abc'}}
elif self.configuration.ise_protocol == 'fibre_channel':
exp_result = {"driver_volume_type": "fibre_channel",
"data": {"target_lun": 1,
"volume_id": '2',
"target_discovered": True,
"initiator_target_map": ISE_INIT_TARGET_MAP,
"target_wwn": ISE_TARGETS}}
act_result =\
self.driver.initialize_connection(VOLUME2, self.connector)
self.assertDictEqual(exp_result, act_result)
def test_initialize_connection_negative_no_host(self, mock_req):
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_HOSTS_HOST2_RESP,
ISE_CREATE_HOST_RESP,
ISE_GET_HOSTS_HOST2_RESP])
self.setup_driver()
self.assertRaises(exception.XIODriverException,
self.driver.initialize_connection,
VOLUME2, self.connector)
def test_initialize_connection_negative_host_type(self, mock_req):
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_HOSTS_HOST1_HOST_TYPE_RESP,
ISE_400_RESP])
self.setup_driver()
self.assertRaises(exception.XIODriverException,
self.driver.initialize_connection,
VOLUME2, self.connector)
def test_terminate_connection_positive(self, mock_req):
self.setup_driver()
if self.configuration.ise_protocol == 'iscsi':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_HOSTS_HOST1_RESP,
ISE_GET_ALLOC_WITH_EP_RESP,
ISE_DELETE_ALLOC_RESP,
ISE_GET_ALLOC_WITH_EP_RESP,
ISE_GET_HOSTS_HOST1_RESP,
ISE_DELETE_ALLOC_RESP])
elif self.configuration.ise_protocol == 'fibre_channel':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_HOSTS_HOST1_RESP,
ISE_GET_ALLOC_WITH_EP_RESP,
ISE_DELETE_ALLOC_RESP,
ISE_GET_ALLOC_WITH_EP_RESP,
ISE_GET_CONTROLLERS_RESP,
ISE_GET_HOSTS_HOST1_RESP,
ISE_DELETE_ALLOC_RESP])
self.driver.terminate_connection(VOLUME1, self.connector)
def test_terminate_connection_positive_noalloc(self, mock_req):
self.setup_driver()
if self.configuration.ise_protocol == 'iscsi':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_HOSTS_HOST1_RESP,
ISE_GET_ALLOC_WITH_NO_ALLOC_RESP,
ISE_GET_ALLOC_WITH_NO_ALLOC_RESP,
ISE_GET_HOSTS_HOST1_RESP,
ISE_DELETE_ALLOC_RESP])
elif self.configuration.ise_protocol == 'fibre_channel':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_HOSTS_HOST1_RESP,
ISE_GET_ALLOC_WITH_NO_ALLOC_RESP,
ISE_GET_ALLOC_WITH_NO_ALLOC_RESP,
ISE_GET_CONTROLLERS_RESP,
ISE_GET_HOSTS_HOST1_RESP,
ISE_DELETE_ALLOC_RESP])
self.driver.terminate_connection(VOLUME1, self.connector)
def test_negative_terminate_connection_bad_host(self, mock_req):
self.setup_driver()
test_connector = {}
if self.configuration.ise_protocol == 'iscsi':
test_connector['initiator'] = 'bad_iqn'
test_connector['host'] = ''
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_HOSTS_HOST1_RESP])
elif self.configuration.ise_protocol == 'fibre_channel':
test_connector['wwpns'] = 'bad_wwn'
test_connector['host'] = ''
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_HOSTS_HOST1_RESP,
ISE_GET_CONTROLLERS_RESP])
self.driver.terminate_connection(VOLUME1, test_connector)
def test_create_snapshot(self, mock_req):
ctxt = context.get_admin_context()
extra_specs = {"Feature:Pool": "1",
"Feature:Raid": "1",
"Affinity:Type": "flash",
"Alloc:Type": "thick"}
type_ref = volume_types.create(ctxt, 'VT1', extra_specs)
specs = {'qos:minIOPS': '20',
'qos:maxIOPS': '2000',
'qos:burstIOPS': '5000'}
qos = qos_specs.create(ctxt, 'fake-qos', specs)
qos_specs.associate_qos_with_type(ctxt, qos['id'], type_ref['id'])
SNAPSHOT1['volume_type_id'] = type_ref['id']
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_PREP_SNAPSHOT_RESP,
ISE_GET_SNAP1_STATUS_RESP,
ISE_CREATE_SNAPSHOT_RESP,
ISE_GET_SNAP1_STATUS_RESP])
self.setup_driver()
self.driver.create_snapshot(SNAPSHOT1)
@mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall',
new=utils.ZeroIntervalLoopingCall)
def test_negative_create_snapshot_invalid_state_recover(self, mock_req):
ctxt = context.get_admin_context()
extra_specs = {"Feature:Pool": "1",
"Feature:Raid": "1",
"Affinity:Type": "flash",
"Alloc:Type": "thick"}
type_ref = volume_types.create(ctxt, 'VT1', extra_specs)
specs = {'qos:minIOPS': '20',
'qos:maxIOPS': '2000',
'qos:burstIOPS': '5000'}
qos = qos_specs.create(ctxt, 'fake-qos', specs)
qos_specs.associate_qos_with_type(ctxt, qos['id'], type_ref['id'])
SNAPSHOT1['volume_type_id'] = type_ref['id']
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_400_INVALID_STATE_RESP,
ISE_PREP_SNAPSHOT_RESP,
ISE_GET_SNAP1_STATUS_RESP,
ISE_CREATE_SNAPSHOT_RESP,
ISE_GET_SNAP1_STATUS_RESP])
self.setup_driver()
self.driver.create_snapshot(SNAPSHOT1)
@mock.patch('oslo_service.loopingcall.FixedIntervalLoopingCall',
new=utils.ZeroIntervalLoopingCall)
def test_negative_create_snapshot_invalid_state_norecover(self, mock_req):
ctxt = context.get_admin_context()
extra_specs = {"Feature:Pool": "1",
"Feature:Raid": "1",
"Affinity:Type": "flash",
"Alloc:Type": "thick"}
type_ref = volume_types.create(ctxt, 'VT1', extra_specs)
specs = {'qos:minIOPS': '20',
'qos:maxIOPS': '2000',
'qos:burstIOPS': '5000'}
qos = qos_specs.create(ctxt, 'fake-qos', specs)
qos_specs.associate_qos_with_type(ctxt, qos['id'], type_ref['id'])
SNAPSHOT1['volume_type_id'] = type_ref['id']
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_400_INVALID_STATE_RESP,
ISE_400_INVALID_STATE_RESP,
ISE_400_INVALID_STATE_RESP,
ISE_400_INVALID_STATE_RESP,
ISE_400_INVALID_STATE_RESP])
self.configuration.ise_completion_retries = 5
self.setup_driver()
self.assertRaises(exception.XIODriverException,
self.driver.create_snapshot, SNAPSHOT1)
def test_negative_create_snapshot_conflict(self, mock_req):
ctxt = context.get_admin_context()
extra_specs = {"Feature:Pool": "1",
"Feature:Raid": "1",
"Affinity:Type": "flash",
"Alloc:Type": "thick"}
type_ref = volume_types.create(ctxt, 'VT1', extra_specs)
specs = {'qos:minIOPS': '20',
'qos:maxIOPS': '2000',
'qos:burstIOPS': '5000'}
qos = qos_specs.create(ctxt, 'fake-qos', specs)
qos_specs.associate_qos_with_type(ctxt, qos['id'], type_ref['id'])
SNAPSHOT1['volume_type_id'] = type_ref['id']
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_409_CONFLICT_RESP])
self.configuration.ise_completion_retries = 1
self.setup_driver()
self.assertRaises(exception.XIODriverException,
self.driver.create_snapshot, SNAPSHOT1)
def test_delete_snapshot(self, mock_req):
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_ALLOC_WITH_EP_RESP,
ISE_DELETE_ALLOC_RESP,
ISE_GET_SNAP1_STATUS_RESP,
ISE_DELETE_VOLUME_RESP])
self.setup_driver()
self.driver.delete_snapshot(SNAPSHOT1)
def test_clone_volume(self, mock_req):
ctxt = context.get_admin_context()
extra_specs = {"Feature:Pool": "1",
"Feature:Raid": "1",
"Affinity:Type": "flash",
"Alloc:Type": "thick"}
type_ref = volume_types.create(ctxt, 'VT1', extra_specs)
specs = {'qos:minIOPS': '20',
'qos:maxIOPS': '2000',
'qos:burstIOPS': '5000'}
qos = qos_specs.create(ctxt, 'fake-qos', specs)
qos_specs.associate_qos_with_type(ctxt, qos['id'], type_ref['id'])
VOLUME1['volume_type_id'] = type_ref['id']
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_PREP_SNAPSHOT_RESP,
ISE_GET_SNAP1_STATUS_RESP,
ISE_CREATE_SNAPSHOT_RESP,
ISE_GET_SNAP1_STATUS_RESP])
self.setup_driver()
self.driver.create_cloned_volume(CLONE1, VOLUME1)
def test_extend_volume(self, mock_req):
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_MODIFY_VOLUME_RESP])
self.setup_driver()
self.driver.extend_volume(VOLUME1, NEW_VOLUME_SIZE)
def test_retype_volume(self, mock_req):
ctxt = context.get_admin_context()
extra_specs = {"Feature:Pool": "1",
"Feature:Raid": "1",
"Affinity:Type": "flash",
"Alloc:Type": "thick"}
type_ref = volume_types.create(ctxt, 'VT1', extra_specs)
specs = {'qos:minIOPS': '20',
'qos:maxIOPS': '2000',
'qos:burstIOPS': '5000'}
qos = qos_specs.create(ctxt, 'fake-qos', specs)
qos_specs.associate_qos_with_type(ctxt, qos['id'], type_ref['id'])
VOLUME1['volume_type_id'] = type_ref['id']
# New volume type
extra_specs = {"Feature:Pool": "1",
"Feature:Raid": "5",
"Affinity:Type": "flash",
"Alloc:Type": "thick"}
type_ref = volume_types.create(ctxt, 'VT2', extra_specs)
specs = {'qos:minIOPS': '30',
'qos:maxIOPS': '3000',
'qos:burstIOPS': '10000'}
qos = qos_specs.create(ctxt, 'fake-qos2', specs)
qos_specs.associate_qos_with_type(ctxt, qos['id'], type_ref['id'])
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_MODIFY_VOLUME_RESP])
self.setup_driver()
self.driver.retype(ctxt, VOLUME1, type_ref, 0, 0)
def test_create_volume_from_snapshot(self, mock_req):
ctxt = context.get_admin_context()
extra_specs = {"Feature:Pool": "1",
"Feature:Raid": "1",
"Affinity:Type": "flash",
"Alloc:Type": "thick"}
type_ref = volume_types.create(ctxt, 'VT1', extra_specs)
specs = {'qos:minIOPS': '20',
'qos:maxIOPS': '2000',
'qos:burstIOPS': '5000'}
qos = qos_specs.create(ctxt, 'fake-qos', specs)
qos_specs.associate_qos_with_type(ctxt, qos['id'], type_ref['id'])
SNAPSHOT1['volume_type_id'] = type_ref['id']
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_SNAP1_STATUS_RESP,
ISE_PREP_SNAPSHOT_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_CREATE_SNAPSHOT_RESP,
ISE_GET_VOL1_STATUS_RESP])
self.setup_driver()
self.driver.create_volume_from_snapshot(VOLUME1, SNAPSHOT1)
def test_manage_existing(self, mock_req):
ctxt = context.get_admin_context()
extra_specs = {"Feature:Pool": "1",
"Feature:Raid": "1",
"Affinity:Type": "flash",
"Alloc:Type": "thick"}
type_ref = volume_types.create(ctxt, 'VT1', extra_specs)
specs = {'qos:minIOPS': '20',
'qos:maxIOPS': '2000',
'qos:burstIOPS': '5000'}
qos = qos_specs.create(ctxt, 'fake-qos', specs)
qos_specs.associate_qos_with_type(ctxt, qos['id'], type_ref['id'])
VOLUME1['volume_type_id'] = type_ref['id']
self.setup_driver()
if self.configuration.ise_protocol == 'iscsi':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_MODIFY_VOLUME_RESP,
ISE_GET_IONETWORKS_RESP])
elif self.configuration.ise_protocol == 'fibre_channel':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_MODIFY_VOLUME_RESP])
self.driver.manage_existing(VOLUME1, {'source-name': 'testvol'})
def test_manage_existing_no_source_name(self, mock_req):
self.setup_driver()
if self.configuration.ise_protocol == 'iscsi':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_MODIFY_VOLUME_RESP,
ISE_GET_IONETWORKS_RESP])
elif self.configuration.ise_protocol == 'fibre_channel':
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP,
ISE_MODIFY_VOLUME_RESP])
self.assertRaises(exception.XIODriverException,
self.driver.manage_existing, VOLUME1, {})
def test_manage_existing_get_size(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP])
exp_result = 10
act_result = \
self.driver.manage_existing_get_size(VOLUME1,
{'source-name': 'a'})
self.assertEqual(exp_result, act_result)
def test_manage_existing_get_size_no_source_name(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP])
self.assertRaises(exception.XIODriverException,
self.driver.manage_existing_get_size, VOLUME1, {})
def test_unmanage(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL1_STATUS_RESP])
self.driver.unmanage(VOLUME1)
def test_negative_unmanage_no_volume_status_xml(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL_STATUS_NO_STATUS_RESP])
self.driver.unmanage(VOLUME1)
def test_negative_unmanage_no_volume_xml(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL_STATUS_NO_VOL_NODE_RESP])
self.assertRaises(exception.XIODriverException,
self.driver.unmanage, VOLUME1)
def test_negative_unmanage_non_existing_volume(self, mock_req):
self.setup_driver()
mock_req.side_effect = iter([ISE_GET_QUERY_RESP,
ISE_GET_VOL_STATUS_404_RESP])
self.assertRaises(exception.XIODriverException,
self.driver.unmanage, VOLUME1)
class XIOISEISCSIDriverTestCase(XIOISEDriverTestCase, test.TestCase):
def setUp(self):
super(XIOISEISCSIDriverTestCase, self).setUp()
self.setup_test('iscsi')
class XIOISEFCDriverTestCase(XIOISEDriverTestCase, test.TestCase):
def setUp(self):
super(XIOISEFCDriverTestCase, self).setUp()
self.setup_test('fibre_channel')