]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Extending IBMNAS driver to support NFS based GPFS storage system
authorSasikanth <sasikanth.eda@in.ibm.com>
Thu, 10 Jul 2014 12:27:16 +0000 (17:57 +0530)
committerSasikanth <sasikanth.eda@in.ibm.com>
Wed, 20 Aug 2014 11:33:44 +0000 (17:03 +0530)
- Adds support for hardware platform "NAS based on IBM GPFS
  deployments".
- This will enable IBM GPFS-NFS based storage systems to be used
  as a backend in OpenStack environments.

Driver certification test results:
  https://bugs.launchpad.net/cinder/+bug/1358590

DocImpact

Needs an update in ibmnas driver documentation as this change
proposes a new configuration option (ibmnas_platform_type)
to cinder.conf

Change-Id: I09098341ca1948775166883e9b2290061fa6ed37
Implements: blueprint add-gpfs-nas-to-ibmnas

cinder/tests/test_ibmnas.py
cinder/volume/drivers/ibm/ibmnas.py
etc/cinder/cinder.conf.sample

index d6b4892fa1fa37743edec1287547098edcb98e56..a446cea430dd7873605af8e7f9f92b28a17c7f73 100644 (file)
@@ -17,7 +17,8 @@
 #    Sasikanth Eda <sasikanth.eda@in.ibm.com>
 
 """
-Tests for the IBM NAS family (SONAS, Storwize V7000 Unified).
+Tests for the IBM NAS family (SONAS, Storwize V7000 Unified,
+NAS based IBM GPFS Storage Systems).
 """
 
 import mock
@@ -26,6 +27,7 @@ from oslo.config import cfg
 from cinder import context
 from cinder import exception
 from cinder.openstack.common import log as logging
+from cinder.openstack.common import units
 from cinder import test
 from cinder.volume import configuration as conf
 from cinder.volume.drivers.ibm import ibmnas
@@ -66,6 +68,7 @@ class IBMNASDriverTestCase(test.TestCase):
                            'nas_ssh_port': 22,
                            'nas_password': 'pass',
                            'nas_private_key': 'nas.key',
+                           'ibmnas_platform_type': 'v7ku',
                            'nfs_shares_config': None,
                            'nfs_sparsed_volumes': True,
                            'nfs_used_ratio': 0.95,
@@ -104,6 +107,9 @@ class IBMNASDriverTestCase(test.TestCase):
 
         self._set_flag('nas_password', None)
         self._set_flag('nas_private_key', None)
+        self.assertRaises(exception.InvalidInput,
+                          self._driver.check_for_setup_error)
+        self._set_flag('ibmnas_platform_type', None)
         self.assertRaises(exception.InvalidInput,
                           self._driver.check_for_setup_error)
 
@@ -134,99 +140,193 @@ class IBMNASDriverTestCase(test.TestCase):
         self.assertEqual(self.TEST_NFS_EXPORT.split(':')[1],
                          mock.drv._get_export_path(volume['id']))
 
-    def test_create_ibmnas_snap_mount_point_provided(self):
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_ensure_shares_mounted')
+    def test_update_volume_stats(self, mock_ensure):
+        """Check update volume stats."""
+
+        drv = self._driver
+        mock_ensure.return_value = True
+        fake_avail = 80 * units.Gi
+        fake_size = 2 * fake_avail
+        fake_used = 10 * units.Gi
+
+        with mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                        '_get_capacity_info',
+                        return_value=(fake_avail, fake_size, fake_used)):
+            stats = drv.get_volume_stats()
+            self.assertEqual(stats['volume_backend_name'], 'IBMNAS_NFS')
+            self.assertEqual(stats['storage_protocol'], 'nfs')
+            self.assertEqual(stats['driver_version'], '1.1.0')
+            self.assertEqual(stats['vendor_name'], 'IBM')
+
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver._run_ssh')
+    def test_ssh_operation(self, mock_ssh):
+
+        drv = self._driver
+        mock_ssh.return_value = None
+
+        self.assertEqual(None, drv._ssh_operation('ssh_cmd'))
+
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver._run_ssh')
+    def test_ssh_operation_exception(self, mock_ssh):
+
+        drv = self._driver
+        mock_ssh.side_effect = (
+            exception.VolumeBackendAPIException(data='Failed'))
+
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          drv._ssh_operation, 'ssh_cmd')
+
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_ssh_operation')
+    @mock.patch('cinder.openstack.common.processutils.execute')
+    def test_create_ibmnas_snap_mount_point_provided(self, mock_ssh,
+                                                     mock_execute):
         """Create ibmnas snap if mount point is provided."""
 
         drv = self._driver
