diff --git a/reports/public_outbound_bandwidth.py b/reports/public_outbound_bandwidth.py new file mode 100644 index 0000000..d03bcdb --- /dev/null +++ b/reports/public_outbound_bandwidth.py @@ -0,0 +1,72 @@ + +import datetime +import json +import logging +import os +import sys + +sys.path.append(os.environ.get('STACKTACH_INSTALL_DIR', '/stacktach')) + +from stacktach import datetime_to_decimal as dt +from stacktach import models + +logging.basicConfig() +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) + +def __get_previous_period(time): + last_period = time - datetime.timedelta(days=1) + start = datetime.datetime(year=last_period.year, + month=last_period.month, + day=last_period.day) + end = datetime.datetime(year=time.year, + month=time.month, + day=time.day) + return start, end + + +def __get_instance_exists(beginning, ending): + filters = { + 'audit_period_beginning__gte': beginning, + 'audit_period_ending__lt': ending, + } + return models.InstanceExists.objects.filter(**filters) + + +def __audit_for_instance_exists(beginning, ending): + beginning_decimal = dt.dt_to_decimal(beginning) + ending_decimal = dt.dt_to_decimal(ending) + instance_exists = __get_instance_exists(beginning_decimal, ending_decimal) + total_bw = reduce(lambda x, y: x + y.bandwidth_public_out, instance_exists, + 0) + report = { + 'total_public_outbound_bandwidth': total_bw, + } + + return report + + +def __store_report_in_db(start, end, report): + values = { + 'json': __make_json_report(report), + 'created': dt.dt_to_decimal(datetime.datetime.utcnow()), + 'period_start': start, + 'period_end': end, + 'version': 1, + 'name': 'public outbound bandwidth' + } + + report = models.JsonReport(**values) + report.save() + + +def __make_json_report(report): + return json.dumps(report) + + +if __name__ == '__main__': + start, end = __get_previous_period(datetime.datetime.utcnow()) + logger.debug("Aggregating bw usage for period: %s to %s" % (start, end)) + report = __audit_for_instance_exists(start, end) + __store_report_in_db(start, end, report) + diff --git a/stacktach/migrations/0007_auto__add_field_instanceexists_bandwidth_public_out.py b/stacktach/migrations/0007_auto__add_field_instanceexists_bandwidth_public_out.py new file mode 100644 index 0000000..7d7da1c --- /dev/null +++ b/stacktach/migrations/0007_auto__add_field_instanceexists_bandwidth_public_out.py @@ -0,0 +1,225 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'InstanceExists.bandwidth_public_out' + db.add_column(u'stacktach_instanceexists', 'bandwidth_public_out', + self.gf('django.db.models.fields.BigIntegerField')(null=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'InstanceExists.bandwidth_public_out' + db.delete_column(u'stacktach_instanceexists', 'bandwidth_public_out') + + + models = { + u'stacktach.deployment': { + 'Meta': {'object_name': 'Deployment'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'stacktach.genericrawdata': { + 'Meta': {'object_name': 'GenericRawData'}, + 'deployment': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.Deployment']"}), + 'event': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'host': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'json': ('django.db.models.fields.TextField', [], {}), + 'message_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'publisher': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'request_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'routing_key': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'service': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'tenant': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'when': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}) + }, + u'stacktach.glancerawdata': { + 'Meta': {'object_name': 'GlanceRawData'}, + 'deployment': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.Deployment']"}), + 'event': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'host': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image_type': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'db_index': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'json': ('django.db.models.fields.TextField', [], {}), + 'owner': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'publisher': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'request_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'routing_key': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'service': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'db_index': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '36', 'null': 'True', 'blank': 'True'}), + 'when': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}) + }, + u'stacktach.imagedeletes': { + 'Meta': {'object_name': 'ImageDeletes'}, + 'deleted_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'raw': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.GlanceRawData']", 'null': 'True'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}) + }, + u'stacktach.imageexists': { + 'Meta': {'object_name': 'ImageExists'}, + 'audit_period_beginning': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'audit_period_ending': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'created_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'delete': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.ImageDeletes']"}), + 'deleted_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'fail_reason': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'raw': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': u"orm['stacktach.GlanceRawData']"}), + 'send_status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}), + 'size': ('django.db.models.fields.BigIntegerField', [], {'max_length': '20'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '50', 'db_index': 'True'}), + 'usage': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.ImageUsage']"}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}) + }, + u'stacktach.imageusage': { + 'Meta': {'object_name': 'ImageUsage'}, + 'created_at': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_raw': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.GlanceRawData']", 'null': 'True'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'size': ('django.db.models.fields.BigIntegerField', [], {'max_length': '20'}), + 'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}) + }, + u'stacktach.instancedeletes': { + 'Meta': {'object_name': 'InstanceDeletes'}, + 'deleted_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'launched_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'raw': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.RawData']", 'null': 'True'}) + }, + u'stacktach.instanceexists': { + 'Meta': {'object_name': 'InstanceExists'}, + 'audit_period_beginning': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'audit_period_ending': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'bandwidth_public_out': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}), + 'delete': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.InstanceDeletes']"}), + 'deleted_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'fail_reason': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '300', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'instance_type_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'launched_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'message_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'os_architecture': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_distro': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_version': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'raw': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.RawData']"}), + 'rax_options': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'send_status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'db_index': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '50', 'db_index': 'True'}), + 'tenant': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'usage': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.InstanceUsage']"}) + }, + u'stacktach.instancereconcile': { + 'Meta': {'object_name': 'InstanceReconcile'}, + 'deleted_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'instance_type_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'launched_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'os_architecture': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_distro': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_version': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'rax_options': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'row_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'row_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'source': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '150', 'null': 'True', 'blank': 'True'}), + 'tenant': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}) + }, + u'stacktach.instanceusage': { + 'Meta': {'object_name': 'InstanceUsage'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'instance_type_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'launched_at': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'os_architecture': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_distro': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_version': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'rax_options': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'request_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'tenant': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}) + }, + u'stacktach.jsonreport': { + 'Meta': {'object_name': 'JsonReport'}, + 'created': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'json': ('django.db.models.fields.TextField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'period_end': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'period_start': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'version': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + u'stacktach.lifecycle': { + 'Meta': {'object_name': 'Lifecycle'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'last_raw': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.RawData']", 'null': 'True'}), + 'last_state': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'last_task_state': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}) + }, + u'stacktach.rawdata': { + 'Meta': {'object_name': 'RawData'}, + 'deployment': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.Deployment']"}), + 'event': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'host': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image_type': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'db_index': 'True'}), + 'instance': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'json': ('django.db.models.fields.TextField', [], {}), + 'old_state': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '20', 'null': 'True', 'blank': 'True'}), + 'old_task': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'publisher': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'request_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'routing_key': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'service': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '20', 'null': 'True', 'blank': 'True'}), + 'task': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'tenant': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'when': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}) + }, + u'stacktach.rawdataimagemeta': { + 'Meta': {'object_name': 'RawDataImageMeta'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'os_architecture': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_distro': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'os_version': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'raw': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.RawData']"}), + 'rax_options': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) + }, + u'stacktach.requesttracker': { + 'Meta': {'object_name': 'RequestTracker'}, + 'completed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'duration': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_timing': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.Timing']", 'null': 'True'}), + 'lifecycle': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.Lifecycle']"}), + 'request_id': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'start': ('django.db.models.fields.DecimalField', [], {'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}) + }, + u'stacktach.timing': { + 'Meta': {'object_name': 'Timing'}, + 'diff': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6', 'db_index': 'True'}), + 'end_raw': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.RawData']"}), + 'end_when': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lifecycle': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['stacktach.Lifecycle']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'start_raw': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['stacktach.RawData']"}), + 'start_when': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '6'}) + } + } + + complete_apps = ['stacktach'] \ No newline at end of file diff --git a/stacktach/models.py b/stacktach/models.py index f60e636..6c1d269 100644 --- a/stacktach/models.py +++ b/stacktach/models.py @@ -291,6 +291,7 @@ class InstanceExists(models.Model): os_distro = models.TextField(null=True, blank=True) os_version = models.TextField(null=True, blank=True) rax_options = models.TextField(null=True, blank=True) + bandwidth_public_out = models.BigIntegerField(null=True) def deployment(self): return self.raw.deployment diff --git a/stacktach/notification.py b/stacktach/notification.py index 3756d7f..292aece 100644 --- a/stacktach/notification.py +++ b/stacktach/notification.py @@ -223,6 +223,9 @@ class NovaNotification(Notification): self.audit_period_ending = self.payload.get( 'audit_period_ending', None) self.message = self.payload.get('message', None) + bandwidth = self.payload.get('bandwidth', {}) + bandwidth_public = bandwidth.get('public', {}) + self.bandwidth_public_out = bandwidth_public.get('bw_out', 0) @property def host(self): diff --git a/stacktach/views.py b/stacktach/views.py index 1342e46..fa30e7a 100644 --- a/stacktach/views.py +++ b/stacktach/views.py @@ -270,6 +270,7 @@ def _process_exists(raw, notification): values['os_architecture'] = notification.os_architecture values['os_version'] = notification.os_version values['os_distro'] = notification.os_distro + values['bandwidth_public_out'] = notification.bandwidth_public_out deleted_at = notification.deleted_at if deleted_at and deleted_at != '': diff --git a/tests/unit/test_notification.py b/tests/unit/test_notification.py index 2e22d5c..5c8a426 100644 --- a/tests/unit/test_notification.py +++ b/tests/unit/test_notification.py @@ -31,6 +31,7 @@ from stacktach.notification import GlanceNotification from stacktach import db from stacktach import image_type from tests.unit import StacktachBaseTestCase +from tests.unit.utils import BANDWIDTH_PUBLIC_OUTBOUND from tests.unit.utils import REQUEST_ID_1 from tests.unit.utils import DECIMAL_DUMMY_TIME from tests.unit.utils import DUMMY_TIME @@ -116,7 +117,11 @@ class NovaNotificationTestCase(StacktachBaseTestCase): "com.rackspace__1__options": 'rax_opt', }, "state": 'state', - "new_task_state": 'task' + "new_task_state": 'task', + "bandwidth": { + "private": {"bw_in": 0, "bw_out": 264902}, + "public": {"bw_in": 0, "bw_out": 1697240969} + } } } deployment = "1" @@ -152,6 +157,79 @@ class NovaNotificationTestCase(StacktachBaseTestCase): self.assertEquals(notification.save(), raw) self.mox.VerifyAll() + def test_bandwidth_public_out_is_read_from_json(self): + body = { + "event_type": "compute.instance.exists", + '_context_request_id': REQUEST_ID_1, + '_context_project_id': TENANT_ID_1, + "timestamp": TIMESTAMP_1, + "publisher_id": "compute.global.preprod-ord.ohthree.com", + "payload": { + 'instance_id': INSTANCE_ID_1, + "status": "saving", + "container_format": "ovf", + "properties": { + "image_type": "snapshot", + }, + "tenant": "5877054", + "old_state": 'old_state', + "old_task_state": 'old_task', + "image_meta": { + "org.openstack__1__architecture": 'os_arch', + "org.openstack__1__os_distro": 'os_distro', + "org.openstack__1__os_version": 'os_version', + "com.rackspace__1__options": 'rax_opt', + }, + "state": 'state', + "new_task_state": 'task', + "bandwidth": { + "private": {"bw_in": 0, "bw_out": 264902}, + "public": {"bw_in": 0, "bw_out": BANDWIDTH_PUBLIC_OUTBOUND} + } + } + } + deployment = "1" + routing_key = "monitor.info" + json_body = json.dumps([routing_key, body]) + notification = NovaNotification(body, deployment, routing_key, + json_body) + self.assertEquals(notification.bandwidth_public_out, + BANDWIDTH_PUBLIC_OUTBOUND) + + def test_bandwidth_public_out_is_set_to_0_if_not_found_in_json(self): + body = { + "event_type": "compute.instance.exists", + '_context_request_id': REQUEST_ID_1, + '_context_project_id': TENANT_ID_1, + "timestamp": TIMESTAMP_1, + "publisher_id": "compute.global.preprod-ord.ohthree.com", + "payload": { + 'instance_id': INSTANCE_ID_1, + "status": "saving", + "container_format": "ovf", + "properties": { + "image_type": "snapshot", + }, + "tenant": "5877054", + "old_state": 'old_state', + "old_task_state": 'old_task', + "image_meta": { + "org.openstack__1__architecture": 'os_arch', + "org.openstack__1__os_distro": 'os_distro', + "org.openstack__1__os_version": 'os_version', + "com.rackspace__1__options": 'rax_opt', + }, + "state": 'state', + "new_task_state": 'task' + } + } + deployment = "1" + routing_key = "monitor.info" + json_body = json.dumps([routing_key, body]) + notification = NovaNotification(body, deployment, routing_key, + json_body) + self.assertEquals(notification.bandwidth_public_out, 0) + class GlanceNotificationTestCase(StacktachBaseTestCase): def setUp(self): diff --git a/tests/unit/test_stacktach.py b/tests/unit/test_stacktach.py index 3c8da30..fc24346 100644 --- a/tests/unit/test_stacktach.py +++ b/tests/unit/test_stacktach.py @@ -24,6 +24,7 @@ import json import mox import utils +from utils import BANDWIDTH_PUBLIC_OUTBOUND from utils import INSTANCE_ID_1 from utils import OS_VERSION_1 from utils import OS_ARCH_1 @@ -840,6 +841,7 @@ class StacktachUsageParsingTestCase(StacktachBaseTestCase): notification.deleted_at = '' notification.instance_type_id = INSTANCE_TYPE_ID_1 notification.message_id = MESSAGE_ID_1 + notification.bandwidth_public_out = BANDWIDTH_PUBLIC_OUTBOUND raw = self.mox.CreateMockAnything() usage = self.mox.CreateMockAnything() launched_range = (launch_decimal, launch_decimal+1) @@ -859,7 +861,8 @@ class StacktachUsageParsingTestCase(StacktachBaseTestCase): 'rax_options': RAX_OPTIONS_1, 'os_architecture': OS_ARCH_1, 'os_version': OS_VERSION_1, - 'os_distro': OS_DISTRO_1 + 'os_distro': OS_DISTRO_1, + 'bandwidth_public_out': BANDWIDTH_PUBLIC_OUTBOUND } exists = self.mox.CreateMockAnything() views.STACKDB.create_instance_exists(**exists_values).AndReturn(exists) @@ -903,6 +906,7 @@ class StacktachUsageParsingTestCase(StacktachBaseTestCase): notification.instance_type_id = INSTANCE_TYPE_ID_1 notification.message_id = MESSAGE_ID_1 notification.deleted_at = str(delete_time) + notification.bandwidth_public_out = BANDWIDTH_PUBLIC_OUTBOUND raw = self.mox.CreateMockAnything() usage = self.mox.CreateMockAnything() launched_range = (launch_decimal, launch_decimal+1) @@ -928,7 +932,8 @@ class StacktachUsageParsingTestCase(StacktachBaseTestCase): 'rax_options': RAX_OPTIONS_1, 'os_architecture': OS_ARCH_1, 'os_version': OS_VERSION_1, - 'os_distro': OS_DISTRO_1 + 'os_distro': OS_DISTRO_1, + 'bandwidth_public_out': BANDWIDTH_PUBLIC_OUTBOUND } exists = self.mox.CreateMockAnything() views.STACKDB.create_instance_exists(**exists_values).AndReturn(exists) diff --git a/tests/unit/utils.py b/tests/unit/utils.py index a940b96..f33873e 100644 --- a/tests/unit/utils.py +++ b/tests/unit/utils.py @@ -39,6 +39,8 @@ DECIMAL_DUMMY_TIME = dt.dt_to_decimal(DUMMY_TIME) MESSAGE_ID_1 = "7f28f81b-29a2-43f2-9ba1-ccb3e53ab6c8" MESSAGE_ID_2 = "4d596126-0f04-4329-865f-7b9a7bd69bcf" +BANDWIDTH_PUBLIC_OUTBOUND = 1697240969 + REQUEST_ID_1 = 'req-611a4d70-9e47-4b27-a95e-27996cc40c06' REQUEST_ID_2 = 'req-a951dec0-52ee-425d-9f56-d68bd1ad00ac' REQUEST_ID_3 = 'req-039a33f7-5849-4406-8166-4db8cd085f52'