Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions db/integrity.sql
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,15 @@ returning *;
--

delete from server_relation_attribute as extra
using attribute, server as target
using attribute
join attribute_target_servertype as ats on ats.attribute_id = attribute.attribute_id,
server as target
where attribute.attribute_id = extra.attribute_id
and target.server_id = extra.value
and attribute.target_servertype_id != target.servertype_id
and target.servertype_id not in (
select ats2.servertype_id from attribute_target_servertype as ats2
where ats2.attribute_id = attribute.attribute_id
)
returning attribute.attribute_id, target.hostname;

commit;
4 changes: 2 additions & 2 deletions serveradmin/access_control/fixtures/serverdb.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"help_link": null,
"inet_address_family": "",
"readonly": false,
"target_servertype": "hv",
"target_servertype": ["hv"],
"reversed_attribute": null,
"clone": false,
"history": true,
Expand All @@ -44,7 +44,7 @@
"help_link": null,
"inet_address_family": "",
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"history": true,
Expand Down
3 changes: 2 additions & 1 deletion serveradmin/serverdb/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class ServerAdmin(admin.ModelAdmin):

class AttributeAdmin(admin.ModelAdmin):
form = AttributeAdminForm
filter_horizontal = ('target_servertype',)
list_display = [
'attribute_id',
'type',
Expand All @@ -90,7 +91,7 @@ def get_readonly_fields(self, request, obj=None):
# support it.
if obj:
fields += (
'type', 'attribute_id', 'target_servertype',
'type', 'attribute_id',
'reversed_attribute'
)

Expand Down
4 changes: 2 additions & 2 deletions serveradmin/serverdb/fixtures/attribute.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"group": "project",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"history": false,
Expand All @@ -34,7 +34,7 @@
"group": "project",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"history": true,
Expand Down
18 changes: 9 additions & 9 deletions serveradmin/serverdb/fixtures/ip_addr_type.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -67,7 +67,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -83,7 +83,7 @@
"group": "other",
"help_link": null,
"readonly": true,
"target_servertype": "route_network",
"target_servertype": ["route_network"],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -100,7 +100,7 @@
"group": "other",
"help_link": null,
"readonly": true,
"target_servertype": "provider_network",
"target_servertype": ["provider_network"],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -117,7 +117,7 @@
"group": "other",
"help_link": null,
"readonly": true,
"target_servertype": "provider_network",
"target_servertype": ["provider_network"],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -133,7 +133,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -149,7 +149,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -165,7 +165,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -181,7 +181,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand Down
22 changes: 11 additions & 11 deletions serveradmin/serverdb/fixtures/test_dataset.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -65,7 +65,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -81,7 +81,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -97,7 +97,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A(0|[1-9][0-9]*)\\Z"
Expand All @@ -113,7 +113,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -129,7 +129,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": "hypervisor",
"target_servertype": ["hypervisor"],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -145,7 +145,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -161,7 +161,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -177,7 +177,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A.*\\Z"
Expand All @@ -193,7 +193,7 @@
"group": "other",
"help_link": null,
"readonly": false,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": null,
"clone": false,
"regexp": "\\A(wheezy|squeeze|jessie|buster|bullseye)\\Z"
Expand All @@ -209,7 +209,7 @@
"group": "other",
"help_link": null,
"readonly": true,
"target_servertype": null,
"target_servertype": [],
"reversed_attribute": "hypervisor",
"clone": false,
"regexp": "\\A.*\\Z"
Expand Down
5 changes: 4 additions & 1 deletion serveradmin/serverdb/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ class Meta:
def clean(self):
attr_type = self.cleaned_data.get('type') or self.instance.type # New or existing attribute ?

if attr_type != 'relation' and self.cleaned_data.get('target_servertype') is not None:
target_servertypes = self.cleaned_data.get('target_servertype')
if attr_type in ('domain', 'supernet') and not (target_servertypes and target_servertypes.exists()):
raise ValidationError('Attributes of type domain or supernet must have at least one target servertype!')
if attr_type not in ('domain', 'supernet', 'relation') and target_servertypes and target_servertypes.exists():
raise ValidationError('Attribute type must be relation when target servertype is selected!')

if attr_type == 'inet' and self.cleaned_data.get('multi') is True:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
('serverdb', '0021_serverinetattribute_server_inet_attribute_value_idx'),
]

