← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~rvb/maas/add-nodegroup_interface into lp:maas

 

Raphaël Badin has proposed merging lp:~rvb/maas/add-nodegroup_interface into lp:maas.

Requested reviews:
  MAAS Maintainers (maas-maintainers)

For more details, see:
https://code.launchpad.net/~rvb/maas/add-nodegroup_interface/+merge/123914

This is branch is the first branch in a series of branches aimed at adding support for multiple interfaces attached to a particular nodegroup (i.e. cluster controller).  The plan is to add support in the db for multiple interfaces but to restrict the number of managed (i.e. an interface on which MAAS exposes a DHCP service) interface to only one for now.  This should allow for a transition as smooth as possible from the model that we have right now.

This branch adds the new model: nodegroup_interface.  With some renaming (for clarity), it boils down to duplicating the network-related fields present on nodegroup (to be removed in a followup branch) and move them into the new model.

= Pre-imp =

This was pre-imp with Julian.

= Notes =

Some fields have been renamed (compared to the fields in nodegroup):
- 'worker_ip' is now named 'ip'.  This is the IP for the interface.
- 'dhcp_interfaces' is now named 'interface'.  Singular because there is a one-to-many relationship between nodegroup and nodegroupinterface.  And I dropped the 'dhcp_' prefix because this new model is now simply describing networks and interfaces.  The fact that this is used for DHCP does not need to be engraved in the model itself.
-- 
https://code.launchpad.net/~rvb/maas/add-nodegroup_interface/+merge/123914
Your team MAAS Maintainers is requested to review the proposed merge of lp:~rvb/maas/add-nodegroup_interface into lp:maas.
=== modified file 'src/maasserver/enum.py'
--- src/maasserver/enum.py	2012-09-10 16:45:38 +0000
+++ src/maasserver/enum.py	2012-09-12 09:53:23 +0000
@@ -160,3 +160,22 @@
     (NODEGROUP_STATUS.ACCEPTED, "Accepted"),
     (NODEGROUP_STATUS.REJECTED, "Rejected"),
     )
+
+
+class NODEGROUPINTERFACE_STATUS:
+    """The vocabulary of a `NodeGroupInterface`'s possible statuses."""
+    # A nodegroupinterface starts out as UNMANAGED.
+    DEFAULT_STATUS = 0
+
+    #: MAAS manages IP address assignments on this interface.
+    UNMANAGED = 0
+    #: MAAS does not manage IP address assignments on this interface.
+    MANAGED = 1
+
+
+# Django choices for NODEGROUP_STATUS: sequence of tuples (key, UI
+# representation).
+NODEGROUPINTERFACE_STATUS_CHOICES = (
+    (NODEGROUPINTERFACE_STATUS.UNMANAGED, "Unmanaged"),
+    (NODEGROUPINTERFACE_STATUS.MANAGED, "Managed"),
+    )

