From 38096b2405899adc63b7ee5f85a4d3b2a66991a7 Mon Sep 17 00:00:00 2001
From: Tomoki Sekiyama <tomoki.sekiyama@hds.com>
Date: Fri, 18 Jul 2014 22:23:55 -0400
Subject: [PATCH] Avoid OSError in get_blkdev_major_minor with network
 filesystems

To avoid OSError (File not found) thrown for volumes on network filesystems
during volume copy with CONF.volume_copy_bps_limit, this patch adds a check
of whether the specified file is on the local block devices to
get_blkdev_major_minor().

It also adds a testcase for get_blkdev_major_minor with a file on nfs and
fixes it with a regular file.

Change-Id: I0b725bba07ad632bfa0c922bfce1652efe59b033
Closes-Bug: 1348230
---
 cinder/tests/test_utils.py | 28 ++++++++++++++++++----------
 cinder/utils.py            |  3 +++
 2 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/cinder/tests/test_utils.py b/cinder/tests/test_utils.py
index ff7b8c7ee..1766a2745 100644
--- a/cinder/tests/test_utils.py
+++ b/cinder/tests/test_utils.py
@@ -584,18 +584,17 @@ class GenericUtilsTestCase(test.TestCase):
         mock_stat.return_value = stat_result
         dev = utils.get_blkdev_major_minor(test_device)
         self.assertEqual('253:7', dev)
-        mock_stat.aseert_called_once_with(test_device)
+        mock_stat.assert_called_once_with(test_device)
 
     @mock.patch('os.stat')
     @mock.patch.object(utils, 'execute')
-    def test_get_blkdev_major_minor_file(self, mock_exec, mock_stat):
-
+    def _test_get_blkdev_major_minor_file(self, test_partition,
+                                          mock_exec, mock_stat):
         mock_exec.return_value = (
-            'Filesystem Size Used Avail Use% Mounted on\n'
-            '/dev/made_up_disk1 4096 2048 2048 50% /tmp\n', None)
+            'Filesystem Size Used Avail Use%% Mounted on\n'
+            '%s 4096 2048 2048 50%% /tmp\n' % test_partition, None)
 
         test_file = '/tmp/file'
-        test_partition = '/dev/made_up_disk1'
         test_disk = '/dev/made_up_disk'
 
         class stat_result_file:
@@ -620,11 +619,20 @@ class GenericUtilsTestCase(test.TestCase):
         mock_stat.side_effect = fake_stat
 
         dev = utils.get_blkdev_major_minor(test_file)
+        mock_stat.assert_any_call(test_file)
+        mock_exec.assert_called_once_with('df', test_file)
+        if test_partition.startswith('/'):
+            mock_stat.assert_any_call(test_partition)
+            mock_stat.assert_any_call(test_disk)
+        return dev
+
+    def test_get_blkdev_major_minor_file(self):
+        dev = self._test_get_blkdev_major_minor_file('/dev/made_up_disk1')
         self.assertEqual('8:64', dev)
-        mock_exec.aseert_called_once_with(test_file)
-        mock_stat.aseert_called_once_with(test_file)
-        mock_stat.aseert_called_once_with(test_partition)
-        mock_stat.aseert_called_once_with(test_disk)
+
+    def test_get_blkdev_major_minor_file_nfs(self):
+        dev = self._test_get_blkdev_major_minor_file('nfs-server:/export/path')
+        self.assertIsNone(dev)
 
 
 class MonkeyPatchTestCase(test.TestCase):
diff --git a/cinder/utils.py b/cinder/utils.py
index 0807e9464..45ffc123a 100644
--- a/cinder/utils.py
+++ b/cinder/utils.py
@@ -820,6 +820,9 @@ def get_blkdev_major_minor(path, lookup_for_file=True):
         # lookup the mounted disk which the file lies on
         out, _err = execute('df', path)
         devpath = out.split("\n")[1].split()[0]
+        if devpath[0] is not '/':
+            # the file is on a network file system
+            return None
         return get_blkdev_major_minor(devpath, False)
     else:
         msg = _("Unable to get a block device for file \'%s\'") % path
-- 
2.45.2