-        mock = self._mock
+        mock_ssh.return_value = True
+        mock_execute.return_value = True
+
+        self.assertEqual(None, drv._create_ibmnas_snap(self.TEST_VOLUME_PATH,
+                                                       self.TEST_SNAP_PATH,
+                                                       self.TEST_MNT_POINT))
 
-        drv._create_ibmnas_snap = mock.drv._run_ssh.return_value.\
-            drv._execute.return_value.drv._create_ibmnas_snap
-        drv._create_ibmnas_snap.return_value = True
-        self.assertEqual(True, mock.drv._run_ssh().
-                         drv._execute().
-                         drv._create_ibmnas_snap(self.TEST_VOLUME_PATH,
-                                                 self.TEST_SNAP_PATH,
-                                                 self.TEST_MNT_POINT))
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_ssh_operation')
+    @mock.patch('cinder.openstack.common.processutils.execute')
+    def test_create_ibmnas_snap_nas_gpfs(self, mock_ssh, mock_execute):
+        """Create ibmnas snap if mount point is provided."""
+
+        drv = self._driver
+        drv.configuration.platform = 'gpfs-nas'
+        mock_ssh.return_value = True
+        mock_execute.return_value = True
+
+        self.assertEqual(None, drv._create_ibmnas_snap(self.TEST_VOLUME_PATH,
+                                                       self.TEST_SNAP_PATH,
+                                                       self.TEST_MNT_POINT))
 
-    def test_create_ibmnas_snap_no_mount_point_provided(self):
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_ssh_operation')
+    def test_create_ibmnas_snap_no_mount_point_provided(self, mock_ssh):
         """Create ibmnas snap if no mount point is provided."""
 
         drv = self._driver
-        mock = self._mock
+        mock_ssh.return_value = True
+
+        self.assertEqual(None, drv._create_ibmnas_snap(self.TEST_VOLUME_PATH,
+                                                       self.TEST_SNAP_PATH,
+                                                       None))
+
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_ssh_operation')
+    def test_create_ibmnas_snap_nas_gpfs_no_mount(self, mock_ssh):
+        """Create ibmnas snap (gpfs-nas) if mount point is provided."""
 
-        drv._create_ibmnas_snap = mock.drv._run_ssh.return_value.\
-            drv._execute.return_value.drv._create_ibmnas_snap
-        drv._create_ibmnas_snap.return_value = None
-        self.assertIsNone(mock.drv._run_ssh().
-                          drv._execute().
-                          drv._create_ibmnas_snap(self.TEST_VOLUME_PATH,
-                                                  self.TEST_SNAP_PATH,
-                                                  None))
+        drv = self._driver
+        drv.configuration.platform = 'gpfs-nas'
+        mock_ssh.return_value = True
+
+        drv._create_ibmnas_snap(self.TEST_VOLUME_PATH,
+                                self.TEST_SNAP_PATH, None)
 
-    def test_create_ibmnas_copy(self):
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_ssh_operation')
+    def test_create_ibmnas_copy(self, mock_ssh):
         """Create ibmnas copy test case."""
 
         drv = self._driver
-        mock = self._mock
+        TEST_DEST_SNAP = '/export/snapshot-123.snap'
+        TEST_DEST_PATH = '/export/snapshot-123'
+        mock_ssh.return_value = True
+
+        drv._create_ibmnas_copy(self.TEST_VOLUME_PATH,
+                                TEST_DEST_PATH,
+                                TEST_DEST_SNAP)
+
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_ssh_operation')
+    def test_create_ibmnas_copy_nas_gpfs(self, mock_ssh):
+        """Create ibmnas copy for gpfs-nas platform test case."""
 
+        drv = self._driver
         TEST_DEST_SNAP = '/export/snapshot-123.snap'
         TEST_DEST_PATH = '/export/snapshot-123'
+        drv.configuration.platform = 'gpfs-nas'
+        mock_ssh.return_value = True
 
