From 8446f245b5f0793dc7e37cf4f59a9119061a0a2c Mon Sep 17 00:00:00 2001
From: wanghao <wanghao749@huawei.com>
Date: Tue, 26 May 2015 15:32:42 +0800
Subject: [PATCH] Notify the transfer volume action in cinder

Now when we transfer a volume, there is not corresponding action
notification that is sent to ceilometer. Include the actions of create,
accept and delete.

The bp that this patch implements, proposes adding those action to enrich
the notification in cinder.

Notify is added in Create, Accept, and Delete actions.

Change-Id: I9d71c55d103cc501f60585b64902d364af21d4d9
Implements:  blueprint notify-the-transfer-volume-action-in-cinder
---
 cinder/tests/unit/test_volume_transfer.py | 39 +++++++++++++++++++++--
 cinder/transfer/api.py                    | 13 ++++++++
 2 files changed, 49 insertions(+), 3 deletions(-)

diff --git a/cinder/tests/unit/test_volume_transfer.py b/cinder/tests/unit/test_volume_transfer.py
index 524cb6ab0..cf45cb9ce 100644
--- a/cinder/tests/unit/test_volume_transfer.py
+++ b/cinder/tests/unit/test_volume_transfer.py
@@ -15,6 +15,7 @@
 
 import datetime
 
+import mock
 from oslo_log import log as logging
 
 from cinder import context
@@ -36,7 +37,8 @@ class VolumeTransferTestCase(test.TestCase):
                                            project_id='project_id')
         self.updated_at = datetime.datetime(1, 1, 1, 1, 1, 1)
 
-    def test_transfer_volume_create_delete(self):
+    @mock.patch('cinder.volume.utils.notify_about_volume_usage')
+    def test_transfer_volume_create_delete(self, mock_notify):
         tx_api = transfer_api.API()
         utils.create_volume(self.ctxt, id='1',
                             updated_at=self.updated_at)
@@ -44,10 +46,18 @@ class VolumeTransferTestCase(test.TestCase):
         volume = db.volume_get(self.ctxt, '1')
         self.assertEqual('awaiting-transfer', volume['status'],
                          'Unexpected state')
+        calls = [mock.call(self.ctxt, mock.ANY, "transfer.create.start"),
+                 mock.call(self.ctxt, mock.ANY, "transfer.create.end")]
+        mock_notify.assert_has_calls(calls)
+        self.assertEqual(2, mock_notify.call_count)
 
         tx_api.delete(self.ctxt, response['id'])
         volume = db.volume_get(self.ctxt, '1')
         self.assertEqual('available', volume['status'], 'Unexpected state')
+        calls = [mock.call(self.ctxt, mock.ANY, "transfer.delete.start"),
+                 mock.call(self.ctxt, mock.ANY, "transfer.delete.end")]
+        mock_notify.assert_has_calls(calls)
+        self.assertEqual(4, mock_notify.call_count)
 
     def test_transfer_invalid_volume(self):
         tx_api = transfer_api.API()
@@ -59,7 +69,8 @@ class VolumeTransferTestCase(test.TestCase):
         volume = db.volume_get(self.ctxt, '1')
         self.assertEqual('in-use', volume['status'], 'Unexpected state')
 
-    def test_transfer_accept(self):
+    @mock.patch('cinder.volume.utils.notify_about_volume_usage')
+    def test_transfer_accept(self, mock_notify):
         svc = self.start_service('volume', host='test_host')
         tx_api = transfer_api.API()
         utils.create_volume(self.ctxt, id='1',
@@ -77,12 +88,23 @@ class VolumeTransferTestCase(test.TestCase):
                           tx_api.accept,
                           self.ctxt, transfer['id'], 'wrong')
 
+        calls = [mock.call(self.ctxt, mock.ANY, "transfer.create.start"),
+                 mock.call(self.ctxt, mock.ANY, "transfer.create.end")]
+        mock_notify.assert_has_calls(calls)
+        self.assertEqual(2, mock_notify.call_count)
+
         db.volume_update(self.ctxt, '1', {'status': 'wrong'})
         self.assertRaises(exception.InvalidVolume,
                           tx_api.accept,
                           self.ctxt, transfer['id'], transfer['auth_key'])
         db.volume_update(self.ctxt, '1', {'status': 'awaiting-transfer'})
 
