feat(itim): Add service template support

!43 #69
This commit is contained in:
2024-07-21 09:17:46 +09:30
parent eac998b5cc
commit 0c8d1c8da1
6 changed files with 100 additions and 11 deletions

View File

@ -1,4 +1,4 @@
# Generated by Django 5.0.7 on 2024-07-20 20:40
# Generated by Django 5.0.7 on 2024-07-20 23:46
import django.db.models.deletion
from django.db import migrations, models

View File

@ -14,4 +14,4 @@ def json_pretty(value):
return str('{}')
return json.dumps(json.loads(value), indent=4, sort_keys=True)
return json.dumps(json.loads(value.replace("'", '"')), indent=4, sort_keys=True)

View File

@ -26,6 +26,10 @@ class ServiceForm(CommonModelForm):
id=self.instance.pk
)
self.fields['template'].queryset = self.fields['template'].queryset.exclude(
id=self.instance.pk
)
def clean(self):
@ -33,16 +37,28 @@ class ServiceForm(CommonModelForm):
device = cleaned_data.get("device")
cluster = cleaned_data.get("cluster")
is_template = cleaned_data.get("is_template")
template = cleaned_data.get("template")
port = cleaned_data.get("port")
if not device and not cluster:
if not is_template and not template:
raise ValidationError('A Service must be assigned to either a "Cluster" or a "Device".')
if not device and not cluster:
raise ValidationError('A Service must be assigned to either a "Cluster" or a "Device".')
if device and cluster:
if device and cluster:
raise ValidationError('A Service must only be assigned to either a "Cluster" or a "Device". Not both.')
raise ValidationError('A Service must only be assigned to either a "Cluster" or a "Device". Not both.')
if not port:
raise ValidationError('Port(s) must be assigned to a service.')
return cleaned_data

View File

@ -1,4 +1,4 @@
# Generated by Django 5.0.7 on 2024-07-20 20:40
# Generated by Django 5.0.7 on 2024-07-20 23:46
import access.fields
import access.models
@ -79,6 +79,7 @@ class Migration(migrations.Migration):
('is_global', models.BooleanField(default=False)),
('model_notes', models.TextField(blank=True, default=None, null=True, verbose_name='Notes')),
('id', models.AutoField(primary_key=True, serialize=False, unique=True)),
('is_template', models.BooleanField(default=False, help_text='Is this service to be used as a template', verbose_name='Template')),
('name', models.CharField(help_text='Name of the Service', max_length=50, verbose_name='Name')),
('config', models.JSONField(blank=True, default=None, help_text='Cluster Configuration', null=True, verbose_name='Configuration')),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
@ -87,7 +88,8 @@ class Migration(migrations.Migration):
('dependent_service', models.ManyToManyField(blank=True, default=None, help_text='Services that this service depends upon', to='itim.service', verbose_name='Dependent Services')),
('device', models.ForeignKey(blank=True, default=None, help_text='Device the service is assigned to', null=True, on_delete=django.db.models.deletion.CASCADE, to='itam.device', verbose_name='Device')),
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists])),
('port', models.ManyToManyField(help_text='Port the service is available on', to='itim.port', verbose_name='Port')),
('port', models.ManyToManyField(blank=True, help_text='Port the service is available on', to='itim.port', verbose_name='Port')),
('template', models.ForeignKey(blank=True, default=None, help_text='Template this service uses', null=True, on_delete=django.db.models.deletion.CASCADE, to='itim.service', verbose_name='Template')),
],
options={
'verbose_name': 'Service',

View File

@ -93,6 +93,23 @@ class Service(TenancyObject):
blank=False
)
is_template = models.BooleanField(
blank = False,
default = False,
help_text = 'Is this service to be used as a template',
verbose_name = 'Template',
)
template = models.ForeignKey(
'self',
blank = True,
default = None,
help_text = 'Template this service uses',
null = True,
on_delete = models.CASCADE,
verbose_name = 'Template',
)
name = models.CharField(
blank = False,
help_text = 'Name of the Service',
@ -132,7 +149,7 @@ class Service(TenancyObject):
port = models.ManyToManyField(
Port,
blank = False,
blank = True,
help_text = 'Port the service is available on',
verbose_name = 'Port',
)
@ -149,6 +166,27 @@ class Service(TenancyObject):
modified = AutoLastModifiedField()
@property
def config_variables(self):
if self.is_template:
return self.config
if self.template:
template_config: dict = Service.objects.get(id=self.template.id).config
template_config.update(self.config)
return template_config
else:
return self.config
return None
def __str__(self):

View File

@ -1,5 +1,6 @@
{% extends 'base.html.j2' %}
{% load json %}
{% load markdown %}
{% block content %}
@ -68,6 +69,7 @@
</svg>Back to Services</button>
<button id="defaultOpen" class="tablinks" onclick="openCity(event, 'Details')">Details</button>
<button class="tablinks" onclick="openCity(event, 'Rendered_Config')">Rendered Config</button>
{% if perms.assistance.change_service %}
<button class="tablinks" onclick="openCity(event, 'Notes')">Notes</button>
{% endif %}
@ -88,6 +90,19 @@
<span>{{ form.name.value }}</span>
</div>
{% if form.template.value or form.is_template.value %}
<div class="detail-view-field">
<label>{{ form.is_template.label }}</label>
<span>
{% if form.is_template.value %}
{{ form.is_template.value }}
{% else %}
<a href="{% url 'ITIM:_service_view' item.template.pk %}">{{ item.template }}</a>
{% endif %}
</span>
</div>
{% endif %}
<div class="detail-view-field">
<label>{{ form.organization.label }}</label>
<span>
@ -157,12 +172,19 @@
<th>Name</th>
<th>Description</th>
</tr>
{% if item.port.all %}
{% if item.port.all and not item.template %}
{% for port in item.port.all %}
<tr>
<td><a href="{% url 'Settings:_port_view' item.pk %}">{{ port }}</a></td>
<td>{{ port.description }}</td>
</tr>
{% endfor %}
{% elif not item.port.all and item.template %}
{% for port in item.template.port.all %}
<tr>
<td><a href="{% url 'Settings:_port_view' item.pk %}">{{ port }}</a></td>
<td>{{ port.description }}</td>
</tr>
{% endfor%}
{% else %}
<tr>
@ -199,7 +221,7 @@
<div style="display: block; width: 100%;">
<h3>Service Config</h3>
<br>
<textarea cols="90" rows="30" readonly>{{ item.config }}</textarea>
<textarea cols="90" rows="30" readonly>{{ item.config | json_pretty }}</textarea>
</div>
<br>
@ -210,6 +232,17 @@
</div>
<div id="Rendered_Config" class="tabcontent">
<h3>
Rendered Config
</h3>
<div>
<textarea cols="90" rows="30" readonly>{{ item.config_variables | json_pretty }}</textarea>
</div>
</div>
{% if perms.assistance.change_knowledgebase %}
<div id="Notes" class="tabcontent">
<h3>