operations = [
migrations.RunSQL(
sql=(
"ALTER TABLE attribute "
"DROP CONSTRAINT IF EXISTS"
" attribute_target_servertype_id_check"
),
reverse_sql=(
"ALTER TABLE attribute ADD CONSTRAINT"
" attribute_target_servertype_id_check "
"CHECK((type IN ('domain', 'supernet', 'relation')) = "
"(target_servertype_id IS NOT NULL OR type = 'relation'))"
),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"""Convert Attribute.target_servertype from ForeignKey to ManyToManyField.

Uses SeparateDatabaseAndState so that:
- Django's state tracker sees standard RemoveField + AddField operations
- The actual DB operations are controlled manually to allow a data
migration between creating the M2M table and dropping the FK column

Steps:
1. Create the M2M join table
2. Copy existing FK data into the join table
3. Drop the old FK column
"""

import django.db.models
from django.db import migrations, models


def copy_fk_to_m2m(apps, schema_editor):
"""Copy existing target_servertype_id FK values to the new M2M table."""
with schema_editor.connection.cursor() as cursor:
cursor.execute(
"INSERT INTO attribute_target_servertype (attribute_id, servertype_id) "
"SELECT attribute_id, target_servertype_id FROM attribute "
"WHERE target_servertype_id IS NOT NULL"
)


def copy_m2m_to_fk(apps, schema_editor):
"""Reverse: copy M2M values back into the FK column (takes first value)."""
with schema_editor.connection.cursor() as cursor:
cursor.execute(
"UPDATE attribute SET target_servertype_id = m2m.servertype_id "
"FROM attribute_target_servertype AS m2m "
"WHERE attribute.attribute_id = m2m.attribute_id"
)


class Migration(migrations.Migration):

dependencies = [
('serverdb', '0022_attribute_relax_target_servertype_constraints')
]

operations = [
# Phase 1: Create the M2M join table (DB only, state updated later).
migrations.SeparateDatabaseAndState(
database_operations=[
migrations.RunSQL(
sql=(
"CREATE TABLE attribute_target_servertype ("
" id BIGSERIAL PRIMARY KEY,"
" attribute_id VARCHAR(32) NOT NULL"
" REFERENCES attribute(attribute_id) ON DELETE CASCADE,"
" servertype_id VARCHAR(32) NOT NULL"
" REFERENCES servertype(servertype_id) ON DELETE CASCADE,"
" UNIQUE (attribute_id, servertype_id)"
")"
),
reverse_sql="DROP TABLE IF EXISTS attribute_target_servertype",
),
],
state_operations=[],
),

# Phase 2: Copy existing FK data into the M2M table.
migrations.RunPython(copy_fk_to_m2m, copy_m2m_to_fk),

# Phase 3: Drop old FK column + constraints; update Django state.
migrations.SeparateDatabaseAndState(
database_operations=[
migrations.RunSQL(
sql=(
"ALTER TABLE attribute "
"DROP CONSTRAINT IF EXISTS"
" attribute_target_servertype_id_0eab2dcc_fk_servertyp"
),
reverse_sql=(
"ALTER TABLE attribute ADD CONSTRAINT"
" attribute_target_servertype_id_0eab2dcc_fk_servertyp"
" FOREIGN KEY (target_servertype_id)"
" REFERENCES servertype(servertype_id)"
" DEFERRABLE INITIALLY DEFERRED"
),
),
migrations.RunSQL(
sql=(
"ALTER TABLE attribute "
"DROP COLUMN IF EXISTS target_servertype_id"
),
reverse_sql=(
"ALTER TABLE attribute ADD COLUMN target_servertype_id "
"VARCHAR(32)"
),
),
],
state_operations=[
migrations.RemoveField(
model_name='attribute',
name='target_servertype',
),
migrations.AddField(
model_name='attribute',
name='target_servertype',
field=models.ManyToManyField(
blank=True,
help_text='Selecting no servertype allows all servertypes.',
to='serverdb.servertype',
),
),
],
),
]
Loading
Loading