← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~andreserl/maas/add_distro_series_support_lp1013146 into lp:maas

 

Andres Rodriguez has proposed merging lp:~andreserl/maas/add_distro_series_support_lp1013146 into lp:maas.

Requested reviews:
  MAAS Maintainers (maas-maintainers)
Related bugs:
  Bug #1013146 in MAAS: "MAAS currently only supports Ubuntu version 12.04 to be installed on the nodes."
  https://bugs.launchpad.net/maas/+bug/1013146

For more details, see:
https://code.launchpad.net/~andreserl/maas/add_distro_series_support_lp1013146/+merge/124482
-- 
https://code.launchpad.net/~andreserl/maas/add_distro_series_support_lp1013146/+merge/124482
Your team MAAS Maintainers is requested to review the proposed merge of lp:~andreserl/maas/add_distro_series_support_lp1013146 into lp:maas.
=== modified file 'src/maasserver/api.py'
--- src/maasserver/api.py	2012-09-14 13:09:39 +0000
+++ src/maasserver/api.py	2012-09-14 17:24:34 +0000
@@ -1185,14 +1185,17 @@
         preseed_url = compose_preseed_url(node)
         hostname = node.hostname
 
-    # XXX JeroenVermeulen 2012-08-06 bug=1013146: Stop hard-coding this.
-    release = 'precise'
+    if node is None or node.status == NODE_STATUS.COMMISSIONING:
+        series = Config.objects.get_config('commissioning_distro_series')
+    else:
+        series = node.get_distro_series()
+
     purpose = get_boot_purpose(node)
     domain = 'local.lan'  # TODO: This is probably not enough!
     server_address = get_maas_facing_server_address()
 
     params = KernelParameters(
-        arch=arch, subarch=subarch, release=release, purpose=purpose,
+        arch=arch, subarch=subarch, release=series, purpose=purpose,
         hostname=hostname, domain=domain, preseed_url=preseed_url,
         log_host=server_address, fs_host=server_address)
 

=== modified file 'src/maasserver/enum.py'
--- src/maasserver/enum.py	2012-09-12 15:37:13 +0000
+++ src/maasserver/enum.py	2012-09-14 17:24:34 +0000
@@ -24,9 +24,12 @@
     'NODE_STATUS_CHOICES',
     'NODE_STATUS_CHOICES_DICT',
     'PRESEED_TYPE',
+    'DISTRO_SERIES',
+    'DISTRO_SERIES_CHOICES',
     ]
 
 from collections import OrderedDict
+from distro_info import UbuntuDistroInfo
 
 
 class NODE_STATUS:
@@ -116,6 +119,31 @@
 )
 
 
+class DISTRO_SERIES:
+    """List of supported ubuntu releases."""
+    default = ''
+
+# Obtain all supported releases and its full name
+series_codenames = UbuntuDistroInfo().supported()
+series_fullnames = UbuntuDistroInfo().supported(result="fullname")
+series = dict(zip(series_codenames, series_fullnames))
+# Create attributes for supported releases starting from `precise`
+for codename in series:
+    if codename >= "precise":
+        setattr(DISTRO_SERIES, codename, codename)
+
+# Release name Description for UI Choices
+DISTRO_SERIES_CHOICES = ()
+real_series = [attr for attr in dir(DISTRO_SERIES())
+    if not callable(attr) and not attr.startswith("__")]
+for codename in real_series:
+    if codename != 'default':
+        DISTRO_SERIES_CHOICES = DISTRO_SERIES_CHOICES + (
+            (codename, series[codename]),)
+    else:
+        DISTRO_SERIES_CHOICES += ((DISTRO_SERIES.default, 'Default Ubuntu release'),)
+
+
 class NODE_PERMISSION:
     """Permissions relating to nodes."""
     VIEW = 'view_node'

=== modified file 'src/maasserver/forms.py'
--- src/maasserver/forms.py	2012-09-14 07:15:49 +0000
+++ src/maasserver/forms.py	2012-09-14 17:24:34 +0000
@@ -60,6 +60,8 @@
     NODE_AFTER_COMMISSIONING_ACTION_CHOICES,
     NODEGROUP_STATUS,
     NODEGROUPINTERFACE_MANAGEMENT,
