Merge ~raharper/curtin:fix/uefi-remove-dups-v2 into curtin:master

Proposed by Ryan Harper
Status: Merged
Approved by: Dan Watkins
Approved revision: d28422dc0bbf0e9066cbf682db1d9be519bc38cb
Merge reported by: Server Team CI bot
Merged at revision: not available
Proposed branch: ~raharper/curtin:fix/uefi-remove-dups-v2
Merge into: curtin:master
Diff against target: 191 lines (+84/-40)
3 files modified
curtin/commands/curthooks.py (+10/-0)
doc/topics/config.rst (+8/-0)
tests/unittests/test_curthooks.py (+66/-40)
Reviewer Review Type Date Requested Status
Dan Watkins (community) Approve
Paride Legovini Approve
Server Team CI bot continuous-integration Approve
Review via email: mp+390431@code.launchpad.net

Commit message

curthooks: UEFI remove dupes: don't remove BootCurrent, config option

When removing duplicate UEFI bootmenu entries do not remove the
BootCurrent entry. Fix this by adding BootCurrent to the seen set
before processing the list and then skip it during iteration of the
entries.

- Add grub config option: remove_duplicate_entries bool
- Add documentation around remove_duplicate_entries grub config
- Add unittests to verify we don't remove boot current and to
  check that dupe removal can be disabled.

LP: #1894217

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Paride Legovini (paride) wrote :

CI failed because the latest versions of pyrsistent are not compatible with py27. Full CI run log at [1]. Can be worked around by pinning the version of pyrsistent:

https://code.launchpad.net/~paride/curtin/+git/curtin/+merge/390446

On the MP itself: do you think it's worth adding a "missing BootCurrent" test case?

[1] https://paste.ubuntu.com/p/fgGwqC6ktv/

review: Needs Information
Revision history for this message
Dan Watkins (oddbloke) wrote :

In https://bugs.launchpad.net/maas/+bug/1894217/comments/3, it looks like there are two issues: (a) we're incorrectly detecting duplicates, and (b) we might remove the BootCurrent value. This branch definitely addresses (b); does it also intend to address (a)?

review: Needs Information
Revision history for this message
Ryan Harper (raharper) wrote :

@Dan

I thought we were incorrectly detecting dups, but in-fact, they are
duplicates. I asked for followup data from the submitter and confirmed there
are duplicates.

See comment 6:

Boot0000* EFI Network 1 PcieRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/MAC(ecf4bbce7b8c,1)/IPv4(0.0.0.00.0.0.0,0,0)
Boot0004* EFI Network 1 PcieRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/MAC(ecf4bbce7b8c,1)/IPv4(0.0.0.00.0.0.0,0,0)
Boot0007* EFI Network 1 PcieRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/MAC(ecf4bbce7b8c,1)/IPv4(0.0.0.00.0.0.0,0,0)

Boot000C* EFI Network 2 PcieRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x1)/MAC(ecf4bbce7b8d,1)/IPv4(0.0.0.00.0.0.0,0,0)
Boot0008* EFI Network 2 PcieRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x1)/MAC(ecf4bbce7b8d,1)/IPv4(0.0.0.00.0.0.0,0,0)

Boot0002 EFI Network 3 PcieRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x2)/MAC(ecf4bbce7b8e,1)/IPv4(0.0.0.00.0.0.0,0,0)
Boot0009* EFI Network 3 PcieRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x2)/MAC(ecf4bbce7b8e,1)/IPv4(0.0.0.00.0.0.0,0,0)
Boot000D* EFI Network 3 PcieRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x2)/MAC(ecf4bbce7b8e,1)/IPv4(0.0.0.00.0.0.0,0,0)

Boot0003 EFI Network 4 PcieRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x3)/MAC(ecf4bbce7b8f,1)/IPv4(0.0.0.00.0.0.0,0,0)
Boot000B* EFI Network 4 PcieRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x3)/MAC(ecf4bbce7b8f,1)/IPv4(0.0.0.00.0.0.0,0,0)
Boot000E* EFI Network 4 PcieRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x3)/MAC(ecf4bbce7b8f,1)/IPv4(0.0.0.00.0.0.0,0,0)

So it turns out there was not an issue with duplicate detection, only removing BootCurrent.

Revision history for this message
Paride Legovini (paride) wrote :

CI should pass now if the MP gets rebased.

800cd78... by Ryan Harper

Refactor remove-dups unittests per feedback

d28422d... by Ryan Harper

Add unittest for missing BootCurrent value when removing duplicates

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Paride Legovini (paride) wrote :

LGTM, thanks Ryan! I'm not setting the MP to Approved yet in case Dan wants to have another look.

review: Approve
Revision history for this message
Dan Watkins (oddbloke) wrote :