=== added file 'src/maasserver/migrations/0023_add_nodegroupinterface.py'
--- src/maasserver/migrations/0023_add_nodegroupinterface.py	1970-01-01 00:00:00 +0000
+++ src/maasserver/migrations/0023_add_nodegroupinterface.py	2012-09-12 09:53:23 +0000
@@ -0,0 +1,198 @@
+# encoding: utf-8
+import datetime
+
+from django.db import models
+from south.db import db
+from south.v2 import SchemaMigration
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        
+        # Adding model 'NodeGroupInterface'
+        db.create_table(u'maasserver_nodegroupinterface', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('created', self.gf('django.db.models.fields.DateTimeField')()),
+            ('updated', self.gf('django.db.models.fields.DateTimeField')()),
+            ('ip', self.gf('django.db.models.fields.IPAddressField')(max_length=15)),
+            ('nodegroup', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['maasserver.NodeGroup'])),
+            ('status', self.gf('django.db.models.fields.IntegerField')(default=0)),
+            ('interface', self.gf('django.db.models.fields.CharField')(default=u'', max_length=255, blank=True)),
+            ('subnet_mask', self.gf('django.db.models.fields.IPAddressField')(default=None, max_length=15, null=True, blank=True)),
+            ('broadcast_ip', self.gf('django.db.models.fields.IPAddressField')(default=None, max_length=15, null=True, blank=True)),
+            ('router_ip', self.gf('django.db.models.fields.IPAddressField')(default=None, max_length=15, null=True, blank=True)),
+            ('ip_range_low', self.gf('django.db.models.fields.IPAddressField')(default=None, max_length=15, unique=True, null=True, blank=True)),
+            ('ip_range_high', self.gf('django.db.models.fields.IPAddressField')(default=None, max_length=15, unique=True, null=True, blank=True)),
+        ))
+        db.send_create_signal(u'maasserver', ['NodeGroupInterface'])
+
+        # Adding unique constraint on 'NodeGroupInterface', fields ['nodegroup', 'interface']
+        db.create_unique(u'maasserver_nodegroupinterface', ['nodegroup_id', 'interface'])
+
+
+    def backwards(self, orm):
+        
+        # Removing unique constraint on 'NodeGroupInterface', fields ['nodegroup', 'interface']
+        db.delete_unique(u'maasserver_nodegroupinterface', ['nodegroup_id', 'interface'])
+
+        # Deleting model 'NodeGroupInterface'
+        db.delete_table(u'maasserver_nodegroupinterface')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        u'maasserver.config': {
+            'Meta': {'object_name': 'Config'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'value': ('maasserver.fields.JSONObjectField', [], {'null': 'True'})
+        },
+        u'maasserver.dhcplease': {
+            'Meta': {'object_name': 'DHCPLease'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15'}),
+            'mac': ('maasserver.fields.MACAddressField', [], {}),
+            'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"})
+        },
+        u'maasserver.filestorage': {
+            'Meta': {'object_name': 'FileStorage'},
+            'data': ('django.db.models.fields.files.FileField', [], {'max_length': '255'}),
+            'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        u'maasserver.macaddress': {
+            'Meta': {'object_name': 'MACAddress'},
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'mac_address': ('maasserver.fields.MACAddressField', [], {'unique': 'True'}),
+            'node': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.Node']"}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {})
+        },
+        u'maasserver.node': {
+            'Meta': {'object_name': 'Node'},
+            'after_commissioning_action': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'architecture': ('django.db.models.fields.CharField', [], {'default': "u'i386'", 'max_length': '10'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'error': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
+            'hostname': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'netboot': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']", 'null': 'True'}),
+            'owner': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'power_parameters': ('maasserver.fields.JSONObjectField', [], {'default': "u''", 'blank': 'True'}),
+            'power_type': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '10', 'blank': 'True'}),
+            'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '10'}),
+            'system_id': ('django.db.models.fields.CharField', [], {'default': "u'node-29a5206a-fc12-11e1-bbff-3c970e0e56dc'", 'unique': 'True', 'max_length': '41'}),
+            'token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Token']", 'null': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {})
+        },
+        u'maasserver.nodegroup': {
+            'Meta': {'object_name': 'NodeGroup'},
+            'api_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '18'}),
+            'api_token': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Token']", 'unique': 'True'}),
+            'broadcast_ip': ('django.db.models.fields.IPAddressField', [], {'default': "u''", 'max_length': '15', 'null': 'True', 'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'dhcp_interfaces': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
+            'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'ip_range_high': ('django.db.models.fields.IPAddressField', [], {'default': "u''", 'max_length': '15', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'ip_range_low': ('django.db.models.fields.IPAddressField', [], {'default': "u''", 'max_length': '15', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'router_ip': ('django.db.models.fields.IPAddressField', [], {'default': "u''", 'max_length': '15', 'null': 'True', 'blank': 'True'}),
+            'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'subnet_mask': ('django.db.models.fields.IPAddressField', [], {'default': "u''", 'max_length': '15', 'null': 'True', 'blank': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}),
+            'worker_ip': ('django.db.models.fields.IPAddressField', [], {'unique': 'True', 'max_length': '15'})
+        },
+        u'maasserver.nodegroupinterface': {
+            'Meta': {'unique_together': "((u'nodegroup', u'interface'),)", 'object_name': 'NodeGroupInterface'},
+            'broadcast_ip': ('django.db.models.fields.IPAddressField', [], {'default': 'None', 'max_length': '15', 'null': 'True', 'blank': 'True'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'interface': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
+            'ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+            'ip_range_high': ('django.db.models.fields.IPAddressField', [], {'default': 'None', 'max_length': '15', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'ip_range_low': ('django.db.models.fields.IPAddressField', [], {'default': 'None', 'max_length': '15', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+            'nodegroup': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['maasserver.NodeGroup']"}),
+            'router_ip': ('django.db.models.fields.IPAddressField', [], {'default': 'None', 'max_length': '15', 'null': 'True', 'blank': 'True'}),
+            'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'subnet_mask': ('django.db.models.fields.IPAddressField', [], {'default': 'None', 'max_length': '15', 'null': 'True', 'blank': 'True'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {})
+        },
+        u'maasserver.sshkey': {
+            'Meta': {'unique_together': "((u'user', u'key'),)", 'object_name': 'SSHKey'},
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.TextField', [], {}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+        },
+        u'maasserver.userprofile': {
+            'Meta': {'object_name': 'UserProfile'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'piston.consumer': {
+            'Meta': {'object_name': 'Consumer'},
+            'description': ('django.db.models.fields.TextField', [], {}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'status': ('django.db.models.fields.CharField', [], {'default': "'pending'", 'max_length': '16'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'consumers'", 'null': 'True', 'to': "orm['auth.User']"})
+        },
+        'piston.token': {
+            'Meta': {'object_name': 'Token'},
+            'callback': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'callback_confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'consumer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['piston.Consumer']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'key': ('django.db.models.fields.CharField', [], {'max_length': '18'}),
+            'secret': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+            'timestamp': ('django.db.models.fields.IntegerField', [], {'default': '1347369065L'}),
+            'token_type': ('django.db.models.fields.IntegerField', [], {}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'tokens'", 'null': 'True', 'to': "orm['auth.User']"}),
+            'verifier': ('django.db.models.fields.CharField', [], {'max_length': '10'})
+        }
+    }
+
+    complete_apps = ['maasserver']

=== modified file 'src/maasserver/models/__init__.py'
--- src/maasserver/models/__init__.py	2012-08-16 13:38:51 +0000
+++ src/maasserver/models/__init__.py	2012-09-12 09:53:23 +0000
@@ -18,6 +18,7 @@
     'MACAddress',
     'Node',
     'NodeGroup',
+    'NodeGroupInterface',
     'SSHKey',
     'UserProfile',
     ]
@@ -35,6 +36,7 @@
 from maasserver.models.macaddress import MACAddress
 from maasserver.models.node import Node
 from maasserver.models.nodegroup import NodeGroup
+from maasserver.models.nodegroupinterface import NodeGroupInterface
 from maasserver.models.sshkey import SSHKey
 from maasserver.models.user import create_user
 from maasserver.models.userprofile import UserProfile
@@ -49,7 +51,7 @@
 # export in __all__.
 ignore_unused(
     Config, DHCPLease, FileStorage, MACAddress, NodeGroup, SSHKey,
-    UserProfile)
+    UserProfile, NodeGroupInterface)
 
 
 # Connect the 'create_user' method to the post save signal of User.

=== added file 'src/maasserver/models/nodegroupinterface.py'
--- src/maasserver/models/nodegroupinterface.py	1970-01-01 00:00:00 +0000
+++ src/maasserver/models/nodegroupinterface.py	2012-09-12 09:53:23 +0000
@@ -0,0 +1,63 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Model definition for NodeGroupInterface."""
+
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = [
+    'NodeGroupInterface',
+    ]
+
+
+from django.db.models import (
+    CharField,
+    ForeignKey,
+    IntegerField,
+    IPAddressField,
+    )
+from maasserver import DefaultMeta
+from maasserver.enum import (
+    NODEGROUPINTERFACE_STATUS,
+    NODEGROUPINTERFACE_STATUS_CHOICES,
+    )
+from maasserver.models.timestampedmodel import TimestampedModel
+
+
+class NodeGroupInterface(TimestampedModel):
+
+    class Meta(DefaultMeta):
+        unique_together = ('nodegroup', 'interface')
+
+    # Static IP of the interface.
+    ip = IPAddressField(null=False, editable=True)
+
+    # The `NodeGroup` this interface belongs to.
+    nodegroup = ForeignKey(
+        'maasserver.NodeGroup', editable=True, null=False, blank=False)
+
+    status = IntegerField(
+        choices=NODEGROUPINTERFACE_STATUS_CHOICES, editable=False,
+        default=NODEGROUPINTERFACE_STATUS.DEFAULT_STATUS)
+
+    # DHCP server settings.
+    interface = CharField(
+        blank=True, editable=False, max_length=255, default='')
+    subnet_mask = IPAddressField(
+        editable=True, unique=False, blank=True, null=True, default=None)
+    broadcast_ip = IPAddressField(
+        editable=True, unique=False, blank=True, null=True, default=None)
+    router_ip = IPAddressField(
+        editable=True, unique=False, blank=True, null=True, default=None)
+    ip_range_low = IPAddressField(
+        editable=True, unique=True, blank=True, null=True, default=None)
+    ip_range_high = IPAddressField(
+        editable=True, unique=True, blank=True, null=True, default=None)
+
+    def __repr__(self):
+        return "<NodeGroupInterface %r,%s>" % (self.nodegroup, self.interface)