-        drv._create_ibmnas_copy = mock.drv._run_ssh.return_value.\
-            drv._create_ibmnas_copy
-        drv._create_ibmnas_copy.return_value = None
-        self.assertIsNone(mock.drv._run_ssh().
-                          drv._create_ibmnas_copy(
-                              self.TEST_VOLUME_PATH,
-                              TEST_DEST_PATH,
-                              TEST_DEST_SNAP))
+        drv._create_ibmnas_copy(self.TEST_VOLUME_PATH,
+                                TEST_DEST_PATH,
+                                TEST_DEST_SNAP)
 
-    def test_resize_volume_file(self):
+    @mock.patch('cinder.image.image_utils.resize_image')
+    def test_resize_volume_file(self, mock_size):
         """Resize volume file test case."""
 
         drv = self._driver
-        mock = self._mock
+        mock_size.return_value = True
 
-        drv._resize_volume_file = mock.image_utils.resize_image.return_value.\
-            drv._resize_volume_file
-        drv._resize_volume_file.return_value = True
-        self.assertEqual(True, mock.image_utils.resize_image().
-                         drv._resize_volume_file(
-                             self.TEST_LOCAL_PATH,
-                             self.TEST_EXTEND_SIZE_IN_GB))
+        self.assertEqual(True, drv._resize_volume_file(self.TEST_LOCAL_PATH,
+                         self.TEST_EXTEND_SIZE_IN_GB))
+
+    @mock.patch('cinder.image.image_utils.resize_image')
+    def test_resize_volume_exception(self, mock_size):
+        """Resize volume file test case."""
 
-    def test_extend_volume(self):
+        drv = self._driver
+        mock_size.side_effect = (
+            exception.VolumeBackendAPIException(data='Failed'))
+
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          drv._resize_volume_file,
+                          self.TEST_LOCAL_PATH,
+                          self.TEST_EXTEND_SIZE_IN_GB)
+
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.local_path')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_resize_volume_file')
+    def test_extend_volume(self, mock_resize, mock_local):
         """Extend volume to greater size test case."""
 
         drv = self._driver
-        mock = self._mock
+        mock_resize.return_value = True
+        mock_local.return_value = self.TEST_LOCAL_PATH
+        volume = FakeEnv()
+        volume['name'] = 'vol-123'
 
-        drv.extend_volume = mock.drv.local_path.return_value.\
-            drv._resize_volume_file.return_value.\
-            drv.extend_volume
-        drv.extend_volume.return_value = None
-        self.assertIsNone(mock.drv.local_path().
-                          drv._resize_volume_file().
-                          drv.extend_volume(
-                              self.TEST_LOCAL_PATH,
-                              self.TEST_EXTEND_SIZE_IN_GB))
+        drv.extend_volume(volume,
+                          self.TEST_EXTEND_SIZE_IN_GB)
 
-    def test_delete_snapfiles(self):
-        """Delete_snapfiles assert test case."""
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver._run_ssh')
+    def test_delete_snapfiles(self, mock_ssh):
+        """Delete_snapfiles test case."""
 
         drv = self._driver
-        mock = self._mock
+        mock_ssh.return_value = ('Parent Depth Parent inode'
+                                 'File name\n yes    0 /ibm/gpfs0/gshare/\n'
+                                 'volume-123\n EFSSG1000I The command'
+                                 'completed successfully.', '')
+
+        drv._delete_snapfiles(self.TEST_VOLUME_PATH,
+                              self.TEST_MNT_POINT)
 
-        drv._delete_snapfiles = mock.drv._run_ssh.return_value.\
-            drv._execute.return_value.\
-            drv._delete_snapfiles
-        drv._delete_snapfiles.return_value = None
-        self.assertIsNone(mock.drv._run_ssh().
-                          drv._execute().
-                          drv._delete_snapfiles(
-                              self.TEST_VOLUME_PATH,
-                              self.TEST_MNT_POINT))
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver._run_ssh')
+    def test_delete_snapfiles_nas_gpfs(self, mock_ssh):
+        """Delete_snapfiles for gpfs-nas platform test case."""
+
+        drv = self._driver
+        drv.configuration.platform = 'gpfs-nas'
+        mock_ssh.return_value = ('Parent  Depth   Parent inode'
+                                 'File name\n'
+                                 '------  -----  -------------'
+                                 '-  ---------\n'
+                                 'yes      0\n'
+                                 '/ibm/gpfs0/gshare/volume-123', '')
+
+        drv._delete_snapfiles(self.TEST_VOLUME_PATH,
+                              self.TEST_MNT_POINT)
 
     def test_delete_volume_no_provider_location(self):
         """Delete volume with no provider location specified."""
