launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #15273
[Merge] lp:~rvb/maas/per-tenant-provider-state into lp:maas
Raphaël Badin has proposed merging lp:~rvb/maas/per-tenant-provider-state into lp:maas.
Commit message:
Add utility to extract the owner of the bootstrap node.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~rvb/maas/per-tenant-provider-state/+merge/151542
This branch adds a tiny utility to parse the provider-state file and extract the owner of the bootstrap node. This will be used by the branch Gavin is working on right now to migrate a shared-namespace instance to a per-tenant namespace (see details here: http://goo.gl/d5XiR).
--
https://code.launchpad.net/~rvb/maas/per-tenant-provider-state/+merge/151542
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/maas/per-tenant-provider-state into lp:maas.
=== added directory 'src/maasserver/support'
=== added file 'src/maasserver/support/__init__.py'
=== added directory 'src/maasserver/support/pertenant'
=== added file 'src/maasserver/support/pertenant/__init__.py'
=== added directory 'src/maasserver/support/pertenant/tests'
=== added file 'src/maasserver/support/pertenant/tests/__init__.py'
=== added file 'src/maasserver/support/pertenant/tests/test_utils.py'
--- src/maasserver/support/pertenant/tests/test_utils.py 1970-01-01 00:00:00 +0000
+++ src/maasserver/support/pertenant/tests/test_utils.py 2013-03-04 16:01:27 +0000
@@ -0,0 +1,66 @@
+# Copyright 2013 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test the utilities of the per-tenant file storage work."""
+
+from __future__ import (
+ absolute_import,
+ print_function,
+ unicode_literals,
+ )
+
+__metaclass__ = type
+__all__ = []
+
+from maasserver.support.pertenant.utils import (
+ extract_bootstrap_node_system_id,
+ get_bootstrap_node_owner,
+ PROVIDER_STATE_FILENAME,
+ )
+from maasserver.testing.factory import factory
+from maasserver.testing.testcase import TestCase
+from maastesting.utils import sample_binary_data
+
+
+class TestExtractBootstrapNodeSystemId(TestCase):
+
+ def test_parses_valid_provider_state_file(self):
+ node = factory.make_node()
+ provider_state_file = factory.make_provider_state_file(node=node)
+ system_id = extract_bootstrap_node_system_id(
+ provider_state_file.content)
+ self.assertEqual(system_id, node.system_id)
+
+ def test_returns_None_if_parsing_fails(self):
+ invalid_contents = [
+ '%', # invalid yaml
+ sample_binary_data, # binary content (invalid yaml)
+ 'invalid content', # invalid provider-state content
+ 'zookeeper-instances: []', # no instances listed
+ ]
+ for invalid_content in invalid_contents:
+ self.assertIsNone(
+ extract_bootstrap_node_system_id(invalid_content))
+
+
+class TestGetBootstrapNodeOwner(TestCase):
+
+ def test_returns_None_if_no_provider_state_file(self):
+ self.assertIsNone(get_bootstrap_node_owner())
+
+ def test_returns_owner_if_node_found(self):
+ node = factory.make_node(owner=factory.make_user())
+ factory.make_provider_state_file(node=node)
+ self.assertEqual(node.owner, get_bootstrap_node_owner())
+
+ def test_returns_None_if_node_does_not_exist(self):
+ node = factory.make_node(owner=factory.make_user())
+ factory.make_provider_state_file(node=node)
+ node.delete()
+ self.assertIsNone(get_bootstrap_node_owner())
+
+ def test_returns_None_if_invalid_yaml(self):
+ invalid_content = '%'.encode('ascii')
+ factory.make_file_storage(
+ filename=PROVIDER_STATE_FILENAME, content=invalid_content)
+ self.assertIsNone(get_bootstrap_node_owner())
=== added file 'src/maasserver/support/pertenant/utils.py'
--- src/maasserver/support/pertenant/utils.py 1970-01-01 00:00:00 +0000
+++ src/maasserver/support/pertenant/utils.py 2013-03-04 16:01:27 +0000
@@ -0,0 +1,65 @@
+# Copyright 2013 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Utilities for the per-tenant file storage work."""
+
+from __future__ import (
+ absolute_import,
+ print_function,
+ unicode_literals,
+ )
+
+__metaclass__ = type
+__all__ = [
+ "get_bootstrap_node_owner",
+ ]
+
+
+from maasserver.models import (
+ FileStorage,
+ Node,
+ )
+import yaml
+
+
+PROVIDER_STATE_FILENAME = 'provider-state'
+
+
+def get_bootstrap_node_owner():
+ """Return the owner of the bootstrap node or None if it cannot be found.
+
+ This method uses the unowned 'provider-state' file to extract the system_id
+ of the bootstrap node.
+ """
+ try:
+ provider_file = FileStorage.objects.get(
+ filename=PROVIDER_STATE_FILENAME, owner=None)
+ except FileStorage.DoesNotExist:
+ return None
+ system_id = extract_bootstrap_node_system_id(provider_file.content)
+ if system_id is None:
+ return None
+ try:
+ return Node.objects.get(system_id=system_id).owner
+ except Node.DoesNotExist:
+ return None
+
+
+def extract_bootstrap_node_system_id(content):
+ """Extract the system_id of the node referenced in the given
+ provider-state file.
+
+ This method implements a very defensive strategy; if the given
+ content is not in yaml format or if the owner of the bootstrap
+ node cannot be found, it returns None.
+ """
+ try:
+ state = yaml.load(content)
+ except yaml.YAMLError:
+ return None
+ try:
+ parts = state['zookeeper-instances'][0].split('/')
+ except (IndexError, TypeError):
+ return None
+ system_id = [part for part in parts if part != ''][-1]
+ return system_id
=== modified file 'src/maasserver/testing/factory.py'
--- src/maasserver/testing/factory.py 2013-02-18 14:28:22 +0000
+++ src/maasserver/testing/factory.py 2013-03-04 16:01:27 +0000
@@ -1,4 +1,4 @@
-# Copyright 2012 Canonical Ltd. This software is licensed under the
+# Copyright 2013 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Test object factories."""
@@ -19,6 +19,7 @@
import time
from django.contrib.auth.models import User
+from django.core.urlresolvers import reverse
from maasserver.enum import (
ARCHITECTURE,
NODE_STATUS,
@@ -37,6 +38,7 @@
Tag,
)
from maasserver.models.node import NODE_TRANSITIONS
+from maasserver.support.pertenant.utils import PROVIDER_STATE_FILENAME
from maasserver.testing import (
get_data,
reload_object,
@@ -334,6 +336,15 @@
fake_file = self.make_file_upload(filename, content)
return FileStorage.objects.save_file(fake_file.name, fake_file, owner)
+ def make_provider_state_file(self, node=None):
+ if node is None:
+ node = factory.make_node()
+ node_link = reverse('node_handler', args=[node.system_id])
+ content = 'zookeeper-instances: [%s]\n' % node_link
+ content_data = content.encode('ascii')
+ return self.make_file_storage(
+ filename=PROVIDER_STATE_FILENAME, content=content_data, owner=None)
+
def make_oauth_header(self, **kwargs):
"""Fake an OAuth authorization header.