~zulcss/cinder/cinder-ca-g2

« back to all changes in this revision

Viewing changes to cinder/volume/nexenta/volume.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-10-09 08:26:21 UTC
  • mfrom: (3.1.10 quantal)
  • Revision ID: package-import@ubuntu.com-20121009082621-stc86vcuyzyrp7ow
Tags: 2012.2-0ubuntu2
* debian/cinder_tgt.conf: Add missing configuration file. (LP: #1064366) 
* debian/README.Debian: Added note about migration from nova-volume
  to cinder-volume.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
#
 
3
# Copyright 2011 Nexenta Systems, Inc.
 
4
# All Rights Reserved.
 
5
#
 
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
 
9
#
 
10
#         http://www.apache.org/licenses/LICENSE-2.0
 
11
#
 
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
 
16
#    under the License.
 
17
"""
 
18
:mod:`nexenta.volume` -- Driver to store volumes on Nexenta Appliance
 
19
=====================================================================
 
20
 
 
21
.. automodule:: nexenta.volume
 
22
.. moduleauthor:: Yuriy Taraday <yorik.sar@gmail.com>
 
23
"""
 
24
 
 
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
 
32
 
 
33
LOG = logging.getLogger("cinder.volume.nexenta.volume")
 
34
FLAGS = flags.FLAGS
 
35
 
 
36
nexenta_opts = [
 
37
    cfg.StrOpt('nexenta_host',
 
38
              default='',
 
39
              help='IP address of Nexenta SA'),
 
40
    cfg.IntOpt('nexenta_rest_port',
 
41
               default=2000,
 
42
               help='HTTP port to connect to Nexenta REST API server'),
 
43
    cfg.StrOpt('nexenta_rest_protocol',
 
44
               default='auto',
 
45
               help='Use http or https for REST connection (default auto)'),
 
46
    cfg.StrOpt('nexenta_user',
 
47
               default='admin',
 
48
               help='User name to connect to Nexenta SA'),
 
49
    cfg.StrOpt('nexenta_password',
 
50
               default='nexenta',
 
51
               help='Password to connect to Nexenta SA'),
 
52
    cfg.IntOpt('nexenta_iscsi_target_portal_port',
 
53
               default=3260,
 
54
               help='Nexenta target portal port'),
 
55
    cfg.StrOpt('nexenta_volume',
 
56
               default='cinder',
 
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',
 
62
               default='cinder/',
 
63
               help='prefix for iSCSI target groups on SA'),
 
64
    cfg.StrOpt('nexenta_blocksize',
 
65
               default='',
 
66
               help='block size for volumes (blank=default,8KB)'),
 
67
    cfg.BoolOpt('nexenta_sparse',
 
68
                default=False,
 
69
                help='flag to create sparse volumes'),
 
70
]
 
71
FLAGS.register_opts(nexenta_opts)
 
72
 
 
73
 
 
74
class NexentaDriver(driver.ISCSIDriver):  # pylint: disable=R0921
 
75
    """Executes volume driver commands on Nexenta Appliance."""
 
76
 
 
77
    def __init__(self):
 
78
        super(NexentaDriver, self).__init__()
 
79
 
 
80
    def do_setup(self, context):
 
81
        protocol = FLAGS.nexenta_rest_protocol
 
82
        auto = protocol == 'auto'
 
83
        if auto:
 
84
            protocol = 'http'
 
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)
 
89
 
 
90
    def check_for_setup_error(self):
 
91
        """Verify that the volume for our zvols exists.
 
92
 
 
93
        :raise: :py:exc:`LookupError`
 
94
        """
 
95
        if not self.nms.volume.object_exists(FLAGS.nexenta_volume):
 
96
            raise LookupError(_("Volume %s does not exist in Nexenta SA"),
 
97
                                    FLAGS.nexenta_volume)
 
98
 
 
99
    @staticmethod
 
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)
 
103
 
 
104
    @staticmethod
 
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)
 
108
 
 
109
    @staticmethod
 
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)
 
113
 
 
114
    def create_volume(self, volume):
 
115
        """Create a zvol on appliance.
 
116
 
 
117
        :param volume: volume reference
 
118
        """
 
119
        self.nms.zvol.create(
 
120
            self._get_zvol_name(volume['name']),
 
121
            '%sG' % (volume['size'],),
 
122
            FLAGS.nexenta_blocksize, FLAGS.nexenta_sparse)
 
123
 
 
124
    def delete_volume(self, volume):
 
125
        """Destroy a zvol on appliance.
 
126
 
 
127
        :param volume: volume reference
 
128
        """
 
129
        try:
 
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
 
134
            else:
 
135
                raise
 
136
 
 
137
    def create_snapshot(self, snapshot):
 
