Browse Source

Merge "Add support for working with multiple glusterfs volumes"

Jenkins 4 years ago
parent
commit
81d82d19af

+ 6
- 0
etc/manila/rootwrap.d/share.filters View File

@@ -17,3 +17,9 @@ ip: CommandFilter, /sbin/ip, root
17 17
 
18 18
 # manila/network/linux/interface.py: 'ovs-vsctl', 'add-port', '%s', '%s'
19 19
 ovs-vsctl: CommandFilter, /usr/bin/ovs-vsctl, root
20
+
21
+# manila/share/drivers/glusterfs_native.py: 'find', '%s', '-mindepth', '1', '-delete'
22
+find_del: RegExpFilter, /bin/find, root, find, .*, -mindepth, 1, -delete
23
+
24
+# manila/share/drivers/glusterfs_native.py: 'umount', '%s'
25
+umount: CommandFilter, umount, root

+ 424
- 67
manila/share/drivers/glusterfs_native.py View File

@@ -19,81 +19,376 @@ Manila share is a GlusterFS volume. Unlike the generic driver, this
19 19
 does not use service VM approach. Instances directly talk with the
20 20
 GlusterFS backend storage pool. Instance use the 'glusterfs' protocol
21 21
 to mount the GlusterFS share. Access to the share is allowed via
22
-SSL Certificates. Only the share which has the SSL trust established
22
+SSL Certificates. Only the instance which has the SSL trust established
23 23
 with the GlusterFS backend can mount and hence use the share.
24
+
25
+Supports working with multiple glusterfs volumes.
24 26
 """
25 27
 
28
+import errno
29
+import pipes
30
+import shutil
31
+import tempfile
32
+
33
+from oslo.config import cfg
34
+import six
26 35
 
27 36
 from manila import exception
28 37
 from manila.openstack.common import log as logging
38
+from manila.share import driver
29 39
 from manila.share.drivers import glusterfs
40
+from manila import utils
30 41
 
31 42
 
32 43
 LOG = logging.getLogger(__name__)
33 44
 
45
+glusterfs_native_manila_share_opts = [
46
+    cfg.ListOpt('glusterfs_targets',
47
+                default=[],
48
+                help='List of GlusterFS volumes that can be used to create '
49
+                     'shares. Each GlusterFS volume should be of the form '
50
+                     '[remoteuser@]<volserver>:/<volid>'),
51
+]
52
+
53
+CONF = cfg.CONF
54
+CONF.register_opts(glusterfs_native_manila_share_opts)
55
+
56
+ACCESS_TYPE_CERT = 'cert'
57
+AUTH_SSL_ALLOW = 'auth.ssl-allow'
34 58
 CLIENT_SSL = 'client.ssl'
59
+NFS_EXPORT_VOL = 'nfs.export-volumes'
35 60
 SERVER_SSL = 'server.ssl'
36
-AUTH_SSL_ALLOW = 'auth.ssl-allow'
37
-ACCESS_TYPE_CERT = 'cert'
38 61
 
39 62
 
40
-class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver):
63
+class GlusterfsNativeShareDriver(driver.ExecuteMixin, driver.ShareDriver):
64
+    """GlusterFS native protocol (glusterfs) share driver.
41 65
 
42
-    def _setup_gluster_vol(self):
43
-        super(GlusterfsNativeShareDriver, self)._setup_gluster_vol()
66
+    Executes commands relating to Shares.
67
+    Supports working with multiple glusterfs volumes.
44 68
 
