diff --git a/completions/jc_zsh_completion.sh b/completions/jc_zsh_completion.sh index a458aedc..66d0fce0 100644 --- a/completions/jc_zsh_completion.sh +++ b/completions/jc_zsh_completion.sh @@ -29,6 +29,7 @@ _jc() { 'dmidecode:run "dmidecode" command with magic syntax.' 'dpkg:run "dpkg" command with magic syntax.' 'du:run "du" command with magic syntax.' + 'efibootmgr:run "efibootmgr" command with magic syntax.' 'env:run "env" command with magic syntax.' 'file:run "file" command with magic syntax.' 'findmnt:run "findmnt" command with magic syntax.' @@ -148,6 +149,7 @@ _jc() { '--dmidecode:`dmidecode` command parser' '--dpkg-l:`dpkg -l` command parser' '--du:`du` command parser' + '--efibootmgr:`efibootmgr` command parser' '--email-address:Email Address string parser' '--env:`env` command parser' '--file:`file` command parser' diff --git a/jc/lib.py b/jc/lib.py index a584a634..e1bdcf7a 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -43,6 +43,7 @@ parsers: List[str] = [ 'dmidecode', 'dpkg-l', 'du', + 'efibootmgr', 'email-address', 'env', 'file', diff --git a/jc/parsers/efibootmgr.py b/jc/parsers/efibootmgr.py new file mode 100644 index 00000000..4f967b35 --- /dev/null +++ b/jc/parsers/efibootmgr.py @@ -0,0 +1,160 @@ +"""jc - JSON Convert `efibootmgr` command output parser + +Only `efibootmgr -v` option is supported with the default output. + +Usage (cli): + + $ sudo efibootmgr | jc --efibootmgr + $ sudo efibootmgr -v | jc --efibootmgr + +or + + $ sudo jc efibootmgr + +Usage (module): + + import jc + result = jc.parse('efibootmgr', efibootmgr_command_output) + +Schema: + +[ + { + "boot_current": string, + "timeout_seconds": number, + "boot_order": [ + string, + string, + string + ], + mirrored_percentage_above_4g: number, + mirror_memory_below_4gb: bool, + "boot_options": [ + { + "boot_option_reference": string, + "display_name": string, + "uefi_device_path": string, + "boot_option_enabled": bool + }, + { + "boot_option_reference": string, + "display_name": string, + "uefi_device_path": string, + "boot_option_enabled": bool + }, + { + "boot_option_reference": string, + "display_name": string, + "uefi_device_path": string, + "boot_option_enabled": bool + } + ] + } +] + +Examples: + +$ sudo efibootmgr -v | jc --efibootmgr --pretty +[ + { + "boot_current": "0002", + "timeout_seconds": 0, + "boot_order": [ + "0002", + "0000", + "0001" + ], + "mirrored_percentage_above_4g": 0.0, + "mirror_memory_below_4gb": false, + "boot_options": [ + { + "boot_option_reference": "Boot0000", + "display_name": "WARNADO", + "uefi_device_path": "HD(1,GPT,05b9944c-1c60-492b-a510-7bbedccdc602,0x800,0xfa000)/File(EFI\\boot\\bootx64.efi)", + "boot_option_enabled": true + }, + { + "boot_option_reference": "Boot0001", + "display_name": "Embedded NIC 1 Port 1 Partition 1", + "uefi_device_path": "VenHw(3a191845-5f86-4e78-8fce-c4cff59f9daa)", + "boot_option_enabled": true + }, + { + "boot_option_reference": "Boot0002", + "display_name": "opensuse-secureboot", + "uefi_device_path": "HD(1,GPT,c5d4f69d-6fc2-48c7-acee-af3f30336dc5,0x800,0x19000)/File(\\EFI\\opensuse\\shim.efi)", + "boot_option_enabled": true + } + ] + } +] + +""" + +import jc.utils +import jc.parsers.universal + +class info(): + """Provides parser metadata (version, author, etc.)""" + version = '0.1' + description = '`efibootmgr -v` command parser' + author = 'Yaofei Zheng' + author_email = ['zyf26256@gmail.com','Yaofei.Zheng@dell.com'] + compatible = ['linux'] + magic_commands = ['efibootmgr'] + tags = ['command'] + +__version__ = info.version + +def _process(proc_data): + if 0 == len(proc_data): + return proc_data + proc_data[0]["timeout_seconds"] = int(proc_data[0]["timeout_seconds"].replace("seconds","").strip(), 10) + proc_data[0]["boot_order"] = proc_data[0]["boot_order"].split(',') + for boot_opt in proc_data[0]["boot_options"]: + boot_opt["boot_option_enabled"] = boot_opt["boot_option_enabled"] == '*' + proc_data[0]["mirrored_percentage_above_4g"] = float(proc_data[0]["mirrored_percentage_above_4g"]) + proc_data[0]["mirror_memory_below_4gb"] = not ("false" == proc_data[0]["mirror_memory_below_4gb"]) + return proc_data + +def parse(data, raw=False, quiet=False): + jc.utils.compatibility(__name__, info.compatible, quiet) + jc.utils.input_type_check(data) + + raw_output = [] + + if jc.utils.has_data(data): + boot_opt_list = [] + efibootmgr_dict = {} + for line in filter(None, data.splitlines()): + if "BootCurrent" in line: + efibootmgr_dict["boot_current"] = line.split(':')[1].strip() + elif "Timeout" in line: + efibootmgr_dict["timeout_seconds"] = line.split(':')[1].strip() + elif "BootOrder" in line: + efibootmgr_dict["boot_order"] = line.split(':')[1].strip() + elif "Boot" in line: + tmp_dict = {} + boot_record = line.split("\t") + tmp_dict["boot_option_reference"] = boot_record[0][0:8] + tmp_dict["display_name"] = boot_record[0][10:].strip() + if len(boot_record) > 1: + tmp_dict["uefi_device_path"] = boot_record[1].strip() + tmp_dict["boot_option_enabled"] = boot_record[0][8] + boot_opt_list.append(tmp_dict) + elif "MirroredPercentageAbove4G" in line: + efibootmgr_dict["mirrored_percentage_above_4g"] = line.split(':')[1].strip() + elif "MirrorMemoryBelow4GB" in line: + efibootmgr_dict["mirror_memory_below_4gb"] = line.split(':')[1].strip() + else: + # print(line) + continue + + efibootmgr_dict["boot_options"] = boot_opt_list + + raw_output.append(efibootmgr_dict) + + if raw: + return raw_output + else: + return _process(raw_output) diff --git a/tests/fixtures/create_fixtures.sh b/tests/fixtures/create_fixtures.sh index ffca60b1..e2980538 100644 --- a/tests/fixtures/create_fixtures.sh +++ b/tests/fixtures/create_fixtures.sh @@ -8,6 +8,7 @@ df -h > df-h.out dig www.google.com AAAA > dig-aaaa.out dig www.cnn.com www.google.com > dig.out dig -x 1.1.1.1 > dig-x.out +sudo efibootmgr -v > efibootmgr.out env > env.out free > free.out free -h > free-h.out diff --git a/tests/fixtures/opensuse-leap-15.5/efibootmgr.json b/tests/fixtures/opensuse-leap-15.5/efibootmgr.json new file mode 100644 index 00000000..b41d783f --- /dev/null +++ b/tests/fixtures/opensuse-leap-15.5/efibootmgr.json @@ -0,0 +1,96 @@ +[ + { + "boot_current": "0004", + "timeout_seconds": 0, + "boot_order": [ + "0004", + "0001", + "0007", + "0002", + "0003", + "0009", + "0000", + "0005", + "0006", + "0008", + "000A", + "000B" + ], + "mirrored_percentage_above_4g": 0.0, + "mirror_memory_below_4gb": false, + "boot_options": [ + { + "boot_option_reference": "Boot0000", + "display_name": "debian", + "uefi_device_path": "HD(1,GPT,66b2a325-3129-45d3-84dd-c1a4e4bd846b,0x800,0x100000)/File(\\EFI\\debian\\shimx64.efi)", + "boot_option_enabled": true + }, + { + "boot_option_reference": "Boot0001", + "display_name": "RecoveryBoot", + "uefi_device_path": "HD(3,GPT,a75ab570-de77-504c-be97-35faa629f1f3,0x5dc800,0x1f4000)/File(EFI\\boot\\bootx64.efi)", + "boot_option_enabled": true + }, + { + "boot_option_reference": "Boot0002", + "display_name": "Embedded NIC 1 Port 1 Partition 1", + "uefi_device_path": "VenHw(3a191845-5f86-4e78-8fce-c4cff59f9daa)", + "boot_option_enabled": false + }, + { + "boot_option_reference": "Boot0003", + "display_name": "Embedded NIC 2 Port 1 Partition 1", + "uefi_device_path": "VenHw(d227c733-f75f-4341-b749-4d1759ec8538)", + "boot_option_enabled": false + }, + { + "boot_option_reference": "Boot0004", + "display_name": "PrimaryBoot", + "uefi_device_path": "HD(1,GPT,faf946be-a174-478f-82ea-d90aa476fb91,0x800,0xfa000)/File(EFI\\boot\\bootx64.efi)", + "boot_option_enabled": true + }, + { + "boot_option_reference": "Boot0005", + "display_name": "EFI Fixed Disk Boot Device 3", + "uefi_device_path": "PciRoot(0x1)/Pci(0x4,0x0)/Pci(0x0,0x0)/NVMe(0x1,8C-E3-8E-E2-0A-D4-0F-01)/HD(1,GPT,c2336d30-9bde-4a0b-b9be-145d6958d5af,0x1000,0x80001)", + "boot_option_enabled": true + }, + { + "boot_option_reference": "Boot0006", + "display_name": "EFI Fixed Disk Boot Device 4", + "uefi_device_path": "PciRoot(0x11)/Pci(0x1,0x0)/Pci(0x0,0x0)/NVMe(0x1,00-25-38-95-21-01-B8-C1)/HD(1,GPT,1fbfeaec-d78d-4d8c-93ae-77bba6c09d3f,0x1000,0x80001)", + "boot_option_enabled": true + }, + { + "boot_option_reference": "Boot0007", + "display_name": "BackendBoot", + "uefi_device_path": "PciRoot(0x11)/Pci(0x3,0x0)/Pci(0x0,0x0)/NVMe(0x1,00-25-38-95-21-01-B5-B6)/HD(1,GPT,b95c4d58-849f-40fa-bf2b-4f0b8a0aa8bd,0x1000,0x80001)", + "boot_option_enabled": true + }, + { + "boot_option_reference": "Boot0008", + "display_name": "EFI Fixed Disk Boot Device 6", + "uefi_device_path": "PciRoot(0x11)/Pci(0x6,0x0)/Pci(0x0,0x0)/NVMe(0x1,8C-E3-8E-E2-0A-D3-90-01)/HD(1,GPT,f543ab7b-9d55-4e57-8afe-bdb907428f15,0x1000,0x80001)", + "boot_option_enabled": true + }, + { + "boot_option_reference": "Boot0009", + "display_name": "EFI Fixed Disk Boot Device 7", + "uefi_device_path": "PciRoot(0x12)/Pci(0x6,0x0)/Pci(0x0,0x0)/NVMe(0x1,00-25-38-95-21-01-B6-08)/HD(1,GPT,db9522dc-062b-4efa-84b5-6956cea7b5a6,0x1000,0x80001)", + "boot_option_enabled": true + }, + { + "boot_option_reference": "Boot000A", + "display_name": "EFI Fixed Disk Boot Device 8", + "uefi_device_path": "PciRoot(0x12)/Pci(0x8,0x0)/Pci(0x0,0x0)/NVMe(0x1,00-25-38-95-21-01-B6-0D)/HD(1,GPT,4a9e1d40-b853-44a0-8d26-a4df6f112dc9,0x1000,0x80001)", + "boot_option_enabled": true + }, + { + "boot_option_reference": "Boot000B", + "display_name": "EFI Fixed Disk Boot Device 9", + "uefi_device_path": "PciRoot(0x13)/Pci(0x5,0x0)/Pci(0x0,0x0)/NVMe(0x1,8C-E3-8E-E2-0A-D3-8E-01)/HD(1,GPT,5a80e168-bac7-4967-9507-e23a432bae18,0x1000,0x80001)", + "boot_option_enabled": true + } + ] + } +] diff --git a/tests/fixtures/opensuse-leap-15.5/efibootmgr.out b/tests/fixtures/opensuse-leap-15.5/efibootmgr.out new file mode 100644 index 00000000..625849c6 --- /dev/null +++ b/tests/fixtures/opensuse-leap-15.5/efibootmgr.out @@ -0,0 +1,17 @@ +BootCurrent: 0004 +Timeout: 0 seconds +BootOrder: 0004,0001,0007,0002,0003,0009,0000,0005,0006,0008,000A,000B +Boot0000* debian HD(1,GPT,66b2a325-3129-45d3-84dd-c1a4e4bd846b,0x800,0x100000)/File(\EFI\debian\shimx64.efi) +Boot0001* RecoveryBoot HD(3,GPT,a75ab570-de77-504c-be97-35faa629f1f3,0x5dc800,0x1f4000)/File(EFI\boot\bootx64.efi) +Boot0002 Embedded NIC 1 Port 1 Partition 1 VenHw(3a191845-5f86-4e78-8fce-c4cff59f9daa) +Boot0003 Embedded NIC 2 Port 1 Partition 1 VenHw(d227c733-f75f-4341-b749-4d1759ec8538) +Boot0004* PrimaryBoot HD(1,GPT,faf946be-a174-478f-82ea-d90aa476fb91,0x800,0xfa000)/File(EFI\boot\bootx64.efi) +Boot0005* EFI Fixed Disk Boot Device 3 PciRoot(0x1)/Pci(0x4,0x0)/Pci(0x0,0x0)/NVMe(0x1,8C-E3-8E-E2-0A-D4-0F-01)/HD(1,GPT,c2336d30-9bde-4a0b-b9be-145d6958d5af,0x1000,0x80001) +Boot0006* EFI Fixed Disk Boot Device 4 PciRoot(0x11)/Pci(0x1,0x0)/Pci(0x0,0x0)/NVMe(0x1,00-25-38-95-21-01-B8-C1)/HD(1,GPT,1fbfeaec-d78d-4d8c-93ae-77bba6c09d3f,0x1000,0x80001) +Boot0007* BackendBoot PciRoot(0x11)/Pci(0x3,0x0)/Pci(0x0,0x0)/NVMe(0x1,00-25-38-95-21-01-B5-B6)/HD(1,GPT,b95c4d58-849f-40fa-bf2b-4f0b8a0aa8bd,0x1000,0x80001) +Boot0008* EFI Fixed Disk Boot Device 6 PciRoot(0x11)/Pci(0x6,0x0)/Pci(0x0,0x0)/NVMe(0x1,8C-E3-8E-E2-0A-D3-90-01)/HD(1,GPT,f543ab7b-9d55-4e57-8afe-bdb907428f15,0x1000,0x80001) +Boot0009* EFI Fixed Disk Boot Device 7 PciRoot(0x12)/Pci(0x6,0x0)/Pci(0x0,0x0)/NVMe(0x1,00-25-38-95-21-01-B6-08)/HD(1,GPT,db9522dc-062b-4efa-84b5-6956cea7b5a6,0x1000,0x80001) +Boot000A* EFI Fixed Disk Boot Device 8 PciRoot(0x12)/Pci(0x8,0x0)/Pci(0x0,0x0)/NVMe(0x1,00-25-38-95-21-01-B6-0D)/HD(1,GPT,4a9e1d40-b853-44a0-8d26-a4df6f112dc9,0x1000,0x80001) +Boot000B* EFI Fixed Disk Boot Device 9 PciRoot(0x13)/Pci(0x5,0x0)/Pci(0x0,0x0)/NVMe(0x1,8C-E3-8E-E2-0A-D3-8E-01)/HD(1,GPT,5a80e168-bac7-4967-9507-e23a432bae18,0x1000,0x80001) +MirroredPercentageAbove4G: 0.00 +MirrorMemoryBelow4GB: false diff --git a/tests/test_efibootmgr.py b/tests/test_efibootmgr.py new file mode 100644 index 00000000..2ef7c846 --- /dev/null +++ b/tests/test_efibootmgr.py @@ -0,0 +1,33 @@ +import os +import json +import unittest +import jc.parsers.efibootmgr + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class MyTests(unittest.TestCase): + + # input + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/opensuse-leap-15.5/efibootmgr.out'), 'r', encoding='utf-8') as f: + opensuse_leap_15_5_efibootmgr = f.read() + + # output + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/opensuse-leap-15.5/efibootmgr.json'), 'r', encoding='utf-8') as f: + opensuse_leap_15_5_efibootmgr_json = json.loads(f.read()) + + def test_efibootmgr_nodata(self): + """ + Test 'efibootmgr' with no data + """ + self.assertEqual(jc.parsers.efibootmgr.parse('', quiet=True), []) + + def test_efibootmgr_opensuse_leap_15_5(self): + """ + Test 'efibootmgr' on Opensuse Leap 15.5 + """ + self.assertEqual(jc.parsers.efibootmgr.parse(self.opensuse_leap_15_5_efibootmgr, quiet=True), self.opensuse_leap_15_5_efibootmgr_json) + + +if __name__ == '__main__': + unittest.main()