Yep, LGTM, thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/curtin/commands/curthooks.py b/curtin/commands/curthooks.py
index 92230fc..eaaae5e 100644
--- a/curtin/commands/curthooks.py
+++ b/curtin/commands/curthooks.py
@@ -558,11 +558,21 @@ def uefi_reorder_loaders(grubcfg, target, efi_orig=None, variant=None):
558558
559559
560def uefi_remove_duplicate_entries(grubcfg, target):560def uefi_remove_duplicate_entries(grubcfg, target):
561 if not grubcfg.get('remove_duplicate_entries', True):
562 LOG.debug("Skipped removing duplicate UEFI boot entries per config.")
563 return
561 seen = set()564 seen = set()
562 to_remove = []565 to_remove = []
563 efi_output = util.get_efibootmgr(target=target)566 efi_output = util.get_efibootmgr(target=target)
564 entries = efi_output.get('entries', {})567 entries = efi_output.get('entries', {})
568 current_bootnum = efi_output.get('current', None)
569 # adding BootCurrent to seen first allows us to remove any other duplicate
570 # entry of BootCurrent.
571 if current_bootnum:
572 seen.add(tuple(entries[current_bootnum].items()))
565 for bootnum in sorted(entries):573 for bootnum in sorted(entries):
574 if bootnum == current_bootnum:
575 continue
566 entry = entries[bootnum]576 entry = entries[bootnum]
567 t = tuple(entry.items())577 t = tuple(entry.items())
568 if t not in seen:578 if t not in seen:
diff --git a/doc/topics/config.rst b/doc/topics/config.rst
index 951f07f..ec8a109 100644
--- a/doc/topics/config.rst
+++ b/doc/topics/config.rst
@@ -255,6 +255,13 @@ even if BootCurrent is present if *reorder_uefi_force_fallback* is True.
255255
256This setting is ignored if *update_nvram* or *reorder_uefi* are False.256This setting is ignored if *update_nvram* or *reorder_uefi* are False.
257257
258**remove_duplicate_entries**: <*boolean: default True>*
259
260When curtin updates UEFI NVRAM it will remove duplicate entries that are
261present in the UEFI menu. If you do not wish for curtin to remove duplicate
262entries setting *remove_duplicate_entries* to False.
263
264This setting is ignored if *update_nvram* is False.
258265
259**Example**::266**Example**::
260267
@@ -264,6 +271,7 @@ This setting is ignored if *update_nvram* or *reorder_uefi* are False.
264 replace_linux_default: False271 replace_linux_default: False
265 update_nvram: True272 update_nvram: True
266 terminal: serial273 terminal: serial
274 remove_duplicate_entries: True
267275
268**Default terminal value, GRUB_TERMINAL=console**::276**Default terminal value, GRUB_TERMINAL=console**::
269277
diff --git a/tests/unittests/test_curthooks.py b/tests/unittests/test_curthooks.py
index ddae280..e5fead3 100644
--- a/tests/unittests/test_curthooks.py
+++ b/tests/unittests/test_curthooks.py
@@ -1027,47 +1027,57 @@ class TestSetupGrub(CiTestCase):
10271027
1028class TestUefiRemoveDuplicateEntries(CiTestCase):1028class TestUefiRemoveDuplicateEntries(CiTestCase):
10291029
1030 efibootmgr_output = {
1031 'current': '0000',
1032 'entries': {
1033 '0000': {
1034 'name': 'ubuntu',
1035 'path': (
1036 'HD(1,GPT)/File(\\EFI\\ubuntu\\shimx64.efi)'),
1037 },
1038 '0001': { # Is duplicate of 0000
1039 'name': 'ubuntu',
1040 'path': (
1041 'HD(1,GPT)/File(\\EFI\\ubuntu\\shimx64.efi)'),
1042 },
1043 '0002': { # Is not a duplicate because of unique path
1044 'name': 'ubuntu',
1045 'path': (
1046 'HD(2,GPT)/File(\\EFI\\ubuntu\\shimx64.efi)'),
1047 },
1048 '0003': { # Is duplicate of 0000
1049 'name': 'ubuntu',
1050 'path': (
1051 'HD(1,GPT)/File(\\EFI\\ubuntu\\shimx64.efi)'),
1052 },
1053 }
1054 }
1055
1030 def setUp(self):1056 def setUp(self):
1031 super(TestUefiRemoveDuplicateEntries, self).setUp()1057 super(TestUefiRemoveDuplicateEntries, self).setUp()
1032 self.target = self.tmp_dir()1058 self.target = self.tmp_dir()
1033 self.add_patch('curtin.util.get_efibootmgr', 'm_efibootmgr')1059 self.add_patch('curtin.util.get_efibootmgr', 'm_efibootmgr')
1034 self.add_patch('curtin.util.subp', 'm_subp')1060 self.add_patch('curtin.util.subp', 'm_subp')
1061 self.m_efibootmgr.return_value = copy.deepcopy(self.efibootmgr_output)
10351062
1036 @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)1063 @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)
1037 def test_uefi_remove_duplicate_entries(self):1064 def test_uefi_remove_duplicate_entries(self):
1038 cfg = {1065 grubcfg = {}
1039 'grub': {1066 curthooks.uefi_remove_duplicate_entries(grubcfg, self.target)
1040 'install_devices': ['/dev/vdb'],1067 self.assertEquals([
1041 'update_nvram': True,1068 call(['efibootmgr', '--bootnum=0001', '--delete-bootnum'],
1042 },1069 target=self.target),
1043 }1070 call(['efibootmgr', '--bootnum=0003', '--delete-bootnum'],
1044 self.m_efibootmgr.return_value = {1071 target=self.target)
1045 'current': '0000',1072 ], self.m_subp.call_args_list)
1046 'entries': {
1047 '0000': {
1048 'name': 'ubuntu',
1049 'path': (
1050 'HD(1,GPT)/File(\\EFI\\ubuntu\\shimx64.efi)'),
1051 },
1052 '0001': {
1053 'name': 'ubuntu',
1054 'path': (
1055 'HD(1,GPT)/File(\\EFI\\ubuntu\\shimx64.efi)'),
1056 },
1057 '0002': { # Is not a duplicate because of unique path
1058 'name': 'ubuntu',
1059 'path': (
1060 'HD(2,GPT)/File(\\EFI\\ubuntu\\shimx64.efi)'),
1061 },
1062 '0003': { # Is duplicate of 0000
1063 'name': 'ubuntu',
1064 'path': (
1065 'HD(1,GPT)/File(\\EFI\\ubuntu\\shimx64.efi)'),
1066 },
1067 }
1068 }
10691073
1070 curthooks.uefi_remove_duplicate_entries(cfg, self.target)1074 @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)
1075 def test_uefi_remove_duplicate_entries_no_bootcurrent(self):
1076 grubcfg = {}
1077 efiout = copy.deepcopy(self.efibootmgr_output)
1078 del efiout['current']
1079 self.m_efibootmgr.return_value = efiout
1080 curthooks.uefi_remove_duplicate_entries(grubcfg, self.target)
1071 self.assertEquals([1081 self.assertEquals([
1072 call(['efibootmgr', '--bootnum=0001', '--delete-bootnum'],1082 call(['efibootmgr', '--bootnum=0001', '--delete-bootnum'],
1073 target=self.target),1083 target=self.target),
@@ -1076,13 +1086,30 @@ class TestUefiRemoveDuplicateEntries(CiTestCase):
1076 ], self.m_subp.call_args_list)1086 ], self.m_subp.call_args_list)
10771087
1078 @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)1088 @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)
1079 def test_uefi_remove_duplicate_entries_no_change(self):1089 def test_uefi_remove_duplicate_entries_disabled(self):
1080 cfg = {1090 grubcfg = {
1081 'grub': {1091 'remove_duplicate_entries': False,
1082 'install_devices': ['/dev/vdb'],
1083 'update_nvram': True,
1084 },
1085 }1092 }
1093 curthooks.uefi_remove_duplicate_entries(grubcfg, self.target)
1094 self.assertEquals([], self.m_subp.call_args_list)
1095
1096 @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)
1097 def test_uefi_remove_duplicate_entries_skip_bootcurrent(self):
1098 grubcfg = {}
1099 efiout = copy.deepcopy(self.efibootmgr_output)
1100 efiout['current'] = '0003'
1101 self.m_efibootmgr.return_value = efiout
1102 curthooks.uefi_remove_duplicate_entries(grubcfg, self.target)
1103 self.assertEquals([
1104 call(['efibootmgr', '--bootnum=0000', '--delete-bootnum'],
1105 target=self.target),
1106 call(['efibootmgr', '--bootnum=0001', '--delete-bootnum'],
1107 target=self.target),
1108 ], self.m_subp.call_args_list)
1109
1110 @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)
1111 def test_uefi_remove_duplicate_entries_no_change(self):
1112 grubcfg = {}
1086 self.m_efibootmgr.return_value = {1113 self.m_efibootmgr.return_value = {
1087 'current': '0000',1114 'current': '0000',
1088 'entries': {1115 'entries': {
@@ -1103,8 +1130,7 @@ class TestUefiRemoveDuplicateEntries(CiTestCase):
1103 },1130 },
1104 }1131 }
1105 }1132 }
11061133 curthooks.uefi_remove_duplicate_entries(grubcfg, self.target)
1107 curthooks.uefi_remove_duplicate_entries(cfg, self.target)
1108 self.assertEquals([], self.m_subp.call_args_list)1134 self.assertEquals([], self.m_subp.call_args_list)
11091135
11101136

Subscribers

People subscribed via source and target branches