45
-        # Enable gluster volume for SSL access.
46
-        # This applies for both service mount and instance mount(s).
69
+    API version history:
70
+
71
+        1.0 - Initial version.
72
+        1.1 - Support for working with multiple gluster volumes.
73
+    """
74
+
75
+    def __init__(self, db, *args, **kwargs):
76
+        super(GlusterfsNativeShareDriver, self).__init__(*args, **kwargs)
77
+        self.db = db
78
+        self._helpers = None
79
+        self.gluster_unused_vols_dict = {}
80
+        self.gluster_used_vols_dict = {}
81
+        self.configuration.append_config_values(
82
+            glusterfs_native_manila_share_opts)
83
+        self.backend_name = self.configuration.safe_get(
84
+            'share_backend_name') or 'GlusterFS-Native'
85
+
86
+    def do_setup(self, context):
87
+        """Setup the GlusterFS volumes."""
88
+        super(GlusterfsNativeShareDriver, self).do_setup(context)
89
+
90
+        # We don't use a service mount as its not necessary for us.
91
+        # Do some sanity checks.
92
+        if len(self.configuration.glusterfs_targets) == 0:
93
+            # No volumes specified in the config file. Raise exception.
94
+            msg = (_("glusterfs_targets list seems to be empty! "
95
+                     "Add one or more gluster volumes to work "
96
+                     "with in the glusterfs_targets configuration "
97
+                     "parameter."))
98
+            LOG.error(msg)
99
+            raise exception.GlusterfsException(msg)
100
+
101
+        LOG.info(_("Number of gluster volumes read from config: "
102
+                   "%(numvols)s"),
103
+                 {'numvols': len(self.configuration.glusterfs_targets)})
47 104
 
48
-        # TODO(deepakcs): Once gluster support dual-access, we can limit
49
-        # service mount to non-ssl access.
50
-        gargs, gkw = self.gluster_address.make_gluster_args(
51
-            'volume', 'set', self.gluster_address.volume,
52
-            CLIENT_SSL, 'on')
105
+        try:
106
+            self._execute('mount.glusterfs', check_exit_code=False)
107
+        except OSError as exc:
108
+            if exc.errno == errno.ENOENT:
109
+                msg = (_("mount.glusterfs is not installed."))
110
+                LOG.error(msg)
111
+                raise exception.GlusterfsException(msg)
112
+            else:
113
+                msg = (_("Error running mount.glusterfs."))
114
+                LOG.error(msg)
115
+                raise
116
+
117
+        # Update gluster_unused_vols_dict, gluster_used_vols_dict by walking
118
+        # through the DB.
119
+        self._update_gluster_vols_dict(context)
120
+        if len(self.gluster_unused_vols_dict) == 0:
121
+            # No volumes available for use as share. Warn user.
122
+            msg = (_("No unused gluster volumes available for use as share! "
123
+                     "Create share won't be supported unless existing shares "
124
+                     "are deleted or add one or more gluster volumes to work "
125
+                     "with in the glusterfs_targets configuration parameter."))
126
+            LOG.warn(msg)
127
+        else:
128
+            LOG.info(_("Number of gluster volumes in use: %(inuse-numvols)s. "
129
+                       "Number of gluster volumes available for use as share: "
130
+                       "%(unused-numvols)s"),
131
+                     {'inuse-numvols': len(self.gluster_used_vols_dict),
132
+                     'unused-numvols': len(self.gluster_unused_vols_dict)})
133
+
134
+        self._setup_gluster_vols()
135
+
136
+    @utils.synchronized("glusterfs_native", external=False)
137
+    def _update_gluster_vols_dict(self, context):
138
+        """Update dict of gluster vols that are used/unused."""
139
+
140
+        shares = self.db.share_get_all(context)
141
+
142
+        # Store the gluster volumes in dict thats helpful to track
143
+        # (push and pop) in future. {gluster_export: gluster_addr, ...}
144
+        # gluster_export is of form hostname:/volname which is unique
145
+        # enough to be used as a key.
146
+        self.gluster_unused_vols_dict = {}
147
+        self.gluster_used_vols_dict = {}
148
+
149
+        for gv in self.configuration.glusterfs_targets:
150
+            gaddr = glusterfs.GlusterAddress(gv)
151
+            exp_locn_gv = gaddr.export
152
+
153
+            # Assume its unused to begin with.
154
+            self.gluster_unused_vols_dict.update({exp_locn_gv: gaddr})
155
+
156
+            for s in shares:
157
+                exp_locn_share = s.get('export_location', None)
158
+                if exp_locn_share == exp_locn_gv:
159
+                    # gluster volume is in use, move it to used list.
160
+                    self.gluster_used_vols_dict.update({exp_locn_gv: gaddr})
161
+                    self.gluster_unused_vols_dict.pop(exp_locn_gv)
162
+                    break
163
+
164
+    @utils.synchronized("glusterfs_native", external=False)
165
+    def _setup_gluster_vols(self):
166
+        # Enable gluster volumes for SSL access only.
167
+
168
+        for gluster_addr in six.itervalues(self.gluster_unused_vols_dict):
169
+
170
+            gargs, gkw = gluster_addr.make_gluster_args(
171
+                'volume', 'set', gluster_addr.volume,
172
+                NFS_EXPORT_VOL, 'off')
173
+            try:
174
+                self._execute(*gargs, **gkw)
175
+            except exception.ProcessExecutionError as exc:
176
+                msg = (_("Error in gluster volume set during volume setup. "
177
+                         "Volume: %(volname)s, Option: %(option)s, "
178
+                         "Error: %(error)s"),
179
+                       {'volname': gluster_addr.volume,
180
+                        'option': NFS_EXPORT_VOL, 'error': exc.stderr})
181
+                LOG.error(msg)
182
+                raise exception.GlusterfsException(msg)
183
+
184
+            gargs, gkw = gluster_addr.make_gluster_args(
185
+                'volume', 'set', gluster_addr.volume,
186
+                CLIENT_SSL, 'on')
187
+            try:
188
+                self._execute(*gargs, **gkw)
189
+            except exception.ProcessExecutionError as exc:
190
+                msg = (_("Error in gluster volume set during volume setup. "
191
+                         "Volume: %(volname)s, Option: %(option)s, "
192
+                         "Error: %(error)s"),
193
+                       {'volname': gluster_addr.volume,
194
+                        'option': CLIENT_SSL, 'error': exc.stderr})
195
+                LOG.error(msg)
196
+                raise exception.GlusterfsException(msg)
197
+
198
+            gargs, gkw = gluster_addr.make_gluster_args(
199
+                'volume', 'set', gluster_addr.volume,
200
+                SERVER_SSL, 'on')
201
+            try:
202
+                self._execute(*gargs, **gkw)
203
+            except exception.ProcessExecutionError as exc:
204
+                msg = (_("Error in gluster volume set during volume setup. "
205
+                         "Volume: %(volname)s, Option: %(option)s, "
206
+                         "Error: %(error)s"),
207
+                       {'volname': gluster_addr.volume,
208
+                        'option': SERVER_SSL, 'error': exc.stderr})
209
+                LOG.error(msg)
210
+                raise exception.GlusterfsException(msg)
211
+
212
+            # TODO(deepakcs) Remove this once ssl options can be
213
+            # set dynamically.
214
+            self._restart_gluster_vol(gluster_addr)
215
+
216
+    def _restart_gluster_vol(self, gluster_addr):
217
+        gargs, gkw = gluster_addr.make_gluster_args(
218
+            'volume', 'stop', gluster_addr.volume, '--mode=script')
53 219
         try:
54 220
             self._execute(*gargs, **gkw)
55 221
         except exception.ProcessExecutionError as exc:
56
-            LOG.error(_("Error in gluster volume set during volume setup."
57
-                        "Volume: %(volname)s, Option: %(option)s, "
58
-                        "Error: %(error)s"),
59
-                      {'volname': self.gluster_address.volume,
60
-                       'option': CLIENT_SSL, 'error': exc.stderr})
222
+            msg = (_("Error stopping gluster volume. "
223
+                     "Volume: %(volname)s, Error: %(error)s"),
224
+                   {'volname': gluster_addr.volume, 'error': exc.stderr})
225
+            LOG.error(msg)
226
+            raise exception.GlusterfsException(msg)
227
+
228
+        gargs, gkw = gluster_addr.make_gluster_args(
229
+            'volume', 'start', gluster_addr.volume)
230
+        try:
231
+            self._execute(*gargs, **gkw)
232
+        except exception.ProcessExecutionError as exc:
233
+            msg = (_("Error starting gluster volume. "
234
+                     "Volume: %(volname)s, Error: %(error)s"),
235
+                   {'volname': gluster_addr.volume, 'error': exc.stderr})
236
+            LOG.error(msg)
237
+            raise exception.GlusterfsException(msg)
238
+
239
+    @utils.synchronized("glusterfs_native", external=False)
240
+    def _pop_gluster_vol(self):
241
+        try:
242
+            exp_locn, gaddr = self.gluster_unused_vols_dict.popitem()
243
+        except KeyError:
244
+            msg = (_("Couldn't find a free gluster volume to use."))
245
+            LOG.error(msg)
246
+            raise exception.GlusterfsException(msg)
247
+
248
+        self.gluster_used_vols_dict.update({exp_locn: gaddr})
249
+        return exp_locn
250
+
251
+    @utils.synchronized("glusterfs_native", external=False)
252
+    def _push_gluster_vol(self, exp_locn):
253
+        try:
254
+            gaddr = self.gluster_used_vols_dict.pop(exp_locn)
255
+        except KeyError:
256
+            msg = (_("Couldn't find the share in used list."))
257
+            LOG.error(msg)
258
+            raise exception.GlusterfsException(msg)
259
+
260
+        self.gluster_unused_vols_dict.update({exp_locn: gaddr})
261
+
262
+    def _do_mount(self, gluster_export, mntdir):
263
+
264
+        cmd = ['mount', '-t', 'glusterfs', gluster_export, mntdir]
265
+        try:
266
+            self._execute(*cmd, run_as_root=True)
267
+        except exception.ProcessExecutionError as exc:
268
+            msg = (_("Unable to mount gluster volume. "
269
+                     "gluster_export: %(export)s, Error: %(error)s"),
270
+                   {'export': gluster_export, 'error': exc.stderr})
271
+            LOG.error(msg)
272
+            raise exception.GlusterfsException(msg)
273
+
274
+    def _do_umount(self, mntdir):
275
+
276
+        cmd = ['umount', mntdir]
277
+        try:
278
+            self._execute(*cmd, run_as_root=True)
279
+        except exception.ProcessExecutionError as exc:
280
+            msg = (_("Unable to unmount gluster volume. "
281
+                     "mount_dir: %(mntdir)s, Error: %(error)s"),
282
+                   {'mntdir': mntdir, 'error': exc.stderr})
283
+            LOG.error(msg)
284
+            raise exception.GlusterfsException(msg)
285
+
286
+    def _wipe_gluster_vol(self, gluster_addr):
287
+
288
+        # Reset the SSL options.
289
+        gargs, gkw = gluster_addr.make_gluster_args(
290
+            'volume', 'set', gluster_addr.volume,
291
+            CLIENT_SSL, 'off')
292
+        try:
293
+            self._execute(*gargs, **gkw)
294
+        except exception.ProcessExecutionError as exc:
295
+            msg = (_("Error in gluster volume set during _wipe_gluster_vol. "
296
+                     "Volume: %(volname)s, Option: %(option)s, "
297
+                     "Error: %(error)s"),
298
+                   {'volname': gluster_addr.volume,
299
+                    'option': CLIENT_SSL, 'error': exc.stderr})
300
+            LOG.error(msg)
301
+            raise exception.GlusterfsException(msg)
302
+
303
+        gargs, gkw = gluster_addr.make_gluster_args(
304
+            'volume', 'set', gluster_addr.volume,
305
+            SERVER_SSL, 'off')
306
+        try:
307
+            self._execute(*gargs, **gkw)
308
+        except exception.ProcessExecutionError as exc:
309
+            msg = (_("Error in gluster volume set during _wipe_gluster_vol. "
310
+                     "Volume: %(volname)s, Option: %(option)s, "
311
+                     "Error: %(error)s"),
312
+                   {'volname': gluster_addr.volume,
313
+                    'option': SERVER_SSL, 'error': exc.stderr})
314
+            LOG.error(msg)
315
+            raise exception.GlusterfsException(msg)
316
+
317
+        self._restart_gluster_vol(gluster_addr)
318
+
319
+        # Create a temporary mount.
320
+        gluster_export = gluster_addr.export
321
+        tmpdir = tempfile.mkdtemp()
322
+        try:
323
+            self._do_mount(gluster_export, tmpdir)
324
+        except exception.GlusterfsException:
325
+            shutil.rmtree(tmpdir, ignore_errors=True)
61 326
             raise
62
-        gargs, gkw = self.gluster_address.make_gluster_args(
63
-            'volume', 'set', self.gluster_address.volume,
327
+
328
+        # Delete only the contents, not the directory.
329
+        cmd = ['find', pipes.quote(tmpdir), '-mindepth', '1', '-delete']
330
+        try:
331
+            self._execute(*cmd, run_as_root=True)
332
+        except exception.ProcessExecutionError as exc:
333
+            msg = (_("Error trying to wipe gluster volume. "
334
+                     "gluster_export: %(export)s, Error: %(error)s"),
335
+                   {'export': gluster_export, 'error': exc.stderr})
336
+            LOG.error(msg)
337
+            raise exception.GlusterfsException(msg)
338
+        finally:
339
+            # Unmount.
340
+            self._do_umount(tmpdir)
341
+            shutil.rmtree(tmpdir, ignore_errors=True)
342
+
343
+        # Set the SSL options.
344
+        gargs, gkw = gluster_addr.make_gluster_args(
345
+            'volume', 'set', gluster_addr.volume,
346
+            CLIENT_SSL, 'on')
347
+        try:
348
+            self._execute(*gargs, **gkw)
349
+        except exception.ProcessExecutionError as exc:
350
+            msg = (_("Error in gluster volume set during _wipe_gluster_vol. "
351
+                     "Volume: %(volname)s, Option: %(option)s, "
352
+                     "Error: %(error)s"),
353
+                   {'volname': gluster_addr.volume,
354
+                    'option': CLIENT_SSL, 'error': exc.stderr})
355
+            LOG.error(msg)
356
+            raise exception.GlusterfsException(msg)
357
+
358
+        gargs, gkw = gluster_addr.make_gluster_args(
359
+            'volume', 'set', gluster_addr.volume,
64 360
             SERVER_SSL, 'on')
65 361
         try:
66 362
             self._execute(*gargs, **gkw)
67 363
         except exception.ProcessExecutionError as exc:
68
-            LOG.error(_("Error in gluster volume set during volume setup."
69
-                        "Volume: %(volname)s, Option: %(option)s, "
70
-                        "Error: %(error)s"),
71
-                      {'volname': self.gluster_address.volume,
72
-                       'option': SERVER_SSL, 'error': exc.stderr})
73
-            raise
364
+            msg = (_("Error in gluster volume set during _wipe_gluster_vol. "
365
+                     "Volume: %(volname)s, Option: %(option)s, "
366
+                     "Error: %(error)s"),
367
+                   {'volname': gluster_addr.volume,
368
+                    'option': SERVER_SSL, 'error': exc.stderr})
369
+            LOG.error(msg)
370
+            raise exception.GlusterfsException(msg)
74 371
 
75
-    def create_share(self, ctx, share, share_server=None):
372
+        self._restart_gluster_vol(gluster_addr)
373
+
374
+    def create_share(self, context, share, share_server=None):
76 375
         """Create a share using GlusterFS volume.
