mirror of
https://github.com/ZwareBear/awx.git
synced 2026-05-17 22:28:38 -05:00
HostMetricSummaryMonthly API and Migrations
This commit is contained in:
@@ -385,6 +385,10 @@ class FieldLookupBackend(BaseFilterBackend):
|
|||||||
raise ParseError(json.dumps(e.messages, ensure_ascii=False))
|
raise ParseError(json.dumps(e.messages, ensure_ascii=False))
|
||||||
|
|
||||||
|
|
||||||
|
class HostMetricSummaryMonthlyFieldLookupBackend(FieldLookupBackend):
|
||||||
|
RESERVED_NAMES = ('page', 'page_size', 'format', 'order', 'order_by', 'search', 'type', 'past_months', 'count_disabled', 'no_truncate', 'limit')
|
||||||
|
|
||||||
|
|
||||||
class OrderByBackend(BaseFilterBackend):
|
class OrderByBackend(BaseFilterBackend):
|
||||||
"""
|
"""
|
||||||
Filter to apply ordering based on query string parameters.
|
Filter to apply ordering based on query string parameters.
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ from awx.main.models import (
|
|||||||
Group,
|
Group,
|
||||||
Host,
|
Host,
|
||||||
HostMetric,
|
HostMetric,
|
||||||
|
HostMetricSummaryMonthly,
|
||||||
Instance,
|
Instance,
|
||||||
InstanceGroup,
|
InstanceGroup,
|
||||||
InstanceLink,
|
InstanceLink,
|
||||||
@@ -5022,6 +5023,13 @@ class HostMetricSerializer(BaseSerializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HostMetricSummaryMonthlySerializer(BaseSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = HostMetricSummaryMonthly
|
||||||
|
read_only_fields = ("id", "date", "license_consumed", "license_capacity", "hosts_added", "hosts_deleted", "indirectly_managed_hosts")
|
||||||
|
fields = read_only_fields
|
||||||
|
|
||||||
|
|
||||||
class InstanceGroupSerializer(BaseSerializer):
|
class InstanceGroupSerializer(BaseSerializer):
|
||||||
show_capabilities = ['edit', 'delete']
|
show_capabilities = ['edit', 'delete']
|
||||||
capacity = serializers.SerializerMethodField()
|
capacity = serializers.SerializerMethodField()
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ from awx.api.views import (
|
|||||||
OAuth2TokenList,
|
OAuth2TokenList,
|
||||||
ApplicationOAuth2TokenList,
|
ApplicationOAuth2TokenList,
|
||||||
OAuth2ApplicationDetail,
|
OAuth2ApplicationDetail,
|
||||||
|
HostMetricSummaryMonthlyList,
|
||||||
)
|
)
|
||||||
from awx.api.views.mesh_visualizer import MeshVisualizer
|
from awx.api.views.mesh_visualizer import MeshVisualizer
|
||||||
|
|
||||||
@@ -113,6 +114,7 @@ v2_urls = [
|
|||||||
re_path(r'^inventories/', include(inventory_urls)),
|
re_path(r'^inventories/', include(inventory_urls)),
|
||||||
re_path(r'^hosts/', include(host_urls)),
|
re_path(r'^hosts/', include(host_urls)),
|
||||||
re_path(r'^host_metrics/', include(host_metric_urls)),
|
re_path(r'^host_metrics/', include(host_metric_urls)),
|
||||||
|
re_path(r'^host_metric_summary_monthly/$', HostMetricSummaryMonthlyList.as_view(), name='host_metric_summary_monthly_list'),
|
||||||
re_path(r'^groups/', include(group_urls)),
|
re_path(r'^groups/', include(group_urls)),
|
||||||
re_path(r'^inventory_sources/', include(inventory_source_urls)),
|
re_path(r'^inventory_sources/', include(inventory_source_urls)),
|
||||||
re_path(r'^inventory_updates/', include(inventory_update_urls)),
|
re_path(r'^inventory_updates/', include(inventory_update_urls)),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
# Python
|
# Python
|
||||||
import dateutil
|
import dateutil
|
||||||
|
import datetime
|
||||||
import functools
|
import functools
|
||||||
import html
|
import html
|
||||||
import itertools
|
import itertools
|
||||||
@@ -17,7 +18,6 @@ from collections import OrderedDict
|
|||||||
|
|
||||||
from urllib3.exceptions import ConnectTimeoutError
|
from urllib3.exceptions import ConnectTimeoutError
|
||||||
|
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import FieldError, ObjectDoesNotExist
|
from django.core.exceptions import FieldError, ObjectDoesNotExist
|
||||||
@@ -122,6 +122,7 @@ from awx.api.views.mixin import (
|
|||||||
UnifiedJobDeletionMixin,
|
UnifiedJobDeletionMixin,
|
||||||
NoTruncateMixin,
|
NoTruncateMixin,
|
||||||
)
|
)
|
||||||
|
from awx.api.filters import HostMetricSummaryMonthlyFieldLookupBackend
|
||||||
from awx.api.pagination import UnifiedJobEventPagination
|
from awx.api.pagination import UnifiedJobEventPagination
|
||||||
from awx.main.utils import set_environ
|
from awx.main.utils import set_environ
|
||||||
|
|
||||||
@@ -1551,6 +1552,37 @@ class HostMetricDetail(RetrieveDestroyAPIView):
|
|||||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
class HostMetricSummaryMonthlyList(ListAPIView):
|
||||||
|
name = _("Host Metrics Summary Monthly")
|
||||||
|
model = models.HostMetricSummaryMonthly
|
||||||
|
permission_classes = (IsSystemAdminOrAuditor,)
|
||||||
|
serializer_class = serializers.HostMetricSummaryMonthlySerializer
|
||||||
|
search_fields = ('date',)
|
||||||
|
filter_backends = [HostMetricSummaryMonthlyFieldLookupBackend]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset()
|
||||||
|
past_months = self.request.query_params.get('past_months', None)
|
||||||
|
date_from = self._get_date_from(past_months)
|
||||||
|
|
||||||
|
queryset = queryset.filter(date__gte=date_from)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_date_from(past_months, default=12, maximum=36):
|
||||||
|
try:
|
||||||
|
months_ago = int(past_months or default)
|
||||||
|
except ValueError:
|
||||||
|
months_ago = default
|
||||||
|
months_ago = min(months_ago, maximum)
|
||||||
|
months_ago = max(months_ago, 1)
|
||||||
|
|
||||||
|
date_from = datetime.date.today() - dateutil.relativedelta.relativedelta(months=months_ago)
|
||||||
|
# Set to beginning of the month
|
||||||
|
date_from = date_from.replace(day=1).isoformat()
|
||||||
|
return date_from
|
||||||
|
|
||||||
|
|
||||||
class HostList(HostRelatedSearchMixin, ListCreateAPIView):
|
class HostList(HostRelatedSearchMixin, ListCreateAPIView):
|
||||||
always_allow_superuser = False
|
always_allow_superuser = False
|
||||||
model = models.Host
|
model = models.Host
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ from awx.main.models import (
|
|||||||
Group,
|
Group,
|
||||||
Host,
|
Host,
|
||||||
HostMetric,
|
HostMetric,
|
||||||
|
HostMetricSummaryMonthly,
|
||||||
Instance,
|
Instance,
|
||||||
InstanceGroup,
|
InstanceGroup,
|
||||||
Inventory,
|
Inventory,
|
||||||
@@ -890,6 +891,33 @@ class HostMetricAccess(BaseAccess):
|
|||||||
return bool(self.user.is_superuser or (obj and obj.user == self.user))
|
return bool(self.user.is_superuser or (obj and obj.user == self.user))
|
||||||
|
|
||||||
|
|
||||||
|
class HostMetricSummaryMonthlyAccess(BaseAccess):
|
||||||
|
"""
|
||||||
|
- I can see host metrics when I'm a super user or system auditor.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model = HostMetricSummaryMonthly
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
if self.user.is_superuser or self.user.is_system_auditor:
|
||||||
|
qs = self.model.objects.all()
|
||||||
|
else:
|
||||||
|
qs = self.filtered_queryset()
|
||||||
|
return qs
|
||||||
|
|
||||||
|
def can_read(self, obj):
|
||||||
|
return bool(self.user.is_superuser or self.user.is_system_auditor or (obj and obj.user == self.user))
|
||||||
|
|
||||||
|
def can_add(self, data):
|
||||||
|
return False # There is no API endpoint to POST new settings.
|
||||||
|
|
||||||
|
def can_change(self, obj, data):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def can_delete(self, obj):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class InventoryAccess(BaseAccess):
|
class InventoryAccess(BaseAccess):
|
||||||
"""
|
"""
|
||||||
I can see inventory when:
|
I can see inventory when:
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Generated by Django 3.2.16 on 2023-02-10 12:26
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('main', '0175_add_hostmetric_fields'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='HostMetricSummaryMonthly',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('date', models.DateField(unique=True)),
|
||||||
|
('license_consumed', models.BigIntegerField(default=0, help_text='How much unique hosts are consumed from the license')),
|
||||||
|
('license_capacity', models.BigIntegerField(default=0, help_text="'License capacity as max. number of unique hosts")),
|
||||||
|
(
|
||||||
|
'hosts_added',
|
||||||
|
models.BigIntegerField(default=0, help_text='How many hosts were added in the associated month, consuming more license capacity'),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'hosts_deleted',
|
||||||
|
models.BigIntegerField(default=0, help_text='How many hosts were deleted in the associated month, freeing the license capacity'),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'indirectly_managed_hosts',
|
||||||
|
models.BigIntegerField(default=0, help_text='Manually entered number indirectly managed hosts for a certain month'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -16,6 +16,7 @@ from awx.main.models.inventory import ( # noqa
|
|||||||
Group,
|
Group,
|
||||||
Host,
|
Host,
|
||||||
HostMetric,
|
HostMetric,
|
||||||
|
HostMetricSummaryMonthly,
|
||||||
Inventory,
|
Inventory,
|
||||||
InventorySource,
|
InventorySource,
|
||||||
InventoryUpdate,
|
InventoryUpdate,
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ from awx.main.utils.execution_environments import to_container_path
|
|||||||
from awx.main.utils.licensing import server_product_name
|
from awx.main.utils.licensing import server_product_name
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['Inventory', 'Host', 'Group', 'InventorySource', 'InventoryUpdate', 'SmartInventoryMembership']
|
__all__ = ['Inventory', 'Host', 'Group', 'InventorySource', 'InventoryUpdate', 'SmartInventoryMembership', 'HostMetric', 'HostMetricSummaryMonthly']
|
||||||
|
|
||||||
logger = logging.getLogger('awx.main.models.inventory')
|
logger = logging.getLogger('awx.main.models.inventory')
|
||||||
|
|
||||||
@@ -850,6 +850,19 @@ class HostMetric(models.Model):
|
|||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|
||||||
|
class HostMetricSummaryMonthly(models.Model):
|
||||||
|
"""
|
||||||
|
HostMetric summaries computed by scheduled task <TODO> monthly
|
||||||
|
"""
|
||||||
|
|
||||||
|
date = models.DateField(unique=True)
|
||||||
|
license_consumed = models.BigIntegerField(default=0, help_text=_("How much unique hosts are consumed from the license"))
|
||||||
|
license_capacity = models.BigIntegerField(default=0, help_text=_("'License capacity as max. number of unique hosts"))
|
||||||
|
hosts_added = models.BigIntegerField(default=0, help_text=_("How many hosts were added in the associated month, consuming more license capacity"))
|
||||||
|
hosts_deleted = models.BigIntegerField(default=0, help_text=_("How many hosts were deleted in the associated month, freeing the license capacity"))
|
||||||
|
indirectly_managed_hosts = models.BigIntegerField(default=0, help_text=("Manually entered number indirectly managed hosts for a certain month"))
|
||||||
|
|
||||||
|
|
||||||
class InventorySourceOptions(BaseModel):
|
class InventorySourceOptions(BaseModel):
|
||||||
"""
|
"""
|
||||||
Common fields for InventorySource and InventoryUpdate.
|
Common fields for InventorySource and InventoryUpdate.
|
||||||
|
|||||||
Reference in New Issue
Block a user