@@ -240,30 +340,43 @@ class IBMNASDriverTestCase(test.TestCase):
         result = drv.delete_volume(volume)
         self.assertIsNone(result)
 
-    def test_delete_volume(self):
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_get_export_path')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_delete_snapfiles')
+    def test_delete_volume(self, mock_export, mock_snap):
         """Delete volume test case."""
 
         drv = self._driver
-        mock = self._mock
+        mock_export.return_value = self.TEST_VOLUME_PATH
+        mock_snap.return_value = True
 
         volume = FakeEnv()
         volume['id'] = '123'
-        volume['provider_location'] = self.TEST_NFS_EXPORT
-
-        drv.delete_volume = mock.drv._get_export_path.return_value.\
-            drv._delete_snapfiles.return_value.drv.delete_volume
-        drv.delete_volume.return_value = True
-        self.assertEqual(True, mock.drv._get_export_path(volume['id']).
-                         drv._delete_snapfiles(
-                             self.TEST_VOLUME_PATH,
-                             self.TEST_MNT_POINT).
-                         drv.delete_volume(volume))
-
-    def test_create_snapshot(self):
+        volume['name'] = '/volume-123'
+        volume['provider_location'] = self.TEST_VOLUME_PATH
+
+        self.assertEqual(None, drv.delete_volume(volume))
+
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_get_export_path')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_get_provider_location')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_get_mount_point_for_share')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_create_ibmnas_snap')
+    def test_create_snapshot(self, mock_export,
+                             mock_provider,
+                             mock_mount,
+                             mock_snap):
         """Create snapshot simple test case."""
 
         drv = self._driver
-        mock = self._mock
+        mock_export.return_value = self.TEST_LOCAL_PATH
+        mock_provider.return_value = self.TEST_VOLUME_PATH
+        mock_mount.return_value = self.TEST_MNT_POINT
+        mock_snap.return_value = True
 
         volume = FakeEnv()
         volume['id'] = '123'
@@ -271,29 +384,23 @@ class IBMNASDriverTestCase(test.TestCase):
 
         snapshot = FakeEnv()
         snapshot['volume_id'] = volume['id']
-        snapshot['volume_name'] = 'volume-123'
-        snapshot.name = 'snapshot-123'
-
-        drv.create_snapshot = mock.drv._get_export_path.return_value.\
-            drv._get_provider_location.return_value.\
-            drv._get_mount_point_for_share.return_value.\
-            drv._create_ibmnas_snap.return_value.\
-            drv.create_snapshot
-        drv.create_snapshot.return_value = None
-        self.assertIsNone(mock.drv._get_export_path(snapshot['volume_id']).
-                          drv._get_provider_location(snapshot['volume_id']).
-                          drv._get_mount_point_for_share(self.TEST_NFS_EXPORT).
-                          drv._create_ibmnas_snap(
-                              src=self.TEST_VOLUME_PATH,
-                              dest=self.TEST_SNAP_PATH,
-                              mount_path=self.TEST_MNT_POINT).
-                          drv.create_snapshot(snapshot))
-
-    def test_delete_snapshot(self):
+        snapshot['volume_name'] = '/volume-123'
+        snapshot['name'] = '/snapshot-123'
+
+        drv.create_snapshot(snapshot)
+
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_get_provider_location')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_get_mount_point_for_share')
+    @mock.patch('cinder.openstack.common.processutils.execute')
+    def test_delete_snapshot(self, mock_mount, mock_provider, mock_execute):
         """Delete snapshot simple test case."""
 
         drv = self._driver
-        mock = self._mock
+        mock_mount.return_value = self.TEST_LOCAL_PATH
+        mock_provider.return_value = self.TEST_VOLUME_PATH
+        mock_execute.return_value = True
 
         volume = FakeEnv()
         volume['id'] = '123'
@@ -304,77 +411,74 @@ class IBMNASDriverTestCase(test.TestCase):
         snapshot['volume_name'] = 'volume-123'
         snapshot['name'] = 'snapshot-123'
 