77 376
 
78
-        1 Manila share = 1 GlusterFS volume. Ensure that the
79
-        GlusterFS volume is properly setup to be consumed as
80
-        a share.
377
+        1 Manila share = 1 GlusterFS volume. Pick an unused
378
+        GlusterFS volume for use as a share.
81 379
         """
82
-
83
-        # Handle the case where create is called after delete share
84 380
         try:
85
-            self._setup_gluster_vol()
86
-        except exception.ProcessExecutionError:
87
-            LOG.error(_("Unable to create share %s"), (share['name'],))
381
+            export_location = self._pop_gluster_vol()
382
+        except exception.GlusterfsException:
383
+            msg = (_("Error creating share %(share_id)s"),
384
+                   {'share_id': share['id']})
385
+            LOG.error(msg)
88 386
             raise
89 387
 
90
-        # TODO(deepakcs): Add validation for gluster mount being present
91
-        # (decorator maybe)
388
+        # TODO(deepakcs): Enable quota and set it to the share size.
92 389
 
93 390
         # For native protocol, the export_location should be of the form:
94 391
         # server:/volname
95
-        export_location = self.gluster_address.export
96
-
97 392
         LOG.info(_("export_location sent back from create_share: %s"),
98 393
                   (export_location,))
99 394
         return export_location
@@ -101,22 +396,30 @@ class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver):
101 396
     def delete_share(self, context, share, share_server=None):
102 397
         """Delete a share on the GlusterFS volume.
103 398
 
104
-        1 Manila share = 1 GlusterFS volume. Ensure that the
105
-        GlusterFS volume is reset back to its original state.
399
+        1 Manila share = 1 GlusterFS volume. Put the gluster
400
+        volume back in the available list.
106 401
         """
107
-        # Get the gluster volume back to its original state
402
+        exp_locn = share.get('export_location', None)
403
+        try:
404
+            # Get the gluster address associated with the export.
405
+            gaddr = self.gluster_used_vols_dict[exp_locn]
406
+        except KeyError:
407
+            msg = (_("Invalid request. Ignoring delete_share request for "
408
+                     "share %(share_id)s"), {'share_id': share['id']},)
409
+            LOG.warn(msg)
410
+            return
108 411
 
109
-        gargs, gkw = self.gluster_address.make_gluster_args(
110
-            'volume', 'reset', self.gluster_address.volume)
111 412
         try:
112
-            self._execute(*gargs, **gkw)
113
-        except exception.ProcessExecutionError as exc:
114
-            LOG.error(_("Error in gluster volume reset during delete share."
115
-                        "Volume: %(volname)s, Error: %(error)s"),
116
-                      {'volname': self.gluster_address.volume,
117
-                       'error': exc.stderr})
413
+            self._wipe_gluster_vol(gaddr)
414
+            self._push_gluster_vol(exp_locn)
415
+        except exception.GlusterfsException:
416
+            msg = (_("Error during delete_share request for "
417
+                     "share %(share_id)s"), {'share_id': share['id']},)
418
+            LOG.error(msg)
118 419
             raise
119 420
 
421
+        # TODO(deepakcs): Disable quota.
422
+
120 423
     def allow_access(self, context, share, access, share_server=None):
121 424
         """Allow access to a share using certs.
122 425
 
@@ -126,20 +429,27 @@ class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver):
126 429
         if access['access_type'] != ACCESS_TYPE_CERT:
127 430
             raise exception.InvalidShareAccess(_("Only 'cert' access type "
128 431
                                                  "allowed"))
432
+        exp_locn = share.get('export_location', None)
433
+        gluster_addr = self.gluster_used_vols_dict.get(exp_locn)
129 434
 
