refactor(config_management): move config_group_hosts to related table

ref: #353
This commit is contained in:
2024-10-16 18:49:08 +09:30
parent 8219bf6c9d
commit 0c9a9f5ae1
12 changed files with 101 additions and 208 deletions

View File

@ -74,6 +74,7 @@ class ConfigGroupsSerializer(ConfigGroupsSerializerBase):
'parent',
'name',
'config',
'hosts',
'url',
]
read_only_fields = [

View File

@ -4,7 +4,7 @@ from rest_framework import serializers
from api.serializers.config import ParentGroupSerializer
from config_management.models.groups import ConfigGroupHosts
from config_management.models.groups import ConfigGroups
from itam.models.device import Device
@ -12,15 +12,13 @@ from itam.models.device import Device
class DeviceConfigGroupsSerializer(serializers.ModelSerializer):
name = serializers.CharField(source='group.name', read_only=True)
url = serializers.HyperlinkedIdentityField(
view_name="API:_api_config_group", format="html"
)
class Meta:
model = ConfigGroupHosts
model = ConfigGroups
fields = [
'id',
@ -43,7 +41,7 @@ class DeviceSerializer(serializers.ModelSerializer):
config = serializers.SerializerMethodField('get_device_config')
groups = DeviceConfigGroupsSerializer(source='configgrouphosts_set', many=True, read_only=True)
groups = DeviceConfigGroupsSerializer(source='configgroups_set', many=True, read_only=True)
def get_device_config(self, device):

View File

@ -1,21 +0,0 @@
from itam.models.device import Device
from config_management.models.groups import ConfigGroups, ConfigGroupHosts
from core.forms.common import CommonModelForm
class ConfigGroupHostsForm(CommonModelForm):
__name__ = 'asdsa'
class Meta:
fields = [
'host'
]
model = ConfigGroupHosts
prefix = 'config_group_hosts'

View File

@ -0,0 +1,75 @@
# Generated by Django 5.1.2 on 2024-10-16 06:54
from django.db import migrations, models
def migrate_to_configgroups_hosts(apps, schema_editor):
if schema_editor.connection.alias != "default":
return
print('')
ConfigGroups = apps.get_model('config_management', 'ConfigGroups')
ConfigGroupHosts = apps.get_model('config_management', 'ConfigGroupHosts')
current_data = ConfigGroupHosts.objects.all()
for host in current_data:
print(f'Begin migrating host {host.host} in group {host.group}:')
config_group = ConfigGroups.objects.get(pk = host.group.id)
print(f' migrate {host.host} in group {config_group}')
config_group.hosts.add( host.host )
try:
was_migrated = ConfigGroups.objects.get(pk = host.group.id)
if host.host in was_migrated.hosts.all():
print(f' successfully migrated {host.id} {host.host} to new table')
ConfigGroupHosts.objects.get(pk = host.id).delete()
try:
ConfigGroupHosts.objects.get(pk = host.id)
print(f' Error Failed to remove old data for host {host.host}')
except ConfigGroupHosts.DoesNotExist:
print(f' Old data removed')
except ConfigGroupHosts.DoesNotExist:
print(f' Error, {host.host} was not migrated to new table')
old_data = ConfigGroupHosts.objects.all()
if len(old_data) == 0:
print(f'Successfully migrated data to new table, removing old table')
migrations.DeleteModel("ConfigGroupHosts")
class Migration(migrations.Migration):
dependencies = [
('config_management', '0007_configgroups_hosts'),
]
operations = [
migrations.RunPython(migrate_to_configgroups_hosts),
]

View File

@ -95,6 +95,13 @@ class ConfigGroups(GroupsCommonFields, SaveHistory):
verbose_name = 'Configuration'
)
hosts = models.ManyToManyField(
to = Device,
blank = True,
help_text = 'Hosts that are part of this group',
verbose_name = 'Hosts'
)
page_layout: dict = [
{

View File

@ -4,11 +4,9 @@ from rest_framework.reverse import reverse
from access.serializers.organization import OrganizationBaseSerializer
from app.serializers.user import UserBaseSerializer
from config_management.models.groups import ConfigGroups
from api.v2.serializers.itam.device import DeviceModelBaseSerializer
class ConfigGroupBaseSerializer(serializers.ModelSerializer):
@ -94,6 +92,7 @@ class ConfigGroupModelSerializer(ConfigGroupBaseSerializer):
'name',
'model_notes',
'config',
'hosts',
'is_global',
'created',
'modified',
@ -163,6 +162,8 @@ class ConfigGroupModelSerializer(ConfigGroupBaseSerializer):
class ConfigGroupViewSerializer(ConfigGroupModelSerializer):
hosts = DeviceModelBaseSerializer(read_only = True, many = True)
parent = ConfigGroupBaseSerializer( read_only = True )
organization = OrganizationBaseSerializer( many=False, read_only=True )

View File

@ -48,20 +48,18 @@
{% include 'content/section.html.j2' with tab=form.tabs.hosts %}
<input type="button" value="Add Host" onclick="window.location='{% url 'Config Management:_group_add_host' group.id %}';">
<table class="data">
<tr>
<th>Name</th>
<th>Organization</th>
<th>&nbsp;</th>
</tr>
{% if config_group_hosts %}
{% for host in config_group_hosts %}
{% if group.hosts %}
{% for host in group.hosts.all %}
<tr>
<td><a href="{% url 'ITAM:_device_view' pk=host.host.id %}">{{ host.host }}</a></td>
<td>{{ host.host.organization }}</td>
<td><a href="{% url 'Config Management:_group_delete_host' group_id=group.id pk=host.id %}">Delete</a></td>
<td><a href="{% url 'ITAM:_device_view' pk=host.id %}">{{ host }}</a></td>
<td>{{ host.organization }}</td>
<td>&nbsp;</td>
</tr>
{% endfor %}
{% else %}

View File

@ -1,10 +1,8 @@
from django.urls import path
from config_management.views.groups import groups
from config_management.views.groups.groups import GroupHostAdd, GroupHostDelete
from config_management.views.groups import software
# from config_management.views.groups.software import GroupSoftwareAdd, GroupSoftwareChange, GroupSoftwareDelete
app_name = "Config Management"
@ -21,7 +19,4 @@ urlpatterns = [
path("group/<int:group_id>/software/<int:pk>", software.Change.as_view(), name="_group_software_change"),
path("group/<int:group_id>/software/<int:pk>/delete", software.Delete.as_view(), name="_group_software_delete"),
path('group/<int:pk>/host', GroupHostAdd.as_view(), name='_group_add_host'),
path('group/<int:group_id>/host/<int:pk>/delete', GroupHostDelete.as_view(), name='_group_delete_host'),
]

View File

@ -13,9 +13,8 @@ from itam.models.device import Device
from settings.models.user_settings import UserSettings
from config_management.forms.group_hosts import ConfigGroupHostsForm
from config_management.forms.group.group import ConfigGroupForm, DetailForm
from config_management.models.groups import ConfigGroups, ConfigGroupHosts, ConfigGroupSoftware
from config_management.models.groups import ConfigGroups, ConfigGroupSoftware
@ -158,7 +157,6 @@ class View(ChangeView):
context['config'] = json.dumps(json.loads(self.object.render_config()), indent=4, sort_keys=True)
context['config_group_hosts'] = ConfigGroupHosts.objects.filter(group_id = self.kwargs['pk']).order_by('-host')
context['tickets'] = TicketLinkedItem.objects.filter(
item = int(self.kwargs['pk']),
@ -245,83 +243,3 @@ class Delete(DeleteView):
def get_success_url(self, **kwargs):
return reverse('Config Management:Groups')
class GroupHostAdd(AddView):
model = ConfigGroupHosts
parent_model = ConfigGroups
permission_required = [
'config_management.add_configgrouphosts',
]
template_name = 'form.html.j2'
form_class = ConfigGroupHostsForm
def form_valid(self, form):
form.instance.group_id = self.kwargs['pk']
form.instance.organization = self.parent_model.objects.get(pk=form.instance.group_id).organization
return super().form_valid(form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Add Host to Group'
return context
def get_form(self, form_class=None):
form_class = super().get_form(form_class=None)
group = ConfigGroups.objects.get(pk=self.kwargs['pk'])
exsting_group_hosts = ConfigGroupHosts.objects.filter(group=group)
form_class.fields["host"].queryset = form_class.fields["host"].queryset.filter(
).exclude(
id__in=exsting_group_hosts.values_list('host', flat=True)
)
return form_class
def get_success_url(self, **kwargs):
return reverse('Config Management:_group_view', args=[self.kwargs['pk'],])
class GroupHostDelete(DeleteView):
model = ConfigGroupHosts
permission_required = [
'config_management.delete_configgrouphosts',
]
template_name = 'form.html.j2'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['content_title'] = 'Delete ' + self.object.host.name
return context
def get_success_url(self, **kwargs):
return reverse('Config Management:_group_view', args=[self.kwargs['group_id'],])

View File

@ -196,17 +196,17 @@
<th>Added</th>
<th>&nbsp;</th>
</tr>
{% if config_groups %}
{% for group in config_groups %}
{% if device.configgroups_set %}
{% for group in device.configgroups_set.all %}
<tr>
<td><a href="{% url 'Config Management:_group_view' pk=group.group.id %}">{{ group.group }}</a></td>
<td><a href="{% url 'Config Management:_group_view' pk=group.id %}">{{ group }}</a></td>
<td>{{ group.created }}</td>
<td><a href="{% url 'Config Management:_group_delete_host' group_id=group.group.id pk=group.id %}">Delete</a></td>
<td>&nbsp;</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="3">Nothing Found</td>
<td colspan="3">Nothing Found.</td>
</tr>
{% endif %}
</table>

View File

@ -374,24 +374,6 @@ class DeviceAPI(TestCase):
assert type(self.api_data['modified']) is str
def test_api_field_exists_groups(self):
""" Test for existance of API Field
groups field must exist
"""
assert 'groups' in self.api_data
def test_api_field_type_groups(self):
""" Test for type for API Field
groups field must be list
"""
assert type(self.api_data['groups']) is list
def test_api_field_exists_organization(self):
""" Test for existance of API Field
@ -428,63 +410,6 @@ class DeviceAPI(TestCase):
assert type(self.api_data['url']) is Hyperlink
def test_api_field_exists_groups_id(self):
""" Test for existance of API Field
groups.id field must exist
"""
assert 'id' in self.api_data['groups'][0]
def test_api_field_type_groups_id(self):
""" Test for type for API Field
groups.id field must be int
"""
assert type(self.api_data['groups'][0]['id']) is int
def test_api_field_exists_groups_name(self):
""" Test for existance of API Field
groups.name field must exist
"""
assert 'name' in self.api_data['groups'][0]
def test_api_field_type_groups_name(self):
""" Test for type for API Field
groups.name field must be str
"""
assert type(self.api_data['groups'][0]['name']) is str
def test_api_field_exists_groups_url(self):
""" Test for existance of API Field
groups.url field must exist
"""
assert 'url' in self.api_data['groups'][0]
def test_api_field_type_groups_url(self):
""" Test for type for API Field
groups.url field must be str
"""
assert type(self.api_data['groups'][0]['url']) is Hyperlink
def test_api_create_device_existing_uuid_matches_status_200(self):
"""Creation of existing device

View File

@ -8,9 +8,6 @@ from django.utils.decorators import method_decorator
from access.models import Organization
from config_management.models.groups import ConfigGroupHosts
from ..models.device import Device, DeviceSoftware, DeviceOperatingSystem
from ..models.software import Software
@ -143,7 +140,6 @@ class View(ChangeView):
config = self.object.get_configuration
context['config'] = json.dumps(config, indent=4, sort_keys=True)
context['config_groups'] = ConfigGroupHosts.objects.filter(host = self.object.id)
context['model_pk'] = self.kwargs['pk']
context['model_name'] = self.model._meta.verbose_name.replace(' ', '')