-        drv.delete_snapshot = mock.drv._get_provider_location.return_value.\
-            drv._get_mount_point_for_share.return_value.drv._execute.\
-            return_value.drv.delete_snapshot
-        drv.delete_snapshot.return_value = None
-        self.assertIsNone(mock.drv._get_provider_location(volume['id']).
-                          drv._get_mount_point_for_share(self.TEST_NFS_EXPORT).
-                          drv._execute().
-                          drv.delete_snapshot(snapshot))
-
-    def test_create_cloned_volume(self):
+        drv.delete_snapshot(snapshot)
+
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_get_export_path')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_create_ibmnas_copy')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_resize_volume_file')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.local_path')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_find_share')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_set_rw_permissions_for_all')
+    def test_create_cloned_volume(self, mock_export, mock_copy,
+                                  mock_resize, mock_local,
+                                  mock_find, mock_rw):
         """Clone volume with equal size test case."""
 
         drv = self._driver
-        mock = self._mock
+        mock_export.return_value = self.TEST_VOLUME_PATH
+        mock_copy.return_value = self.TEST_LOCAL_PATH
 
         volume_src = FakeEnv()
         volume_src['id'] = '123'
-        volume_src['name'] = 'volume-123'
+        volume_src['name'] = '/volume-123'
         volume_src.size = self.TEST_SIZE_IN_GB
 
         volume_dest = FakeEnv()
         volume_dest['id'] = '456'
-        volume_dest['name'] = 'volume-456'
+        volume_dest['name'] = '/volume-456'
         volume_dest['size'] = self.TEST_SIZE_IN_GB
         volume_dest.size = self.TEST_SIZE_IN_GB
 
-        drv.create_cloned_volume = mock.drv._get_export_path.\
-            return_value.drv._create_ibmnas_copy.return_value.\
-            drv._find_share.return_value.\
-            drv._set_rw_permissions_for_all.return_value.\
-            drv._resize_volume_file.return_value.\
-            drv.create_cloned_volume
-        drv.create_cloned_volume.return_value = self.TEST_NFS_EXPORT
-        self.assertEqual(self.TEST_NFS_EXPORT,
-                         mock.drv._get_export_path(volume_src['id']).
-                         drv._create_ibmnas_copy().
-                         drv._find_share().
-                         drv._set_rw_permissions_for_all().
-                         drv._resize_volume_file().
-                         drv.create_cloned_volume(
-                             volume_dest,
-                             volume_src))
-
-    def test_create_volume_from_snapshot(self):
+        self.assertEqual({'provider_location': self.TEST_LOCAL_PATH},
+                         drv.create_cloned_volume(volume_dest, volume_src))
+
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_get_export_path')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_create_ibmnas_snap')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_resize_volume_file')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.local_path')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_find_share')
+    @mock.patch('cinder.volume.drivers.ibm.ibmnas.IBMNAS_NFSDriver.'
+                '_set_rw_permissions_for_all')
+    def test_create_volume_from_snapshot(self, mock_export, mock_snap,
+                                         mock_resize, mock_local,
+                                         mock_find, mock_rw):
         """Create volume from snapshot test case."""
 
         drv = self._driver
-        mock = self._mock
+        mock_export.return_value = '/export'
+        mock_snap.return_value = self.TEST_LOCAL_PATH
+        mock_local.return_value = self.TEST_VOLUME_PATH
+        mock_find.return_value = self.TEST_LOCAL_PATH
 
         volume = FakeEnv()
         volume['id'] = '123'
-        volume['name'] = 'volume-123'
+        volume['name'] = '/volume-123'
         volume['size'] = self.TEST_SIZE_IN_GB
 
         snapshot = FakeEnv()
         snapshot['volume_id'] = volume['id']
         snapshot['volume_name'] = 'volume-123'
         snapshot['volume_size'] = self.TEST_SIZE_IN_GB
