Merge ~ack/maas:kernel-options-all-tags into maas:master

Proposed by Alberto Donato
Status: Merged
Approved by: Alberto Donato
Approved revision: 75f57a97f099d0ea3e78a95375f041404ab17772
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ack/maas:kernel-options-all-tags
Merge into: maas:master
Diff against target: 299 lines (+57/-95)
9 files modified
src/maasserver/api/examples/tags.json (+2/-2)
src/maasserver/api/tags.py (+3/-3)
src/maasserver/migrations/maasserver/0230_tag_kernel_opts_blank_instead_of_null.py (+19/-0)
src/maasserver/models/node.py (+10/-20)
src/maasserver/models/tag.py (+1/-1)
src/maasserver/models/tests/test_node.py (+16/-49)
src/maasserver/models/tests/test_tag.py (+1/-1)
src/maasserver/rpc/boot.py (+4/-18)
src/maasserver/testing/factory.py (+1/-1)
Reviewer Review Type Date Requested Status
Adam Collard (community) Approve
MAAS Lander Needs Fixing
Review via email: mp+399201@code.launchpad.net

Commit message

combine kernel commandline options node from all associated tags

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b kernel-options-all-tags lp:~ack/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/9414/console
COMMIT: d055378c902d55b5f8901ea1c5391bd66aee5983

review: Needs Fixing
Revision history for this message
Adam Collard (adam-collard) wrote :

Make sure we document the order in which we combine kernel tags and nod towards that being important if the options aren't complete.

review: Approve

