Skip to content

Commit da00b6b

Browse files
author
Lakhinder Walia
committed
HDS Cinder Driver. Rev rnirmal#1
blueprint hds-hus-iscsi-cinder-driver This is the first rev of Hitachi Data Systems Cinder iSCSI driver. This driver works with HUS (df850) array. This driver contains all the base-line features specified for Havana release. Amended into this submission are changes from code-reviews. Docimpact: Bug #1180648 Change-Id: Ia27d076443b10da2c653456f9292dd192362b853
1 parent 77a7745 commit da00b6b

8 files changed

Lines changed: 888 additions & 6 deletions

File tree

cinder/exception.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,10 @@ class ConfigNotFound(NotFound):
426426
message = _("Could not find config at %(path)s")
427427

428428

429+
class ParameterNotFound(NotFound):
430+
message = _("Could not find parameter %(param)s")
431+
432+
429433
class PasteAppNotFound(NotFound):
430434
message = _("Could not load paste app '%(name)s' from %(path)s")
431435

cinder/tests/test_hds.py

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
# Copyright (c) 2013 Hitachi Data Systems, Inc.
2+
# Copyright (c) 2013 OpenStack LLC.
3+
# All Rights Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License. You may obtain
7+
# a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
# License for the specific language governing permissions and limitations
15+
# under the License.
16+
#
17+
18+
"""
19+
Self test for Hitachi Unified Storage (HUS) platform.
20+
"""
21+
22+
import mox
23+
import os
24+
import tempfile
25+
26+
from cinder import test
27+
from cinder.volume import configuration as conf
28+
from cinder.volume.drivers.hds import hds
29+
30+
31+
CONF = """<?xml version="1.0" encoding="UTF-8" ?>
32+
<config>
33+
<mgmt_ip0>172.17.44.16</mgmt_ip0>
34+
<mgmt_ip1>172.17.44.17</mgmt_ip1>
35+
<username>system</username>
36+
<password>manager</password>
37+
<svc_0>
38+
<volume_type>default</volume_type>
39+
<iscsi_ip>172.17.39.132</iscsi_ip>
40+
<hdp>9</hdp>
41+
</svc_0>
42+
<svc_1>
43+
<volume_type>silver</volume_type>
44+
<iscsi_ip>172.17.39.133</iscsi_ip>
45+
<hdp>9</hdp>
46+
</svc_1>
47+
<svc_2>
48+
<volume_type>gold</volume_type>
49+
<iscsi_ip>172.17.39.134</iscsi_ip>
50+
<hdp>9</hdp>
51+
</svc_2>
52+
<svc_3>
53+
<volume_type>platinum</volume_type>
54+
<iscsi_ip>172.17.39.135</iscsi_ip>
55+
<hdp>9</hdp>
56+
</svc_3>
57+
<snapshot>
58+
<hdp>9</hdp>
59+
</snapshot>
60+
<lun_start>
61+
3300
62+
</lun_start>
63+
</config>
64+
"""
65+
66+
67+
class SimulatedHusBackend:
68+
"""Simulation Back end. Talks to HUS."""
69+
70+
alloc_lun = [] # allocated LUs
71+
connections = [] # iSCSI connections
72+
73+
def __init__(self):
74+
self.start_lun = 0
75+
76+
def get_version(self, cmd, ip0, ip1, user, pw):
77+
out = ("Array_ID: 92210013 (HUS130) version: 0920/B-S LU: 4096"
78+
" RG: 75 RG_LU: 1024 Utility_version: 1.0.0")
79+
return out
80+
81+
def get_iscsi_info(self, cmd, ip0, ip1, user, pw):
82+
out = """CTL: 0 Port: 4 IP: 172.17.39.132 Port: 3260 Link: Up
83+
CTL: 0 Port: 5 IP: 172.17.39.133 Port: 3260 Link: Up
84+
CTL: 1 Port: 4 IP: 172.17.39.134 Port: 3260 Link: Up
85+
CTL: 1 Port: 5 IP: 172.17.39.135 Port: 3260 Link: Up"""
86+
return out
87+
88+
def get_hdp_info(self, cmd, ip0, ip1, user, pw):
89+
out = """HDP: 2 272384 MB 33792 MB 12 % LUs: 70 Normal Normal
90+
HDP: 9 546816 MB 73728 MB 13 % LUs: 194 Normal Normal"""
91+
return out
92+
93+
def create_lu(self, cmd, ip0, ip1, user, pw, id, hdp, start, end, size):
94+
if self.start_lun < int(start): # initialize first time
95+
self.start_lun = int(start)
96+
out = ("LUN: %d HDP: 9 size: %s MB, is successfully created" %
97+
(self.start_lun, size))
98+
self.alloc_lun.append(str(self.start_lun))
99+
self.start_lun += 1
100+
return out
101+
102+
def delete_lu(self, cmd, ip0, ip1, user, pw, id, lun):
103+
out = ""
104+
if lun in self.alloc_lun:
105+
out = "LUN: %s is successfully deleted" % (lun)
106+
self.alloc_lun.remove(lun)
107+
return out
108+
109+
def create_dup(self, cmd, ip0, ip1, user, pw, id, src_lun,
110+
hdp, start, end, size):
111+
out = ("LUN: %s HDP: 9 size: %s MB, is successfully created" %
112+
(self.start_lun, size))
113+
self.alloc_lun.append(str(self.start_lun))
114+
self.start_lun += 1
115+
return out
116+
117+
def add_iscsi_conn(self, cmd, ip0, ip1, user, pw, id, lun, ctl, port, iqn,
118+
tgt_alias, initiator, init_alias):
119+
conn = (initiator, iqn, ctl, port)
120+
out = ("iSCSI Initiator: %s, index: 26, and Target: %s, index 8 is \
121+
successfully paired @ CTL: %s, Port: %s" % conn)
122+
SimulatedHusBackend.connections.append(conn)
123+
return out
124+
125+
def del_iscsi_conn(self, cmd, ip0, ip1, user, pw, id, lun, ctl, port, iqn,
126+
initiator, force):
127+
conn = (initiator, iqn, ctl, port)
128+
out = ("iSCSI Initiator: %s, index: 26, and Target: %s, index 8 is \
129+
successfully un-paired @ CTL: %s, Port: %s" % conn)
130+
if conn in SimulatedHusBackend.connections:
131+
SimulatedHusBackend.connections.remove(conn)
132+
return out
133+
134+
135+
# The following information is passed on to tests, when creating a volume
136+
137+
_VOLUME = {'volume_id': '1234567890', 'size': 128,
138+
'volume_type': None, 'provider_location': None, 'id': 'abcdefg'}
139+
140+
141+
class HUSiSCSIDriverTest(test.TestCase):
142+
"""Test HUS iSCSI volume driver."""
143+
144+
def __init__(self, *args, **kwargs):
145+
super(HUSiSCSIDriverTest, self).__init__(*args, **kwargs)
146+
147+
def setUp(self):
148+
super(HUSiSCSIDriverTest, self).setUp()
149+
(handle, self.config_file) = tempfile.mkstemp('.xml')
150+
os.write(handle, CONF)
151+
os.close(handle)
152+
SimulatedHusBackend.alloc_lun = []
153+
SimulatedHusBackend.connections = []
154+
self.mox = mox.Mox()
155+
self.mox.StubOutWithMock(hds, 'factory_bend')
156+
hds.factory_bend().AndReturn(SimulatedHusBackend())
157+
self.mox.ReplayAll()
158+
self.configuration = mox.MockObject(conf.Configuration)
159+
self.configuration.hds_cinder_config_file = self.config_file
160+
self.driver = hds.HUSDriver(configuration=self.configuration)
161+
162+
def tearDown(self):
163+
os.remove(self.config_file)
164+
self.mox.UnsetStubs()
165+
super(HUSiSCSIDriverTest, self).tearDown()
166+
167+
def test_get_volume_stats(self):
168+
stats = self.driver.get_volume_stats(True)
169+
self.assertEqual(stats["vendor_name"], "HDS")
170+
self.assertEqual(stats["storage_protocol"], "iSCSI")
171+
self.assertTrue(stats["total_capacity_gb"] > 0)
172+
173+
def test_create_volume(self):
174+
loc = self.driver.create_volume(_VOLUME)
175+
self.assertNotEqual(loc, None)
176+
vol = _VOLUME.copy()
177+
vol['provider_location'] = loc['provider_location']
178+
self.assertNotEqual(loc['provider_location'], None)
179+
return vol
180+
181+
def test_delete_volume(self):
182+
"""Delete a volume (test).
183+
184+
Note: this API call should not expect any exception:
185+
This driver will silently accept a delete request, because
186+
the DB can be out of sync, and Cinder manager will keep trying
187+
to delete, even though the volume has been wiped out of the
188+
Array. We don't want to have a dangling volume entry in the
189+
customer dashboard.
190+
"""
191+
vol = self.test_create_volume()
192+
self.assertTrue(SimulatedHusBackend.alloc_lun)
193+
num_luns_before = len(SimulatedHusBackend.alloc_lun)
194+
self.driver.delete_volume(vol)
195+
num_luns_after = len(SimulatedHusBackend.alloc_lun)
196+
self.assertTrue(num_luns_before > num_luns_after)
197+
198+
def test_create_snapshot(self):
199+
vol = self.test_create_volume()
200+
self.mox.StubOutWithMock(self.driver, '_id_to_vol')
201+
self.driver._id_to_vol(vol['volume_id']).AndReturn(vol)
202+
self.mox.ReplayAll()
203+
svol = vol.copy()
204+
svol['volume_size'] = svol['size']
205+
loc = self.driver.create_snapshot(svol)
206+
self.assertNotEqual(loc, None)
207+
svol['provider_location'] = loc['provider_location']
208+
return svol
209+
210+
def test_delete_snapshot(self):
211+
"""Delete a snapshot (test).
212+
213+
Note: this API call should not expect any exception:
214+
This driver will silently accept a delete request, because
215+
the DB can be out of sync, and Cinder manager will keep trying
216+
to delete, even though the snapshot has been wiped out of the
217+
Array. We don't want to have a dangling snapshot entry in the
218+
customer dashboard.
219+
"""
220+
svol = self.test_create_snapshot()
221+
num_luns_before = len(SimulatedHusBackend.alloc_lun)
222+
self.driver.delete_snapshot(svol)
223+
num_luns_after = len(SimulatedHusBackend.alloc_lun)
224+
self.assertTrue(num_luns_before > num_luns_after)
225+
226+
def test_create_volume_from_snapshot(self):
227+
svol = self.test_create_snapshot()
228+
vol = self.driver.create_volume_from_snapshot(_VOLUME, svol)
229+
self.assertNotEqual(vol, None)
230+
return vol
231+
232+
def test_initialize_connection(self):
233+
connector = {}
234+
connector['initiator'] = 'iqn.1993-08.org.debian:01:11f90746eb2'
235+
connector['host'] = 'dut_1.lab.hds.com'
236+
vol = self.test_create_volume()
237+
conn = self.driver.initialize_connection(vol, connector)
238+
self.assertTrue('hitachi' in conn['data']['target_iqn'])
239+
self.assertTrue('3260' in conn['data']['target_portal'])
240+
return (vol, connector)
241+
242+
def test_terminate_connection(self):
243+
"""Terminate a connection (test).
244+
245+
Note: this API call should not expect any exception:
246+
This driver will silently accept a terminate_connection request
247+
because an error/exception return will only jeopardize the
248+
connection tear down at a host.
249+
"""
250+
(vol, conn) = self.test_initialize_connection()
251+
num_conn_before = len(SimulatedHusBackend.connections)
252+
self.driver.terminate_connection(vol, conn)
253+
num_conn_after = len(SimulatedHusBackend.connections)
254+
self.assertTrue(num_conn_before > num_conn_after)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copyright (c) 2013 Hitachi Data Systems, Inc.
2+
# Copyright (c) 2013 OpenStack LLC.
3+
# All Rights Reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License. You may obtain
7+
# a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
# License for the specific language governing permissions and limitations
15+
# under the License.
16+
#

0 commit comments

Comments
 (0)