|
|
@@ -1,7 +1,10 @@
|
|
|
# Generated by hand on 2025-12-29
|
|
|
# Creates Machine, Binary, NetworkInterface, and Process tables using raw SQL
|
|
|
|
|
|
-from django.db import migrations
|
|
|
+from django.db import migrations, models
|
|
|
+import django.db.models.deletion
|
|
|
+import django.utils.timezone
|
|
|
+from archivebox.uuid_compat import uuid7
|
|
|
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
|
@@ -12,9 +15,10 @@ class Migration(migrations.Migration):
|
|
|
]
|
|
|
|
|
|
operations = [
|
|
|
- migrations.RunSQL(
|
|
|
- # Forward SQL
|
|
|
- sql="""
|
|
|
+ migrations.SeparateDatabaseAndState(
|
|
|
+ database_operations=[
|
|
|
+ migrations.RunSQL(
|
|
|
+ sql="""
|
|
|
-- Create machine_machine table
|
|
|
CREATE TABLE IF NOT EXISTS machine_machine (
|
|
|
id TEXT PRIMARY KEY NOT NULL,
|
|
|
@@ -136,12 +140,133 @@ class Migration(migrations.Migration):
|
|
|
CREATE INDEX IF NOT EXISTS machine_process_binary_id_idx ON machine_process(binary_id);
|
|
|
CREATE INDEX IF NOT EXISTS machine_process_machine_status_retry_idx ON machine_process(machine_id, status, retry_at);
|
|
|
""",
|
|
|
- # Reverse SQL
|
|
|
- reverse_sql="""
|
|
|
- DROP TABLE IF EXISTS machine_process;
|
|
|
- DROP TABLE IF EXISTS machine_binary;
|
|
|
- DROP TABLE IF EXISTS machine_networkinterface;
|
|
|
- DROP TABLE IF EXISTS machine_machine;
|
|
|
- """
|
|
|
+ reverse_sql="""
|
|
|
+ DROP TABLE IF EXISTS machine_process;
|
|
|
+ DROP TABLE IF EXISTS machine_binary;
|
|
|
+ DROP TABLE IF EXISTS machine_networkinterface;
|
|
|
+ DROP TABLE IF EXISTS machine_machine;
|
|
|
+ """
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ state_operations=[
|
|
|
+ migrations.CreateModel(
|
|
|
+ name='Machine',
|
|
|
+ fields=[
|
|
|
+ ('id', models.UUIDField(default=uuid7, editable=False, primary_key=True, serialize=False, unique=True)),
|
|
|
+ ('created_at', models.DateTimeField(db_index=True, default=django.utils.timezone.now)),
|
|
|
+ ('modified_at', models.DateTimeField(auto_now=True)),
|
|
|
+ ('num_uses_succeeded', models.PositiveIntegerField(default=0)),
|
|
|
+ ('num_uses_failed', models.PositiveIntegerField(default=0)),
|
|
|
+ ('guid', models.CharField(default=None, editable=False, max_length=64, unique=True)),
|
|
|
+ ('hostname', models.CharField(default=None, max_length=63)),
|
|
|
+ ('hw_in_docker', models.BooleanField(default=False)),
|
|
|
+ ('hw_in_vm', models.BooleanField(default=False)),
|
|
|
+ ('hw_manufacturer', models.CharField(default=None, max_length=63)),
|
|
|
+ ('hw_product', models.CharField(default=None, max_length=63)),
|
|
|
+ ('hw_uuid', models.CharField(default=None, max_length=255)),
|
|
|
+ ('os_arch', models.CharField(default=None, max_length=15)),
|
|
|
+ ('os_family', models.CharField(default=None, max_length=15)),
|
|
|
+ ('os_platform', models.CharField(default=None, max_length=63)),
|
|
|
+ ('os_release', models.CharField(default=None, max_length=63)),
|
|
|
+ ('os_kernel', models.CharField(default=None, max_length=255)),
|
|
|
+ ('stats', models.JSONField(blank=True, default=dict, null=True)),
|
|
|
+ ('config', models.JSONField(blank=True, default=dict, help_text='Machine-specific config overrides (e.g., resolved binary paths like WGET_BINARY)', null=True)),
|
|
|
+ ],
|
|
|
+ options={
|
|
|
+ 'app_label': 'machine',
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ migrations.CreateModel(
|
|
|
+ name='NetworkInterface',
|
|
|
+ fields=[
|
|
|
+ ('id', models.UUIDField(default=uuid7, editable=False, primary_key=True, serialize=False, unique=True)),
|
|
|
+ ('created_at', models.DateTimeField(db_index=True, default=django.utils.timezone.now)),
|
|
|
+ ('modified_at', models.DateTimeField(auto_now=True)),
|
|
|
+ ('num_uses_succeeded', models.PositiveIntegerField(default=0)),
|
|
|
+ ('num_uses_failed', models.PositiveIntegerField(default=0)),
|
|
|
+ ('mac_address', models.CharField(default=None, editable=False, max_length=17)),
|
|
|
+ ('ip_public', models.GenericIPAddressField(default=None, editable=False)),
|
|
|
+ ('ip_local', models.GenericIPAddressField(default=None, editable=False)),
|
|
|
+ ('dns_server', models.GenericIPAddressField(default=None, editable=False)),
|
|
|
+ ('hostname', models.CharField(default=None, max_length=63)),
|
|
|
+ ('iface', models.CharField(default=None, max_length=15)),
|
|
|
+ ('isp', models.CharField(default=None, max_length=63)),
|
|
|
+ ('city', models.CharField(default=None, max_length=63)),
|
|
|
+ ('region', models.CharField(default=None, max_length=63)),
|
|
|
+ ('country', models.CharField(default=None, max_length=63)),
|
|
|
+ ('machine', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='machine.machine')),
|
|
|
+ ],
|
|
|
+ options={
|
|
|
+ 'unique_together': {('machine', 'ip_public', 'ip_local', 'mac_address', 'dns_server')},
|
|
|
+ 'app_label': 'machine',
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ migrations.CreateModel(
|
|
|
+ name='Binary',
|
|
|
+ fields=[
|
|
|
+ ('id', models.UUIDField(default=uuid7, editable=False, primary_key=True, serialize=False, unique=True)),
|
|
|
+ ('created_at', models.DateTimeField(db_index=True, default=django.utils.timezone.now)),
|
|
|
+ ('modified_at', models.DateTimeField(auto_now=True)),
|
|
|
+ ('num_uses_succeeded', models.PositiveIntegerField(default=0)),
|
|
|
+ ('num_uses_failed', models.PositiveIntegerField(default=0)),
|
|
|
+ ('name', models.CharField(blank=True, db_index=True, default='', max_length=63)),
|
|
|
+ ('binproviders', models.CharField(blank=True, default='env', help_text='Comma-separated list of allowed providers: apt,brew,pip,npm,env', max_length=127)),
|
|
|
+ ('overrides', models.JSONField(blank=True, default=dict, help_text="Provider-specific overrides: {'apt': {'packages': ['pkg']}, ...}")),
|
|
|
+ ('binprovider', models.CharField(blank=True, default='', help_text='Provider that successfully installed this binary', max_length=31)),
|
|
|
+ ('abspath', models.CharField(blank=True, default='', max_length=255)),
|
|
|
+ ('version', models.CharField(blank=True, default='', max_length=32)),
|
|
|
+ ('sha256', models.CharField(blank=True, default='', max_length=64)),
|
|
|
+ ('status', models.CharField(choices=[('queued', 'Queued'), ('started', 'Started'), ('succeeded', 'Succeeded'), ('failed', 'Failed')], db_index=True, default='queued', max_length=16)),
|
|
|
+ ('retry_at', models.DateTimeField(blank=True, db_index=True, default=django.utils.timezone.now, help_text='When to retry this binary installation', null=True)),
|
|
|
+ ('output_dir', models.CharField(blank=True, default='', help_text='Directory where installation hook logs are stored', max_length=255)),
|
|
|
+ ('machine', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machine.machine')),
|
|
|
+ ],
|
|
|
+ options={
|
|
|
+ 'verbose_name': 'Binary',
|
|
|
+ 'verbose_name_plural': 'Binaries',
|
|
|
+ 'unique_together': {('machine', 'name', 'abspath', 'version', 'sha256')},
|
|
|
+ 'app_label': 'machine',
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ migrations.CreateModel(
|
|
|
+ name='Process',
|
|
|
+ fields=[
|
|
|
+ ('id', models.UUIDField(default=uuid7, editable=False, primary_key=True, serialize=False, unique=True)),
|
|
|
+ ('created_at', models.DateTimeField(db_index=True, default=django.utils.timezone.now)),
|
|
|
+ ('modified_at', models.DateTimeField(auto_now=True)),
|
|
|
+ ('num_uses_succeeded', models.PositiveIntegerField(default=0)),
|
|
|
+ ('num_uses_failed', models.PositiveIntegerField(default=0)),
|
|
|
+ ('pwd', models.CharField(blank=True, default='', help_text='Working directory for process execution', max_length=512)),
|
|
|
+ ('cmd', models.JSONField(blank=True, default=list, help_text='Command as array of arguments')),
|
|
|
+ ('env', models.JSONField(blank=True, default=dict, help_text='Environment variables for process')),
|
|
|
+ ('timeout', models.IntegerField(default=120, help_text='Timeout in seconds')),
|
|
|
+ ('pid', models.IntegerField(blank=True, default=None, help_text='OS process ID', null=True)),
|
|
|
+ ('exit_code', models.IntegerField(blank=True, default=None, help_text='Process exit code (0 = success)', null=True)),
|
|
|
+ ('stdout', models.TextField(blank=True, default='', help_text='Standard output from process')),
|
|
|
+ ('stderr', models.TextField(blank=True, default='', help_text='Standard error from process')),
|
|
|
+ ('started_at', models.DateTimeField(blank=True, default=None, help_text='When process was launched', null=True)),
|
|
|
+ ('ended_at', models.DateTimeField(blank=True, default=None, help_text='When process completed/terminated', null=True)),
|
|
|
+ ('url', models.URLField(blank=True, default=None, help_text='Connection URL (CDP endpoint, sonic server, etc.)', max_length=2048, null=True)),
|
|
|
+ ('status', models.CharField(choices=[('queued', 'Queued'), ('running', 'Running'), ('exited', 'Exited')], db_index=True, default='queued', max_length=16)),
|
|
|
+ ('retry_at', models.DateTimeField(blank=True, db_index=True, default=django.utils.timezone.now, help_text='When to retry this process', null=True)),
|
|
|
+ ('machine', models.ForeignKey(help_text='Machine where this process executed', on_delete=django.db.models.deletion.CASCADE, related_name='processes', to='machine.machine')),
|
|
|
+ ('binary', models.ForeignKey(blank=True, help_text='Binary used by this process', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='processes', to='machine.binary')),
|
|
|
+ ('iface', models.ForeignKey(blank=True, help_text='Network interface used by this process', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='processes', to='machine.networkinterface')),
|
|
|
+ ],
|
|
|
+ options={
|
|
|
+ 'verbose_name': 'Process',
|
|
|
+ 'verbose_name_plural': 'Processes',
|
|
|
+ 'app_label': 'machine',
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ migrations.AddIndex(
|
|
|
+ model_name='process',
|
|
|
+ index=models.Index(fields=['machine', 'status', 'retry_at'], name='machine_pro_machine_c69cf0_idx'),
|
|
|
+ ),
|
|
|
+ migrations.AddIndex(
|
|
|
+ model_name='process',
|
|
|
+ index=models.Index(fields=['binary', 'exit_code'], name='machine_pro_binary__f79cc6_idx'),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
),
|
|
|
]
|