-        snapshot.name = 'snapshot-123'
-
-        drv.create_volume_from_snapshot = mock.drv._get_export_path.\
-            return_value.drv._create_ibmnas_snap.return_value.\
-            drv._find_share.return_value.\
-            drv._set_rw_permissions_for_all.return_value.\
-            drv._resize_volume_file.return_value.\
-            drv.create_volume_from_snapshot
-        drv.create_volume_from_snapshot.return_value = self.TEST_NFS_EXPORT
-        self.assertEqual(self.TEST_NFS_EXPORT,
-                         mock.drv._get_export_path(volume['id']).
-                         drv._create_ibmnas_snap().
-                         drv._find_share().
-                         drv._set_rw_permissions_for_all().
-                         drv._resize_volume_file().
-                         drv.create_volume_from_snapshot(snapshot))
+        snapshot.name = '/snapshot-123'
+
+        self.assertEqual({'provider_location': self.TEST_LOCAL_PATH},
+                         drv.create_volume_from_snapshot(volume, snapshot))
index 9bf1ae73e8aa97fa413111949df77ac45b808d5d..bf69d299bc1e2a24d6951a51473cc4c3ecf35bad 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright 2013 IBM Corp.
+# Copyright 2014 IBM Corp.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
 #    not use this file except in compliance with the License. You may obtain
 # Authors:
 #   Nilesh Bhosale <nilesh.bhosale@in.ibm.com>
 #   Sasikanth Eda <sasikanth.eda@in.ibm.com>
+
 """
 IBM NAS Volume Driver.
 Currently, it supports the following IBM Storage Systems:
 1. IBM Scale Out NAS (SONAS)
 2. IBM Storwize V7000 Unified
+3. NAS based IBM GPFS Storage Systems
 
 Notes:
 1. If you specify both a password and a key file, this driver will use the
@@ -43,18 +45,31 @@ from cinder.volume.drivers import nfs
 from cinder.volume.drivers.nfs import nas_opts
 from cinder.volume.drivers.san import san
 
-VERSION = '1.0.0'
+VERSION = '1.1.0'
 
 LOG = logging.getLogger(__name__)
+
+platform_opts = [
+    cfg.StrOpt('ibmnas_platform_type',
+               default='v7ku',
+               help=('IBMNAS platform type to be used as backend storage; '
+                     'valid values are - '
+                     'v7ku : for using IBM Storwize V7000 Unified, '
+                     'sonas : for using IBM Scale Out NAS, '
+                     'gpfs-nas : for using NFS based IBM GPFS deployments.')),
+]
+
 CONF = cfg.CONF
+CONF.register_opts(platform_opts)
 
 
 class IBMNAS_NFSDriver(nfs.NfsDriver, san.SanDriver):
-    """IBM NAS NFS based cinder driver.
+    """IBMNAS NFS based cinder driver.
 
     Creates file on NFS share for using it as block device on hypervisor.
     Version history:
     1.0.0 - Initial driver
+    1.1.0 - Support for NFS based GPFS storage backend
     """
 
     driver_volume_type = 'nfs'
@@ -64,12 +79,17 @@ class IBMNAS_NFSDriver(nfs.NfsDriver, san.SanDriver):
         self._context = None
         super(IBMNAS_NFSDriver, self).__init__(*args, **kwargs)
         self.configuration.append_config_values(nas_opts)
+        self.configuration.append_config_values(platform_opts)
         self.configuration.san_ip = self.configuration.nas_ip
         self.configuration.san_login = self.configuration.nas_login
         self.configuration.san_password = self.configuration.nas_password
         self.configuration.san_private_key = \
             self.configuration.nas_private_key
         self.configuration.san_ssh_port = self.configuration.nas_ssh_port
+        self.configuration.ibmnas_platform_type = \
+            self.configuration.ibmnas_platform_type.lower()
+        LOG.info(_('Initialized driver for IBMNAS Platform: %s.'),
+                 self.configuration.ibmnas_platform_type)
 
     def set_execute(self, execute):
         self._execute = utils.execute
@@ -81,7 +101,9 @@ class IBMNAS_NFSDriver(nfs.NfsDriver, san.SanDriver):
 
     def check_for_setup_error(self):
         """Ensure that the flags are set properly."""
-        required_flags = ['nas_ip', 'nas_ssh_port', 'nas_login']
+        required_flags = ['nas_ip', 'nas_ssh_port', 'nas_login',
+                          'ibmnas_platform_type']
+        valid_platforms = ['v7ku', 'sonas', 'gpfs-nas']
 
         for flag in required_flags:
             if not self.configuration.safe_get(flag):
@@ -95,6 +117,14 @@ class IBMNAS_NFSDriver(nfs.NfsDriver, san.SanDriver):
                          'authentication: set either nas_password or '
                          'nas_private_key option'))
 