+    DISTRO_SERIES,
+    DISTRO_SERIES_CHOICES,
     )
 from maasserver.fields import MACAddressFormField
 from maasserver.models import (
@@ -98,6 +100,9 @@
 INVALID_ARCHITECTURE_MESSAGE = compose_invalid_choice_text(
     'architecture', ARCHITECTURE_CHOICES)
 
+INVALID_DISTRO_SERIES_MESSAGE = compose_invalid_choice_text(
+    'distro_series', DISTRO_SERIES_CHOICES)
+
 
 class NodeForm(ModelForm):
     after_commissioning_action = forms.TypedChoiceField(
@@ -105,6 +110,12 @@
         choices=NODE_AFTER_COMMISSIONING_ACTION_CHOICES, required=False,
         empty_value=NODE_AFTER_COMMISSIONING_ACTION.DEFAULT)
 
+    distro_series = forms.ChoiceField(
+        choices=DISTRO_SERIES_CHOICES, required=False,
+        initial=DISTRO_SERIES.default,
+        label="Release",
+        error_messages={'invalid_choice': INVALID_DISTRO_SERIES_MESSAGE})
+
     architecture = forms.ChoiceField(
         choices=ARCHITECTURE_CHOICES, required=True,
         initial=ARCHITECTURE.i386,
@@ -116,6 +127,7 @@
             'hostname',
             'after_commissioning_action',
             'architecture',
+            'distro_series',
             )
 
 
@@ -155,6 +167,7 @@
             'hostname',
             'after_commissioning_action',
             'architecture',
+            'distro_series',
             'power_type',
             'power_parameters',
             )

=== added file 'src/maasserver/migrations/0026_add_node_distro_series.py'
--- src/maasserver/migrations/0026_add_node_distro_series.py	1970-01-01 00:00:00 +0000
+++ src/maasserver/migrations/0026_add_node_distro_series.py	2012-09-14 17:24:34 +0000
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'Node.distro_series'
+        db.add_column(u'maasserver_node', 'distro_series',
+                      self.gf('django.db.models.fields.CharField')(default=None, max_length=10, null=True, blank=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Node.distro_series'
+        db.delete_column(u'maasserver_node', 'distro_series')
+
+
+    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.bootimage': {
+            'Meta': {'unique_together': "((u'architecture', u'subarchitecture', u'release', u'purpose'),)", 'object_name': 'BootImage'},
+            'architecture': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'purpose': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'release': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'subarchitecture': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        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', [], {}),
+            'distro_series': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '10', 'null': 'True', 'blank': 'True'}),
+            '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-84ba9666-fe87-11e1-93b3-58946bf1d72c'", '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'}),
+            'created': ('django.db.models.fields.DateTimeField', [], {}),
+            'dhcp_key': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'updated': ('django.db.models.fields.DateTimeField', [], {}),
+            'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'})
+        },
+        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'}),
+            'management': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            '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'}),
+            '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': '1347639379L'}),
+            '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']
\ No newline at end of file

=== modified file 'src/maasserver/models/config.py'
--- src/maasserver/models/config.py	2012-09-05 13:30:21 +0000
+++ src/maasserver/models/config.py	2012-09-14 17:24:34 +0000
@@ -27,6 +27,7 @@
 from django.db.models.signals import post_save
 from maasserver import DefaultMeta
 from maasserver.enum import (
+    DISTRO_SERIES,
     DNS_DHCP_MANAGEMENT,
     NODE_AFTER_COMMISSIONING_ACTION,
     )
@@ -53,6 +54,8 @@
         'enlistment_domain': b'local',
         'dns_dhcp_management': DNS_DHCP_MANAGEMENT.NONE,
         ## /settings
+        'default_distro_series': DISTRO_SERIES.precise,
+        'commissioning_distro_series': DISTRO_SERIES.precise,
         }
 
 

=== modified file 'src/maasserver/models/node.py'
--- src/maasserver/models/node.py	2012-08-24 10:45:47 +0000
+++ src/maasserver/models/node.py	2012-09-14 17:24:34 +0000
@@ -38,6 +38,8 @@
 from maasserver.enum import (
     ARCHITECTURE,
     ARCHITECTURE_CHOICES,
+    DISTRO_SERIES,
+    DISTRO_SERIES_CHOICES,
     NODE_AFTER_COMMISSIONING_ACTION,
     NODE_AFTER_COMMISSIONING_ACTION_CHOICES,
     NODE_PERMISSION,
@@ -353,6 +355,10 @@
         choices=NODE_AFTER_COMMISSIONING_ACTION_CHOICES,
         default=NODE_AFTER_COMMISSIONING_ACTION.DEFAULT)
 