138
        """Create snapshot of existing zvol on appliance.
 
139
 
 
140
        :param snapshot: shapshot reference
 
141
        """
 
142
        self.nms.zvol.create_snapshot(
 
143
            self._get_zvol_name(snapshot['volume_name']),
 
144
            snapshot['name'], '')
 
145
 
 
146
    def create_volume_from_snapshot(self, volume, snapshot):
 
147
        """Create new volume from other's snapshot on appliance.
 
148
 
 
149
        :param volume: reference of volume to be created
 
150
        :param snapshot: reference of source snapshot
 
151
        """
 
152
        self.nms.zvol.clone(
 
153
            '%s@%s' % (self._get_zvol_name(snapshot['volume_name']),
 
154
                       snapshot['name']),
 
155
            self._get_zvol_name(volume['name']))
 
156
 
 
157
    def delete_snapshot(self, snapshot):
 
158
        """Delete volume's snapshot on appliance.
 
159
 
 
160
        :param snapshot: shapshot reference
 
161
        """
 
162
        try:
 
163
            self.nms.snapshot.destroy(
 
164
                '%s@%s' % (self._get_zvol_name(snapshot['volume_name']),
 
165
                           snapshot['name']),
 
166
                '')
 
167
        except nexenta.NexentaException as exc:
 
168
            if "snapshot has dependent clones" in exc.args[1]:
 
169
                raise exception.SnapshotIsBusy
 
170
            else:
 
171
                raise
 
172
 
 
173
    def local_path(self, volume):
 
174
        """Return local path to existing local volume.
 
175
 
 
176
        We never have local volumes, so it raises NotImplementedError.
 
177
 
 
178
        :raise: :py:exc:`NotImplementedError`
 
179
        """
 
180
        LOG.error(_("Call to local_path should not happen."
 
181
                        " Verify that use_local_volumes flag is turned off."))
 
182
        raise NotImplementedError
 
183
 
 
184
    def _do_export(self, _ctx, volume, ensure=False):
 
185
        """Do all steps to get zvol exported as LUN 0 at separate target.
 
186
 
 
187
        :param volume: reference of volume to be exported
 
188
        :param ensure: if True, ignore errors caused by already existing
 
189
            resources
 
190
        :return: iscsiadm-formatted provider location string
 
191
        """
 
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'])
 
195
 
 
196
        try:
 
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]:
 
200
                raise
 
201
            else:
 
202
                LOG.info(_('Ignored target creation error "%s"'
 
203
                                             ' while ensuring export'), exc)
 
204
        try:
 
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]:
 
208
                raise
 
209
            else:
 
210
                LOG.info(_('Ignored target group creation error "%s"'
 
211
                                             ' while ensuring export'), exc)
 
212
        try:
 
213
            self.nms.stmf.add_targetgroup_member(target_group_name,
 
214
                                                 target_name)
 
215
        except nexenta.NexentaException as exc:
 
216
            if not ensure or 'already exists' not in exc.args[1]:
 
217
                raise
 
218
            else:
 
219
                LOG.info(_('Ignored target group member addition error "%s"'
 
220
                                             ' while ensuring export'), exc)
 
221
        try:
 
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]:
 
225
                raise
 
226
            else:
 
227
                LOG.info(_('Ignored LU creation error "%s"'
 
228
                                             ' while ensuring export'), exc)
 
229
        try:
 
230
            self.nms.scsidisk.add_lun_mapping_entry(zvol_name, {
 
231
                'target_group': target_group_name,
 
232
                'lun': '0'})
 
233
        except nexenta.NexentaException as exc:
 
234
            if not ensure or 'view entry exists' not in exc.args[1]:
 
235
                raise
 
236
            else:
 
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,
 
241
                               target_name)
 
242
 
 
243
    def create_export(self, _ctx, volume):
 
244
        """Create new export for zvol.
 
245
 
 
246
        :param volume: reference of volume to be exported
 
247
        :return: iscsiadm-formatted provider location string
 
248
        """
 
249
        loc = self._do_export(_ctx, volume, ensure=False)
 
250
        return {'provider_location': loc}
 
251
 
 
252
    def ensure_export(self, _ctx, volume):
 
253
        """Recreate parts of export if necessary.
 
254
 
 
255
        :param volume: reference of volume to be exported
 
256
        """
 
257
        self._do_export(_ctx, volume, ensure=True)
 
258
 
 
259
    def remove_export(self, _ctx, volume):
 
260
        """Destroy all resources created to export zvol.
 
261
 
 
262
        :param volume: reference of volume to be unexported
 
263
        """
 
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)
 
268
 
 
269
        try:
 
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})
 
276
        try:
 
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})