← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~rvb/maas/first-cluster-registration into lp:maas

 

Raphaël Badin has proposed merging lp:~rvb/maas/first-cluster-registration into lp:maas with lp:~rvb/maas/ensure-master-takes-first as a prerequisite.

Commit message:
Change the nodegroup registration API method: when a cluster controller registers, if the master nodegroup still has it's UUID not set to a proper UUID, link this cluster controller to the master nodegroup.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~rvb/maas/first-cluster-registration/+merge/126501

Change the nodegroup registration API method: when a cluster controller registers, if the master nodegroup still has it's UUID not set to a proper UUID, link this cluster controller to the master nodegroup.

= Pre-imp =

This was discussed with Julian and Jeroen.
-- 
https://code.launchpad.net/~rvb/maas/first-cluster-registration/+merge/126501
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/maas/first-cluster-registration into lp:maas.
=== modified file 'src/maasserver/api.py'
--- src/maasserver/api.py	2012-09-26 10:00:14 +0000
+++ src/maasserver/api.py	2012-09-26 16:53:22 +0000
@@ -911,6 +911,14 @@
         return ('files_handler', [])
 
 
+def get_celery_credentials():
+    """Return the credentials needed to connect to the broker."""
+    celery_conf = app_or_default().conf
+    return {
+        'BROKER_URL': celery_conf.BROKER_URL,
+    }
+
+
 DISPLAYED_NODEGROUP_FIELDS = ('uuid', 'status', 'name')
 
 
@@ -974,24 +982,33 @@
         uuid = get_mandatory_param(request.data, 'uuid')
         existing_nodegroup = get_one(NodeGroup.objects.filter(uuid=uuid))
         if existing_nodegroup is None:
-            # This nodegroup (identified by its uuid), does not exist yet,
-            # create it if the data validates.
-            form = NodeGroupWithInterfacesForm(request.data)
-            if form.is_valid():
-                form.save()
-                return HttpResponse(
-                    "Cluster registered.  Awaiting admin approval.",
-                    status=httplib.ACCEPTED)
+            master = NodeGroup.objects.ensure_master()
+            # Does master.uuid look like it's a proper uuid?
+            if len(master.uuid) != 36:
+                # Master nodegroup not yet configured, configure it.
+                form = NodeGroupWithInterfacesForm(
+                    data=request.data, instance=master)
+                if form.is_valid():
+                    form.save()
+                    return get_celery_credentials()
+                else:
+                    raise ValidationError(form.errors)
             else:
-                raise ValidationError(form.errors)
+                # This nodegroup (identified by its uuid), does not exist yet,
+                # create it if the data validates.
+                form = NodeGroupWithInterfacesForm(
+                    data=request.data, status=NODEGROUP_STATUS.PENDING)
+                if form.is_valid():
+                    form.save()
+                    return HttpResponse(
+                        "Cluster registered.  Awaiting admin approval.",
+                        status=httplib.ACCEPTED)
+                else:
+                    raise ValidationError(form.errors)
         else:
             if existing_nodegroup.status == NODEGROUP_STATUS.ACCEPTED:
                 # The nodegroup exists and is validated, return the RabbitMQ
-                # credentials as JSON.
-                celery_conf = app_or_default().conf
-                return {
-                    'BROKER_URL': celery_conf.BROKER_URL,
-                }
+                return get_celery_credentials()
             elif existing_nodegroup.status == NODEGROUP_STATUS.REJECTED:
                 raise PermissionDenied('Rejected cluster.')
             elif existing_nodegroup.status == NODEGROUP_STATUS.PENDING:

=== modified file 'src/maasserver/forms.py'
--- src/maasserver/forms.py	2012-09-23 14:14:20 +0000
+++ src/maasserver/forms.py	2012-09-26 16:53:22 +0000
@@ -59,7 +59,6 @@
     DISTRO_SERIES_CHOICES,
     NODE_AFTER_COMMISSIONING_ACTION,
     NODE_AFTER_COMMISSIONING_ACTION_CHOICES,
-    NODEGROUP_STATUS,
     NODEGROUPINTERFACE_MANAGEMENT,
     NODEGROUPINTERFACE_MANAGEMENT_CHOICES,
     )
@@ -688,7 +687,7 @@
 
 
 class NodeGroupWithInterfacesForm(ModelForm):
-    """Create a pending NodeGroup with unmanaged interfaces."""
+    """Create a NodeGroup with unmanaged interfaces."""
 
     interfaces = forms.CharField(required=False)
 
@@ -699,6 +698,10 @@
             'uuid',
             )
 