+        # Ensure whether ibmnas platform type is set to appropriate value
+        if self.configuration.ibmnas_platform_type not in valid_platforms:
+            raise exception.InvalidInput(
+                reason = (_("Unsupported ibmnas_platform_type: %(given)s."
+                            " Supported platforms: %(valid)s")
+                          % {'given': self.configuration.ibmnas_platform_type,
+                             'valid': (', '.join(valid_platforms))}))
+
     def _get_provider_location(self, volume_id):
         """Returns provider location for given volume."""
         LOG.debug("Enter _get_provider_location: volume_id %s" % volume_id)
@@ -134,34 +164,38 @@ class IBMNAS_NFSDriver(nfs.NfsDriver, san.SanDriver):
         self._stats = data
         LOG.debug("Exit _update_volume_stats")
 
+    def _ssh_operation(self, ssh_cmd):
+        try:
+            self._run_ssh(ssh_cmd)
+        except processutils.ProcessExecutionError as e:
+            msg = (_('Failed in _ssh_operation while execution of ssh_cmd:'
+                   '%(cmd)s. Error: %(error)s') % {'cmd': ssh_cmd, 'error': e})
+            LOG.exception(msg)
+            raise exception.VolumeBackendAPIException(data=msg)
+
     def _create_ibmnas_snap(self, src, dest, mount_path):
         """Create volume clones and snapshots."""
         LOG.debug("Enter _create_ibmnas_snap: src %(src)s, dest %(dest)s"
                   % {'src': src, 'dest': dest})
-        if mount_path is not None:
-            tmp_file_path = dest + '.snap'
-            ssh_cmd = ['mkclone', '-p', dest, '-s', src, '-t', tmp_file_path]
-            try:
-                self._run_ssh(ssh_cmd)
-            except processutils.ProcessExecutionError as e:
-                msg = (_("Failed in _create_ibmnas_snap during "
-                         "create_snapshot. Error: %s") % e.stderr)
-                LOG.error(msg)
-                raise exception.VolumeBackendAPIException(data=msg)
-
-            #Now remove the tmp file
-            tmp_file_local_path = os.path.join(mount_path,
-                                               os.path.basename(tmp_file_path))
-            self._execute('rm', '-f', tmp_file_local_path, run_as_root=True)
+        if self.configuration.ibmnas_platform_type == 'gpfs-nas':
+            ssh_cmd = ['mmclone', 'snap', src, dest]
+            self._ssh_operation(ssh_cmd)
         else:
-            ssh_cmd = ['mkclone', '-s', src, '-t', dest]
-            try:
-                self._run_ssh(ssh_cmd)
-            except processutils.ProcessExecutionError as e:
-                msg = (_("Failed in _create_ibmnas_snap during "
-                         "create_volume_from_snapshot. Error: %s") % e.stderr)
-                LOG.error(msg)
-                raise exception.VolumeBackendAPIException(data=msg)
+            if mount_path is not None:
+                tmp_file_path = dest + '.snap'
+                ssh_cmd = ['mkclone', '-p', dest, '-s', src, '-t',
+                           tmp_file_path]
+                try:
+                    self._ssh_operation(ssh_cmd)
+                finally:
+                    # Now remove the tmp file
+                    tmp_file_local_path = os.path.join(mount_path, os.path.
+                                                       basename(tmp_file_path))
+                    self._execute('rm', '-f', tmp_file_local_path,
+                                  run_as_root=True)
+            else:
+                ssh_cmd = ['mkclone', '-s', src, '-t', dest]
+                self._ssh_operation(ssh_cmd)
         LOG.debug("Exit _create_ibmnas_snap")
 
     def _create_ibmnas_copy(self, src, dest, snap):
@@ -170,18 +204,19 @@ class IBMNAS_NFSDriver(nfs.NfsDriver, san.SanDriver):
                   'snap %(snap)s' % {'src': src,
                                      'dest': dest,
                                      'snap': snap})
-        ssh_cmd = ['mkclone', '-p', snap, '-s', src, '-t', dest]
-        try:
-            self._run_ssh(ssh_cmd)
-        except processutils.ProcessExecutionError as e:
-            msg = (_("Failed in _create_ibmnas_copy. Error: %s") % e.stderr)
-            LOG.error(msg)
-            raise exception.VolumeBackendAPIException(data=msg)
+        if self.configuration.ibmnas_platform_type == 'gpfs-nas':
+            ssh_cmd = ['mmclone', 'snap', src, snap]
+            self._ssh_operation(ssh_cmd)
+            ssh_cmd = ['mmclone', 'copy', snap, dest]
+            self._ssh_operation(ssh_cmd)
+        else:
+            ssh_cmd = ['mkclone', '-p', snap, '-s', src, '-t', dest]
+            self._ssh_operation(ssh_cmd)
         LOG.debug("Exit _create_ibmnas_copy")
 
     def _resize_volume_file(self, path, new_size):
         """Resize the image file on share to new size."""
