netbox-toolkit-plugin 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- netbox_toolkit/__init__.py +30 -0
- netbox_toolkit/admin.py +16 -0
- netbox_toolkit/api/__init__.py +0 -0
- netbox_toolkit/api/mixins.py +54 -0
- netbox_toolkit/api/schemas.py +234 -0
- netbox_toolkit/api/serializers.py +158 -0
- netbox_toolkit/api/urls.py +10 -0
- netbox_toolkit/api/views/__init__.py +10 -0
- netbox_toolkit/api/views/command_logs.py +170 -0
- netbox_toolkit/api/views/commands.py +267 -0
- netbox_toolkit/config.py +159 -0
- netbox_toolkit/connectors/__init__.py +15 -0
- netbox_toolkit/connectors/base.py +97 -0
- netbox_toolkit/connectors/factory.py +301 -0
- netbox_toolkit/connectors/netmiko_connector.py +443 -0
- netbox_toolkit/connectors/scrapli_connector.py +486 -0
- netbox_toolkit/exceptions.py +36 -0
- netbox_toolkit/filtersets.py +85 -0
- netbox_toolkit/forms.py +31 -0
- netbox_toolkit/migrations/0001_initial.py +54 -0
- netbox_toolkit/migrations/0002_alter_command_options_alter_command_unique_together_and_more.py +66 -0
- netbox_toolkit/migrations/0003_permission_system_update.py +48 -0
- netbox_toolkit/migrations/0004_remove_django_permissions.py +77 -0
- netbox_toolkit/migrations/0005_alter_command_options_and_more.py +25 -0
- netbox_toolkit/migrations/0006_commandlog_parsed_data_commandlog_parsing_success_and_more.py +28 -0
- netbox_toolkit/migrations/0007_alter_commandlog_parsing_template.py +18 -0
- netbox_toolkit/migrations/__init__.py +0 -0
- netbox_toolkit/models.py +89 -0
- netbox_toolkit/navigation.py +30 -0
- netbox_toolkit/search.py +21 -0
- netbox_toolkit/services/__init__.py +7 -0
- netbox_toolkit/services/command_service.py +357 -0
- netbox_toolkit/services/device_service.py +87 -0
- netbox_toolkit/services/rate_limiting_service.py +228 -0
- netbox_toolkit/static/netbox_toolkit/css/toolkit.css +143 -0
- netbox_toolkit/static/netbox_toolkit/js/toolkit.js +657 -0
- netbox_toolkit/tables.py +37 -0
- netbox_toolkit/templates/netbox_toolkit/command.html +108 -0
- netbox_toolkit/templates/netbox_toolkit/command_edit.html +10 -0
- netbox_toolkit/templates/netbox_toolkit/command_list.html +12 -0
- netbox_toolkit/templates/netbox_toolkit/commandlog.html +170 -0
- netbox_toolkit/templates/netbox_toolkit/commandlog_list.html +4 -0
- netbox_toolkit/templates/netbox_toolkit/device_toolkit.html +536 -0
- netbox_toolkit/urls.py +22 -0
- netbox_toolkit/utils/__init__.py +1 -0
- netbox_toolkit/utils/connection.py +125 -0
- netbox_toolkit/utils/error_parser.py +428 -0
- netbox_toolkit/utils/logging.py +58 -0
- netbox_toolkit/utils/network.py +157 -0
- netbox_toolkit/views.py +385 -0
- netbox_toolkit_plugin-0.1.0.dist-info/METADATA +76 -0
- netbox_toolkit_plugin-0.1.0.dist-info/RECORD +56 -0
- netbox_toolkit_plugin-0.1.0.dist-info/WHEEL +5 -0
- netbox_toolkit_plugin-0.1.0.dist-info/entry_points.txt +2 -0
- netbox_toolkit_plugin-0.1.0.dist-info/licenses/LICENSE +200 -0
- netbox_toolkit_plugin-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
# Generated by Django 5.1.4 on 2025-05-23 14:41
|
2
|
+
|
3
|
+
import django.db.models.deletion
|
4
|
+
import taggit.managers
|
5
|
+
import utilities.json
|
6
|
+
from django.db import migrations, models
|
7
|
+
|
8
|
+
|
9
|
+
class Migration(migrations.Migration):
|
10
|
+
|
11
|
+
initial = True
|
12
|
+
|
13
|
+
dependencies = [
|
14
|
+
('dcim', '0200_populate_mac_addresses'),
|
15
|
+
('extras', '0122_charfield_null_choices'),
|
16
|
+
]
|
17
|
+
|
18
|
+
operations = [
|
19
|
+
migrations.CreateModel(
|
20
|
+
name='Command',
|
21
|
+
fields=[
|
22
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
|
23
|
+
('created', models.DateTimeField(auto_now_add=True, null=True)),
|
24
|
+
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
25
|
+
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
|
26
|
+
('name', models.CharField(max_length=100)),
|
27
|
+
('command', models.TextField()),
|
28
|
+
('description', models.TextField(blank=True)),
|
29
|
+
('device_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='toolkit_commands', to='dcim.devicetype')),
|
30
|
+
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
31
|
+
],
|
32
|
+
options={
|
33
|
+
'abstract': False,
|
34
|
+
},
|
35
|
+
),
|
36
|
+
migrations.CreateModel(
|
37
|
+
name='CommandLog',
|
38
|
+
fields=[
|
39
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
|
40
|
+
('created', models.DateTimeField(auto_now_add=True, null=True)),
|
41
|
+
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
42
|
+
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
|
43
|
+
('output', models.TextField()),
|
44
|
+
('username', models.CharField(max_length=100)),
|
45
|
+
('execution_time', models.DateTimeField(auto_now_add=True)),
|
46
|
+
('command', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='netbox_toolkit.command')),
|
47
|
+
('device', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='command_logs', to='dcim.device')),
|
48
|
+
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
49
|
+
],
|
50
|
+
options={
|
51
|
+
'abstract': False,
|
52
|
+
},
|
53
|
+
),
|
54
|
+
]
|
netbox_toolkit/migrations/0002_alter_command_options_alter_command_unique_together_and_more.py
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Generated by Django 5.1.4 on 2025-05-26 12:11
|
2
|
+
|
3
|
+
import django.db.models.deletion
|
4
|
+
from django.db import migrations, models
|
5
|
+
|
6
|
+
|
7
|
+
class Migration(migrations.Migration):
|
8
|
+
|
9
|
+
dependencies = [
|
10
|
+
('dcim', '0200_populate_mac_addresses'),
|
11
|
+
('netbox_toolkit', '0001_initial'),
|
12
|
+
]
|
13
|
+
|
14
|
+
operations = [
|
15
|
+
# First remove the old field
|
16
|
+
migrations.RemoveField(
|
17
|
+
model_name='command',
|
18
|
+
name='device_type',
|
19
|
+
),
|
20
|
+
# Add all new fields
|
21
|
+
migrations.AddField(
|
22
|
+
model_name='command',
|
23
|
+
name='platform',
|
24
|
+
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='toolkit_commands', to='dcim.platform'),
|
25
|
+
preserve_default=False,
|
26
|
+
),
|
27
|
+
migrations.AddField(
|
28
|
+
model_name='command',
|
29
|
+
name='command_type',
|
30
|
+
field=models.CharField(default='show', max_length=50),
|
31
|
+
),
|
32
|
+
migrations.AddField(
|
33
|
+
model_name='command',
|
34
|
+
name='requires_config_mode',
|
35
|
+
field=models.BooleanField(default=False),
|
36
|
+
),
|
37
|
+
migrations.AddField(
|
38
|
+
model_name='command',
|
39
|
+
name='requires_enable',
|
40
|
+
field=models.BooleanField(default=False),
|
41
|
+
),
|
42
|
+
migrations.AddField(
|
43
|
+
model_name='commandlog',
|
44
|
+
name='error_message',
|
45
|
+
field=models.TextField(blank=True),
|
46
|
+
),
|
47
|
+
migrations.AddField(
|
48
|
+
model_name='commandlog',
|
49
|
+
name='execution_duration',
|
50
|
+
field=models.FloatField(blank=True, null=True),
|
51
|
+
),
|
52
|
+
migrations.AddField(
|
53
|
+
model_name='commandlog',
|
54
|
+
name='success',
|
55
|
+
field=models.BooleanField(default=True),
|
56
|
+
),
|
57
|
+
# Then alter model options after fields exist
|
58
|
+
migrations.AlterModelOptions(
|
59
|
+
name='command',
|
60
|
+
options={'ordering': ['platform', 'name']},
|
61
|
+
),
|
62
|
+
migrations.AlterUniqueTogether(
|
63
|
+
name='command',
|
64
|
+
unique_together={('platform', 'name')},
|
65
|
+
),
|
66
|
+
]
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Generated by Django 5.1.4 on 2025-05-26 15:28
|
2
|
+
|
3
|
+
from django.db import migrations
|
4
|
+
|
5
|
+
|
6
|
+
def migrate_command_types(apps, schema_editor):
|
7
|
+
"""
|
8
|
+
Migrate existing diagnostic and troubleshooting commands to 'show' type
|
9
|
+
"""
|
10
|
+
Command = apps.get_model('netbox_toolkit', 'Command')
|
11
|
+
|
12
|
+
# Update diagnostic commands to show
|
13
|
+
diagnostic_count = Command.objects.filter(command_type='diagnostic').update(command_type='show')
|
14
|
+
|
15
|
+
# Update troubleshooting commands to show
|
16
|
+
troubleshooting_count = Command.objects.filter(command_type='troubleshooting').update(command_type='show')
|
17
|
+
|
18
|
+
print(f"Migration: Updated {diagnostic_count} diagnostic commands to 'show' type")
|
19
|
+
print(f"Migration: Updated {troubleshooting_count} troubleshooting commands to 'show' type")
|
20
|
+
|
21
|
+
|
22
|
+
def reverse_migrate_command_types(apps, schema_editor):
|
23
|
+
"""
|
24
|
+
Reverse migration - this is a simplified reverse that sets all to 'show'
|
25
|
+
since we can't reliably determine original diagnostic vs troubleshooting
|
26
|
+
"""
|
27
|
+
# Note: This is a lossy reverse operation
|
28
|
+
# In practice, this is acceptable since diagnostic/troubleshooting
|
29
|
+
# were being removed as part of the permission system design
|
30
|
+
pass
|
31
|
+
|
32
|
+
|
33
|
+
class Migration(migrations.Migration):
|
34
|
+
|
35
|
+
dependencies = [
|
36
|
+
('netbox_toolkit', '0002_alter_command_options_alter_command_unique_together_and_more'),
|
37
|
+
]
|
38
|
+
|
39
|
+
operations = [
|
40
|
+
migrations.AlterModelOptions(
|
41
|
+
name='command',
|
42
|
+
options={'ordering': ['platform', 'name'], 'permissions': [('execute_show_command', 'Can execute show commands'), ('execute_config_command', 'Can execute configuration commands')]},
|
43
|
+
),
|
44
|
+
migrations.RunPython(
|
45
|
+
migrate_command_types,
|
46
|
+
reverse_migrate_command_types,
|
47
|
+
),
|
48
|
+
]
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Generated migration to remove Django custom permissions for object-based permissions migration
|
2
|
+
|
3
|
+
from django.db import migrations
|
4
|
+
|
5
|
+
|
6
|
+
def remove_django_permissions(apps, schema_editor):
|
7
|
+
"""Remove old Django permissions that are no longer needed"""
|
8
|
+
Permission = apps.get_model('auth', 'Permission')
|
9
|
+
ContentType = apps.get_model('contenttypes', 'ContentType')
|
10
|
+
|
11
|
+
try:
|
12
|
+
# Get the Command content type
|
13
|
+
command_ct = ContentType.objects.get(app_label='netbox_toolkit', model='command')
|
14
|
+
|
15
|
+
# Remove the custom Django permissions
|
16
|
+
custom_permissions = [
|
17
|
+
'execute_show_command',
|
18
|
+
'execute_config_command'
|
19
|
+
]
|
20
|
+
|
21
|
+
deleted_count = 0
|
22
|
+
for codename in custom_permissions:
|
23
|
+
count, _ = Permission.objects.filter(
|
24
|
+
content_type=command_ct,
|
25
|
+
codename=codename
|
26
|
+
).delete()
|
27
|
+
deleted_count += count
|
28
|
+
|
29
|
+
print(f"Removed {deleted_count} old Django permissions")
|
30
|
+
|
31
|
+
except ContentType.DoesNotExist:
|
32
|
+
# Command model doesn't exist yet, skip
|
33
|
+
print("Command model not found, skipping permission removal")
|
34
|
+
except Exception as e:
|
35
|
+
print(f"Warning: Could not remove old permissions: {e}")
|
36
|
+
|
37
|
+
def restore_django_permissions(apps, schema_editor):
|
38
|
+
"""Restore Django permissions if migration is reversed"""
|
39
|
+
Permission = apps.get_model('auth', 'Permission')
|
40
|
+
ContentType = apps.get_model('contenttypes', 'ContentType')
|
41
|
+
|
42
|
+
try:
|
43
|
+
# Get the Command content type
|
44
|
+
command_ct = ContentType.objects.get(app_label='netbox_toolkit', model='command')
|
45
|
+
|
46
|
+
# Recreate the custom Django permissions
|
47
|
+
permissions_to_create = [
|
48
|
+
('execute_show_command', 'Can execute show commands'),
|
49
|
+
('execute_config_command', 'Can execute configuration commands'),
|
50
|
+
]
|
51
|
+
|
52
|
+
for codename, name in permissions_to_create:
|
53
|
+
Permission.objects.get_or_create(
|
54
|
+
content_type=command_ct,
|
55
|
+
codename=codename,
|
56
|
+
defaults={'name': name}
|
57
|
+
)
|
58
|
+
|
59
|
+
print("Restored old Django permissions")
|
60
|
+
|
61
|
+
except ContentType.DoesNotExist:
|
62
|
+
# Command model doesn't exist, skip
|
63
|
+
print("Command model not found, skipping permission restoration")
|
64
|
+
|
65
|
+
|
66
|
+
class Migration(migrations.Migration):
|
67
|
+
|
68
|
+
dependencies = [
|
69
|
+
('netbox_toolkit', '0003_permission_system_update'),
|
70
|
+
]
|
71
|
+
|
72
|
+
operations = [
|
73
|
+
migrations.RunPython(
|
74
|
+
remove_django_permissions,
|
75
|
+
restore_django_permissions,
|
76
|
+
),
|
77
|
+
]
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Generated by Django 5.1.4 on 2025-05-27 12:54
|
2
|
+
|
3
|
+
from django.db import migrations
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('netbox_toolkit', '0004_remove_django_permissions'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AlterModelOptions(
|
14
|
+
name='command',
|
15
|
+
options={'ordering': ['platform', 'name']},
|
16
|
+
),
|
17
|
+
migrations.RemoveField(
|
18
|
+
model_name='command',
|
19
|
+
name='requires_config_mode',
|
20
|
+
),
|
21
|
+
migrations.RemoveField(
|
22
|
+
model_name='command',
|
23
|
+
name='requires_enable',
|
24
|
+
),
|
25
|
+
]
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Generated by Django 5.1.4 on 2025-05-29 11:47
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('netbox_toolkit', '0005_alter_command_options_and_more'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AddField(
|
14
|
+
model_name='commandlog',
|
15
|
+
name='parsed_data',
|
16
|
+
field=models.JSONField(blank=True, null=True),
|
17
|
+
),
|
18
|
+
migrations.AddField(
|
19
|
+
model_name='commandlog',
|
20
|
+
name='parsing_success',
|
21
|
+
field=models.BooleanField(default=False),
|
22
|
+
),
|
23
|
+
migrations.AddField(
|
24
|
+
model_name='commandlog',
|
25
|
+
name='parsing_template',
|
26
|
+
field=models.CharField(blank=True, max_length=255),
|
27
|
+
),
|
28
|
+
]
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Generated by Django 5.1.4 on 2025-05-29 12:00
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('netbox_toolkit', '0006_commandlog_parsed_data_commandlog_parsing_success_and_more'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AlterField(
|
14
|
+
model_name='commandlog',
|
15
|
+
name='parsing_template',
|
16
|
+
field=models.CharField(blank=True, max_length=255, null=True),
|
17
|
+
),
|
18
|
+
]
|
File without changes
|
netbox_toolkit/models.py
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
from django.db import models
|
2
|
+
from django.core.exceptions import ValidationError
|
3
|
+
from netbox.models import NetBoxModel
|
4
|
+
from dcim.models import Device
|
5
|
+
|
6
|
+
class Command(NetBoxModel):
|
7
|
+
name = models.CharField(max_length=100)
|
8
|
+
command = models.TextField()
|
9
|
+
description = models.TextField(blank=True)
|
10
|
+
|
11
|
+
# Platform-based association (required)
|
12
|
+
platform = models.ForeignKey(
|
13
|
+
to='dcim.Platform',
|
14
|
+
on_delete=models.CASCADE,
|
15
|
+
related_name='toolkit_commands',
|
16
|
+
help_text="Platform this command is designed for (e.g., cisco_ios, cisco_nxos, generic)"
|
17
|
+
)
|
18
|
+
|
19
|
+
# Command categorization
|
20
|
+
command_type = models.CharField(
|
21
|
+
max_length=50,
|
22
|
+
choices=[
|
23
|
+
('show', 'Show Command'),
|
24
|
+
('config', 'Configuration Command'),
|
25
|
+
],
|
26
|
+
default='show',
|
27
|
+
help_text="Type of command for categorization and permission control"
|
28
|
+
)
|
29
|
+
|
30
|
+
class Meta:
|
31
|
+
ordering = ['platform', 'name']
|
32
|
+
unique_together = [['platform', 'name']]
|
33
|
+
|
34
|
+
def __str__(self):
|
35
|
+
return f"{self.name} ({self.platform})"
|
36
|
+
|
37
|
+
def get_absolute_url(self):
|
38
|
+
"""Return the URL for this object"""
|
39
|
+
from django.urls import reverse
|
40
|
+
return reverse('plugins:netbox_toolkit:command_detail', kwargs={'pk': self.pk})
|
41
|
+
|
42
|
+
class CommandLog(NetBoxModel):
|
43
|
+
command = models.ForeignKey(
|
44
|
+
to=Command,
|
45
|
+
on_delete=models.CASCADE,
|
46
|
+
related_name='logs'
|
47
|
+
)
|
48
|
+
device = models.ForeignKey(
|
49
|
+
to='dcim.Device',
|
50
|
+
on_delete=models.CASCADE,
|
51
|
+
related_name='command_logs'
|
52
|
+
)
|
53
|
+
output = models.TextField()
|
54
|
+
username = models.CharField(max_length=100)
|
55
|
+
execution_time = models.DateTimeField(auto_now_add=True)
|
56
|
+
|
57
|
+
# Execution details added in migration
|
58
|
+
success = models.BooleanField(default=True)
|
59
|
+
error_message = models.TextField(blank=True)
|
60
|
+
execution_duration = models.FloatField(
|
61
|
+
blank=True,
|
62
|
+
null=True,
|
63
|
+
help_text="Command execution time in seconds"
|
64
|
+
)
|
65
|
+
|
66
|
+
# Parsing details for TextFSM
|
67
|
+
parsed_data = models.JSONField(
|
68
|
+
blank=True,
|
69
|
+
null=True,
|
70
|
+
help_text="Parsed structured data from command output"
|
71
|
+
)
|
72
|
+
parsing_success = models.BooleanField(
|
73
|
+
default=False,
|
74
|
+
help_text="Whether the output was successfully parsed"
|
75
|
+
)
|
76
|
+
parsing_template = models.CharField(
|
77
|
+
max_length=255,
|
78
|
+
blank=True,
|
79
|
+
null=True,
|
80
|
+
help_text="Name of the TextFSM template used for parsing"
|
81
|
+
)
|
82
|
+
|
83
|
+
def __str__(self):
|
84
|
+
return f"{self.command} on {self.device}"
|
85
|
+
|
86
|
+
def get_absolute_url(self):
|
87
|
+
"""Return the URL for this object"""
|
88
|
+
from django.urls import reverse
|
89
|
+
return reverse('plugins:netbox_toolkit:commandlog_view', kwargs={'pk': self.pk})
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from netbox.choices import ButtonColorChoices
|
2
|
+
from netbox.plugins import PluginMenu, PluginMenuButton, PluginMenuItem
|
3
|
+
|
4
|
+
menu = PluginMenu(
|
5
|
+
label="Command Toolkit",
|
6
|
+
icon_class="mdi mdi-console",
|
7
|
+
groups=(
|
8
|
+
(
|
9
|
+
"Commands",
|
10
|
+
(
|
11
|
+
PluginMenuItem(
|
12
|
+
link="plugins:netbox_toolkit:command_list",
|
13
|
+
link_text="Commands",
|
14
|
+
buttons=(
|
15
|
+
PluginMenuButton(
|
16
|
+
"plugins:netbox_toolkit:command_add",
|
17
|
+
"Add",
|
18
|
+
"mdi mdi-plus-thick",
|
19
|
+
ButtonColorChoices.GRAY
|
20
|
+
),
|
21
|
+
)
|
22
|
+
),
|
23
|
+
PluginMenuItem(
|
24
|
+
link="plugins:netbox_toolkit:commandlog_list",
|
25
|
+
link_text="Command Logs",
|
26
|
+
),
|
27
|
+
),
|
28
|
+
),
|
29
|
+
),
|
30
|
+
)
|
netbox_toolkit/search.py
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
from netbox.search import SearchIndex
|
2
|
+
from .models import Command, CommandLog
|
3
|
+
|
4
|
+
class CommandIndex(SearchIndex):
|
5
|
+
model = Command
|
6
|
+
fields = (
|
7
|
+
('name', 100),
|
8
|
+
('command', 200),
|
9
|
+
('description', 500),
|
10
|
+
)
|
11
|
+
display_attrs = ('platform', 'command_type', 'description')
|
12
|
+
|
13
|
+
class CommandLogIndex(SearchIndex):
|
14
|
+
model = CommandLog
|
15
|
+
fields = (
|
16
|
+
('command__name', 100),
|
17
|
+
('device__name', 150),
|
18
|
+
('username', 200),
|
19
|
+
('output', 1000),
|
20
|
+
)
|
21
|
+
display_attrs = ('command', 'device', 'success', 'execution_time')
|
@@ -0,0 +1,7 @@
|
|
1
|
+
"""Services package for business logic."""
|
2
|
+
|
3
|
+
from .command_service import CommandExecutionService
|
4
|
+
from .device_service import DeviceService
|
5
|
+
from .rate_limiting_service import RateLimitingService
|
6
|
+
|
7
|
+
__all__ = ['CommandExecutionService', 'DeviceService', 'RateLimitingService']
|