130
-        gargs, gkw = self.gluster_address.make_gluster_args(
131
-            'volume', 'set', self.gluster_address.volume,
435
+        gargs, gkw = gluster_addr.make_gluster_args(
436
+            'volume', 'set', gluster_addr.volume,
132 437
             AUTH_SSL_ALLOW, access['access_to'])
133 438
         try:
134 439
             self._execute(*gargs, **gkw)
135 440
         except exception.ProcessExecutionError as exc:
136
-            LOG.error(_("Error in gluster volume set during allow access."
137
-                        "Volume: %(volname)s, Option: %(option)s, "
138
-                        "access_to: %(access_to)s, Error: %(error)s"),
139
-                      {'volname': self.gluster_address.volume,
140
-                       'option': AUTH_SSL_ALLOW,
141
-                       'access_to': access['access_to'], 'error': exc.stderr})
142
-            raise
441
+            msg = (_("Error in gluster volume set during allow access. "
442
+                     "Volume: %(volname)s, Option: %(option)s, "
443
+                     "access_to: %(access_to)s, Error: %(error)s"),
444
+                   {'volname': gluster_addr.volume,
445
+                    'option': AUTH_SSL_ALLOW,
446
+                    'access_to': access['access_to'], 'error': exc.stderr})
447
+            LOG.error(msg)
448
+            raise exception.GlusterfsException(msg)
449
+
450
+        # TODO(deepakcs) Remove this once ssl options can be
451
+        # set dynamically.
452
+        self._restart_gluster_vol(gluster_addr)
143 453
 
144 454
     def deny_access(self, context, share, access, share_server=None):
145 455
         """Deny access to a share that's using cert based auth.
@@ -151,16 +461,63 @@ class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver):
151 461
             raise exception.InvalidShareAccess(_("Only 'cert' access type "
152 462
                                                  "allowed for access "
153 463
                                                  "removal."))
464
+        exp_locn = share.get('export_location', None)
465
+        gluster_addr = self.gluster_used_vols_dict.get(exp_locn)
154 466
 
155
-        gargs, gkw = self.gluster_address.make_gluster_args(
156
-            'volume', 'reset', self.gluster_address.volume,
467
+        gargs, gkw = gluster_addr.make_gluster_args(
468
+            'volume', 'reset', gluster_addr.volume,
157 469
             AUTH_SSL_ALLOW)
158 470
         try:
159 471
             self._execute(*gargs, **gkw)
160 472
         except exception.ProcessExecutionError as exc:
161
-            LOG.error(_("Error in gluster volume reset during deny access."
162
-                        "Volume: %(volname)s, Option: %(option)s, "
163
-                        "Error: %(error)s"),
164
-                      {'volname': self.gluster_address.volume,
165
-                       'option': AUTH_SSL_ALLOW, 'error': exc.stderr})
166
-            raise
473
+            msg = (_("Error in gluster volume reset during deny access. "
474
+                     "Volume: %(volname)s, Option: %(option)s, "
475
+                     "Error: %(error)s"),
476
+                   {'volname': gluster_addr.volume,
477
+                    'option': AUTH_SSL_ALLOW, 'error': exc.stderr})
478
+            LOG.error(msg)
479
+            raise exception.GlusterfsException(msg)
480
+
481
+        # TODO(deepakcs) Remove this once ssl options can be
482
+        # set dynamically.
483
+        self._restart_gluster_vol(gluster_addr)
484
+
485
+    def get_share_stats(self, refresh=False):
486
+        """Get share stats.
487
+
488
+        If 'refresh' is True, update the stats first.
489
+        """
490
+        if refresh:
491
+            self._update_share_stats()
492
+
493
+        return self._stats
494
+
495
+    def _update_share_stats(self):
496
+        """Send stats info for the GlusterFS volume."""
497
+
498
+        LOG.debug("Updating share stats")
499
+
500
+        data = {}
501
+
502
+        data["share_backend_name"] = self.backend_name
503
+        data["vendor_name"] = 'Red Hat'
504
+        data["driver_version"] = '1.1'
505
+        data["storage_protocol"] = 'glusterfs'
506
+
507
+        data['reserved_percentage'] = (
508
+            self.configuration.reserved_share_percentage)
509
+        data['QoS_support'] = False
510
+
511
+        # We don't use a service mount to get stats data.
512
+        # Instead we use glusterfs quota feature and use that to limit
513
+        # the share to its expected share['size'].
514
+
515
+        # TODO(deepakcs): Change below once glusterfs supports volume
516
+        # specific stats via the gluster cli.
517
+        data['total_capacity_gb'] = 'infinite'
518
+        data['free_capacity_gb'] = 'infinite'
519
+        self._stats = data
520
+
521
+    def ensure_share(self, context, share, share_server=None):
522
+        """Invoked to ensure that share is exported."""
523
+        pass

+ 806
- 0
manila/tests/share/drivers/test_glusterfs_native.py View File

@@ -0,0 +1,806 @@
1
+# Copyright (c) 2014 Red Hat, Inc.
2
+# All Rights Reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+
16
+""" GlusterFS native protocol (glusterfs) driver for shares.
17
+
18
+Test cases for GlusterFS native protocol driver.
19
+"""
20
+
21
+
22
+import shutil
23
+import tempfile
24
+
25
+import mock
26
+from oslo.config import cfg
27
+
28
+from manila import context
29
+from manila import exception
30
+from manila.share import configuration as config
31
+from manila.share.drivers import glusterfs
32
+from manila.share.drivers import glusterfs_native
33
+from manila import test
34
+from manila.tests import fake_utils
35
+
36
+
37
+CONF = cfg.CONF
38
+
39
+
40
+def fake_db_share1(**kwargs):
41
+    share = {
42
+        'id': 'fakeid',
43
+        'name': 'fakename',
44
+        'size': 1,
45
+        'share_proto': 'glusterfs',
46
+        'export_location': 'host1:/gv1',
47
+    }
48
+    share.update(kwargs)
49
+    return [share]
50
+
51
+
52
+def fake_db_share2(**kwargs):
53
+    share = {
54
+        'id': 'fakeid',
55
+        'name': 'fakename',
56
+        'size': 1,
57
+        'share_proto': 'glusterfs',
58
+        'export_location': 'host2:/gv2',
59
+    }
60
+    share.update(kwargs)
61
+    return [share]
62
+
63
+
64
+def new_share(**kwargs):
65
+    share = {
66
+        'id': 'fakeid',
67
+        'name': 'fakename',
68
+        'size': 1,
69
+        'share_proto': 'glusterfs',
70
+    }
71
+    share.update(kwargs)
72
+    return share
73
+
74
+
75
+class GlusterfsNativeShareDriverTestCase(test.TestCase):
76
+    """Tests GlusterfsNativeShareDriver."""
77
+
78
+    def setUp(self):
79
+        super(GlusterfsNativeShareDriverTestCase, self).setUp()
80
+        fake_utils.stub_out_utils_execute(self.stubs)
81
+        self._execute = fake_utils.fake_execute
82
+        self._context = context.get_admin_context()
83
+
84
+        self.gluster_target1 = 'root@host1:/gv1'
85
+        self.gluster_target2 = 'root@host2:/gv2'
86
+        CONF.set_default('glusterfs_targets',
87
+                         [self.gluster_target1, self.gluster_target2])
88
+
89
+        self.fake_conf = config.Configuration(None)
90
+        self._db = mock.Mock()
91
+        self._driver = glusterfs_native.GlusterfsNativeShareDriver(
92
+            self._db, execute=self._execute,
93
+            configuration=self.fake_conf)
94
+        self.stubs.Set(tempfile, 'mkdtemp',
95
+                       mock.Mock(return_value='/tmp/tmpKGHKJ'))
96
+
97
+        self.addCleanup(fake_utils.fake_execute_set_repliers, [])
98
+        self.addCleanup(fake_utils.fake_execute_clear_log)
99
+
100
+    def test_do_setup(self):
101
+        self._driver._setup_gluster_vols = mock.Mock()
102
+        self._db.share_get_all = mock.Mock(return_value=[])
103
+        expected_exec = ['mount.glusterfs']
104
+        gaddr = glusterfs.GlusterAddress
105
+
106
+        self._driver.do_setup(self._context)
107
+
108
+        self.assertEqual(2, len(self._driver.gluster_unused_vols_dict))
109
+        self.assertTrue(gaddr(self.gluster_target1).export in
110
+                        self._driver.gluster_unused_vols_dict)
111
+        self.assertTrue(gaddr(self.gluster_target2).export in
112
+                        self._driver.gluster_unused_vols_dict)
113
+        self.assertTrue(self._driver._setup_gluster_vols.called)
114
+        self.assertTrue(self._db.share_get_all.called)
115
+        self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
116
+
117
+    def test_do_setup_glusterfs_targets_empty(self):
118
+        self._driver.configuration.glusterfs_targets = []
119
+        self.assertRaises(exception.GlusterfsException, self._driver.do_setup,
120
+                          self._context)
121
+
122
+    def test_update_gluster_vols_dict(self):
123
+        self._db.share_get_all = mock.Mock(return_value=fake_db_share1())
124
+
125
+        self._driver._update_gluster_vols_dict(self._context)
126
+
127
+        self.assertEqual(1, len(self._driver.gluster_used_vols_dict))
128
+        self.assertEqual(1, len(self._driver.gluster_unused_vols_dict))
129
+        self.assertTrue(self._db.share_get_all.called)
130
+
131
+        share_in_use = fake_db_share1()[0]
132
+        share_not_in_use = fake_db_share2()[0]
133
+
134
+        self.assertTrue(
135
+            share_in_use['export_location'] in
136
+            self._driver.gluster_used_vols_dict)
137
+        self.assertFalse(
138
+            share_not_in_use['export_location'] in
139
+            self._driver.gluster_used_vols_dict)
140
+        self.assertTrue(
141
+            share_not_in_use['export_location'] in
142
+            self._driver.gluster_unused_vols_dict)
143
+        self.assertFalse(
144
+            share_in_use['export_location'] in
145
+            self._driver.gluster_unused_vols_dict)
146
+
147
+    def test_setup_gluster_vols(self):
148
+        self._driver._restart_gluster_vol = mock.Mock()
149
+
150
+        gaddr = glusterfs.GlusterAddress
151
+        gaddr1 = gaddr(self.gluster_target1)
152
+        gaddr2 = gaddr(self.gluster_target2)
153
+
154
+        self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
155
+        self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
156
+
157
+        expected_exec = [
158
+            'ssh root@host2 gluster volume set gv2 nfs.export-volumes off',
159
+            'ssh root@host2 gluster volume set gv2 client.ssl on',
160
+            'ssh root@host2 gluster volume set gv2 server.ssl on']
161
+
162
+        self._driver._setup_gluster_vols()
163
+
164
+        self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
165
+        self.assertTrue(self._driver._restart_gluster_vol.called)
166
+
167
+    def test_setup_gluster_vols_excp1(self):
168
+        self._driver._restart_gluster_vol = mock.Mock()
169
+
170
+        gaddr = glusterfs.GlusterAddress
171
+        gaddr1 = gaddr(self.gluster_target1)
172
+        gaddr2 = gaddr(self.gluster_target2)
173
+
174
+        self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
175
+        self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
176
+
177
+        def exec_runner(*ignore_args, **ignore_kwargs):
178
+            raise exception.ProcessExecutionError
179
+
180
+        expected_exec = [
181
+            'ssh root@host2 gluster volume set gv2 nfs.export-volumes off']
182
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
183
+
184
+        self.assertRaises(exception.GlusterfsException,
185
+                          self._driver._setup_gluster_vols)
186
+        self.assertFalse(self._driver._restart_gluster_vol.called)
187
+
188
+    def test_setup_gluster_vols_excp2(self):
189
+        self._driver._restart_gluster_vol = mock.Mock()
190
+
191
+        gaddr = glusterfs.GlusterAddress
192
+        gaddr1 = gaddr(self.gluster_target1)
193
+        gaddr2 = gaddr(self.gluster_target2)
194
+
195
+        self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
196
+        self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
197
+
198
+        def exec_runner(*ignore_args, **ignore_kwargs):
199
+            raise exception.ProcessExecutionError
200
+
201
+        expected_exec = [
202
+            'ssh root@host2 gluster volume set gv2 client.ssl on']
203
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
204
+
205
+        self.assertRaises(exception.GlusterfsException,
206
+                          self._driver._setup_gluster_vols)
207
+        self.assertFalse(self._driver._restart_gluster_vol.called)
208
+
209
+    def test_setup_gluster_vols_excp3(self):
210
+        self._driver._restart_gluster_vol = mock.Mock()
211
+
212
+        gaddr = glusterfs.GlusterAddress
213
+        gaddr1 = gaddr(self.gluster_target1)
214
+        gaddr2 = gaddr(self.gluster_target2)
215
+
216
+        self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
217
+        self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
218
+
219
+        def exec_runner(*ignore_args, **ignore_kwargs):
220
+            raise exception.ProcessExecutionError
221
+
222
+        expected_exec = [
223
+            'ssh root@host2 gluster volume set gv2 server.ssl on']
224
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
225
+
226
+        self.assertRaises(exception.GlusterfsException,
227
+                          self._driver._setup_gluster_vols)
228
+        self.assertFalse(self._driver._restart_gluster_vol.called)
229
+
230
+    def test_restart_gluster_vol(self):
231
+        gaddr = glusterfs.GlusterAddress
232
+        gaddr1 = gaddr(self.gluster_target1)
233
+
234
+        expected_exec = [
235
+            'ssh root@host1 gluster volume stop gv1 --mode=script',
236
+            'ssh root@host1 gluster volume start gv1']
237
+
238
+        self._driver._restart_gluster_vol(gaddr1)
239
+        self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
240
+
241
+    def test_restart_gluster_vol_excp1(self):
242
+        gaddr = glusterfs.GlusterAddress
243
+        gaddr1 = gaddr(self.gluster_target1)
244
+
245
+        def exec_runner(*ignore_args, **ignore_kwargs):
246
+            raise exception.ProcessExecutionError
247
+
248
+        expected_exec = [
249
+            'ssh root@host1 gluster volume stop gv1 --mode=script']
250
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
251
+
252
+        self.assertRaises(exception.GlusterfsException,
253
+                          self._driver._restart_gluster_vol, gaddr1)
254
+
255
+    def test_restart_gluster_vol_excp2(self):
256
+        gaddr = glusterfs.GlusterAddress
257
+        gaddr1 = gaddr(self.gluster_target1)
258
+
259
+        def exec_runner(*ignore_args, **ignore_kwargs):
260
+            raise exception.ProcessExecutionError
261
+
262
+        expected_exec = [
263
+            'ssh root@host1 gluster volume start gv1']
264
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
265
+
266
+        self.assertRaises(exception.GlusterfsException,
267
+                          self._driver._restart_gluster_vol, gaddr1)
268
+
269
+    def test_pop_gluster_vol(self):
270
+        gaddr = glusterfs.GlusterAddress
271
+        gaddr1 = gaddr(self.gluster_target1)
272
+        gaddr2 = gaddr(self.gluster_target2)
273
+
274
+        self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
275
+        self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
276
+
277
+        exp_locn = self._driver._pop_gluster_vol()
278
+
279
+        self.assertEqual(0, len(self._driver.gluster_unused_vols_dict))
280
+        self.assertFalse(
281
+            gaddr2.export in self._driver.gluster_unused_vols_dict)
282
+        self.assertEqual(2, len(self._driver.gluster_used_vols_dict))
283
+        self.assertTrue(
284
+            gaddr2.export in self._driver.gluster_used_vols_dict)
285
+        self.assertEqual(exp_locn, gaddr2.export)
286
+
287
+    def test_pop_gluster_vol_excp(self):
288
+        gaddr = glusterfs.GlusterAddress
289
+        gaddr1 = gaddr(self.gluster_target1)
290
+        gaddr2 = gaddr(self.gluster_target2)
291
+
292
+        self._driver.gluster_used_vols_dict = {
293
+            gaddr2.export: gaddr2, gaddr1.export: gaddr1}
294
+        self._driver.gluster_unused_vols_dict = {}
295
+
296
+        self.assertRaises(exception.GlusterfsException,
297
+                          self._driver._pop_gluster_vol)
298
+
299
+    def test_push_gluster_vol(self):
300
+        gaddr = glusterfs.GlusterAddress
301
+        gaddr1 = gaddr(self.gluster_target1)
302
+        gaddr2 = gaddr(self.gluster_target2)
303
+
304
+        self._driver.gluster_used_vols_dict = {
305
+            gaddr1.export: gaddr1, gaddr2.export: gaddr2}
306
+        self._driver.gluster_unused_vols_dict = {}
307
+
308
+        self._driver._push_gluster_vol(gaddr2.export)
309
+
310
+        self.assertEqual(1, len(self._driver.gluster_unused_vols_dict))
311
+        self.assertTrue(
312
+            gaddr2.export in self._driver.gluster_unused_vols_dict)
313
+        self.assertEqual(1, len(self._driver.gluster_used_vols_dict))
314
+        self.assertFalse(
315
+            gaddr2.export in self._driver.gluster_used_vols_dict)
316
+
317
+    def test_push_gluster_vol_excp(self):
318
+        gaddr = glusterfs.GlusterAddress
319
+        gaddr1 = gaddr(self.gluster_target1)
320
+        gaddr2 = gaddr(self.gluster_target2)
321
+
322
+        self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
323
+        self._driver.gluster_unused_vols_dict = {}
324
+
325
+        self.assertRaises(exception.GlusterfsException,
326
+                          self._driver._push_gluster_vol, gaddr2.export)
327
+
328
+    def test_do_mount(self):
329
+        gaddr = glusterfs.GlusterAddress
330
+        gaddr1 = gaddr(self.gluster_target1)
331
+        tmpdir = '/tmp/tmpKGHKJ'
332
+        expected_exec = ['mount -t glusterfs host1:/gv1 /tmp/tmpKGHKJ']
333
+
334
+        self._driver._do_mount(gaddr1.export, tmpdir)
335
+
336
+        self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
337
+
338
+    def test_do_mount_excp(self):
339
+        def exec_runner(*ignore_args, **ignore_kwargs):
340
+            raise exception.ProcessExecutionError
341
+
342
+        gaddr = glusterfs.GlusterAddress
343
+        gaddr1 = gaddr(self.gluster_target1)
344
+        tmpdir = '/tmp/tmpKGHKJ'
345
+        expected_exec = ['mount -t glusterfs host1:/gv1 /tmp/tmpKGHKJ']
346
+
347
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
348
+
349
+        self.assertRaises(exception.GlusterfsException,
350
+                          self._driver._do_mount, gaddr1.export, tmpdir)
351
+
352
+    def test_do_umount(self):
353
+        tmpdir = '/tmp/tmpKGHKJ'
354
+        expected_exec = ['umount /tmp/tmpKGHKJ']
355
+
356
+        self._driver._do_umount(tmpdir)
357
+
358
+        self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
359
+
360
+    def test_do_umount_excp(self):
361
+        def exec_runner(*ignore_args, **ignore_kwargs):
362
+            raise exception.ProcessExecutionError
363
+
364
+        tmpdir = '/tmp/tmpKGHKJ'
365
+        expected_exec = ['umount /tmp/tmpKGHKJ']
366
+
367
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
368
+
369
+        self.assertRaises(exception.GlusterfsException,
370
+                          self._driver._do_umount, tmpdir)
371
+
372
+    def test_wipe_gluster_vol(self):
373
+        self._driver._restart_gluster_vol = mock.Mock()
374
+        self._driver._do_mount = mock.Mock()
375
+        self._driver._do_umount = mock.Mock()
376
+        shutil.rmtree = mock.Mock()
377
+
378
+        gaddr = glusterfs.GlusterAddress
379
+        gaddr1 = gaddr(self.gluster_target1)
380
+
381
+        expected_exec = [
382
+            'ssh root@host1 gluster volume set gv1 client.ssl off',
383
+            'ssh root@host1 gluster volume set gv1 server.ssl off',
384
+            'find /tmp/tmpKGHKJ -mindepth 1 -delete',
385
+            'ssh root@host1 gluster volume set gv1 client.ssl on',
386
+            'ssh root@host1 gluster volume set gv1 server.ssl on']
387
+
388
+        self._driver._wipe_gluster_vol(gaddr1)
389
+
390
+        self.assertEqual(2, self._driver._restart_gluster_vol.call_count)
391
+        self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
392
+        self.assertTrue(tempfile.mkdtemp.called)
393
+        self.assertTrue(self._driver._do_mount.called)
394
+        self.assertTrue(self._driver._do_umount.called)
395
+        self.assertTrue(shutil.rmtree.called)
396
+
397
+    def test_wipe_gluster_vol_excp1(self):
398
+        self._driver._restart_gluster_vol = mock.Mock()
399
+        self._driver._do_mount = mock.Mock()
400
+        self._driver._do_umount = mock.Mock()
401
+        shutil.rmtree = mock.Mock()
402
+
403
+        gaddr = glusterfs.GlusterAddress
404
+        gaddr1 = gaddr(self.gluster_target1)
405
+
406
+        def exec_runner(*ignore_args, **ignore_kwargs):
407
+            raise exception.ProcessExecutionError
408
+
409
+        expected_exec = [
410
+            'ssh root@host1 gluster volume set gv1 client.ssl off']
411
+
412
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
413
+
414
+        self.assertRaises(exception.GlusterfsException,
415
+                          self._driver._wipe_gluster_vol, gaddr1)
416
+
417
+        self.assertFalse(self._driver._restart_gluster_vol.called)
418
+        self.assertFalse(tempfile.mkdtemp.called)
419
+        self.assertFalse(self._driver._do_mount.called)
420
+        self.assertFalse(self._driver._do_umount.called)
421
+        self.assertFalse(shutil.rmtree.called)
422
+
423
+    def test_wipe_gluster_vol_excp2(self):
424
+        self._driver._restart_gluster_vol = mock.Mock()
425
+        self._driver._do_mount = mock.Mock()
426
+        self._driver._do_umount = mock.Mock()
427
+        shutil.rmtree = mock.Mock()
428
+
429
+        gaddr = glusterfs.GlusterAddress
430
+        gaddr1 = gaddr(self.gluster_target1)
431
+
432
+        def exec_runner(*ignore_args, **ignore_kwargs):
433
+            raise exception.ProcessExecutionError
434
+
435
+        expected_exec = [
436
+            'ssh root@host1 gluster volume set gv1 server.ssl off']
437
+
438
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
439
+
440
+        self.assertRaises(exception.GlusterfsException,
441
+                          self._driver._wipe_gluster_vol, gaddr1)
442
+
443
+        self.assertFalse(self._driver._restart_gluster_vol.called)
444
+        self.assertFalse(tempfile.mkdtemp.called)
445
+        self.assertFalse(self._driver._do_mount.called)
446
+        self.assertFalse(self._driver._do_umount.called)
447
+        self.assertFalse(shutil.rmtree.called)
448
+
449
+    def test_wipe_gluster_vol_excp3(self):
450
+        self._driver._restart_gluster_vol = mock.Mock()
451
+        self._driver._do_mount = mock.Mock()
452
+        self._driver._do_umount = mock.Mock()
453
+        shutil.rmtree = mock.Mock()
454
+
455
+        gaddr = glusterfs.GlusterAddress
456
+        gaddr1 = gaddr(self.gluster_target1)
457
+
458
+        def exec_runner(*ignore_args, **ignore_kwargs):
459
+            raise exception.ProcessExecutionError
460
+
461
+        expected_exec = [
462
+            'ssh root@host1 gluster volume set gv1 client.ssl on']
463
+
464
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
465
+
466
+        self.assertRaises(exception.GlusterfsException,
467
+                          self._driver._wipe_gluster_vol, gaddr1)
468
+
469
+        self.assertTrue(self._driver._restart_gluster_vol.called)
470
+        self.assertTrue(tempfile.mkdtemp.called)
471
+        self.assertTrue(self._driver._do_mount.called)
472
+        self.assertTrue(self._driver._do_umount.called)
473
+        self.assertTrue(shutil.rmtree.called)
474
+
475
+    def test_wipe_gluster_vol_excp4(self):
476
+        self._driver._restart_gluster_vol = mock.Mock()
477
+        self._driver._do_mount = mock.Mock()
478
+        self._driver._do_umount = mock.Mock()
479
+        shutil.rmtree = mock.Mock()
480
+
481
+        gaddr = glusterfs.GlusterAddress
482
+        gaddr1 = gaddr(self.gluster_target1)
483
+
484
+        def exec_runner(*ignore_args, **ignore_kwargs):
485
+            raise exception.ProcessExecutionError
486
+
487
+        expected_exec = [
488
+            'ssh root@host1 gluster volume set gv1 server.ssl on']
489
+
490
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
491
+
492
+        self.assertRaises(exception.GlusterfsException,
493
+                          self._driver._wipe_gluster_vol, gaddr1)
494
+
495
+        self.assertTrue(self._driver._restart_gluster_vol.called)
496
+        self.assertTrue(tempfile.mkdtemp.called)
497
+        self.assertTrue(self._driver._do_mount.called)
498
+        self.assertTrue(self._driver._do_umount.called)
499
+        self.assertTrue(shutil.rmtree.called)
500
+
501
+    def test_wipe_gluster_vol_excp5(self):
502
+        self._driver._restart_gluster_vol = mock.Mock()
503
+        self._driver._do_mount = mock.Mock()
504
+        self._driver._do_umount = mock.Mock()
505
+        shutil.rmtree = mock.Mock()
506
+
507
+        gaddr = glusterfs.GlusterAddress
508
+        gaddr1 = gaddr(self.gluster_target1)
509
+
510
+        def exec_runner(*ignore_args, **ignore_kwargs):
511
+            raise exception.ProcessExecutionError
512
+
513
+        expected_exec = [
514
+            'find /tmp/tmpKGHKJ -mindepth 1 -delete']
515
+
516
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
517
+
518
+        self.assertRaises(exception.GlusterfsException,
519
+                          self._driver._wipe_gluster_vol, gaddr1)
520
+
521
+        self.assertTrue(self._driver._restart_gluster_vol.called)
522
+        self.assertTrue(tempfile.mkdtemp.called)
523
+        self.assertTrue(self._driver._do_mount.called)
524
+        self.assertTrue(self._driver._do_umount.called)
525
+        self.assertTrue(shutil.rmtree.called)
526
+
527
+    def test_wipe_gluster_vol_mount_fail(self):
528
+        self._driver._restart_gluster_vol = mock.Mock()
529
+        self._driver._do_mount = mock.Mock()
530
+        self._driver._do_mount.side_effect = exception.GlusterfsException
531
+        self._driver._do_umount = mock.Mock()
532
+        shutil.rmtree = mock.Mock()
533
+
534
+        gaddr = glusterfs.GlusterAddress
535
+        gaddr1 = gaddr(self.gluster_target1)
536
+
537
+        expected_exec = [
538
+            'ssh root@host1 gluster volume set gv1 client.ssl off',
539
+            'ssh root@host1 gluster volume set gv1 server.ssl off']
540
+
541
+        self.assertRaises(exception.GlusterfsException,
542
+                          self._driver._wipe_gluster_vol, gaddr1)
543
+
544
+        self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
545
+        self.assertTrue(self._driver._restart_gluster_vol.called)
546
+        self.assertTrue(tempfile.mkdtemp.called)
547
+        self.assertTrue(self._driver._do_mount.called)
548
+        self.assertFalse(self._driver._do_umount.called)
549
+        self.assertTrue(shutil.rmtree.called)
550
+
551
+    def test_wipe_gluster_vol_umount_fail(self):
552
+        self._driver._restart_gluster_vol = mock.Mock()
553
+        self._driver._do_mount = mock.Mock()
554
+        self._driver._do_umount = mock.Mock()
555
+        self._driver._do_umount.side_effect = exception.GlusterfsException
556
+        shutil.rmtree = mock.Mock()
557
+
558
+        gaddr = glusterfs.GlusterAddress
559
+        gaddr1 = gaddr(self.gluster_target1)
560
+
561
+        expected_exec = [
562
+            'ssh root@host1 gluster volume set gv1 client.ssl off',
563
+            'ssh root@host1 gluster volume set gv1 server.ssl off',
564
+            'find /tmp/tmpKGHKJ -mindepth 1 -delete']
565
+
566
+        self.assertRaises(exception.GlusterfsException,
567
+                          self._driver._wipe_gluster_vol, gaddr1)
568
+
569
+        self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
570
+        self.assertTrue(self._driver._restart_gluster_vol.called)
571
+        self.assertTrue(tempfile.mkdtemp.called)
572
+        self.assertTrue(self._driver._do_mount.called)
573
+        self.assertTrue(self._driver._do_umount.called)
574
+        self.assertFalse(shutil.rmtree.called)
575
+
576
+    def test_create_share(self):
577
+        gaddr = glusterfs.GlusterAddress
578
+        gaddr1 = gaddr(self.gluster_target1)
579
+        gaddr2 = gaddr(self.gluster_target2)
580
+
581
+        self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
582
+        self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
583
+
584
+        share = new_share()
585
+
586
+        exp_locn = self._driver.create_share(self._context, share)
587
+
588
+        self.assertEqual(exp_locn, gaddr2.export)
589
+
590
+    def test_create_share_excp(self):
591
+        gaddr = glusterfs.GlusterAddress
592
+        gaddr1 = gaddr(self.gluster_target1)
593
+        gaddr2 = gaddr(self.gluster_target2)
594
+
595
+        self._driver.gluster_used_vols_dict = {
596
+            gaddr2.export: gaddr2, gaddr1.export: gaddr1}
597
+        self._driver.gluster_unused_vols_dict = {}
598
+
599
+        share = new_share()
600
+
601
+        self.assertRaises(exception.GlusterfsException,
602
+                          self._driver.create_share, self._context, share)
603
+
604
+    def test_delete_share(self):
605
+        self._driver._wipe_gluster_vol = mock.Mock()
606
+
607
+        gaddr = glusterfs.GlusterAddress
608
+        gaddr1 = gaddr(self.gluster_target1)
609
+        gaddr2 = gaddr(self.gluster_target2)
610
+
611
+        self._driver.gluster_used_vols_dict = {
612
+            gaddr2.export: gaddr2, gaddr1.export: gaddr1}
613
+        self._driver.gluster_unused_vols_dict = {}
614
+
615
+        share = fake_db_share2()[0]
616
+
617
+        self._driver.delete_share(self._context, share)
618
+
619
+        self.assertEqual(1, len(self._driver.gluster_used_vols_dict))
620
+        self.assertEqual(1, len(self._driver.gluster_unused_vols_dict))
621
+        self.assertTrue(self._driver._wipe_gluster_vol.called)
622
+
623
+    def test_delete_share_warn(self):
624
+        glusterfs_native.LOG.warn = mock.Mock()
625
+        self._driver._wipe_gluster_vol = mock.Mock()
626
+        self._driver._push_gluster_vol = mock.Mock()
627
+
628
+        gaddr = glusterfs.GlusterAddress
629
+        gaddr1 = gaddr(self.gluster_target1)
630
+        gaddr2 = gaddr(self.gluster_target2)
631
+
632
+        self._driver.gluster_used_vols_dict = {}
633
+        self._driver.gluster_unused_vols_dict = {
634
+            gaddr2.export: gaddr2, gaddr1.export: gaddr1}
635
+
636
+        share = fake_db_share2()[0]
637
+
638
+        self._driver.delete_share(self._context, share)
639
+
640
+        self.assertTrue(glusterfs_native.LOG.warn.called)
641
+        self.assertFalse(self._driver._wipe_gluster_vol.called)
642
+        self.assertFalse(self._driver._push_gluster_vol.called)
643
+
644
+    def test_delete_share_excp1(self):
645
+        self._driver._wipe_gluster_vol = mock.Mock()
646
+        self._driver._wipe_gluster_vol.side_effect = (
647
+            exception.GlusterfsException)
648
+        self._driver._push_gluster_vol = mock.Mock()
649
+
650
+        gaddr = glusterfs.GlusterAddress
651
+        gaddr1 = gaddr(self.gluster_target1)
652
+        gaddr2 = gaddr(self.gluster_target2)
653
+
654
+        self._driver.gluster_used_vols_dict = {
655
+            gaddr2.export: gaddr2, gaddr1.export: gaddr1}
656
+        self._driver.gluster_unused_vols_dict = {}
657
+
658
+        share = fake_db_share2()[0]
659
+
660
+        self.assertRaises(exception.GlusterfsException,
661
+                          self._driver.delete_share, self._context, share)
662
+
663
+        self.assertTrue(self._driver._wipe_gluster_vol.called)
664
+        self.assertFalse(self._driver._push_gluster_vol.called)
665
+
666
+    def test_allow_access(self):
667
+        self._driver._restart_gluster_vol = mock.Mock()
668
+        access = {'access_type': 'cert', 'access_to': 'client.example.com'}
669
+        gaddr = glusterfs.GlusterAddress
670
+        gaddr1 = gaddr(self.gluster_target1)
671
+        gaddr2 = gaddr(self.gluster_target2)
672
+
673
+        self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
674
+        self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
675
+
676
+        share = fake_db_share1()[0]
677
+
678
+        expected_exec = [
679
+            'ssh root@host1 gluster volume set gv1 '
680
+            'auth.ssl-allow client.example.com']
681
+
682
+        self._driver.allow_access(self._context, share, access)
683
+        self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
684
+        self.assertTrue(self._driver._restart_gluster_vol.called)
685
+
686
+    def test_allow_access_invalid_access_type(self):
687
+        self._driver._restart_gluster_vol = mock.Mock()
688
+        access = {'access_type': 'invalid', 'access_to': 'client.example.com'}
689
+        share = fake_db_share1()[0]
690
+        expected_exec = []
691
+
692
+        self.assertRaises(exception.InvalidShareAccess,
693
+                          self._driver.allow_access,
694
+                          self._context, share, access)
695
+        self.assertFalse(self._driver._restart_gluster_vol.called)
696
+        self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
697
+
698
+    def test_allow_access_excp(self):
699
+        self._driver._restart_gluster_vol = mock.Mock()
700
+        access = {'access_type': 'cert', 'access_to': 'client.example.com'}
701
+        gaddr = glusterfs.GlusterAddress
702
+        gaddr1 = gaddr(self.gluster_target1)
703
+        gaddr2 = gaddr(self.gluster_target2)
704
+
705
+        self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
706
+        self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
707
+
708
+        share = fake_db_share1()[0]
709
+
710
+        def exec_runner(*ignore_args, **ignore_kwargs):
711
+            raise exception.ProcessExecutionError
712
+
713
+        expected_exec = [
714
+            'ssh root@host1 gluster volume set gv1 '
715
+            'auth.ssl-allow client.example.com']
716
+
717
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
718
+
719
+        self.assertRaises(exception.GlusterfsException,
720
+                          self._driver.allow_access,
721
+                          self._context, share, access)
722
+        self.assertFalse(self._driver._restart_gluster_vol.called)
723
+
724
+    def test_deny_access(self):
725
+        self._driver._restart_gluster_vol = mock.Mock()
726
+        access = {'access_type': 'cert', 'access_to': 'NotApplicable'}
727
+        gaddr = glusterfs.GlusterAddress
728
+        gaddr1 = gaddr(self.gluster_target1)
729
+        gaddr2 = gaddr(self.gluster_target2)
730
+
731
+        self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
732
+        self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
733
+
734
+        share = fake_db_share1()[0]
735
+
736
+        expected_exec = [
737
+            'ssh root@host1 gluster volume reset gv1 auth.ssl-allow']
738
+
739
+        self._driver.deny_access(self._context, share, access)
740
+        self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
741
+        self.assertTrue(self._driver._restart_gluster_vol.called)
742
+
743
+    def test_deny_access_invalid_access_type(self):
744
+        self._driver._restart_gluster_vol = mock.Mock()
745
+        access = {'access_type': 'invalid', 'access_to': 'NotApplicable'}
746
+        share = fake_db_share1()[0]
747
+        expected_exec = []
748
+
749
+        self.assertRaises(exception.InvalidShareAccess,
750
+                          self._driver.deny_access,
751
+                          self._context, share, access)
752
+        self.assertFalse(self._driver._restart_gluster_vol.called)
753
+        self.assertEqual(expected_exec, fake_utils.fake_execute_get_log())
754
+
755
+    def test_deny_access_excp(self):
756
+        self._driver._restart_gluster_vol = mock.Mock()
757
+        access = {'access_type': 'cert', 'access_to': 'NotApplicable'}
758
+        gaddr = glusterfs.GlusterAddress
759
+        gaddr1 = gaddr(self.gluster_target1)
760
+        gaddr2 = gaddr(self.gluster_target2)
761
+
762
+        self._driver.gluster_used_vols_dict = {gaddr1.export: gaddr1}
763
+        self._driver.gluster_unused_vols_dict = {gaddr2.export: gaddr2}
764
+
765
+        share = fake_db_share1()[0]
766
+
767
+        expected_exec = [
768
+            'ssh root@host1 gluster volume reset gv1 auth.ssl-allow']
769
+
770
+        def exec_runner(*ignore_args, **ignore_kwargs):
771
+            raise exception.ProcessExecutionError
772
+
773
+        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
774
+
775
+        self.assertRaises(exception.GlusterfsException,
776
+                          self._driver.deny_access,
777
+                          self._context, share, access)
778
+        self.assertFalse(self._driver._restart_gluster_vol.called)
779
+
780
+    def test_get_share_stats_refresh_false(self):
781
+        self._driver._stats = mock.Mock()
782
+        ret = self._driver.get_share_stats()
783
+        self.assertEqual(ret, self._driver._stats)
784
+
785
+    def test_get_share_stats_refresh_true(self):
786
+        def foo():
787
+            self._driver._stats = {'key': 'value'}
788
+        self._driver._update_share_stats = mock.Mock(side_effect=foo)
789
+        ret = self._driver.get_share_stats(refresh=True)
790
+        self.assertEqual(ret, {'key': 'value'})
791
+
792
+    def test_update_share_stats(self):
793
+        test_data = {
794
+            'share_backend_name': 'GlusterFS-Native',
795
+            'vendor_name': 'Red Hat',
796
+            'driver_version': '1.1',
797
+            'storage_protocol': 'glusterfs',
798
+            'reserved_percentage': 0,
799
+            'QoS_support': False,
800
+            'total_capacity_gb': 'infinite',
801
+            'free_capacity_gb': 'infinite',
802
+        }
803
+
804
+        self._driver._update_share_stats()
805
+
806
+        self.assertEqual(self._driver._stats, test_data)

+ 0
- 199
manila/tests/test_share_glusterfs_native.py View File

@@ -1,199 +0,0 @@
1
-# Copyright (c) 2014 Red Hat, Inc.
2
-# All Rights Reserved.
3
-#
4
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
-#    not use this file except in compliance with the License. You may obtain
6
-#    a copy of the License at
7
-#
8
-#         http://www.apache.org/licenses/LICENSE-2.0
9
-#
10
-#    Unless required by applicable law or agreed to in writing, software
11
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
-#    License for the specific language governing permissions and limitations
14
-#    under the License.
15
-
16
-""" GlusterFS native protocol (glusterfs) driver for shares.
17
-
18
-Test cases for GlusterFS native protocol driver.
19
-"""
20
-
21
-
22
-import mock
23
-from oslo.config import cfg
24
-
25
-from manila import context
26
-from manila import exception
27
-from manila.share import configuration as config
28
-from manila.share.drivers import glusterfs_native
29
-from manila import test
30
-from manila.tests.db import fakes as db_fakes
31
-from manila.tests import fake_utils
32
-
33
-
34
-CONF = cfg.CONF
35
-
36
-
37
-gluster_address_attrs = {
38
-    'export': '127.0.0.1:/testvol',
39
-    'host': '127.0.0.1',
40
-    'qualified': 'testuser@127.0.0.1:/testvol',
41
-    'remote_user': 'testuser',
42
-    'volume': 'testvol',
43
-}
44
-
45
-
46
-def fake_share(**kwargs):
47
-    share = {
48
-        'id': 'fakeid',
49
-        'name': 'fakename',
50
-        'size': 1,
51
-        'share_proto': 'glusterfs',
52
-        'export_location': '127.0.0.1:/mnt/glusterfs/testvol',
53
-    }
54
-    share.update(kwargs)
55
-    return db_fakes.FakeModel(share)
56
-
57
-
58
-class GlusterfsNativeShareDriverTestCase(test.TestCase):
59
-    """Tests GlusterfsNativeShareDriver."""
60
-
61
-    def setUp(self):
62
-        super(GlusterfsNativeShareDriverTestCase, self).setUp()
63
-        fake_utils.stub_out_utils_execute(self.stubs)
64
-        self._execute = fake_utils.fake_execute
65
-        self._context = context.get_admin_context()
66
-
67
-        CONF.set_default('glusterfs_mount_point_base', '/mnt/glusterfs')
68
-        CONF.set_default('reserved_share_percentage', 50)
69
-
70
-        self.fake_conf = config.Configuration(None)
71
-        self._db = mock.Mock()
72
-        self._driver = glusterfs_native.GlusterfsNativeShareDriver(
73
-            self._db, execute=self._execute,
74
-            configuration=self.fake_conf)
75
-        self._driver.gluster_address = mock.Mock(**gluster_address_attrs)
76
-        self.share = fake_share()
77
-
78
-        self.addCleanup(fake_utils.fake_execute_set_repliers, [])
79
-        self.addCleanup(fake_utils.fake_execute_clear_log)
80
-
81
-    def test_create_share(self):
82
-        self._driver._setup_gluster_vol = mock.Mock()
83
-
84
-        expected = gluster_address_attrs['export']
85
-        actual = self._driver.create_share(self._context, self.share)
86
-
87
-        self.assertTrue(self._driver._setup_gluster_vol.called)
88
-        self.assertEqual(actual, expected)
89
-
90
-    def test_create_share_error(self):
91
-        self._driver._setup_gluster_vol = mock.Mock()
92
-        self._driver._setup_gluster_vol.side_effect = (
93
-            exception.ProcessExecutionError)
94
-
95
-        self.assertRaises(exception.ProcessExecutionError,
96
-                          self._driver.create_share, self._context, self.share)
97
-
98
-    def test_delete_share(self):
99
-        self._driver.gluster_address = mock.Mock(
100
-            make_gluster_args=mock.Mock(return_value=(('true',), {})))
101
-
102
-        self._driver.delete_share(self._context, self.share)
103
-
104
-        self.assertTrue(self._driver.gluster_address.make_gluster_args.called)
105
-        self.assertEqual(
106
-            self._driver.gluster_address.make_gluster_args.call_args[0][1],
107
-            'reset')
108
-
109
-    def test_delete_share_error(self):
110
-        self._driver.gluster_address = mock.Mock(
111
-            make_gluster_args=mock.Mock(return_value=(('true',), {})))
112
-
113
-        def exec_runner(*ignore_args, **ignore_kw):
114
-            raise exception.ProcessExecutionError
115
-
116
-        expected_exec = ['true']
117
-        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
118
-
119
-        self.assertRaises(exception.ProcessExecutionError,
120
-                          self._driver.delete_share, self._context, self.share)
121
-
122
-    def test_allow_access(self):
123
-        self._driver.gluster_address = mock.Mock(
124
-            make_gluster_args=mock.Mock(return_value=(('true',), {})))
125
-        access = {'access_type': 'cert', 'access_to': 'client.example.com'}
126
-
127
-        self._driver.allow_access(self._context, self.share, access)
128
-
129
-        self.assertTrue(self._driver.gluster_address.make_gluster_args.called)
130
-        self.assertEqual(
131
-            self._driver.gluster_address.make_gluster_args.call_args[0][1],
132
-            'set')
133
-        self.assertEqual(
134
-            self._driver.gluster_address.make_gluster_args.call_args[0][-2],
135
-            'auth.ssl-allow')
136
-        self.assertEqual(
137
-            self._driver.gluster_address.make_gluster_args.call_args[0][-1],
138
-            access['access_to'])
139
-
140
-    def test_allow_access_error(self):
141
-        # Invalid access type
142
-        access = {'access_type': 'invalid', 'access_to': 'client.example.com'}
143
-
144
-        self.assertRaises(exception.InvalidShareAccess,
145
-                          self._driver.allow_access, self._context, self.share,
146
-                          access)
147
-
148
-        # ProcessExecutionError
149
-        self._driver.gluster_address = mock.Mock(
150
-            make_gluster_args=mock.Mock(return_value=(('true',), {})))
151
-        access = {'access_type': 'cert', 'access_to': 'client.example.com'}
152
-
153
-        def exec_runner(*ignore_args, **ignore_kw):
154
-            raise exception.ProcessExecutionError
155
-
156
-        expected_exec = ['true']
157
-        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
158
-
159
-        self.assertRaises(exception.ProcessExecutionError,
160
-                          self._driver.allow_access, self._context, self.share,
161
-                          access)
162
-
163
-    def test_deny_access(self):
164
-        self._driver.gluster_address = mock.Mock(
165
-            make_gluster_args=mock.Mock(return_value=(('true',), {})))
166
-        access = {'access_type': 'cert', 'access_to': 'client.example.com'}
167
-
168
-        self._driver.deny_access(self._context, self.share, access)
169
-
170
-        self.assertTrue(self._driver.gluster_address.make_gluster_args.called)
171
-        self.assertEqual(
172
-            self._driver.gluster_address.make_gluster_args.call_args[0][1],
173
-            'reset')
174
-        self.assertEqual(
175
-            self._driver.gluster_address.make_gluster_args.call_args[0][-1],
176
-            'auth.ssl-allow')
177
-
178
-    def test_deny_access_error(self):
179
-        # Invalid access type
180
-        access = {'access_type': 'invalid', 'access_to': 'client.example.com'}
181
-
182
-        self.assertRaises(exception.InvalidShareAccess,
183
-                          self._driver.deny_access, self._context, self.share,
184
-                          access)
185
-
186
-        # ProcessExecutionError
187
-        self._driver.gluster_address = mock.Mock(
188
-            make_gluster_args=mock.Mock(return_value=(('true',), {})))
189
-        access = {'access_type': 'cert', 'access_to': 'client.example.com'}
190
-
191
-        def exec_runner(*ignore_args, **ignore_kw):
192
-            raise exception.ProcessExecutionError
193
-
194
-        expected_exec = ['true']
195
-        fake_utils.fake_execute_set_repliers([(expected_exec[0], exec_runner)])
196
-
197
-        self.assertRaises(exception.ProcessExecutionError,
198
-                          self._driver.deny_access, self._context, self.share,
199
-                          access)

Loading…
Cancel
Save