diff options
author | Jenkins <jenkins@review.openstack.org> | 2014-09-24 15:33:25 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2014-09-24 15:33:25 +0000 |
commit | 81d82d19afe8c45695f3b7dfffa7fe3a695d72f3 (patch) | |
tree | 66cd8af4562a35c8b0e970f830d9a241ca89916c | |
parent | d00bfb14c39107d2b196f72ee64ee12df8cad07d (diff) | |
parent | 10c3f8fb1f83eaa3a3c89f1842730e1365fe6938 (diff) |
Merge "Add support for working with multiple glusterfs volumes"2014.2.rc1
-rw-r--r-- | etc/manila/rootwrap.d/share.filters | 6 | ||||
-rw-r--r-- | manila/share/drivers/glusterfs_native.py | 491 | ||||
-rw-r--r-- | manila/tests/share/drivers/test_glusterfs_native.py | 806 | ||||
-rw-r--r-- | manila/tests/test_share_glusterfs_native.py | 199 |
4 files changed, 1236 insertions, 266 deletions
diff --git a/etc/manila/rootwrap.d/share.filters b/etc/manila/rootwrap.d/share.filters index 57f2a63..621583c 100644 --- a/etc/manila/rootwrap.d/share.filters +++ b/etc/manila/rootwrap.d/share.filters | |||
@@ -17,3 +17,9 @@ ip: CommandFilter, /sbin/ip, root | |||
17 | 17 | ||
18 | # manila/network/linux/interface.py: 'ovs-vsctl', 'add-port', '%s', '%s' | 18 | # manila/network/linux/interface.py: 'ovs-vsctl', 'add-port', '%s', '%s' |
19 | ovs-vsctl: CommandFilter, /usr/bin/ovs-vsctl, root | 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 | ||
diff --git a/manila/share/drivers/glusterfs_native.py b/manila/share/drivers/glusterfs_native.py index c4f5574..dba7c15 100644 --- a/manila/share/drivers/glusterfs_native.py +++ b/manila/share/drivers/glusterfs_native.py | |||
@@ -19,81 +19,376 @@ Manila share is a GlusterFS volume. Unlike the generic driver, this | |||
19 | does not use service VM approach. Instances directly talk with the | 19 | does not use service VM approach. Instances directly talk with the |
20 | GlusterFS backend storage pool. Instance use the 'glusterfs' protocol | 20 | GlusterFS backend storage pool. Instance use the 'glusterfs' protocol |
21 | to mount the GlusterFS share. Access to the share is allowed via | 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 | with the GlusterFS backend can mount and hence use the share. | 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 | from manila import exception | 36 | from manila import exception |
28 | from manila.openstack.common import log as logging | 37 | from manila.openstack.common import log as logging |
38 | from manila.share import driver | ||
29 | from manila.share.drivers import glusterfs | 39 | from manila.share.drivers import glusterfs |
40 | from manila import utils | ||
30 | 41 | ||
31 | 42 | ||
32 | LOG = logging.getLogger(__name__) | 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 | CLIENT_SSL = 'client.ssl' | 58 | CLIENT_SSL = 'client.ssl' |
59 | NFS_EXPORT_VOL = 'nfs.export-volumes' | ||
35 | SERVER_SSL = 'server.ssl' | 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): | 66 | Executes commands relating to Shares. |
43 | super(GlusterfsNativeShareDriver, self)._setup_gluster_vol() | 67 | Supports working with multiple glusterfs volumes. |
44 | 68 | ||
45 | # Enable gluster volume for SSL access. | 69 | API version history: |
46 | # This applies for both service mount and instance mount(s). | 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 | 105 | try: |
49 | # service mount to non-ssl access. | 106 | self._execute('mount.glusterfs', check_exit_code=False) |
50 | gargs, gkw = self.gluster_address.make_gluster_args( | 107 | except OSError as exc: |
51 | 'volume', 'set', self.gluster_address.volume, | 108 | if exc.errno == errno.ENOENT: |
52 | CLIENT_SSL, 'on') | 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 | try: | 219 | try: |
54 | self._execute(*gargs, **gkw) | 220 | self._execute(*gargs, **gkw) |
55 | except exception.ProcessExecutionError as exc: | 221 | except exception.ProcessExecutionError as exc: |
56 | LOG.error(_("Error in gluster volume set during volume setup." | 222 | msg = (_("Error stopping gluster volume. " |
57 | "Volume: %(volname)s, Option: %(option)s, " | 223 | "Volume: %(volname)s, Error: %(error)s"), |
58 | "Error: %(error)s"), | 224 | {'volname': gluster_addr.volume, 'error': exc.stderr}) |
59 | {'volname': self.gluster_address.volume, | 225 | LOG.error(msg) |
60 | 'option': CLIENT_SSL, 'error': exc.stderr}) | 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 | raise | 326 | raise |
62 | gargs, gkw = self.gluster_address.make_gluster_args( | 327 | |
63 | 'volume', 'set', self.gluster_address.volume, | 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 | SERVER_SSL, 'on') | 360 | SERVER_SSL, 'on') |
65 | try: | 361 | try: |
66 | self._execute(*gargs, **gkw) | 362 | self._execute(*gargs, **gkw) |
67 | except exception.ProcessExecutionError as exc: | 363 | except exception.ProcessExecutionError as exc: |
68 | LOG.error(_("Error in gluster volume set during volume setup." | 364 | msg = (_("Error in gluster volume set during _wipe_gluster_vol. " |
69 | "Volume: %(volname)s, Option: %(option)s, " | 365 | "Volume: %(volname)s, Option: %(option)s, " |
70 | "Error: %(error)s"), | 366 | "Error: %(error)s"), |
71 | {'volname': self.gluster_address.volume, | 367 | {'volname': gluster_addr.volume, |
72 | 'option': SERVER_SSL, 'error': exc.stderr}) | 368 | 'option': SERVER_SSL, 'error': exc.stderr}) |
73 | raise | 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 | """Create a share using GlusterFS volume. | 375 | """Create a share using GlusterFS volume. |
77 | 376 | ||
78 | 1 Manila share = 1 GlusterFS volume. Ensure that the | 377 | 1 Manila share = 1 GlusterFS volume. Pick an unused |
79 | GlusterFS volume is properly setup to be consumed as | 378 | GlusterFS volume for use as a share. |
80 | a share. | ||
81 | """ | 379 | """ |
82 | |||
83 | # Handle the case where create is called after delete share | ||
84 | try: | 380 | try: |
85 | self._setup_gluster_vol() | 381 | export_location = self._pop_gluster_vol() |
86 | except exception.ProcessExecutionError: | 382 | except exception.GlusterfsException: |
87 | LOG.error(_("Unable to create share %s"), (share['name'],)) | 383 | msg = (_("Error creating share %(share_id)s"), |
384 | {'share_id': share['id']}) | ||
385 | LOG.error(msg) | ||
88 | raise | 386 | raise |
89 | 387 | ||
90 | # TODO(deepakcs): Add validation for gluster mount being present | 388 | # TODO(deepakcs): Enable quota and set it to the share size. |
91 | # (decorator maybe) | ||
92 | 389 | ||
93 | # For native protocol, the export_location should be of the form: | 390 | # For native protocol, the export_location should be of the form: |
94 | # server:/volname | 391 | # server:/volname |
95 | export_location = self.gluster_address.export | ||
96 | |||
97 | LOG.info(_("export_location sent back from create_share: %s"), | 392 | LOG.info(_("export_location sent back from create_share: %s"), |
98 | (export_location,)) | 393 | (export_location,)) |
99 | return export_location | 394 | return export_location |
@@ -101,22 +396,30 @@ class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver): | |||
101 | def delete_share(self, context, share, share_server=None): | 396 | def delete_share(self, context, share, share_server=None): |
102 | """Delete a share on the GlusterFS volume. | 397 | """Delete a share on the GlusterFS volume. |
103 | 398 | ||
104 | 1 Manila share = 1 GlusterFS volume. Ensure that the | 399 | 1 Manila share = 1 GlusterFS volume. Put the gluster |
105 | GlusterFS volume is reset back to its original state. | 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 | try: | 412 | try: |
112 | self._execute(*gargs, **gkw) | 413 | self._wipe_gluster_vol(gaddr) |
113 | except exception.ProcessExecutionError as exc: | 414 | self._push_gluster_vol(exp_locn) |
114 | LOG.error(_("Error in gluster volume reset during delete share." | 415 | except exception.GlusterfsException: |
115 | "Volume: %(volname)s, Error: %(error)s"), | 416 | msg = (_("Error during delete_share request for " |
116 | {'volname': self.gluster_address.volume, | 417 | "share %(share_id)s"), {'share_id': share['id']},) |
117 | 'error': exc.stderr}) | 418 | LOG.error(msg) |
118 | raise | 419 | raise |
119 | 420 | ||
421 | # TODO(deepakcs): Disable quota. | ||
422 | |||
120 | def allow_access(self, context, share, access, share_server=None): | 423 | def allow_access(self, context, share, access, share_server=None): |
121 | """Allow access to a share using certs. | 424 | """Allow access to a share using certs. |
122 | 425 | ||
@@ -126,20 +429,27 @@ class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver): | |||
126 | if access['access_type'] != ACCESS_TYPE_CERT: | 429 | if access['access_type'] != ACCESS_TYPE_CERT: |
127 | raise exception.InvalidShareAccess(_("Only 'cert' access type " | 430 | raise exception.InvalidShareAccess(_("Only 'cert' access type " |
128 | "allowed")) | 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( | 435 | gargs, gkw = gluster_addr.make_gluster_args( |
131 | 'volume', 'set', self.gluster_address.volume, | 436 | 'volume', 'set', gluster_addr.volume, |
132 | AUTH_SSL_ALLOW, access['access_to']) | 437 | AUTH_SSL_ALLOW, access['access_to']) |
133 | try: | 438 | try: |
134 | self._execute(*gargs, **gkw) | 439 | self._execute(*gargs, **gkw) |
135 | except exception.ProcessExecutionError as exc: | 440 | except exception.ProcessExecutionError as exc: |
136 | LOG.error(_("Error in gluster volume set during allow access." | 441 | msg = (_("Error in gluster volume set during allow access. " |
137 | "Volume: %(volname)s, Option: %(option)s, " | 442 | "Volume: %(volname)s, Option: %(option)s, " |
138 | "access_to: %(access_to)s, Error: %(error)s"), | 443 | "access_to: %(access_to)s, Error: %(error)s"), |
139 | {'volname': self.gluster_address.volume, | 444 | {'volname': gluster_addr.volume, |
140 | 'option': AUTH_SSL_ALLOW, | 445 | 'option': AUTH_SSL_ALLOW, |
141 | 'access_to': access['access_to'], 'error': exc.stderr}) | 446 | 'access_to': access['access_to'], 'error': exc.stderr}) |
142 | raise | 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 | def deny_access(self, context, share, access, share_server=None): | 454 | def deny_access(self, context, share, access, share_server=None): |
145 | """Deny access to a share that's using cert based auth. | 455 | """Deny access to a share that's using cert based auth. |
@@ -151,16 +461,63 @@ class GlusterfsNativeShareDriver(glusterfs.GlusterfsShareDriver): | |||
151 | raise exception.InvalidShareAccess(_("Only 'cert' access type " | 461 | raise exception.InvalidShareAccess(_("Only 'cert' access type " |
152 | "allowed for access " | 462 | "allowed for access " |
153 | "removal.")) | 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( | 467 | gargs, gkw = gluster_addr.make_gluster_args( |
156 | 'volume', 'reset', self.gluster_address.volume, | 468 | 'volume', 'reset', gluster_addr.volume, |
157 | AUTH_SSL_ALLOW) | 469 | AUTH_SSL_ALLOW) |
158 | try: | 470 | try: |
159 | self._execute(*gargs, **gkw) | 471 | self._execute(*gargs, **gkw) |
160 | except exception.ProcessExecutionError as exc: | 472 | except exception.ProcessExecutionError as exc: |
161 | LOG.error(_("Error in gluster volume reset during deny access." | 473 | msg = (_("Error in gluster volume reset during deny access. " |
162 | "Volume: %(volname)s, Option: %(option)s, " | 474 | "Volume: %(volname)s, Option: %(option)s, " |
163 | "Error: %(error)s"), | 475 | "Error: %(error)s"), |
164 | {'volname': self.gluster_address.volume, | 476 | {'volname': gluster_addr.volume, |
165 | 'option': AUTH_SSL_ALLOW, 'error': exc.stderr}) | 477 | 'option': AUTH_SSL_ALLOW, 'error': exc.stderr}) |
166 | raise | 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 | ||
diff --git a/manila/tests/share/drivers/test_glusterfs_native.py b/manila/tests/share/drivers/test_glusterfs_native.py new file mode 100644 index 0000000..899031e --- /dev/null +++ b/manila/tests/share/drivers/test_glusterfs_native.py | |||
@@ -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) | ||
diff --git a/manila/tests/test_share_glusterfs_native.py b/manila/tests/test_share_glusterfs_native.py deleted file mode 100644 index ec9e83e..0000000 --- a/manila/tests/test_share_glusterfs_native.py +++ /dev/null | |||
@@ -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) | ||