+        # Because the InvalidVolume exception is raised in tx_api, so there is
+        # only transfer.accept.start called and missing transfer.accept.end.
+        calls = [mock.call(self.ctxt, mock.ANY, "transfer.accept.start")]
+        mock_notify.assert_has_calls(calls)
+        self.assertEqual(3, mock_notify.call_count)
+
         self.ctxt.user_id = 'new_user_id'
         self.ctxt.project_id = 'new_project_id'
         response = tx_api.accept(self.ctxt,
@@ -99,6 +121,11 @@ class VolumeTransferTestCase(test.TestCase):
         self.assertEqual(transfer['id'], response['id'],
                          'Unexpected transfer id in response.')
 
+        calls = [mock.call(self.ctxt, mock.ANY, "transfer.accept.start"),
+                 mock.call(self.ctxt, mock.ANY, "transfer.accept.end")]
+        mock_notify.assert_has_calls(calls)
+        self.assertEqual(5, mock_notify.call_count)
+
         svc.stop()
 
     def test_transfer_get(self):
@@ -123,7 +150,8 @@ class VolumeTransferTestCase(test.TestCase):
         ts = tx_api.get_all(nctxt)
         self.assertEqual(len(ts), 0, 'Unexpected transfers listed.')
 
-    def test_delete_transfer_with_deleted_volume(self):
+    @mock.patch('cinder.volume.utils.notify_about_volume_usage')
+    def test_delete_transfer_with_deleted_volume(self, mock_notify):
         # create a volume
         volume = utils.create_volume(self.ctxt, id='1',
                                      updated_at=self.updated_at)
@@ -132,6 +160,11 @@ class VolumeTransferTestCase(test.TestCase):
         transfer = tx_api.create(self.ctxt, volume['id'], 'Description')
         t = tx_api.get(self.ctxt, transfer['id'])
         self.assertEqual(t['id'], transfer['id'], 'Unexpected transfer id')
+
+        calls = [mock.call(self.ctxt, mock.ANY, "transfer.create.start"),
+                 mock.call(self.ctxt, mock.ANY, "transfer.create.end")]
+        mock_notify.assert_has_calls(calls)
+        self.assertEqual(2, mock_notify.call_count)
         # force delete volume
         db.volume_destroy(context.get_admin_context(), volume['id'])
         # Make sure transfer has been deleted.
diff --git a/cinder/transfer/api.py b/cinder/transfer/api.py
index 0ad3fccf3..e29dee773 100644
--- a/cinder/transfer/api.py
+++ b/cinder/transfer/api.py
@@ -31,6 +31,7 @@ from cinder import exception
 from cinder.i18n import _, _LE, _LI, _LW
 from cinder import quota
 from cinder.volume import api as volume_api
+from cinder.volume import utils as volume_utils
 
 
 volume_transfer_opts = [
@@ -64,9 +65,13 @@ class API(base.Base):
         transfer = self.db.transfer_get(context, transfer_id)
 
         volume_ref = self.db.volume_get(context, transfer.volume_id)
+        volume_utils.notify_about_volume_usage(context, volume_ref,
+                                               "transfer.delete.start")
         if volume_ref['status'] != 'awaiting-transfer':
             LOG.error(_LE("Volume in unexpected state"))
         self.db.transfer_destroy(context, transfer_id)
+        volume_utils.notify_about_volume_usage(context, volume_ref,
+                                               "transfer.delete.end")
 
     def get_all(self, context, filters=None):
         filters = filters or {}
@@ -105,6 +110,8 @@ class API(base.Base):
         if volume_ref['status'] != "available":
             raise exception.InvalidVolume(reason=_("status must be available"))
 
+        volume_utils.notify_about_volume_usage(context, volume_ref,
+                                               "transfer.create.start")
         # The salt is just a short random string.
         salt = self._get_random_string(CONF.volume_transfer_salt_length)
         auth_key = self._get_random_string(CONF.volume_transfer_key_length)
@@ -123,6 +130,8 @@ class API(base.Base):
             LOG.error(_LE("Failed to create transfer record "
                           "for %s"), volume_id)
             raise
+        volume_utils.notify_about_volume_usage(context, volume_ref,
+                                               "transfer.create.end")
         return {'id': transfer['id'],
                 'volume_id': transfer['volume_id'],
                 'display_name': transfer['display_name'],
@@ -145,6 +154,8 @@ class API(base.Base):
 
         volume_id = transfer['volume_id']
         vol_ref = self.db.volume_get(context.elevated(), volume_id)
+        volume_utils.notify_about_volume_usage(context, vol_ref,
+                                               "transfer.accept.start")
 
         try:
             reservations = QUOTAS.reserve(context, volumes=1,
@@ -210,6 +221,8 @@ class API(base.Base):
                                     project_id=donor_id)
 
         vol_ref = self.db.volume_get(context, volume_id)
+        volume_utils.notify_about_volume_usage(context, vol_ref,
+                                               "transfer.accept.end")
         return {'id': transfer_id,
                 'display_name': transfer['display_name'],
                 'volume_id': vol_ref['id']}
-- 
2.45.2