1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2011 Nexenta Systems, Inc.
6
# Licensed under the Apache License, Version 2.0 (the "License"); you may
7
# not use this file except in compliance with the License. You may obtain
8
# a copy of the License at
10
# http://www.apache.org/licenses/LICENSE-2.0
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
# License for the specific language governing permissions and limitations
18
:mod:`nexenta.volume` -- Driver to store volumes on Nexenta Appliance
19
=====================================================================
21
.. automodule:: nexenta.volume
22
.. moduleauthor:: Yuriy Taraday <yorik.sar@gmail.com>
25
from cinder import exception
26
from cinder import flags
27
from cinder.openstack.common import log as logging
28
from cinder.openstack.common import cfg
29
from cinder.volume import driver
30
from cinder.volume import nexenta
31
from cinder.volume.nexenta import jsonrpc
33
LOG = logging.getLogger("cinder.volume.nexenta.volume")
37
cfg.StrOpt('nexenta_host',
39
help='IP address of Nexenta SA'),
40
cfg.IntOpt('nexenta_rest_port',
42
help='HTTP port to connect to Nexenta REST API server'),
43
cfg.StrOpt('nexenta_rest_protocol',
45
help='Use http or https for REST connection (default auto)'),
46
cfg.StrOpt('nexenta_user',
48
help='User name to connect to Nexenta SA'),
49
cfg.StrOpt('nexenta_password',
51
help='Password to connect to Nexenta SA'),
52
cfg.IntOpt('nexenta_iscsi_target_portal_port',
54
help='Nexenta target portal port'),
55
cfg.StrOpt('nexenta_volume',
57
help='pool on SA that will hold all volumes'),
58
cfg.StrOpt('nexenta_target_prefix',
59
default='iqn.1986-03.com.sun:02:cinder-',
60
help='IQN prefix for iSCSI targets'),
61
cfg.StrOpt('nexenta_target_group_prefix',
63
help='prefix for iSCSI target groups on SA'),
64
cfg.StrOpt('nexenta_blocksize',
66
help='block size for volumes (blank=default,8KB)'),
67
cfg.BoolOpt('nexenta_sparse',
69
help='flag to create sparse volumes'),
71
FLAGS.register_opts(nexenta_opts)
74
class NexentaDriver(driver.ISCSIDriver): # pylint: disable=R0921
75
"""Executes volume driver commands on Nexenta Appliance."""
78
super(NexentaDriver, self).__init__()
80
def do_setup(self, context):
81
protocol = FLAGS.nexenta_rest_protocol
82
auto = protocol == 'auto'
85
self.nms = jsonrpc.NexentaJSONProxy(
86
'%s://%s:%s/rest/nms/' % (protocol, FLAGS.nexenta_host,
87
FLAGS.nexenta_rest_port),
88
FLAGS.nexenta_user, FLAGS.nexenta_password, auto=auto)
90
def check_for_setup_error(self):
91
"""Verify that the volume for our zvols exists.
93
:raise: :py:exc:`LookupError`
95
if not self.nms.volume.object_exists(FLAGS.nexenta_volume):
96
raise LookupError(_("Volume %s does not exist in Nexenta SA"),
100
def _get_zvol_name(volume_name):
101
"""Return zvol name that corresponds given volume name."""
102
return '%s/%s' % (FLAGS.nexenta_volume, volume_name)
105
def _get_target_name(volume_name):
106
"""Return iSCSI target name to access volume."""
107
return '%s%s' % (FLAGS.nexenta_target_prefix, volume_name)
110
def _get_target_group_name(volume_name):
111
"""Return Nexenta iSCSI target group name for volume."""
112
return '%s%s' % (FLAGS.nexenta_target_group_prefix, volume_name)
114
def create_volume(self, volume):
115
"""Create a zvol on appliance.
117
:param volume: volume reference
119
self.nms.zvol.create(
120
self._get_zvol_name(volume['name']),
121
'%sG' % (volume['size'],),
122
FLAGS.nexenta_blocksize, FLAGS.nexenta_sparse)
124
def delete_volume(self, volume):
125
"""Destroy a zvol on appliance.
127
:param volume: volume reference
130
self.nms.zvol.destroy(self._get_zvol_name(volume['name']), '')
131
except nexenta.NexentaException as exc:
132
if "zvol has children" in exc.args[1]:
133
raise exception.VolumeIsBusy
137
def create_snapshot(self, snapshot):
138
"""Create snapshot of existing zvol on appliance.
140
:param snapshot: shapshot reference
142
self.nms.zvol.create_snapshot(
143
self._get_zvol_name(snapshot['volume_name']),
144
snapshot['name'], '')
146
def create_volume_from_snapshot(self, volume, snapshot):
147
"""Create new volume from other's snapshot on appliance.
149
:param volume: reference of volume to be created
150
:param snapshot: reference of source snapshot
153
'%s@%s' % (self._get_zvol_name(snapshot['volume_name']),
155
self._get_zvol_name(volume['name']))
157
def delete_snapshot(self, snapshot):
158
"""Delete volume's snapshot on appliance.
160
:param snapshot: shapshot reference
163
self.nms.snapshot.destroy(
164
'%s@%s' % (self._get_zvol_name(snapshot['volume_name']),
167
except nexenta.NexentaException as exc:
168
if "snapshot has dependent clones" in exc.args[1]:
169
raise exception.SnapshotIsBusy
173
def local_path(self, volume):
174
"""Return local path to existing local volume.
176
We never have local volumes, so it raises NotImplementedError.
178
:raise: :py:exc:`NotImplementedError`
180
LOG.error(_("Call to local_path should not happen."
181
" Verify that use_local_volumes flag is turned off."))
182
raise NotImplementedError
184
def _do_export(self, _ctx, volume, ensure=False):
185
"""Do all steps to get zvol exported as LUN 0 at separate target.
187
:param volume: reference of volume to be exported
188
:param ensure: if True, ignore errors caused by already existing
190
:return: iscsiadm-formatted provider location string
192
zvol_name = self._get_zvol_name(volume['name'])
193
target_name = self._get_target_name(volume['name'])
194
target_group_name = self._get_target_group_name(volume['name'])
197
self.nms.iscsitarget.create_target({'target_name': target_name})
198
except nexenta.NexentaException as exc:
199
if not ensure or 'already configured' not in exc.args[1]:
202
LOG.info(_('Ignored target creation error "%s"'
203
' while ensuring export'), exc)
205
self.nms.stmf.create_targetgroup(target_group_name)
206
except nexenta.NexentaException as exc:
207
if not ensure or 'already exists' not in exc.args[1]:
210
LOG.info(_('Ignored target group creation error "%s"'
211
' while ensuring export'), exc)
213
self.nms.stmf.add_targetgroup_member(target_group_name,
215
except nexenta.NexentaException as exc:
216
if not ensure or 'already exists' not in exc.args[1]:
219
LOG.info(_('Ignored target group member addition error "%s"'
220
' while ensuring export'), exc)
222
self.nms.scsidisk.create_lu(zvol_name, {})
223
except nexenta.NexentaException as exc:
224
if not ensure or 'in use' not in exc.args[1]:
227
LOG.info(_('Ignored LU creation error "%s"'
228
' while ensuring export'), exc)
230
self.nms.scsidisk.add_lun_mapping_entry(zvol_name, {
231
'target_group': target_group_name,
233
except nexenta.NexentaException as exc:
234
if not ensure or 'view entry exists' not in exc.args[1]:
237
LOG.info(_('Ignored LUN mapping entry addition error "%s"'
238
' while ensuring export'), exc)
239
return '%s:%s,1 %s' % (FLAGS.nexenta_host,
240
FLAGS.nexenta_iscsi_target_portal_port,
243
def create_export(self, _ctx, volume):
244
"""Create new export for zvol.
246
:param volume: reference of volume to be exported
247
:return: iscsiadm-formatted provider location string
249
loc = self._do_export(_ctx, volume, ensure=False)
250
return {'provider_location': loc}
252
def ensure_export(self, _ctx, volume):
253
"""Recreate parts of export if necessary.
255
:param volume: reference of volume to be exported
257
self._do_export(_ctx, volume, ensure=True)
259
def remove_export(self, _ctx, volume):
260
"""Destroy all resources created to export zvol.
262
:param volume: reference of volume to be unexported
264
zvol_name = self._get_zvol_name(volume['name'])
265
target_name = self._get_target_name(volume['name'])
266
target_group_name = self._get_target_group_name(volume['name'])
267
self.nms.scsidisk.delete_lu(zvol_name)
270
self.nms.stmf.destroy_targetgroup(target_group_name)
271
except nexenta.NexentaException as exc:
272
# We assume that target group is already gone
273
LOG.warn(_('Got error trying to destroy target group'
274
' %(target_group)s, assuming it is already gone: %(exc)s'),
275
{'target_group': target_group_name, 'exc': exc})
277
self.nms.iscsitarget.delete_target(target_name)
278
except nexenta.NexentaException as exc:
279
# We assume that target is gone as well
280
LOG.warn(_('Got error trying to delete target %(target)s,'
281
' assuming it is already gone: %(exc)s'),
282
{'target': target_name, 'exc': exc})