-        LOG.info(_('Resizing file to %sG'), new_size)
+        LOG.debug("Resizing file to %sG." % new_size)
         try:
             image_utils.resize_image(path, new_size, run_as_root=True)
         except processutils.ProcessExecutionError as e:
@@ -195,7 +230,7 @@ class IBMNAS_NFSDriver(nfs.NfsDriver, san.SanDriver):
 
     def extend_volume(self, volume, new_size):
         """Extend an existing volume to the new size."""
-        LOG.info(_('Extending volume %s.'), volume['name'])
+        LOG.debug("Extending volume %s" % volume['name'])
         path = self.local_path(volume)
         self._resize_volume_file(path, new_size)
 
@@ -204,7 +239,10 @@ class IBMNAS_NFSDriver(nfs.NfsDriver, san.SanDriver):
                   'mount_point %(mount_point)s'
                   % {'fchild': fchild,
                      'mount_point': mount_point})
-        ssh_cmd = ['lsclone', fchild]
+        if self.configuration.ibmnas_platform_type == 'gpfs-nas':
+            ssh_cmd = ['mmclone', 'show', fchild]
+        else:
+            ssh_cmd = ['lsclone', fchild]
         try:
             (out, _err) = self._run_ssh(ssh_cmd, check_exit_code=False)
         except processutils.ProcessExecutionError as e:
@@ -296,13 +334,18 @@ class IBMNAS_NFSDriver(nfs.NfsDriver, san.SanDriver):
         export_path = self._get_export_path(snapshot['volume_id'])
         snapshot_path = os.path.join(export_path, snapshot.name)
         volume_path = os.path.join(export_path, volume['name'])
-        self._create_ibmnas_snap(snapshot_path, volume_path, None)
+
+        if self.configuration.ibmnas_platform_type == 'gpfs-nas':
+            ssh_cmd = ['mmclone', 'copy', snapshot_path, volume_path]
+            self._ssh_operation(ssh_cmd)
+        else:
+            self._create_ibmnas_snap(snapshot_path, volume_path, None)
 
         volume['provider_location'] = self._find_share(volume['size'])
         volume_path = self.local_path(volume)
         self._set_rw_permissions_for_all(volume_path)
 
-        #Extend the volume if required
+        # Extend the volume if required
         self._resize_volume_file(volume_path, volume['size'])
         return {'provider_location': volume['provider_location']}
 
@@ -325,7 +368,7 @@ class IBMNAS_NFSDriver(nfs.NfsDriver, san.SanDriver):
 
         self._set_rw_permissions_for_all(volume_path)
 
-        #Extend the volume if required
+        # Extend the volume if required
         self._resize_volume_file(volume_path, volume['size'])
 
         return {'provider_location': volume['provider_location']}
index 7cc9b416843cc346e070fab74ad9bc5daddc844c..69b2a576f91a8a225f4d60bd7ea03dcb5877d707 100644 (file)
 #gpfs_storage_pool=<None>
 
 
+#
+# Options defined in cinder.volume.drivers.ibm.ibmnas
+#
+
+# IP address or Hostname of NAS system. (string value)
+#nas_ip=
+
+# User name to connect to NAS system. (string value)
+#nas_login=admin
+
+# Password to connect to NAS system. (string value)
+#nas_password=
+
+# SSH port to use to connect to NAS system. (integer value)
+#nas_ssh_port=22
+
+# Filename of private key to use for SSH authentication.
+# (string value)
+#nas_private_key=
+
+# IBMNAS platform type to be used as backend storage; valid
+# values are - v7ku : for using IBM Storwize V7000 Unified,
+# sonas : for using IBM Scale Out NAS, gpfs-nas : for using
+# NFS based IBM GPFS deployments. (string value)
+#ibmnas_platform_type=v7ku
+
+
 #
 # Options defined in cinder.volume.drivers.ibm.storwize_svc
 #