diff --git a/bin/cinder-volume-usage-audit b/bin/cinder-volume-usage-audit index 96660839ea1..053de79a13d 100755 --- a/bin/cinder-volume-usage-audit +++ b/bin/cinder-volume-usage-audit @@ -67,6 +67,12 @@ if __name__ == '__main__': print _("Starting volume usage audit") msg = _("Creating usages for %(begin_period)s until %(end_period)s") print (msg % {"begin_period": str(begin), "end_period": str(end)}) + + extra_info = { + 'audit_period_beginning': str(begin), + 'audit_period_ending': str(end), + } + volumes = db.volume_get_active_by_window(admin_context, begin, end) @@ -77,4 +83,18 @@ if __name__ == '__main__': admin_context, volume_ref) except Exception, e: print traceback.format_exc(e) + + snapshots = db.snapshot_get_active_by_window(admin_context, + begin, + end) + print _("Found %d snapshots") % len(snapshots) + for snapshot_ref in snapshots: + try: + cinder.volume.utils.notify_about_snapshot_usage(admin_context, + snapshot_ref, + 'exists', + extra_info) + except Exception, e: + print traceback.fromat_exc(e) + print _("Volume usage audit completed") diff --git a/cinder/db/api.py b/cinder/db/api.py index 4f3b92544ab..dc5e675da5f 100644 --- a/cinder/db/api.py +++ b/cinder/db/api.py @@ -317,6 +317,13 @@ def snapshot_data_get_for_project(context, project_id, session=None): session=None) +def snapshot_get_active_by_window(context, begin, end=None, project_id=None): + """Get all the snapshots inside the window. + + Specifying a project_id will filter for a certain project.""" + return IMPL.snapshot_get_active_by_window(context, begin, end, project_id) + + #################### diff --git a/cinder/db/sqlalchemy/api.py b/cinder/db/sqlalchemy/api.py index 67a4e1e7f6c..68b9671eb06 100644 --- a/cinder/db/sqlalchemy/api.py +++ b/cinder/db/sqlalchemy/api.py @@ -1260,6 +1260,22 @@ def snapshot_data_get_for_project(context, project_id, session=None): return (result[0] or 0, result[1] or 0) +@require_context +def snapshot_get_active_by_window(context, begin, end=None, project_id=None): + """Return snapshots that were active during window.""" + session = get_session() + query = session.query(models.Snapshot) + + query = query.filter(or_(models.Snapshot.deleted_at == None, + models.Snapshot.deleted_at > begin)) + if end: + query = query.filter(models.Snapshot.created_at < end) + if project_id: + query = query.filter_by(project_id=project_id) + + return query.all() + + @require_context def snapshot_update(context, snapshot_id, values): session = get_session() diff --git a/cinder/tests/test_volume.py b/cinder/tests/test_volume.py index 95ae9cf33ad..278e10584f7 100644 --- a/cinder/tests/test_volume.py +++ b/cinder/tests/test_volume.py @@ -857,6 +857,159 @@ def test_volume_api_update_snapshot(self): snap = db.snapshot_get(context.get_admin_context(), snapshot['id']) self.assertEquals(snap['display_name'], 'test update name') + def test_volume_get_active_by_window(self): + # Find all all volumes valid within a timeframe window. + try: # Not in window + db.volume_create( + self.context, + { + 'id': 1, + 'host': 'devstack', + 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), + 'deleted': True, 'status': 'deleted', + 'deleted_at': datetime.datetime(1, 2, 1, 1, 1, 1), + } + ) + except exception.VolumeNotFound: + pass + + try: # In - deleted in window + db.volume_create( + self.context, + { + 'id': 2, + 'host': 'devstack', + 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), + 'deleted': True, 'status': 'deleted', + 'deleted_at': datetime.datetime(1, 3, 10, 1, 1, 1), + } + ) + except exception.VolumeNotFound: + pass + + try: # In - deleted after window + db.volume_create( + self.context, + { + 'id': 3, + 'host': 'devstack', + 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), + 'deleted': True, 'status': 'deleted', + 'deleted_at': datetime.datetime(1, 5, 1, 1, 1, 1), + } + ) + except exception.VolumeNotFound: + pass + + # In - created in window + db.volume_create( + self.context, + { + 'id': 4, + 'host': 'devstack', + 'created_at': datetime.datetime(1, 3, 10, 1, 1, 1), + } + ) + + # Not of window. + db.volume_create( + self.context, + { + 'id': 5, + 'host': 'devstack', + 'created_at': datetime.datetime(1, 5, 1, 1, 1, 1), + } + ) + + volumes = db.volume_get_active_by_window( + self.context, + datetime.datetime(1, 3, 1, 1, 1, 1), + datetime.datetime(1, 4, 1, 1, 1, 1)) + self.assertEqual(len(volumes), 3) + self.assertEqual(volumes[0].id, u'2') + self.assertEqual(volumes[1].id, u'3') + self.assertEqual(volumes[2].id, u'4') + + def test_snapshot_get_active_by_window(self): + # Find all all snapshots valid within a timeframe window. + vol = db.volume_create(self.context, {'id': 1}) + + try: # Not in window + db.snapshot_create( + self.context, + { + 'id': 1, + 'host': 'devstack', + 'volume_id': 1, + 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), + 'deleted': True, 'status': 'deleted', + 'deleted_at': datetime.datetime(1, 2, 1, 1, 1, 1), + } + ) + except exception.SnapshotNotFound: + pass + + try: # In - deleted in window + db.snapshot_create( + self.context, + { + 'id': 2, + 'host': 'devstack', + 'volume_id': 1, + 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), + 'deleted': True, 'status': 'deleted', + 'deleted_at': datetime.datetime(1, 3, 10, 1, 1, 1), + } + ) + except exception.SnapshotNotFound: + pass + + try: # In - deleted after window + db.snapshot_create( + self.context, + { + 'id': 3, + 'host': 'devstack', + 'volume_id': 1, + 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1), + 'deleted': True, 'status': 'deleted', + 'deleted_at': datetime.datetime(1, 5, 1, 1, 1, 1), + } + ) + except exception.SnapshotNotFound: + pass + + # In - created in window + db.snapshot_create( + self.context, + { + 'id': 4, + 'host': 'devstack', + 'volume_id': 1, + 'created_at': datetime.datetime(1, 3, 10, 1, 1, 1), + } + ) + + # Not of window. + db.snapshot_create( + self.context, + { + 'id': 5, + 'host': 'devstack', + 'volume_id': 1, + 'created_at': datetime.datetime(1, 5, 1, 1, 1, 1), + } + ) + + snapshots = db.snapshot_get_active_by_window( + self.context, + datetime.datetime(1, 3, 1, 1, 1, 1), + datetime.datetime(1, 4, 1, 1, 1, 1)) + self.assertEqual(len(snapshots), 3) + self.assertEqual(snapshots[0].id, u'2') + self.assertEqual(snapshots[1].id, u'3') + self.assertEqual(snapshots[2].id, u'4') + class DriverTestCase(test.TestCase): """Base Test class for Drivers.""" diff --git a/cinder/volume/utils.py b/cinder/volume/utils.py index 5d53219bf1c..b7f7b354683 100644 --- a/cinder/volume/utils.py +++ b/cinder/volume/utils.py @@ -54,10 +54,11 @@ def notify_usage_exists(context, volume_ref, current_period=False): 'exists', extra_usage_info=extra_usage_info) -def _usage_from_volume(context, volume_ref, **kw): - def null_safe_str(s): - return str(s) if s else '' +def null_safe_str(s): + return str(s) if s else '' + +def _usage_from_volume(context, volume_ref, **kw): usage_info = dict(tenant_id=volume_ref['project_id'], user_id=volume_ref['user_id'], volume_id=volume_ref['id'], @@ -86,3 +87,35 @@ def notify_about_volume_usage(context, volume, event_suffix, notifier_api.notify(context, 'volume.%s' % host, 'volume.%s' % event_suffix, notifier_api.INFO, usage_info) + + +def _usage_from_snapshot(context, snapshot_ref, **extra_usage_info): + usage_info = { + 'tenant_id': snapshot_ref['project_id'], + 'user_id': snapshot_ref['user_id'], + 'volume_id': snapshot_ref['volume_id'], + 'volume_size': snapshot_ref['volume_size'], + 'snapshot_id': snapshot_ref['id'], + 'display_name': snapshot_ref['display_name'], + 'created_at': str(snapshot_ref['created_at']), + 'status': snapshot_ref['status'], + 'deleted': null_safe_str(snapshot_ref['deleted']) + } + + usage_info.update(extra_usage_info) + return usage_info + + +def notify_about_snapshot_usage(context, snapshot, event_suffix, + extra_usage_info=None, host=None): + if not host: + host = FLAGS.host + + if not extra_usage_info: + extra_usage_info = {} + + usage_info = _usage_from_snapshot(context, snapshot, **extra_usage_info) + + notifier_api.notify(context, 'snapshot.%s' % host, + 'snapshot.%s' % event_suffix, + notifier_api.INFO, usage_info)