There was an error fetching revisions from git servers. Please try again in a few minutes. If the problem persists, contact Launchpad support.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/src/maasserver/api/examples/tags.json b/src/maasserver/api/examples/tags.json
index 98804ed..504001d 100644
--- a/src/maasserver/api/examples/tags.json
+++ b/src/maasserver/api/examples/tags.json
@@ -4,7 +4,7 @@
4 "name": "virtual",4 "name": "virtual",
5 "definition": "",5 "definition": "",
6 "comment": "",6 "comment": "",
7 "kernel_opts": null,7 "kernel_opts": "",
8 "resource_uri": "/MAAS/api/2.0/tags/virtual/"8 "resource_uri": "/MAAS/api/2.0/tags/virtual/"
9 }9 }
10 ],10 ],
@@ -12,7 +12,7 @@
12 "name": "virtual",12 "name": "virtual",
13 "definition": "",13 "definition": "",
14 "comment": "",14 "comment": "",
15 "kernel_opts": null,15 "kernel_opts": "",
16 "resource_uri": "/MAAS/api/2.0/tags/virtual/"16 "resource_uri": "/MAAS/api/2.0/tags/virtual/"
17 },17 },
18 "get-devices-by-tag": [18 "get-devices-by-tag": [
diff --git a/src/maasserver/api/tags.py b/src/maasserver/api/tags.py
index 42c8466..b4ce8eb 100644
--- a/src/maasserver/api/tags.py
+++ b/src/maasserver/api/tags.py
@@ -420,9 +420,9 @@ class TagsHandler(OperationsHandler):
420 @param (string) "kernel_opts" [required=false] Nodes associated420 @param (string) "kernel_opts" [required=false] Nodes associated
421 with this tag will add this string to their kernel options421 with this tag will add this string to their kernel options
422 when booting. The value overrides the global ``kernel_opts``422 when booting. The value overrides the global ``kernel_opts``
423 setting. If more than one tag is associated with a node, the423 setting. If more than one tag is associated with a node, command
424 one with the lower alphabetical name will be picked. For example,424 line will be concatenated from all associated tags, in alphabetic
425 ``01-my-tag`` will be chosen instead of ``99-tag-name``.425 tag name order.
426 @param-example "kernel_opts"426 @param-example "kernel_opts"
427 nouveau.noaccel=1427 nouveau.noaccel=1
428428
diff --git a/src/maasserver/migrations/maasserver/0230_tag_kernel_opts_blank_instead_of_null.py b/src/maasserver/migrations/maasserver/0230_tag_kernel_opts_blank_instead_of_null.py
429new file mode 100644429new file mode 100644
index 0000000..397b945
--- /dev/null
+++ b/src/maasserver/migrations/maasserver/0230_tag_kernel_opts_blank_instead_of_null.py
@@ -0,0 +1,19 @@
1# Generated by Django 2.2.12 on 2021-03-05 10:56
2
3from django.db import migrations, models
4
5
6class Migration(migrations.Migration):
7
8 dependencies = [
9 ("maasserver", "0229_drop_physicalblockdevice_storage_pool"),
10 ]
11
12 operations = [
13 migrations.AlterField(
14 model_name="tag",
15 name="kernel_opts",
16 field=models.TextField(blank=True, default=""),
17 preserve_default=False,
18 ),
19 ]
diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py
index a7c41d5..641e2d8 100644
--- a/src/maasserver/models/node.py
+++ b/src/maasserver/models/node.py
@@ -3024,26 +3024,16 @@ class Node(CleanSave, TimestampedModel):
3024 raise UnknownPowerType("Node power type is unconfigured")3024 raise UnknownPowerType("Node power type is unconfigured")
3025 return self.bmc.power_type3025 return self.bmc.power_type
30263026
3027 def get_effective_kernel_options(self, default_kernel_opts=undefined):3027 def get_effective_kernel_options(self, default_kernel_opts=""):
3028 """Determine any special kernel parameters for this node.3028 """Return a string with kernel commandline."""
30293029 options = (
3030 :return: (tag, kernel_options)3030 self.tags.exclude(kernel_opts="")
3031 tag is a Tag object or None. If None, the kernel_options came from3031 .order_by("name")
3032 the global setting.3032 .values_list("kernel_opts", flat=True)
3033 kernel_options, a string indicating extra kernel_options that3033 )
3034 should be used when booting this node. May be None if no tags match3034 if options:
3035 and no global setting has been configured.3035 return " ".join(options)
3036 """3036 return default_kernel_opts or ""
3037 # First, see if there are any tags associated with this node that has a
3038 # custom kernel parameter
3039 tags = self.tags.filter(kernel_opts__isnull=False)
3040 tags = tags.order_by("name")
3041 for tag in tags:
3042 if tag.kernel_opts != "":
3043 return tag, tag.kernel_opts
3044 if default_kernel_opts is undefined:
3045 default_kernel_opts = Config.objects.get_config("kernel_opts")
3046 return None, default_kernel_opts
30473037
3048 def get_osystem(self, default=undefined):3038 def get_osystem(self, default=undefined):
3049 """Return the operating system to install that node."""3039 """Return the operating system to install that node."""
diff --git a/src/maasserver/models/tag.py b/src/maasserver/models/tag.py
index 0a49b99..5806b3d 100644
--- a/src/maasserver/models/tag.py
+++ b/src/maasserver/models/tag.py
@@ -71,7 +71,7 @@ class Tag(CleanSave, TimestampedModel):
71 )71 )
72 definition = TextField(blank=True)72 definition = TextField(blank=True)
73 comment = TextField(blank=True)73 comment = TextField(blank=True)
74 kernel_opts = TextField(blank=True, null=True)74 kernel_opts = TextField(blank=True)
7575
76 objects = TagManager()76 objects = TagManager()
7777
diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py
index d77a37d..954740f 100644
--- a/src/maasserver/models/tests/test_node.py
+++ b/src/maasserver/models/tests/test_node.py
@@ -1818,24 +1818,16 @@ class TestNode(MAASServerTestCase):
18181818
1819 def test_get_effective_kernel_options_with_nothing_set(self):1819 def test_get_effective_kernel_options_with_nothing_set(self):
1820 node = factory.make_Node()1820 node = factory.make_Node()
1821 self.assertEqual((None, None), node.get_effective_kernel_options())1821 self.assertEqual(node.get_effective_kernel_options(), "")
1822
1823 def test_get_effective_kernel_options_sees_global_config(self):
1824 node = factory.make_Node()
1825 kernel_opts = factory.make_string()
1826 Config.objects.set_config("kernel_opts", kernel_opts)
1827 self.assertEqual(
1828 (None, kernel_opts), node.get_effective_kernel_options()
1829 )
18301822
1831 def test_get_effective_kernel_options_not_confused_by_None_opts(self):1823 def test_get_effective_kernel_options_not_confused_by_None_opts(self):
1832 node = factory.make_Node()1824 node = factory.make_Node()
1833 tag = factory.make_Tag()1825 tag = factory.make_Tag()
1834 node.tags.add(tag)1826 node.tags.add(tag)
1835 kernel_opts = factory.make_string()1827 kernel_opts = factory.make_string()
1836 Config.objects.set_config("kernel_opts", kernel_opts)
1837 self.assertEqual(1828 self.assertEqual(
1838 (None, kernel_opts), node.get_effective_kernel_options()1829 node.get_effective_kernel_options(default_kernel_opts=kernel_opts),
1830 kernel_opts,
1839 )1831 )
18401832
1841 def test_get_effective_kernel_options_not_confused_by_empty_str_opts(self):1833 def test_get_effective_kernel_options_not_confused_by_empty_str_opts(self):
@@ -1843,69 +1835,44 @@ class TestNode(MAASServerTestCase):
1843 tag = factory.make_Tag(kernel_opts="")1835 tag = factory.make_Tag(kernel_opts="")
1844 node.tags.add(tag)1836 node.tags.add(tag)
1845 kernel_opts = factory.make_string()1837 kernel_opts = factory.make_string()
1846 Config.objects.set_config("kernel_opts", kernel_opts)
1847 self.assertEqual(1838 self.assertEqual(
1848 (None, kernel_opts), node.get_effective_kernel_options()1839 node.get_effective_kernel_options(default_kernel_opts=kernel_opts),
1840 kernel_opts,
1849 )1841 )
18501842
1851 def test_get_effective_kernel_options_multiple_tags_with_opts(self):1843 def test_get_effective_kernel_options_multiple_tags_with_opts(self):
1852 # In this scenario:
1853 # global kernel_opts='fish-n-chips'
1854 # tag_a kernel_opts=null
1855 # tag_b kernel_opts=''
1856 # tag_c kernel_opts='bacon-n-eggs'
1857 # we require that 'bacon-n-eggs' is chosen as it is the first
1858 # tag with a valid kernel option.
1859 Config.objects.set_config("kernel_opts", "fish-n-chips")
1860 node = factory.make_Node()1844 node = factory.make_Node()
1861 node.tags.add(factory.make_Tag("tag_a"))1845 node.tags.add(factory.make_Tag("tag_a"))
1862 node.tags.add(factory.make_Tag("tag_b", kernel_opts=""))1846 node.tags.add(factory.make_Tag("tag_b", kernel_opts=""))
1863 tag_c = factory.make_Tag("tag_c", kernel_opts="bacon-n-eggs")1847 tag_c = factory.make_Tag("tag_c", kernel_opts="bacon-n-eggs")
1848 tag_d = factory.make_Tag("tag_d", kernel_opts="foo-bar")
1864 node.tags.add(tag_c)1849 node.tags.add(tag_c)
18651850 node.tags.add(tag_d)
1866 self.assertEqual(1851 self.assertEqual(
1867 (tag_c, "bacon-n-eggs"), node.get_effective_kernel_options()1852 node.get_effective_kernel_options(), "bacon-n-eggs foo-bar"
1868 )1853 )
18691854
1870 def test_get_effective_kernel_options_ignores_unassociated_tag_value(self):1855 def test_get_effective_kernel_options_ignores_unassociated_tag_value(self):
1871 node = factory.make_Node()1856 node = factory.make_Node()
1872 factory.make_Tag(kernel_opts=factory.make_string())1857 factory.make_Tag(kernel_opts=factory.make_string())
1873 self.assertEqual((None, None), node.get_effective_kernel_options())1858 self.assertEqual(node.get_effective_kernel_options(), "")
18741859
1875 def test_get_effective_kernel_options_uses_tag_value(self):1860 def test_get_effective_kernel_options_uses_tag_value(self):
1876 node = factory.make_Node()1861 node = factory.make_Node()
1877 tag = factory.make_Tag(kernel_opts=factory.make_string())1862 tag = factory.make_Tag(kernel_opts=factory.make_string())
1878 node.tags.add(tag)1863 node.tags.add(tag)
1879 self.assertEqual(1864 self.assertEqual(node.get_effective_kernel_options(), tag.kernel_opts)
1880 (tag, tag.kernel_opts), node.get_effective_kernel_options()
1881 )
18821865
1883 def test_get_effective_kernel_options_tag_overrides_global(self):1866 def test_get_effective_kernel_options_tag_overrides_default(self):
1884 node = factory.make_Node()1867 node = factory.make_Node()
1885 global_opts = factory.make_string()1868 default_opts = factory.make_string()
1886 Config.objects.set_config("kernel_opts", global_opts)
1887 tag = factory.make_Tag(kernel_opts=factory.make_string())1869 tag = factory.make_Tag(kernel_opts=factory.make_string())
1888 node.tags.add(tag)1870 node.tags.add(tag)
1889 self.assertEqual(1871 self.assertEqual(
1890 (tag, tag.kernel_opts), node.get_effective_kernel_options()1872 node.get_effective_kernel_options(
1891 )1873 default_kernel_opts=default_opts
18921874 ),
1893 def test_get_effective_kernel_options_uses_first_real_tag_value(self):1875 tag.kernel_opts,
1894 node = factory.make_Node()
1895 # Intentionally create them in reverse order, so the default 'db' order
1896 # doesn't work, and we have asserted that we sort them.
1897 tag3 = factory.make_Tag(
1898 factory.make_name("tag-03-"), kernel_opts=factory.make_string()
1899 )
1900 tag2 = factory.make_Tag(
1901 factory.make_name("tag-02-"), kernel_opts=factory.make_string()
1902 )
1903 tag1 = factory.make_Tag(factory.make_name("tag-01-"), kernel_opts=None)
1904 self.assertTrue(tag1.name < tag2.name)
1905 self.assertTrue(tag2.name < tag3.name)
1906 node.tags.add(tag1, tag2, tag3)
1907 self.assertEqual(
1908 (tag2, tag2.kernel_opts), node.get_effective_kernel_options()
1909 )1876 )
19101877
1911 def test_acquire(self):1878 def test_acquire(self):
diff --git a/src/maasserver/models/tests/test_tag.py b/src/maasserver/models/tests/test_tag.py
index 1a0666f..fdf7b58 100644
--- a/src/maasserver/models/tests/test_tag.py
+++ b/src/maasserver/models/tests/test_tag.py
@@ -28,7 +28,7 @@ class TagTest(MAASServerTestCase):
28 self.assertEqual("tag-name", tag.name)28 self.assertEqual("tag-name", tag.name)
29 self.assertEqual("//node[@id=display]", tag.definition)29 self.assertEqual("//node[@id=display]", tag.definition)
30 self.assertEqual("", tag.comment)30 self.assertEqual("", tag.comment)
31 self.assertIs(None, tag.kernel_opts)31 self.assertEqual(tag.kernel_opts, "")
32 self.assertIsNot(None, tag.updated)32 self.assertIsNot(None, tag.updated)
33 self.assertIsNot(None, tag.created)33 self.assertIsNot(None, tag.created)
3434
diff --git a/src/maasserver/rpc/boot.py b/src/maasserver/rpc/boot.py
index fdd9ef7..ce7af59 100644
--- a/src/maasserver/rpc/boot.py
+++ b/src/maasserver/rpc/boot.py
@@ -515,9 +515,7 @@ def get_config(
515 machine, configs, purpose515 machine, configs, purpose
516 )516 )
517517
518 # We don't care if the kernel opts is from the global setting or a tag,518 extra_kernel_opts = machine.get_effective_kernel_options(
519 # just get the options
520 _, effective_kernel_opts = machine.get_effective_kernel_options(
521 default_kernel_opts=configs["kernel_opts"]519 default_kernel_opts=configs["kernel_opts"]
522 )520 )
523521
@@ -527,21 +525,9 @@ def get_config(
527 driver = get_third_party_driver(machine, series=series)525 driver = get_third_party_driver(machine, series=series)
528 driver_kernel_opts = driver.get("kernel_opts", "")526 driver_kernel_opts = driver.get("kernel_opts", "")
529527
530 combined_opts = (528 extra_kernel_opts += f" {driver_kernel_opts}"
531 "%s %s"529
532 % (530 extra_kernel_opts.strip()
533 ""
534 if effective_kernel_opts is None
535 else effective_kernel_opts,
536 driver_kernel_opts,
537 )
538 ).strip()
539 if len(combined_opts):
540 extra_kernel_opts = combined_opts
541 else:
542 extra_kernel_opts = None
543 else:
544 extra_kernel_opts = effective_kernel_opts
545531
546 kparams = BootResource.objects.get_kparams_for_node(532 kparams = BootResource.objects.get_kparams_for_node(
547 machine,533 machine,
diff --git a/src/maasserver/testing/factory.py b/src/maasserver/testing/factory.py
index 94a156d..d5bc9ea 100644
--- a/src/maasserver/testing/factory.py
+++ b/src/maasserver/testing/factory.py
@@ -1930,7 +1930,7 @@ class Factory(maastesting.factory.Factory):
1930 name=None,1930 name=None,
1931 definition=None,1931 definition=None,
1932 comment="",1932 comment="",
1933 kernel_opts=None,1933 kernel_opts="",
1934 created=None,1934 created=None,
1935 updated=None,1935 updated=None,
1936 populate=True,1936 populate=True,

Subscribers

People subscribed via source and target branches