+    def __init__(self, status=None, *args, **kwargs):
+        super(NodeGroupWithInterfacesForm, self).__init__(*args, **kwargs)
+        self.status = status
+
     def clean_interfaces(self):
         data = self.cleaned_data['interfaces']
         # Stop here if the data is empty.
@@ -741,9 +744,9 @@
         for interface in self.cleaned_data['interfaces']:
             form = NodeGroupInterfaceForm(data=interface)
             form.save(nodegroup=nodegroup)
-        # Set the nodegroup to be 'PENDING'.
-        nodegroup.status = NODEGROUP_STATUS.PENDING
-        nodegroup.save()
+        if self.status is not None:
+            nodegroup.status = self.status
+            nodegroup.save()
         return nodegroup
 
 

=== modified file 'src/maasserver/tests/test_api.py'
--- src/maasserver/tests/test_api.py	2012-09-26 16:53:22 +0000
+++ src/maasserver/tests/test_api.py	2012-09-26 16:53:22 +0000
@@ -2705,6 +2705,11 @@
         ('celery', FixtureResource(CeleryFixture())),
         )
 
+    def create_configured_master(self):
+        master = NodeGroup.objects.ensure_master()
+        master.uuid = factory.getRandomUUID()
+        master.save()
+
     def test_refresh_calls_refresh_worker(self):
         nodegroup = factory.make_node_group()
         response = self.client.post(
@@ -2723,6 +2728,7 @@
             (response.status_code, response.content))
 
     def test_register_nodegroup_creates_nodegroup_and_interfaces(self):
+        self.create_configured_master()
         name = factory.make_name('name')
         uuid = factory.getRandomUUID()
         interface = make_interface_settings()
@@ -2747,7 +2753,45 @@
         # validated by an admin.
         self.assertEqual(httplib.ACCEPTED, response.status_code)
 
+    def test_register_nodegroup_returns_credentials_if_master(self):
+        name = factory.make_name('name')
+        uuid = factory.getRandomUUID()
+        fake_broker_url = factory.make_name('fake-broker_url')
+        celery_conf = app_or_default().conf
+        self.patch(celery_conf, 'BROKER_URL', fake_broker_url)
+        response = self.client.post(
+            reverse('nodegroups_handler'),
+            {
+                'op': 'register',
+                'name': name,
+                'uuid': uuid,
+            })
+        self.assertEqual(httplib.OK, response.status_code, response)
+        parsed_result = json.loads(response.content)
+        self.assertEqual(
+            ({'BROKER_URL': fake_broker_url}, uuid),
+            (parsed_result, NodeGroup.objects.ensure_master().uuid))
+
+    def test_register_nodegroup_configures_master_if_unconfigured(self):
+        name = factory.make_name('name')
+        uuid = factory.getRandomUUID()
+        interface = make_interface_settings()
+        self.client.post(
+            reverse('nodegroups_handler'),
+            {
+                'op': 'register',
+                'name': name,
+                'uuid': uuid,
+                'interfaces': json.dumps([interface]),
+            })
+        master = NodeGroup.objects.ensure_master()
+        self.assertThat(
+            master.nodegroupinterface_set.all()[0],
+            MatchesStructure.byEquality(**interface))
+        self.assertEqual(NODEGROUP_STATUS.ACCEPTED, master.status)
+
     def test_register_accepts_only_one_managed_interface(self):
+        self.create_configured_master()
         name = factory.make_name('name')
         uuid = factory.getRandomUUID()
         # This will try to create 2 "managed" interfaces.
@@ -2774,6 +2818,7 @@
             (response.status_code, json.loads(response.content)))
 
     def test_register_nodegroup_validates_data(self):
+        self.create_configured_master()
         response = self.client.post(
             reverse('nodegroups_handler'),
             {

=== modified file 'src/maasserver/tests/test_forms.py'
--- src/maasserver/tests/test_forms.py	2012-09-21 07:09:00 +0000
+++ src/maasserver/tests/test_forms.py	2012-09-26 16:53:22 +0000
@@ -673,6 +673,16 @@
                 nodegroup.nodegroupinterface_set.count(),
             ))
 
+    def test_NodeGroupWithInterfacesForm_creates_nodegroup_with_status(self):
+        name = factory.make_name('name')
+        uuid = factory.getRandomUUID()
+        form = NodeGroupWithInterfacesForm(
+            status=NODEGROUP_STATUS.ACCEPTED,
+            data={'name': name, 'uuid': uuid})
+        self.assertTrue(form.is_valid(), form._errors)
+        nodegroup = form.save()
+        self.assertEqual(NODEGROUP_STATUS.ACCEPTED, nodegroup.status)
+
     def test_NodeGroupWithInterfacesForm_validates_parameters(self):
         name = factory.make_name('name')
         too_long_uuid = 'test' * 30


Follow ups