← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~jtv/maas/bug-1025582-api into lp:maas

 

Jeroen T. Vermeulen has proposed merging lp:~jtv/maas/bug-1025582-api into lp:maas with lp:~jtv/maas/bug-1025582-model as a prerequisite.

Requested reviews:
  MAAS Maintainers (maas-maintainers)

For more details, see:
https://code.launchpad.net/~jtv/maas/bug-1025582-api/+merge/123711

I did not have a separate pre-imp call for this part of the work, although I did discuss the approach (including having an API call along these lines) with Julian.


Jeroen
-- 
https://code.launchpad.net/~jtv/maas/bug-1025582-api/+merge/123711
Your team MAAS Maintainers is requested to review the proposed merge of lp:~jtv/maas/bug-1025582-api into lp:maas.
=== modified file 'src/maasserver/api.py'
--- src/maasserver/api.py	2012-09-03 04:59:58 +0000
+++ src/maasserver/api.py	2012-09-11 10:28:19 +0000
@@ -56,6 +56,7 @@
 __all__ = [
     "api_doc",
     "api_doc_title",
+    "BootImagesHandler",
     "generate_api_doc",
     "get_oauth_token",
     "AccountHandler",
@@ -117,6 +118,7 @@
     get_node_edit_form,
     )
 from maasserver.models import (
+    BootImage,
     Config,
     DHCPLease,
     FileStorage,
@@ -1013,6 +1015,20 @@
         value = Config.objects.get_config(name)
         return HttpResponse(json.dumps(value), content_type='application/json')
 
+    @api_exported('POST')
+    def report_boot_images(self, request):
+        """Report available boot images.
+
+        A boot image consists of a kernel and initrd, which a netbooting
+        node can download from TFTP (as directed over PXE).  These are
+        downloaded by the `maas-import-pxe-files` script, running on the
+        same system as the master worker.  The master worker can report
+        to the server which boot images are available to nodes.
+
+        :param images: A list of tuples: (architecture, sub-architecture,
+            release, purpose).
+        """
+
 
 # Title section for the API documentation.  Matches in style, format,
 # etc. whatever generate_api_doc() produces, so that you can concatenate
@@ -1154,3 +1170,30 @@
     return HttpResponse(
         json.dumps(params._asdict()),
         content_type="application/json")
+
+
+@api_operations
+class BootImagesHandler(BaseHandler):
+
+    @classmethod
+    def resource_uri(cls):
+        return ('boot_images_handler', [])
+
+    @api_exported('POST')
+    def report_boot_images(self, request):
+        """Report images available to net-boot nodes from.
+
+        :param images: A list of dicts, each describing a boot image with
+            these properties: `architecture`, `subarchitecture`, `release`,
+            `purpose`, all as in the code that determines TFTP paths for
+            these images.
+        """
+        get_nodegroup_for_worker(request, 'master')
+        images = json.loads(get_mandatory_param(request.data, 'images'))
+        for image in images:
+            BootImage.objects.register_image(
+                architecture=image['architecture'],
+                subarchitecture=image.get('subarchitecture', 'generic'),
+                release=image['release'],
+                purpose=image['purpose'])
+        return HttpResponse("Images noted.")

=== modified file 'src/maasserver/tests/test_api.py'
--- src/maasserver/tests/test_api.py	2012-09-05 13:30:21 +0000
+++ src/maasserver/tests/test_api.py	2012-09-11 10:28:19 +0000
@@ -52,6 +52,7 @@
 from maasserver.exceptions import Unauthorized
 from maasserver.fields import mac_error_msg
 from maasserver.models import (
+    BootImage,
     Config,
     DHCPLease,
     MACAddress,
@@ -2547,15 +2548,17 @@
             nodegroup_path, 'update_leases', leases=json.dumps(leases))
 
 
+def log_in_as_normal_user(client):
+    """Log `client` in as a normal user."""
+    password = factory.getRandomString()
+    user = factory.make_user(password=password)
+    client.login(username=user.username, password=password)
+    return user
+
+
 class TestNodeGroupAPIAuth(APIv10TestMixin, TestCase):
     """Authorization tests for nodegroup API."""
 
-    def log_in_as_normal_user(self):
-        """Log `self.client` in as a normal user."""
-        password = factory.getRandomString()
-        user = factory.make_user(password=password)
-        self.client.login(username=user.username, password=password)
-
     def test_nodegroup_requires_authentication(self):
         nodegroup = factory.make_node_group()
         response = self.client.get(
@@ -2574,7 +2577,7 @@
 
     def test_update_leases_does_not_work_for_normal_user(self):
         nodegroup = factory.make_node_group()
-        self.log_in_as_normal_user()
+        log_in_as_normal_user(self.client)
         response = self.client.post(
             reverse('nodegroup_handler', args=[nodegroup.name]),
             {'op': 'update_leases', 'leases': json.dumps({})})
@@ -2592,3 +2595,59 @@
         self.assertEqual(
             httplib.FORBIDDEN, response.status_code,
             explain_unexpected_response(httplib.FORBIDDEN, response))
+
+
+class TestBootImagesAPI(APITestCase):
+
+    def report_images(self, images, client=None):
+        if client is None:
+            client = self.client
+        return client.post(
+            reverse('boot_images_handler'),
+            {'op': 'report_boot_images', 'images': json.dumps(images)})
+
+    def test_report_boot_images_does_not_work_for_normal_user(self):
+        NodeGroup.objects.ensure_master()
+        log_in_as_normal_user(self.client)
+        response = self.report_images([])
+        self.assertEqual(httplib.FORBIDDEN, response.status_code)
+
+    def test_report_boot_images_works_for_master_worker(self):
+        client = make_worker_client(NodeGroup.objects.ensure_master())
+        response = self.report_images([], client=client)
+        self.assertEqual(httplib.OK, response.status_code)
+
+    def test_report_boot_images_does_not_work_for_other_workers(self):
+        NodeGroup.objects.ensure_master()
+        client = make_worker_client(factory.make_node_group())
+        response = self.report_images([], client=client)
+        self.assertEqual(httplib.FORBIDDEN, response.status_code)
+
+    def test_report_boot_images_stores_images(self):
+        image = {
+            'architecture': factory.make_name('architecture'),
+            'subarchitecture': factory.make_name('subarchitecture'),
+            'release': factory.make_name('release'),
+            'purpose': factory.make_name('purpose'),
+        }
+        client = make_worker_client(NodeGroup.objects.ensure_master())
+        response = self.report_images([image], client=client)
+        self.assertEqual(
+            (httplib.OK, "Images noted."),
+            (response.status_code, response.content))
+        self.assertTrue(
+            BootImage.objects.have_image(**image))
+
+    def test_report_boot_images_ignores_unknown_image_properties(self):
+        image = {
+            'architecture': factory.make_name('architecture'),
+            'subarchitecture': factory.make_name('subarchitecture'),
+            'release': factory.make_name('release'),
+            'purpose': factory.make_name('purpose'),
+            'nonesuch': factory.make_name('nonesuch'),
+        }
+        client = make_worker_client(NodeGroup.objects.ensure_master())
+        response = self.report_images([image], client=client)
+        self.assertEqual(
+            (httplib.OK, "Images noted."),
+            (response.status_code, response.content))

=== modified file 'src/maasserver/urls_api.py'
--- src/maasserver/urls_api.py	2012-08-17 02:27:29 +0000
+++ src/maasserver/urls_api.py	2012-09-11 10:28:19 +0000
@@ -20,6 +20,7 @@
     AccountHandler,
     AdminRestrictedResource,
     api_doc,
+    BootImagesHandler,
     FilesHandler,
     MAASHandler,
     NodeGroupHandler,
@@ -44,6 +45,8 @@
     NodeMacsHandler, authentication=api_auth)
 nodegroup_handler = RestrictedResource(
     NodeGroupHandler, authentication=api_auth)
+boot_images_handler = RestrictedResource(
+    BootImagesHandler, authentication=api_auth)
 
 # The nodegroups view is anonymously accessible, but anonymous users
 # can't drill down into individual nodegruops.
@@ -80,6 +83,7 @@
         nodegroup_handler, name='nodegroup_handler'),
     url(r'files/$', files_handler, name='files_handler'),
     url(r'account/$', account_handler, name='account_handler'),
+    url(r'boot-images/$', boot_images_handler, name='boot_images_handler'),
 )
 
 


Follow ups