diff --git a/os_brick/initiator/connectors/storpool.py b/os_brick/initiator/connectors/storpool.py index 3e5a2da17..d5e5601df 100644 --- a/os_brick/initiator/connectors/storpool.py +++ b/os_brick/initiator/connectors/storpool.py @@ -16,6 +16,8 @@ from __future__ import absolute_import import os +import time + import six from oslo_log import log as logging @@ -212,11 +214,27 @@ class StorPoolConnector(base.BaseLinuxConnector): # should have picked up the change already, so it is enough to query # the actual disk device to see if its size is correct. # - # TODO(pp): query the API to see if this is really the case volume_id = connection_properties.get('volume', None) if volume_id is None: raise exception.BrickException( 'Invalid StorPool connection data, no volume ID specified.') + + # Get the expected (new) size from the StorPool API volume = self._attach.volumeName(volume_id) + LOG.debug('Querying the StorPool API for the size of %(vol)s', + {'vol': volume}) + vdata = self._attach.api().volumeList(volume)[0] + LOG.debug('Got size %(size)d', {'size': vdata.size}) + + # Wait for the StorPool client to update the size of the local device path = '/dev/storpool/' + volume - return self._get_device_size(path) + for _ in range(10): + size = self._get_device_size(path) + LOG.debug('Got local size %(size)d', {'size': size}) + if size == vdata.size: + return size + time.sleep(0.1) + else: + size = self._get_device_size(path) + LOG.debug('Last attempt: local size %(size)d', {'size': size}) + return size diff --git a/os_brick/tests/initiator/connectors/test_storpool.py b/os_brick/tests/initiator/connectors/test_storpool.py index 733409c38..76230b5ea 100644 --- a/os_brick/tests/initiator/connectors/test_storpool.py +++ b/os_brick/tests/initiator/connectors/test_storpool.py @@ -79,13 +79,16 @@ class StorPoolConnectorTestCase(test_connector.ConnectorTestCase): def volumeName(self, vid): return volumeNameExt(vid) + def get_fake_size(self): + return self.fakeSize + def execute(self, *cmd, **kwargs): if cmd[0] == 'blockdev': self.assertEqual(len(cmd), 3) self.assertEqual(cmd[1], '--getsize64') self.assertEqual(cmd[2], '/dev/storpool/' + self.volumeName(self.fakeProp['volume'])) - return (str(self.fakeSize) + '\n', None) + return (str(self.get_fake_size()) + '\n', None) raise Exception("Unrecognized command passed to " + type(self).__name__ + ".execute(): " + str.join(", ", map(lambda s: "'" + s + "'", cmd))) @@ -148,5 +151,42 @@ class StorPoolConnectorTestCase(test_connector.ConnectorTestCase): self.test_connect_volume() self.fakeSize += 1024 * 1024 * 1024 - newSize = self.connector.extend_volume(self.fakeProp) + + size_list = [self.fakeSize, self.fakeSize - 1, self.fakeSize - 2] + + vdata = mock.MagicMock(spec=['size']) + vdata.size = self.fakeSize + vdata_list = [[vdata]] + + def fake_volume_list(name): + self.assertEqual( + name, + self.adb.volumeName(self.fakeProp['volume']) + ) + return vdata_list.pop() + + api = mock.MagicMock(spec=['volumeList']) + api.volumeList = mock.MagicMock(spec=['__call__']) + + with mock.patch.object( + self.adb, attribute='api', spec=['__call__'] + ) as fake_api, mock.patch.object( + self, attribute='get_fake_size', spec=['__call__'] + ) as fake_size, mock.patch('time.sleep') as fake_sleep: + fake_api.return_value = api + api.volumeList.side_effect = fake_volume_list + + fake_size.side_effect = size_list.pop + + newSize = self.connector.extend_volume(self.fakeProp) + + self.assertEqual(fake_api.call_count, 1) + self.assertEqual(api.volumeList.call_count, 1) + self.assertListEqual(vdata_list, []) + + self.assertEqual(fake_size.call_count, 3) + self.assertListEqual(size_list, []) + + self.assertEqual(fake_sleep.call_count, 2) + self.assertEqual(newSize, self.fakeSize)