From 5bb153399b32ea208abed5701caff4795cbff11c Mon Sep 17 00:00:00 2001 From: Victoria Martinez de la Cruz Date: Mon, 22 May 2017 01:13:17 -0300 Subject: [PATCH] Add create/delete/extend/shrink share notifications This patchset adds notifications to share operations in manila. Precisely, create, delete, extend and shrink operations. Partially-Implements: blueprint ceilometer-integration Change-Id: I48324cf45536392576a2c29734b8f62a1f5676c9 --- manila/share/manager.py | 37 +++++++++++++++++-- manila/share/utils.py | 43 +++++++++++++++++++++- manila/test.py | 10 ++++++ manila/tests/share/test_manager.py | 46 ++++++++++++++++++++++-- manila/tests/share/test_share_utils.py | 49 ++++++++++++++++++++++++++ 5 files changed, 179 insertions(+), 6 deletions(-) diff --git a/manila/share/manager.py b/manila/share/manager.py index 9e75f3fc86..ec6e47bcd4 100644 --- a/manila/share/manager.py +++ b/manila/share/manager.py @@ -1549,7 +1549,12 @@ class ShareManager(manager.SchedulerDependentManager): context = context.elevated() share_instance = self._get_share_instance(context, share_instance_id) - share_network_id = share_instance.get('share_network_id', None) + share_id = share_instance.get('share_id') + share_network_id = share_instance.get('share_network_id') + share = self.db.share_get(context, share_id) + + self._notify_about_share_usage(context, share, + share_instance, "create.start") if not share_instance['availability_zone']: share_instance = self.db.share_instance_update( @@ -1641,7 +1646,6 @@ class ShareManager(manager.SchedulerDependentManager): else: LOG.info("Share instance %s created successfully.", share_instance_id) - share = self.db.share_get(context, share_instance['share_id']) updates = { 'status': constants.STATUS_AVAILABLE, 'launched_at': timeutils.utcnow(), @@ -1651,6 +1655,9 @@ class ShareManager(manager.SchedulerDependentManager): self.db.share_instance_update(context, share_instance_id, updates) + self._notify_about_share_usage(context, share, + share_instance, "create.end") + def _update_share_replica_access_rules_state(self, context, share_replica_id, state): """Update the access_rules_status for the share replica.""" @@ -2544,7 +2551,12 @@ class ShareManager(manager.SchedulerDependentManager): """Delete a share instance.""" context = context.elevated() share_instance = self._get_share_instance(context, share_instance_id) + share_id = share_instance.get('share_id') share_server = self._get_share_server(context, share_instance) + share = self.db.share_get(context, share_id) + + self._notify_about_share_usage(context, share, + share_instance, "delete.start") try: self.access_helper.update_access_rules( @@ -2598,6 +2610,9 @@ class ShareManager(manager.SchedulerDependentManager): self._check_delete_share_server(context, share_instance) + self._notify_about_share_usage(context, share, + share_instance, "delete.end") + def _check_delete_share_server(self, context, share_instance): if CONF.delete_share_server_with_last_share: @@ -3336,6 +3351,9 @@ class ShareManager(manager.SchedulerDependentManager): project_id = share['project_id'] user_id = share['user_id'] + self._notify_about_share_usage(context, share, + share_instance, "extend.start") + try: self.driver.extend_share( share_instance, new_size, share_server=share_server) @@ -3369,6 +3387,9 @@ class ShareManager(manager.SchedulerDependentManager): LOG.info("Extend share completed successfully.", resource=share) + self._notify_about_share_usage(context, share, + share_instance, "extend.end") + @add_hooks @utils.require_driver_initialized def shrink_share(self, context, share_id, new_size): @@ -3380,6 +3401,9 @@ class ShareManager(manager.SchedulerDependentManager): user_id = share['user_id'] new_size = int(new_size) + self._notify_about_share_usage(context, share, + share_instance, "shrink.start") + def error_occurred(exc, msg, status=constants.STATUS_SHRINKING_ERROR): LOG.exception(msg, resource=share) self.db.share_update(context, share['id'], {'status': status}) @@ -3433,6 +3457,9 @@ class ShareManager(manager.SchedulerDependentManager): LOG.info("Shrink share completed successfully.", resource=share) + self._notify_about_share_usage(context, share, + share_instance, "shrink.end") + @utils.require_driver_initialized def create_share_group(self, context, share_group_id): context = context.elevated() @@ -3816,3 +3843,9 @@ class ShareManager(manager.SchedulerDependentManager): self.snapshot_access_helper.update_access_rules( context, snapshot_instance['id'], share_server=share_server) + + def _notify_about_share_usage(self, context, share, share_instance, + event_suffix, extra_usage_info=None): + share_utils.notify_about_share_usage( + context, share, share_instance, event_suffix, + extra_usage_info=extra_usage_info, host=self.host) diff --git a/manila/share/utils.py b/manila/share/utils.py index 164bf3041d..975f7afc0a 100644 --- a/manila/share/utils.py +++ b/manila/share/utils.py @@ -16,10 +16,13 @@ """Share-related Utilities and helpers.""" -from manila.common import constants +from oslo_config import cfg +from manila.common import constants +from manila import rpc DEFAULT_POOL_NAME = '_pool0' +CONF = cfg.CONF def extract_host(host, level='backend', use_default_pool_name=False): @@ -104,3 +107,41 @@ def cast_access_object_to_dict_in_readonly(rules): 'access_to': rule['access_to'] }) return dict_rules + + +def notify_about_share_usage(context, share, share_instance, + event_suffix, extra_usage_info=None, host=None): + + if not host: + host = CONF.host + + if not extra_usage_info: + extra_usage_info = {} + + usage_info = _usage_from_share(share, share_instance, **extra_usage_info) + + rpc.get_notifier("share", host).info(context, 'share.%s' % event_suffix, + usage_info) + + +def _usage_from_share(share_ref, share_instance_ref, **extra_usage_info): + + usage_info = { + 'share_id': share_ref['id'], + 'user_id': share_ref['user_id'], + 'project_id': share_ref['project_id'], + 'snapshot_id': share_ref['snapshot_id'], + 'share_group_id': share_ref['share_group_id'], + 'size': share_ref['size'], + 'name': share_ref['display_name'], + 'description': share_ref['display_description'], + 'proto': share_ref['share_proto'], + 'is_public': share_ref['is_public'], + 'availability_zone': share_instance_ref['availability_zone'], + 'host': share_instance_ref['host'], + 'status': share_instance_ref['status'], + } + + usage_info.update(extra_usage_info) + + return usage_info diff --git a/manila/test.py b/manila/test.py index 0382392bcd..cdd1dcc9b0 100644 --- a/manila/test.py +++ b/manila/test.py @@ -363,3 +363,13 @@ class TestCase(base_test.BaseTestCase): def is_microversion_ge(self, left, right): return (api_version.APIVersionRequest(left) >= api_version.APIVersionRequest(right)) + + def assert_notify_called(self, mock_notify, calls): + for i in range(0, len(calls)): + mock_call = mock_notify.call_args_list[i] + call = calls[i] + + posargs = mock_call[0] + + self.assertEqual(call[0], posargs[0]) + self.assertEqual(call[1], posargs[2]) diff --git a/manila/tests/share/test_manager.py b/manila/tests/share/test_manager.py index f010ff02e0..1ade84b04c 100644 --- a/manila/tests/share/test_manager.py +++ b/manila/tests/share/test_manager.py @@ -1933,6 +1933,29 @@ class ShareManagerTestCase(test.TestCase): utils.IsAMatcher(context.RequestContext), fake_server, metadata={'request_host': 'fake_host'}) + @mock.patch('manila.tests.fake_notifier.FakeNotifier._notify') + def test_create_delete_share_instance(self, mock_notify): + """Test share can be created and deleted.""" + share = db_utils.create_share() + + mock_notify.assert_not_called() + + self.share_manager.create_share_instance( + self.context, share.instance['id']) + + self.assert_notify_called(mock_notify, + (['INFO', 'share.create.start'], + ['INFO', 'share.create.end'])) + + self.share_manager.delete_share_instance( + self.context, share.instance['id']) + + self.assert_notify_called(mock_notify, + (['INFO', 'share.create.start'], + ['INFO', 'share.create.end'], + ['INFO', 'share.delete.start'], + ['INFO', 'share.delete.end'])) + @ddt.data(True, False) def test_create_delete_share_instance_error(self, exception_update_access): """Test share can be created and deleted with error.""" @@ -2993,11 +3016,14 @@ class ShareManagerTestCase(test.TestCase): 'server1') timeutils.utcnow.assert_called_once_with() - def test_extend_share_invalid(self): + @mock.patch('manila.tests.fake_notifier.FakeNotifier._notify') + def test_extend_share_invalid(self, mock_notify): share = db_utils.create_share() share_id = share['id'] reservations = {} + mock_notify.assert_not_called() + self.mock_object(self.share_manager, 'driver') self.mock_object(self.share_manager.db, 'share_update') self.mock_object(quota.QUOTAS, 'rollback') @@ -3015,7 +3041,8 @@ class ShareManagerTestCase(test.TestCase): user_id=six.text_type(share['user_id']) ) - def test_extend_share(self): + @mock.patch('manila.tests.fake_notifier.FakeNotifier._notify') + def test_extend_share(self, mock_notify): share = db_utils.create_share() share_id = share['id'] new_size = 123 @@ -3026,6 +3053,8 @@ class ShareManagerTestCase(test.TestCase): reservations = {} fake_share_server = 'fake' + mock_notify.assert_not_called() + manager = self.share_manager self.mock_object(manager, 'driver') self.mock_object(manager.db, 'share_get', @@ -3052,6 +3081,10 @@ class ShareManagerTestCase(test.TestCase): mock.ANY, share_id, shr_update ) + self.assert_notify_called(mock_notify, + (['INFO', 'share.extend.start'], + ['INFO', 'share.extend.end'])) + def test_shrink_share_quota_error(self): size = 5 new_size = 1 @@ -3115,7 +3148,8 @@ class ShareManagerTestCase(test.TestCase): ) self.assertTrue(self.share_manager.db.share_get.called) - def test_shrink_share(self): + @mock.patch('manila.tests.fake_notifier.FakeNotifier._notify') + def test_shrink_share(self, mock_notify): share = db_utils.create_share() share_id = share['id'] new_size = 123 @@ -3126,6 +3160,8 @@ class ShareManagerTestCase(test.TestCase): fake_share_server = 'fake' size_decrease = int(share['size']) - new_size + mock_notify.assert_not_called() + manager = self.share_manager self.mock_object(manager, 'driver') self.mock_object(manager.db, 'share_get', @@ -3158,6 +3194,10 @@ class ShareManagerTestCase(test.TestCase): mock.ANY, share_id, shr_update ) + self.assert_notify_called(mock_notify, + (['INFO', 'share.shrink.start'], + ['INFO', 'share.shrink.end'])) + def test_report_driver_status_driver_handles_ss_false(self): fake_stats = {'field': 'val'} fake_pool = {'name': 'pool1'} diff --git a/manila/tests/share/test_share_utils.py b/manila/tests/share/test_share_utils.py index cbd5578a70..4238583f32 100644 --- a/manila/tests/share/test_share_utils.py +++ b/manila/tests/share/test_share_utils.py @@ -16,6 +16,8 @@ """Tests For miscellaneous util methods used with share.""" +import mock + from manila.common import constants from manila.share import utils as share_utils from manila import test @@ -159,3 +161,50 @@ class ShareUtilsTestCase(test.TestCase): ] replica = share_utils.get_active_replica(replica_list) self.assertIsNone(replica) + + +class NotifyUsageTestCase(test.TestCase): + @mock.patch('manila.share.utils._usage_from_share') + @mock.patch('manila.share.utils.CONF') + @mock.patch('manila.share.utils.rpc') + def test_notify_about_share_usage(self, mock_rpc, mock_conf, mock_usage): + mock_conf.host = 'host1' + output = share_utils.notify_about_share_usage(mock.sentinel.context, + mock.sentinel.share, + mock.sentinel. + share_instance, + 'test_suffix') + self.assertIsNone(output) + mock_usage.assert_called_once_with(mock.sentinel.share, + mock.sentinel.share_instance) + mock_rpc.get_notifier.assert_called_once_with('share', + 'host1') + mock_rpc.get_notifier.return_value.info.assert_called_once_with( + mock.sentinel.context, + 'share.test_suffix', + mock_usage.return_value) + + @mock.patch('manila.share.utils._usage_from_share') + @mock.patch('manila.share.utils.CONF') + @mock.patch('manila.share.utils.rpc') + def test_notify_about_share_usage_with_kwargs(self, mock_rpc, mock_conf, + mock_usage): + mock_conf.host = 'host1' + output = share_utils.notify_about_share_usage(mock.sentinel.context, + mock.sentinel.share, + mock.sentinel. + share_instance, + 'test_suffix', + extra_usage_info={ + 'a': 'b', 'c': 'd'}, + host='host2') + self.assertIsNone(output) + mock_usage.assert_called_once_with(mock.sentinel.share, + mock.sentinel.share_instance, + a='b', c='d') + mock_rpc.get_notifier.assert_called_once_with('share', + 'host2') + mock_rpc.get_notifier.return_value.info.assert_called_once_with( + mock.sentinel.context, + 'share.test_suffix', + mock_usage.return_value)