diff --git a/cinder/api/openstack/api_version_request.py b/cinder/api/openstack/api_version_request.py index 3372bb1c385..ef974e9175b 100644 --- a/cinder/api/openstack/api_version_request.py +++ b/cinder/api/openstack/api_version_request.py @@ -106,6 +106,7 @@ REST_API_VERSION_HISTORY = """ 'volume:extend_attached_volume' policy rule. Extend in reserved state is intentionally NOT allowed. * 3.43 - Support backup CRUD with metadata. + * 3.44 - Add attachment-complete. """ # The minimum and maximum versions of the API supported @@ -113,7 +114,7 @@ REST_API_VERSION_HISTORY = """ # minimum version of the API supported. # Explicitly using /v1 or /v2 endpoints will still work _MIN_API_VERSION = "3.0" -_MAX_API_VERSION = "3.43" +_MAX_API_VERSION = "3.44" _LEGACY_API_VERSION1 = "1.0" _LEGACY_API_VERSION2 = "2.0" diff --git a/cinder/api/openstack/rest_api_version_history.rst b/cinder/api/openstack/rest_api_version_history.rst index 39c66686cc5..fed4c51bd16 100644 --- a/cinder/api/openstack/rest_api_version_history.rst +++ b/cinder/api/openstack/rest_api_version_history.rst @@ -365,3 +365,7 @@ user documentation. 3.43 ---- Support backup CRUD with metadata. + +3.44 +---- + Support attachment completion. diff --git a/cinder/api/v3/attachments.py b/cinder/api/v3/attachments.py index 120f6f9b60c..67630012f19 100644 --- a/cinder/api/v3/attachments.py +++ b/cinder/api/v3/attachments.py @@ -27,6 +27,7 @@ from cinder.volume import api as volume_api LOG = logging.getLogger(__name__) API_VERSION = '3.27' +ATTACHMENT_COMPLETION_VERSION = '3.44' class AttachmentsController(wsgi.Controller): @@ -266,6 +267,20 @@ class AttachmentsController(wsgi.Controller): attachments = self.volume_api.attachment_delete(context, attachment) return attachment_views.ViewBuilder.list(attachments) + @wsgi.response(202) + @wsgi.Controller.api_version(ATTACHMENT_COMPLETION_VERSION) + @wsgi.action('os-complete') + def complete(self, req, id, body): + """Mark a volume attachment process as completed (in-use).""" + context = req.environ['cinder.context'] + attachment_ref = ( + objects.VolumeAttachment.get_by_id(context, id)) + volume_ref = objects.Volume.get_by_id( + context, + attachment_ref.volume_id) + attachment_ref.update({'attach_status': 'attached'}) + volume_ref.update({'status': 'in-use', 'attach_status': 'attached'}) + def create_resource(ext_mgr): """Create the wsgi resource for this controller.""" diff --git a/cinder/db/api.py b/cinder/db/api.py index 25c406595c2..ac24e0c4cec 100644 --- a/cinder/db/api.py +++ b/cinder/db/api.py @@ -223,10 +223,10 @@ def volume_attach(context, values): def volume_attached(context, volume_id, instance_id, host_name, mountpoint, - attach_mode='rw'): + attach_mode='rw', mark_attached=True): """Ensure that a volume is set as attached.""" return IMPL.volume_attached(context, volume_id, instance_id, host_name, - mountpoint, attach_mode) + mountpoint, attach_mode, mark_attached) def volume_create(context, values): diff --git a/cinder/db/sqlalchemy/api.py b/cinder/db/sqlalchemy/api.py index f7fcfa65e78..73968d166e5 100644 --- a/cinder/db/sqlalchemy/api.py +++ b/cinder/db/sqlalchemy/api.py @@ -1451,14 +1451,25 @@ def volume_attach(context, values): @require_admin_context def volume_attached(context, attachment_id, instance_uuid, host_name, - mountpoint, attach_mode='rw'): + mountpoint, attach_mode, mark_attached): """This method updates a volume attachment entry. This function saves the information related to a particular attachment for a volume. It also updates the volume record - to mark the volume as attached. + to mark the volume as attached or attaching. + + The mark_attached argument is a boolean, when set to True, + we mark the volume as 'in-use' and the 'attachment' as + 'attached', if False, we use 'attaching' for both of these + status settings. """ + attach_status = fields.VolumeAttachStatus.ATTACHED + volume_status = 'in-use' + if not mark_attached: + attach_status = fields.VolumeAttachStatus.ATTACHING + volume_status = 'attaching' + if instance_uuid and not uuidutils.is_uuid_like(instance_uuid): raise exception.InvalidUUID(uuid=instance_uuid) @@ -1468,7 +1479,7 @@ def volume_attached(context, attachment_id, instance_uuid, host_name, session=session) updated_values = {'mountpoint': mountpoint, - 'attach_status': fields.VolumeAttachStatus.ATTACHED, + 'attach_status': attach_status, 'instance_uuid': instance_uuid, 'attached_host': host_name, 'attach_time': timeutils.utcnow(), @@ -1480,8 +1491,8 @@ def volume_attached(context, attachment_id, instance_uuid, host_name, volume_ref = _volume_get(context, volume_attachment_ref['volume_id'], session=session) - volume_ref['status'] = 'in-use' - volume_ref['attach_status'] = fields.VolumeAttachStatus.ATTACHED + volume_ref['status'] = volume_status + volume_ref['attach_status'] = attach_status volume_ref.save(session=session) return (volume_ref, updated_values) diff --git a/cinder/tests/unit/attachments/test_attachments_manager.py b/cinder/tests/unit/attachments/test_attachments_manager.py index 4f79b0bf033..015ed9224d8 100644 --- a/cinder/tests/unit/attachments/test_attachments_manager.py +++ b/cinder/tests/unit/attachments/test_attachments_manager.py @@ -119,8 +119,8 @@ class AttachmentManagerTestCase(test.TestCase): self.assertEqual('rw', new_attachment_ref['attach_mode']) new_volume_ref = db.volume_get(self.context, vref.id) - self.assertEqual('in-use', new_volume_ref.status) - self.assertEqual(fields.VolumeAttachStatus.ATTACHED, + self.assertEqual('attaching', new_volume_ref.status) + self.assertEqual(fields.VolumeAttachStatus.ATTACHING, new_volume_ref.attach_status) def test_attachment_delete(self): diff --git a/cinder/tests/unit/objects/test_volume_attachment.py b/cinder/tests/unit/objects/test_volume_attachment.py index eccbd58c7e8..fe2d0411f21 100644 --- a/cinder/tests/unit/objects/test_volume_attachment.py +++ b/cinder/tests/unit/objects/test_volume_attachment.py @@ -92,7 +92,8 @@ class TestVolumeAttachment(test_objects.BaseObjectsTestCase): fake.INSTANCE_ID, 'fake_host', '/dev/sda', - 'rw') + 'rw', + True) self.assertEqual('/dev/sda', attachment.mountpoint) self.assertEqual(fake.INSTANCE_ID, attachment.instance_uuid) self.assertEqual(fields.VolumeAttachStatus.ATTACHED, diff --git a/cinder/tests/unit/volume/test_volume.py b/cinder/tests/unit/volume/test_volume.py index e8bd668e1d3..65024eea8c8 100644 --- a/cinder/tests/unit/volume/test_volume.py +++ b/cinder/tests/unit/volume/test_volume.py @@ -2793,6 +2793,21 @@ class VolumeTestCase(base.BaseVolumeTestCase): manager._set_resource_host(volume) save_mock.assert_not_called() + def test_volume_attach_attaching(self): + """Test volume_attach .""" + + instance_uuid = '12345678-1234-5678-1234-567812345678' + volume = tests_utils.create_volume(self.context, **self.volume_params) + attachment = db.volume_attach(self.context, + {'volume_id': volume['id'], + 'attached_host': 'fake-host'}) + db.volume_attached(self.context, attachment['id'], instance_uuid, + 'fake-host', 'vdb', mark_attached=False) + volume_api = cinder.volume.api.API() + volume = volume_api.get(self.context, volume['id']) + self.assertEqual("attaching", volume['status']) + self.assertEqual("attaching", volume['attach_status']) + class VolumeTestCaseLocks(base.BaseVolumeTestCase): MOCK_TOOZ = False diff --git a/cinder/volume/manager.py b/cinder/volume/manager.py index 20c7115ed05..667f4094fc4 100644 --- a/cinder/volume/manager.py +++ b/cinder/volume/manager.py @@ -4373,13 +4373,13 @@ class VolumeManager(manager.CleanableManager, attachment_ref.instance_uuid, connector.get('host', ''), connector.get('mountpoint', 'na'), - mode) + mode, + False) vref.refresh() + attachment_ref.refresh() self._notify_about_volume_usage(context, vref, "attach.end") - LOG.info("Attach volume completed successfully.", + LOG.info("attachment_update completed successfully.", resource=vref) - attachment_ref = objects.VolumeAttachment.get_by_id(context, - attachment_id) return connection_info def _connection_terminate(self, context, volume,