launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #07075
[Merge] lp:~jtv/maas/commissioning-files into lp:maas
Jeroen T. Vermeulen has proposed merging lp:~jtv/maas/commissioning-files into lp:maas with lp:~julian-edwards/maas/commission-result-model as a prerequisite.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~jtv/maas/commissioning-files/+merge/101497
Pre-imp with Julian (and basic-commissioning design document updated accordingly).
We are required to support storage of commissioning information in the release. This branch puts several of the pieces together. As a node goes through its commissioning steps, it calls the metadata server's signalling API. Regardless of status, any such call may attach files. They all go into Julian's brand-new NodeCommissionResult model.
The files are specified to be UTF-8 encoded text, up to 1MiB in length.
--
https://code.launchpad.net/~jtv/maas/commissioning-files/+merge/101497
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jtv/maas/commissioning-files into lp:maas.
=== modified file 'src/metadataserver/api.py'
--- src/metadataserver/api.py 2012-04-04 17:15:46 +0000
+++ src/metadataserver/api.py 2012-04-11 07:07:20 +0000
@@ -36,6 +36,7 @@
SSHKey,
)
from metadataserver.models import (
+ NodeCommissionResult,
NodeKey,
NodeUserData,
)
@@ -126,6 +127,12 @@
shown_fields.remove('user-data')
return make_list_response(sorted(shown_fields))
+ def _store_commissioning_results(self, node, request):
+ """Store commissioning result files for `node`."""
+ for name, uploaded_file in request.FILES.items():
+ contents = uploaded_file.read().decode('utf-8')
+ NodeCommissionResult.objects.store_data(node, name, contents)
+
@api_exported('signal', 'POST')
def signal(self, request, version=None):
"""Signal commissioning status.
@@ -161,6 +168,8 @@
# Already registered. Nothing to be done.
return rc.ALL_OK
+ self._store_commissioning_results(node, request)
+
target_status = self.signaling_statuses.get(status)
if target_status in (None, node.status):
# No status change. Nothing to be done.
=== modified file 'src/metadataserver/migrations/0002_add_nodecommissionresult.py'
--- src/metadataserver/migrations/0002_add_nodecommissionresult.py 2012-04-11 07:07:20 +0000
+++ src/metadataserver/migrations/0002_add_nodecommissionresult.py 2012-04-11 07:07:20 +0000
@@ -4,9 +4,11 @@
# encoding: utf-8
import datetime
+
+from django.db import models
from south.db import db
from south.v2 import SchemaMigration
-from django.db import models
+
class Migration(SchemaMigration):
=== modified file 'src/metadataserver/tests/test_api.py'
--- src/metadataserver/tests/test_api.py 2012-04-11 05:42:20 +0000
+++ src/metadataserver/tests/test_api.py 2012-04-11 07:07:20 +0000
@@ -34,6 +34,7 @@
UnknownMetadataVersion,
)
from metadataserver.models import (
+ NodeCommissionResult,
NodeKey,
NodeUserData,
)
@@ -142,6 +143,7 @@
params.update(kwargs)
for name, content in files.items():
params[name] = BytesIO(content)
+ params[name].name = name
return client.post(self.make_url('/%s/' % version), params)
def test_no_anonymous_access(self):
@@ -371,3 +373,71 @@
response = self.call_signal(client)
self.assertEqual(httplib.OK, response.status_code)
self.assertEqual('', reload_object(node).error)
+
+ def test_signalling_stores_files_for_any_status(self):
+ statuses = ['WORKING', 'OK', 'FAILED']
+ filename = factory.getRandomString()
+ nodes = {
+ status: factory.make_node(status=NODE_STATUS.COMMISSIONING)
+ for status in statuses}
+ for status, node in nodes.items():
+ client = self.make_node_client(node=node)
+ self.call_signal(
+ client, status=status,
+ files={filename: factory.getRandomString().encode('ascii')})
+ self.assertEqual(
+ {status: filename for status in statuses},
+ {
+ status: NodeCommissionResult.objects.get(node=node).name
+ for status, node in nodes.items()})
+
+ def test_signal_stores_file_contents(self):
+ node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
+ client = self.make_node_client(node=node)
+ text = factory.getRandomString().encode('ascii')
+ response = self.call_signal(client, files={'file.txt': text})
+ self.assertEqual(httplib.OK, response.status_code)
+ self.assertEqual(
+ text, NodeCommissionResult.objects.get_data(node, 'file.txt'))
+
+ def test_signal_decodes_file_from_UTF8(self):
+ unicode_text = '<\u2621>'
+ node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
+ client = self.make_node_client(node=node)
+ response = self.call_signal(
+ client, files={'file.txt': unicode_text.encode('utf-8')})
+ self.assertEqual(httplib.OK, response.status_code)
+ self.assertEqual(
+ unicode_text,
+ NodeCommissionResult.objects.get_data(node, 'file.txt'))
+
+ def test_signal_stores_multiple_files(self):
+ contents = {
+ factory.getRandomString(): factory.getRandomString().encode(
+ 'ascii')
+ for counter in range(3)}
+ node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
+ client = self.make_node_client(node=node)
+ response = self.call_signal(client, files=contents)
+ self.assertEqual(httplib.OK, response.status_code)
+ self.assertEqual(
+ contents,
+ {
+ result.name: result.data
+ for result in NodeCommissionResult.objects.filter(node=node)
+ })
+
+ def test_signal_stores_files_up_to_documented_size_limit(self):
+ # The documented size limit for commissioning result files:
+ # one megabyte. What happens above this limit is none of
+ # anybody's business, but files up to this size should work.
+ size_limit = 2 ** 20
+ contents = factory.getRandomString(size_limit, spaces=True)
+ node = factory.make_node(status=NODE_STATUS.COMMISSIONING)
+ client = self.make_node_client(node=node)
+ response = self.call_signal(
+ client, files={'output.txt': contents.encode('utf-8')})
+ self.assertEqual(httplib.OK, response.status_code)
+ stored_data = NodeCommissionResult.objects.get_data(
+ node, 'output.txt')
+ self.assertEqual(size_limit, len(stored_data))
=== modified file 'src/metadataserver/tests/test_models.py'
--- src/metadataserver/tests/test_models.py 2012-04-11 07:07:20 +0000
+++ src/metadataserver/tests/test_models.py 2012-04-11 07:07:20 +0000
@@ -13,7 +13,6 @@
from django.db import IntegrityError
from django.http import Http404
-
from maasserver.testing.factory import factory
from maastesting.testcase import TestCase
from metadataserver.models import (