+    distro_series = CharField(
+        max_length=10, choices=DISTRO_SERIES_CHOICES, null=True,
+        blank=True, default=None)
+
     architecture = CharField(
         max_length=10, choices=ARCHITECTURE_CHOICES, blank=False,
         default=ARCHITECTURE.i386)
@@ -550,6 +556,13 @@
         else:
             return None
 
+    def get_distro_series(self):
+        """Return the distro series to install that node."""
+        if not self.distro_series or self.distro_series == DISTRO_SERIES.default:
+            return Config.objects.get_config('default_distro_series')
+        else:
+            return self.distro_series
+
     def get_effective_power_parameters(self):
         """Return effective power parameters, including any defaults."""
         if self.power_parameters:

=== modified file 'src/maasserver/preseed.py'
--- src/maasserver/preseed.py	2012-08-21 20:27:47 +0000
+++ src/maasserver/preseed.py	2012-09-14 17:24:34 +0000
@@ -30,6 +30,7 @@
     )
 from maasserver.server_address import get_maas_facing_server_host
 from maasserver.utils import absolute_reverse
+from maasserver.models import Config
 import tempita
 
 
@@ -54,28 +55,26 @@
     return render_preseed(None, PRESEED_TYPE.ENLIST_USERDATA)
 
 
-# XXX: rvb 2012-06-21 bug=1013146:  'precise' is hardcoded here.
-def get_preseed(node, release="precise"):
+def get_preseed(node):
     """Return the preseed for a given node.  Depending on the node's status
     this will be a commissioning preseed (if the node is commissioning) or the
     standard preseed (normal installation preseed).
 
     :param node: The node to return preseed for.
     :type node: :class:`maasserver.models.Node`
-    :param release: The Ubuntu release to be used.
-    :type release: basestring
     :return: The rendered preseed string.
     :rtype: basestring.
     """
     if node.status == NODE_STATUS.COMMISSIONING:
         return render_preseed(
-            node, PRESEED_TYPE.COMMISSIONING, release=release)
+            node, PRESEED_TYPE.COMMISSIONING, 
+            release=Config.objects.get_config('commissioning_distro_series'))
     else:
-        return render_preseed(node, PRESEED_TYPE.DEFAULT, release=release)
-
-
-# XXX: rvb 2012-06-14 bug=1013146:  'precise' is hardcoded here.
-def get_preseed_filenames(node, prefix='', release='precise', default=False):
+        return render_preseed(node, PRESEED_TYPE.DEFAULT,
+            release=node.get_distro_series())
+
+
+def get_preseed_filenames(node, prefix='', release='', default=False):
     """List possible preseed template filenames for the given node.
 
     :param node: The node to return template preseed filenames for.
@@ -174,8 +173,7 @@
         self.name = name
 
 
-# XXX: rvb 2012-06-18 bug=1013146:  'precise' is hardcoded here.
-def load_preseed_template(node, prefix, release="precise"):
+def load_preseed_template(node, prefix, release=''):
     """Find and load a `PreseedTemplate` for the given node.
 
     :param node: See `get_preseed_filenames`.
@@ -201,8 +199,7 @@
     return get_template(prefix, None, default=True)
 
 
-# XXX: rvb 2012-06-19 bug=1013146:  'precise' is hardcoded here.
-def get_preseed_context(node, release="precise"):
+def get_preseed_context(node, release=''):
     """Return the context dictionary to be used to render preseed templates
     for this node.
 
@@ -236,8 +233,7 @@
     return context
 
 
-# XXX: rvb 2012-06-19 bug=1013146:  'precise' is hardcoded here.
-def render_preseed(node, prefix, release="precise"):
+def render_preseed(node, prefix, release=''):
     """Find and load a `PreseedTemplate` for the given node.
 
     :param node: See `get_preseed_filenames`.

=== modified file 'src/maasserver/templates/maasserver/snippets.html'
--- src/maasserver/templates/maasserver/snippets.html	2012-06-11 12:25:37 +0000
+++ src/maasserver/templates/maasserver/snippets.html	2012-09-14 17:24:34 +0000
@@ -20,6 +20,10 @@
     {{ node_form.after_commissioning_action }}
   </p>
   <p>
+    <label for="id_distro_series">Release</label>
+    {{ node_form.distr_series }}
+  </p>
+  <p>
     <label for="id_architecture">Architecture</label>
     {{ node_form.architecture }}
   </p>