def attach_volumes(self, task): """Informs the storage subsystem to attach all volumes for the node. :param task: The task object. :raises: StorageError If an underlying exception or failure is detected. """ node = task.node targets = [target.volume_id for target in task.volume_targets] # If there are no targets, then we have nothing to do. if not targets: return connector = self._generate_connector(task) try: connected = cinder.attach_volumes(task, targets, connector) except exception.StorageError as e: with excutils.save_and_reraise_exception(): LOG.error( "Error attaching volumes for node %(node)s: " "%(err)s", { 'node': node.uuid, 'err': e }) self.detach_volumes(task, connector=connector, aborting_attach=True) if len(targets) != len(connected): LOG.error( "The number of volumes defined for node %(node)s does " "not match the number of attached volumes. Attempting " "detach and abort operation.", {'node': node.uuid}) self.detach_volumes(task, connector=connector, aborting_attach=True) raise exception.StorageError( ("Mismatch between the number of " "configured volume targets for " "node %(uuid)s and the number of " "completed attachments.") % {'uuid': node.uuid}) for volume in connected: # Volumes that were already attached are # skipped. Updating target volume properties # for these volumes is nova's responsibility. if not volume.get('already_attached'): volume_uuid = volume['data']['ironic_volume_uuid'] targets = objects.VolumeTarget.list_by_volume_id( task.context, volume_uuid) for target in targets: target.properties = volume['data'] target.save()
def test_attach_volumes_one_attached(self, mock_create_meta, mock_reserve, mock_init, mock_attach, mock_get, mock_set_meta, mock_session): """Iterate with two volumes, one already attached.""" volume_id = '111111111-0000-0000-0000-000000000003' expected = [{ 'driver_volume_type': 'iscsi', 'data': { 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002', 'target_portal': '127.0.0.0.1:3260', 'volume_id': volume_id, 'target_lun': 2, 'ironic_volume_uuid': '000-000' } }, { 'already_attached': True, 'data': { 'volume_id': 'already_attached', 'ironic_volume_uuid': '000-001' } }] volumes = [volume_id, 'already_attached'] connector = {'foo': 'bar'} mock_create_meta.return_value = {'bar': 'baz'} mock_get.side_effect = [ mock.Mock(attachments=[], id='000-000'), mock.Mock(attachments=[{ 'server_id': self.node.uuid }], id='000-001') ] mock_init.return_value = { 'driver_volume_type': 'iscsi', 'data': { 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002', 'target_portal': '127.0.0.0.1:3260', 'target_lun': 2 } } with task_manager.acquire(self.context, self.node.uuid) as task: attachments = cinder.attach_volumes(task, volumes, connector) self.assertEqual(expected, attachments) mock_reserve.assert_called_once_with(mock.ANY, volume_id) mock_init.assert_called_once_with(mock.ANY, volume_id, connector) mock_attach.assert_called_once_with(mock.ANY, volume_id, self.node.instance_uuid, self.mount_point) mock_set_meta.assert_called_once_with(mock.ANY, volume_id, {'bar': 'baz'})
def attach_volumes(self, task): """Informs the storage subsystem to attach all volumes for the node. :param task: The task object. :raises: StorageError If an underlying exception or failure is detected. """ node = task.node targets = [target.volume_id for target in task.volume_targets] # If there are no targets, then we have nothing to do. if not targets: return connector = self._generate_connector(task) try: connected = cinder.attach_volumes(task, targets, connector) except exception.StorageError as e: with excutils.save_and_reraise_exception(): LOG.error("Error attaching volumes for node %(node)s: " "%(err)s", {'node': node.uuid, 'err': e}) self.detach_volumes(task, connector=connector, aborting_attach=True) if len(targets) != len(connected): LOG.error("The number of volumes defined for node %(node)s does " "not match the number of attached volumes. Attempting " "detach and abort operation.", {'node': node.uuid}) self.detach_volumes(task, connector=connector, aborting_attach=True) raise exception.StorageError(("Mismatch between the number of " "configured volume targets for " "node %(uuid)s and the number of " "completed attachments.") % {'uuid': node.uuid}) for volume in connected: # Volumes that were already attached are # skipped. Updating target volume properties # for these volumes is nova's responsibility. if not volume.get('already_attached'): volume_uuid = volume['data']['ironic_volume_uuid'] targets = objects.VolumeTarget.list_by_volume_id(task.context, volume_uuid) for target in targets: target.properties = volume['data'] target.save()
def test_attach_volumes_attach_set_meta_failure( self, mock_log, mock_create_meta, mock_is_attached, mock_reserve, mock_init, mock_attach, mock_get, mock_set_meta, mock_session): """Attach a volume and tolerate set_metadata failure.""" expected = [{ 'driver_volume_type': 'iscsi', 'data': { 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002', 'target_portal': '127.0.0.0.1:3260', 'volume_id': '111111111-0000-0000-0000-000000000003', 'target_lun': 2, 'ironic_volume_uuid': '000-000' } }] volume_id = '111111111-0000-0000-0000-000000000003' volumes = [volume_id] connector = {'foo': 'bar'} mock_create_meta.return_value = {'bar': 'baz'} mock_is_attached.return_value = False mock_get.return_value = mock.Mock(attachments=[], id='000-000') mock_init.return_value = { 'driver_volume_type': 'iscsi', 'data': { 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002', 'target_portal': '127.0.0.0.1:3260', 'target_lun': 2 } } mock_set_meta.side_effect = cinder_exceptions.NotAcceptable( http_client.NOT_ACCEPTABLE) with task_manager.acquire(self.context, self.node.uuid) as task: attachments = cinder.attach_volumes(task, volumes, connector) self.assertEqual(expected, attachments) mock_reserve.assert_called_once_with(mock.ANY, volume_id) mock_init.assert_called_once_with(mock.ANY, volume_id, connector) mock_attach.assert_called_once_with(mock.ANY, volume_id, self.node.instance_uuid, self.mount_point) mock_set_meta.assert_called_once_with(mock.ANY, volume_id, {'bar': 'baz'}) mock_get.assert_called_once_with(mock.ANY, volume_id) mock_is_attached.assert_called_once_with(mock.ANY, mock_get.return_value) self.assertTrue(mock_log.warning.called)
def test_attach_volumes_one_attached( self, mock_create_meta, mock_reserve, mock_init, mock_attach, mock_get, mock_set_meta, mock_session): """Iterate with two volumes, one already attached.""" volume_id = '111111111-0000-0000-0000-000000000003' expected = [ {'driver_volume_type': 'iscsi', 'data': { 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002', 'target_portal': '127.0.0.0.1:3260', 'volume_id': volume_id, 'target_lun': 2, 'ironic_volume_uuid': '000-000'}}, {'already_attached': True, 'data': { 'volume_id': 'already_attached', 'ironic_volume_uuid': '000-001'}}] volumes = [volume_id, 'already_attached'] connector = {'foo': 'bar'} mock_create_meta.return_value = {'bar': 'baz'} mock_get.side_effect = [ mock.Mock(attachments=[], id='000-000'), mock.Mock(attachments=[{'server_id': self.node.uuid}], id='000-001') ] mock_init.return_value = { 'driver_volume_type': 'iscsi', 'data': { 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002', 'target_portal': '127.0.0.0.1:3260', 'target_lun': 2}} with task_manager.acquire(self.context, self.node.uuid) as task: attachments = cinder.attach_volumes(task, volumes, connector) self.assertEqual(expected, attachments) mock_reserve.assert_called_once_with(mock.ANY, volume_id) mock_init.assert_called_once_with(mock.ANY, volume_id, connector) mock_attach.assert_called_once_with(mock.ANY, volume_id, self.node.instance_uuid, None) mock_set_meta.assert_called_once_with(mock.ANY, volume_id, {'bar': 'baz'})
def test_attach_volumes_attach_set_meta_failure( self, mock_log, mock_create_meta, mock_is_attached, mock_reserve, mock_init, mock_attach, mock_get, mock_set_meta, mock_session): """Attach a volume and tolerate set_metadata failure.""" expected = [{ 'driver_volume_type': 'iscsi', 'data': { 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002', 'target_portal': '127.0.0.0.1:3260', 'volume_id': '111111111-0000-0000-0000-000000000003', 'target_lun': 2, 'ironic_volume_uuid': '000-000'}}] volume_id = '111111111-0000-0000-0000-000000000003' volumes = [volume_id] connector = {'foo': 'bar'} mock_create_meta.return_value = {'bar': 'baz'} mock_is_attached.return_value = False mock_get.return_value = mock.Mock(attachments=[], id='000-000') mock_init.return_value = { 'driver_volume_type': 'iscsi', 'data': { 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002', 'target_portal': '127.0.0.0.1:3260', 'target_lun': 2}} mock_set_meta.side_effect = cinder_exceptions.NotAcceptable( http_client.NOT_ACCEPTABLE) with task_manager.acquire(self.context, self.node.uuid) as task: attachments = cinder.attach_volumes(task, volumes, connector) self.assertEqual(expected, attachments) mock_reserve.assert_called_once_with(mock.ANY, volume_id) mock_init.assert_called_once_with(mock.ANY, volume_id, connector) mock_attach.assert_called_once_with(mock.ANY, volume_id, self.node.instance_uuid, self.mount_point) mock_set_meta.assert_called_once_with(mock.ANY, volume_id, {'bar': 'baz'}) mock_get.assert_called_once_with(mock.ANY, volume_id) mock_is_attached.assert_called_once_with(mock.ANY, mock_get.return_value) self.assertTrue(mock_log.warning.called)
def test_attach_volumes(self, mock_create_meta, mock_is_attached, mock_reserve, mock_init, mock_attach, mock_get, mock_set_meta, mock_session): """Iterate once on a single volume with success.""" volume_id = '111111111-0000-0000-0000-000000000003' expected = [{ 'driver_volume_type': 'iscsi', 'data': { 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002', 'target_portal': '127.0.0.0.1:3260', 'volume_id': volume_id, 'target_lun': 2, 'ironic_volume_uuid': '000-001' } }] volumes = [volume_id] connector = {'foo': 'bar'} mock_create_meta.return_value = {'bar': 'baz'} mock_is_attached.return_value = False mock_get.return_value = mock.Mock(attachments=[], id='000-001') mock_init.return_value = { 'driver_volume_type': 'iscsi', 'data': { 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002', 'target_portal': '127.0.0.0.1:3260', 'target_lun': 2 } } with task_manager.acquire(self.context, self.node.uuid) as task: attachments = cinder.attach_volumes(task, volumes, connector) self.assertEqual(expected, attachments) mock_reserve.assert_called_once_with(mock.ANY, volume_id) mock_init.assert_called_once_with(mock.ANY, volume_id, connector) mock_attach.assert_called_once_with(mock.ANY, volume_id, self.node.instance_uuid, None) mock_set_meta.assert_called_once_with(mock.ANY, volume_id, {'bar': 'baz'}) mock_get.assert_called_once_with(mock.ANY, volume_id)
def test_attach_volumes(self, mock_create_meta, mock_is_attached, mock_reserve, mock_init, mock_attach, mock_get, mock_set_meta, mock_session): """Iterate once on a single volume with success.""" volume_id = '111111111-0000-0000-0000-000000000003' expected = [{ 'driver_volume_type': 'iscsi', 'data': { 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002', 'target_portal': '127.0.0.0.1:3260', 'volume_id': volume_id, 'target_lun': 2, 'ironic_volume_uuid': '000-001'}}] volumes = [volume_id] connector = {'foo': 'bar'} mock_create_meta.return_value = {'bar': 'baz'} mock_is_attached.return_value = False mock_get.return_value = mock.Mock(attachments=[], id='000-001') mock_init.return_value = { 'driver_volume_type': 'iscsi', 'data': { 'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002', 'target_portal': '127.0.0.0.1:3260', 'target_lun': 2}} with task_manager.acquire(self.context, self.node.uuid) as task: attachments = cinder.attach_volumes(task, volumes, connector) self.assertEqual(expected, attachments) mock_reserve.assert_called_once_with(mock.ANY, volume_id) mock_init.assert_called_once_with(mock.ANY, volume_id, connector) mock_attach.assert_called_once_with(mock.ANY, volume_id, self.node.instance_uuid, self.mount_point) mock_set_meta.assert_called_once_with(mock.ANY, volume_id, {'bar': 'baz'}) mock_get.assert_called_once_with(mock.ANY, volume_id)