FreeNAS/freenas f686624src/middlewared/middlewared/plugins boot.py, src/middlewared/middlewared/plugins/boot __init__.py format.py

Convert boot plugin to typesafe pattern

## Problem
The boot service was old-style dict-based code spread across three files (boot.py, boot_/format.py, boot_/boot_loader.py) that all declared the same BootService, so the framework silently merged them into a CompoundService. None of it was type-checked end to end, and every in-process consumer reached it through untyped string middleware.call('boot.*').

## Solution
- Consolidate everything into a single plugins/boot/ package using the port pattern: a lean BootService in __init__.py that delegates to plain, fully type-annotated module functions in disks.py/format.py/pool_ops.py, with @api_method(check_annotations=True) public methods and @private stubs. boot.get_state now returns a BootGetState model; internal callers use a dict state helper so nothing breaks on attribute-vs-key access.
- Register boot in main.py's ServiceContainer and have it own boot.environment as a sub-service, replacing BootServicesContainer.
- Expose the boot-pool name/disks cache through accessor functions instead of a module global. This also fixes a latent bug where libvirt/cdrom.py imported the global at module load (before setup runs), bound it to None, and so never filtered the boot pool out of CDROM path validation.
- Convert every same-process boot.* string call to typed call2/call_sync2 across sysdataset, zfs_events, disk, failover, and the zpool/audit/tunable/update/system_advanced plugins, the alert sources, and the cron/fstab makos, keeping each site's sync/async flavour.
- Keep the private update_initramfs/format models in the plugin (check_model_module requires private-method models to live there, not in the api package) and import them lazily from consumers, otherwise pulling boot/__init__ into the early service-init chain deadlocks on a partially-initialised middlewared.service.
- Add plugins/boot/ to mypy.yml.
DeltaFile
+0-372src/middlewared/middlewared/plugins/boot.py
+250-0src/middlewared/middlewared/plugins/boot/__init__.py
+161-0src/middlewared/middlewared/plugins/boot/format.py
+148-0src/middlewared/middlewared/plugins/boot/pool_ops.py
+0-115src/middlewared/middlewared/plugins/boot_/format.py
+65-0src/middlewared/middlewared/plugins/boot/disks.py
+624-48732 files not shown
+751-58838 files

FreeNAS/freenas 68baaf3src/middlewared/middlewared/plugins boot.py, src/middlewared/middlewared/plugins/boot __init__.py format.py

Convert boot plugin to typesafe pattern

## Problem
The boot service was old-style dict-based code spread across three files (boot.py, boot_/format.py, boot_/boot_loader.py) that all declared the same BootService, so the framework silently merged them into a CompoundService. None of it was type-checked end to end, and every in-process consumer reached it through untyped string middleware.call('boot.*').

## Solution
- Consolidate everything into a single plugins/boot/ package using the port pattern: a lean BootService in __init__.py that delegates to plain, fully type-annotated module functions in disks.py/format.py/pool_ops.py, with @api_method(check_annotations=True) public methods and @private stubs. boot.get_state now returns a BootGetState model; internal callers use a dict state helper so nothing breaks on attribute-vs-key access.
- Register boot in main.py's ServiceContainer and have it own boot.environment as a sub-service, replacing BootServicesContainer.
- Expose the boot-pool name/disks cache through accessor functions instead of a module global. This also fixes a latent bug where libvirt/cdrom.py imported the global at module load (before setup runs), bound it to None, and so never filtered the boot pool out of CDROM path validation.
- Convert every same-process boot.* string call to typed call2/call_sync2 across sysdataset, zfs_events, disk, failover, and the zpool/audit/tunable/update/system_advanced plugins, the alert sources, and the cron/fstab makos, keeping each site's sync/async flavour.
- Keep the private update_initramfs/format models in the plugin (check_model_module requires private-method models to live there, not in the api package) and import them lazily from consumers, otherwise pulling boot/__init__ into the early service-init chain deadlocks on a partially-initialised middlewared.service.
- Add plugins/boot/ to mypy.yml.
DeltaFile
+0-372src/middlewared/middlewared/plugins/boot.py
+250-0src/middlewared/middlewared/plugins/boot/__init__.py
+161-0src/middlewared/middlewared/plugins/boot/format.py
+152-0src/middlewared/middlewared/plugins/boot/pool_ops.py
+0-115src/middlewared/middlewared/plugins/boot_/format.py
+63-0src/middlewared/middlewared/plugins/boot/disks.py
+626-48732 files not shown
+753-58838 files

FreeNAS/freenas c652d78src/middlewared/middlewared/alert base.py, src/middlewared/middlewared/pytest/unit/alert test_base.py

Move `assert` from middleware startup to tests

(cherry picked from commit 7641efee9aaf3408acdb637c5727eac8f6d36964)
DeltaFile
+5-0src/middlewared/middlewared/pytest/unit/alert/test_base.py
+0-3src/middlewared/middlewared/alert/base.py
+5-32 files

FreeNAS/freenas 9dfc53esrc/middlewared/middlewared/alert base.py, src/middlewared/middlewared/alert/source scheduled_reboot.py

NAS-141626 / 27.0.0-BETA.1 / Remove High-Availability, Alert Setting options from non-HA Enterprise systems (#19289)
DeltaFile
+14-3src/middlewared/middlewared/plugins/alert/queries.py
+5-0src/middlewared/middlewared/pytest/unit/alert/test_base.py
+2-2src/middlewared/middlewared/alert/source/scheduled_reboot.py
+0-3src/middlewared/middlewared/alert/base.py
+1-1src/middlewared/middlewared/plugins/alert/__init__.py
+22-95 files

FreeNAS/freenas 19d9d2csrc/middlewared/middlewared/plugins/boot_environment crud.py __init__.py, tests README.md

Replace zectl with the truenas_bootenv engine

zectl is being retired in favor of the truenas_bootenv package that
ships with truenas_pylibzfs (companion commit there). The
boot_environment plugin now calls the engine in-process on
middleware's thread-local libzfs handle: query, clone, activate,
destroy and keep all go through it, utils.py with run_zectl_cmd is
deleted, and the boot-time promotion of installer-cloned datasets
uses the engine instead of parsing zfs list -j output.

Mutations serialize on an asyncio lock. A truenas:grub_pending
marker on the boot pool tracks pending menu regenerations and
setup() reconciles it on start. If the menu cannot be written,
activate rolls bootfs back. The boot pool is synced after every
menu write so the change is crash safe.

Re-activating the already-activated boot environment is allowed
again as the retry path after a failed menu regeneration, and
can_activate treats a missing truenas:kernel_version as not
activatable instead of only the literal "-".
DeltaFile
+243-91src/middlewared/middlewared/plugins/boot_environment/crud.py
+116-6src/middlewared/middlewared/plugins/boot_environment/__init__.py
+0-19src/middlewared/middlewared/plugins/boot_environment/utils.py
+11-5tests/api2/test_boot_environments.py
+4-5tests/README.md
+374-1265 files

FreeNAS/freenas 05af3a4src/middlewared/middlewared/plugins/boot_environment crud.py __init__.py, tests README.md

Replace zectl with the truenas_bootenv engine

The typesafe boot_environment plugin now calls the engine from the
truenas_pylibzfs truenas_bootenv package on the middleware
thread-local libzfs handle instead of shelling out to zectl. Mutations
serialize under an asyncio lock, the boot menu regenerates through
etc.generate('grub') behind a truenas:grub_pending marker that setup()
reconciles after a crash, activate rolls bootfs back when the menu
cannot be written, and the pool is synced afterwards so the menu
change is crash durable.

Re-activating the activated boot environment is allowed again as the
retry path after a failed menu regeneration; the api2 test asserting
the old rejection now asserts the retry succeeds. can_activate treats
a missing truenas:kernel_version as not activatable instead of only
the literal dash. The boot-time dataset promotion uses the engine
instead of parsing zfs list -j output.
DeltaFile
+243-91src/middlewared/middlewared/plugins/boot_environment/crud.py
+116-6src/middlewared/middlewared/plugins/boot_environment/__init__.py
+0-19src/middlewared/middlewared/plugins/boot_environment/utils.py
+11-5tests/api2/test_boot_environments.py
+4-5tests/README.md
+374-1265 files

FreeNAS/freenas 684f0a5src/middlewared/middlewared/plugins/boot_environment crud.py __init__.py, tests README.md

boot.environment: replace zectl with the truenas_bootenv engine

The typesafe boot_environment plugin now calls the engine from the
truenas_pylibzfs truenas_bootenv package on the middleware
thread-local libzfs handle instead of shelling out to zectl. Mutations
serialize under an asyncio lock, the boot menu regenerates through
etc.generate('grub') behind a truenas:grub_pending marker that setup()
reconciles after a crash, activate rolls bootfs back when the menu
cannot be written, and the pool is synced afterwards so the menu
change is crash durable.

Re-activating the activated boot environment is allowed again as the
retry path after a failed menu regeneration; the api2 test asserting
the old rejection now asserts the retry succeeds. can_activate treats
a missing truenas:kernel_version as not activatable instead of only
the literal dash. The boot-time dataset promotion uses the engine
instead of parsing zfs list -j output.
DeltaFile
+243-91src/middlewared/middlewared/plugins/boot_environment/crud.py
+116-6src/middlewared/middlewared/plugins/boot_environment/__init__.py
+0-19src/middlewared/middlewared/plugins/boot_environment/utils.py
+11-5tests/api2/test_boot_environments.py
+4-5tests/README.md
+374-1265 files

FreeNAS/freenas b3a60f9src/middlewared/middlewared/api/v25_10_0 nvmet_subsys.py, src/middlewared/middlewared/api/v25_10_1 nvmet_subsys.py

NAS-141719 / 26.0.0-RC.1 / Fix invalid nvmet defaults that make the test__verbose_subsys_query fail (by themylogin) (#19291)

Original PR: https://github.com/truenas/middleware/pull/19283

Co-authored-by: themylogin <themylogin at gmail.com>
DeltaFile
+12-4src/middlewared/middlewared/api/v25_10_0/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_1/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_2/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_3/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_4/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v26_0_0/nvmet_subsys.py
+72-246 files

FreeNAS/freenas 3445853src/middlewared/middlewared/api/v25_10_0 nvmet_subsys.py, src/middlewared/middlewared/api/v25_10_1 nvmet_subsys.py

Fix nvmet defaults

(cherry picked from commit 74fbdcdb8c5f92e6ba726d3ed54876dee9a67847)
DeltaFile
+12-4src/middlewared/middlewared/api/v25_10_1/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_0/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_2/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_3/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_4/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v26_0_0/nvmet_subsys.py
+72-246 files

FreeNAS/freenas 4e7aec2src/middlewared/middlewared/pytest/unit/plugins/pool test_validate_topology.py

Fix ruff check
DeltaFile
+2-1src/middlewared/middlewared/pytest/unit/plugins/pool/test_validate_topology.py
+2-11 files

FreeNAS/freenas d728a48src/middlewared/middlewared/api/v25_10_0 nvmet_subsys.py, src/middlewared/middlewared/api/v25_10_1 nvmet_subsys.py

Fix nvmet defaults

(cherry picked from commit 74fbdcdb8c5f92e6ba726d3ed54876dee9a67847)
DeltaFile
+12-4src/middlewared/middlewared/api/v25_10_1/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_0/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_2/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_3/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v26_0_0/nvmet_subsys.py
+60-205 files

FreeNAS/freenas 7acdd4bsrc/middlewared/middlewared/api/v25_10_0 nvmet_subsys.py, src/middlewared/middlewared/api/v25_10_1 nvmet_subsys.py

NAS-141719 / 27.0.0-BETA.1 / Fix invalid nvmet defaults that make the test__verbose_subsys_query fail (#19283)
DeltaFile
+12-4src/middlewared/middlewared/api/v26_0_0/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_1/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_2/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v25_10_0/nvmet_subsys.py
+12-4src/middlewared/middlewared/api/v27_0_0/nvmet_subsys.py
+60-205 files

FreeNAS/freenas ca10e94src/middlewared/middlewared/plugins/boot_environment crud.py __init__.py, tests README.md

boot.environment: replace zectl with the truenas_bootenv engine

The typesafe boot_environment plugin now calls the engine from the
truenas_pylibzfs truenas_bootenv package on the middleware
thread-local libzfs handle instead of shelling out to zectl. Mutations
serialize under an asyncio lock, the boot menu regenerates through
etc.generate('grub') behind a truenas:grub_pending marker that setup()
reconciles after a crash, activate rolls bootfs back when the menu
cannot be written, and the pool is synced afterwards so the menu
change is crash durable.

Re-activating the activated boot environment is allowed again as the
retry path after a failed menu regeneration; the api2 test asserting
the old rejection now asserts the retry succeeds. can_activate treats
a missing truenas:kernel_version as not activatable instead of only
the literal dash. The boot-time dataset promotion uses the engine
instead of parsing zfs list -j output.
DeltaFile
+243-91src/middlewared/middlewared/plugins/boot_environment/crud.py
+116-6src/middlewared/middlewared/plugins/boot_environment/__init__.py
+0-19src/middlewared/middlewared/plugins/boot_environment/utils.py
+4-5tests/api2/test_boot_environments.py
+4-5tests/README.md
+367-1265 files

FreeNAS/freenas 7884721src/middlewared/middlewared/plugins/mail send_queue.py

NAS-141699 / 27.0.0-BETA.1 / fix duplicate From header in email (#19284)

`send_mail_queue()` updates the `From` address of queued messages with
`item.message["From"] = ...`, but `Message.__setitem__` *appends* a
second From header instead of replacing the existing one set by
`send_raw()`. The resulting message violates RFC 5322 and Gmail rejects
it with 550-5.7.1 "There are multiple From headers". Use
`replace_header()` so the flushed message keeps a single From header.
DeltaFile
+3-2src/middlewared/middlewared/plugins/mail/send_queue.py
+3-21 files

FreeNAS/freenas a9229c3src/middlewared/middlewared/plugins mail.py

NAS-141699 / 26.0.0-RC.1 / fix duplicate From header and SMTP crash (#19286)

Two related bugs caused alert/test emails to be RFC 5322 non-compliant
(double From: header), which Gmail now rejects with 550-5.7.1:

1. When `fromname` is empty, `_from_addr()` returned an
`email.header.Header`; `Header.encode()` returns `str`, so the
`.encode().decode()` in `send_raw()` raised `AttributeError: 'str'
object has no attribute 'decode'` on every direct SMTP send.
`_from_addr()` now always returns `str`.
2. The failed message was then queued, and `send_mail_queue()` did
`queue.message['From'] = ...`, which *appends* a second From header
instead of replacing the existing one. Use `replace_header()` so the
flushed message keeps a single From header.
DeltaFile
+5-14src/middlewared/middlewared/plugins/mail.py
+5-141 files

FreeNAS/freenas 86aa3a3src/middlewared/middlewared/plugins mail.py

NAS-141699 / 25.10.5 / fix duplicate From header and SMTP crash (#19287)

Two related bugs caused alert/test emails to be RFC 5322 non-compliant
(double From: header), which Gmail now rejects with 550-5.7.1:

1. When `fromname` is empty, `_from_addr()` returned an
`email.header.Header`; `Header.encode()` returns `str`, so the
`.encode().decode()` in `send_raw()` raised `AttributeError: 'str'
object has no attribute 'decode'` on every direct SMTP send.
`_from_addr()` now always returns `str`.
2. The failed message was then queued, and `send_mail_queue()` did
`queue.message['From'] = ...`, which *appends* a second From header
instead of replacing the existing one. Use `replace_header()` so the
flushed message keeps a single From header.
DeltaFile
+5-14src/middlewared/middlewared/plugins/mail.py
+5-141 files

FreeNAS/freenas 314cf4csrc/middlewared/middlewared/api/v27_0_0 alert.py, src/middlewared/middlewared/plugins smb.py

Use `check_annotations=True` for alert service
DeltaFile
+30-20src/middlewared/middlewared/plugins/alert/queries.py
+16-12src/middlewared/middlewared/plugins/alert/__init__.py
+22-5src/middlewared/middlewared/api/v27_0_0/alert.py
+4-3src/middlewared/middlewared/plugins/alert/serialize.py
+3-3src/middlewared/middlewared/plugins/zfs_/zfs_events.py
+3-3src/middlewared/middlewared/plugins/smb.py
+78-465 files not shown
+85-5311 files

FreeNAS/freenas 2a6c5ebsrc/middlewared/middlewared/plugins/alert queries.py __init__.py

Do not show `HA` category on non-failover enterprise systems
DeltaFile
+14-3src/middlewared/middlewared/plugins/alert/queries.py
+1-1src/middlewared/middlewared/plugins/alert/__init__.py
+15-42 files

FreeNAS/freenas 0447043src/middlewared/middlewared/alert/source scheduled_reboot.py

Set proper alert categories
DeltaFile
+2-2src/middlewared/middlewared/alert/source/scheduled_reboot.py
+2-21 files

FreeNAS/freenas 7641efesrc/middlewared/middlewared/alert base.py, src/middlewared/middlewared/pytest/unit/alert test_base.py

Move `assert` from middleware startup to tests
DeltaFile
+5-0src/middlewared/middlewared/pytest/unit/alert/test_base.py
+0-3src/middlewared/middlewared/alert/base.py
+5-32 files

FreeNAS/freenas 8b57228src/middlewared/middlewared/pytest/unit/plugins/pool test_validate_topology.py

pool: ruff format topology validation tests

(cherry picked from commit dfff39c73ba9bfcc9586f45ef2fc4d4d963de315)
DeltaFile
+42-52src/middlewared/middlewared/pytest/unit/plugins/pool/test_validate_topology.py
+42-521 files

FreeNAS/freenas 771b756src/middlewared/middlewared/api/v26_0_0 pool.py, src/middlewared/middlewared/plugins/pool_ pool.py

pool: enforce vdev geometry limits and add force_topology

Enforce equal data-vdev width and cap RAIDZ/mirror width at
15/4 in pool.create and pool.update, mirroring truenas_pylibzfs.
Add force_topology (26 API) to bypass these topology policy
checks and the special/dedup redundancy rule; structural checks
still apply. Reject force_topology on Enterprise-licensed
systems.

(cherry picked from commit f07f7804cbb84ec1fe331e04485ec1b667dd3a2d)
DeltaFile
+205-0src/middlewared/middlewared/pytest/unit/plugins/pool/test_validate_topology.py
+58-1src/middlewared/middlewared/plugins/pool_/pool.py
+10-0src/middlewared/middlewared/api/v26_0_0/pool.py
+273-13 files

FreeNAS/freenas d8910fbsrc/middlewared/middlewared/plugins/pool_ pool.py

pool: hoist vdev min/max disk maps to module constants

(cherry picked from commit 01ffa4d112799b6259188ebb62403a26ad95afc7)
DeltaFile
+31-24src/middlewared/middlewared/plugins/pool_/pool.py
+31-241 files

FreeNAS/freenas 103a152src/middlewared/middlewared/api/v27_0_0 pool.py

pool: add force_topology to v27_0_0 PoolCreate
DeltaFile
+10-0src/middlewared/middlewared/api/v27_0_0/pool.py
+10-01 files

FreeNAS/freenas de5649asrc/middlewared/middlewared/api/v27_0_0 pool.py

pool: add force_topology to v27_0_0 PoolCreate
DeltaFile
+10-0src/middlewared/middlewared/api/v27_0_0/pool.py
+10-01 files

FreeNAS/freenas 3b822eesrc/middlewared/middlewared/plugins/boot_environment crud.py __init__.py, tests README.md

boot.environment: replace zectl with the truenas_bootenv engine

The typesafe boot_environment plugin now calls the engine from the
truenas_pylibzfs truenas_bootenv package on the middleware
thread-local libzfs handle instead of shelling out to zectl. Mutations
serialize under an asyncio lock, the boot menu regenerates through
etc.generate('grub') behind a truenas:grub_pending marker that setup()
reconciles after a crash, activate rolls bootfs back when the menu
cannot be written, and the pool is synced afterwards so the menu
change is crash durable.

Re-activating the activated boot environment is allowed again as the
retry path after a failed menu regeneration; the api2 test asserting
the old rejection now asserts the retry succeeds. can_activate treats
a missing truenas:kernel_version as not activatable instead of only
the literal dash. The boot-time dataset promotion uses the engine
instead of parsing zfs list -j output.
DeltaFile
+202-88src/middlewared/middlewared/plugins/boot_environment/crud.py
+99-1src/middlewared/middlewared/plugins/boot_environment/__init__.py
+0-19src/middlewared/middlewared/plugins/boot_environment/utils.py
+4-5tests/README.md
+4-5tests/api2/test_boot_environments.py
+309-1185 files

FreeNAS/freenas aab1c31src/middlewared/middlewared/plugins/boot_ environments.py

boot.environment: replace zectl subprocess with truenas_bootenv engine

Proof of concept for testing alongside the truenas_pylibzfs
truenas_bootenv branch. The plugin calls the engine directly on the
middleware thread-local libzfs handle instead of shelling out to
zectl: mutations serialize under a module lock, the grub menu is
regenerated through etc.generate('grub') with a truenas:grub_pending
marker reconciled at startup after a crash, and the pool is synced so
the menu change is crash durable.

Based on the tree just before the boot_environment typesafe plugin
split; porting onto the new plugin layout is a known follow-up before
this can target master.
DeltaFile
+290-55src/middlewared/middlewared/plugins/boot_/environments.py
+290-551 files

FreeNAS/freenas f61704esrc/middlewared/middlewared/alert/source zfs_tier.py, src/middlewared/middlewared/plugins/failover_ event.py

NAS-141711 / 26.0.0-RC.1 / Ensure truenas_zfstierd state on failover (by anodos325) (#19288)

Regenerate the truenas_zfstierd config on the promoted node.

Materialize enum_jobs() in the tier alert source so per-job get_info()
no longer opens a second read transaction inside the live iterator
(MDB_BAD_RSLOT; LMDB allows one read txn per thread per db).

Original PR: https://github.com/truenas/middleware/pull/19280

Co-authored-by: Andrew Walker <andrew.walker at truenas.com>
DeltaFile
+9-0src/middlewared/middlewared/plugins/failover_/event.py
+6-1src/middlewared/middlewared/alert/source/zfs_tier.py
+1-0src/middlewared/middlewared/plugins/service_/services/truenas_zfstierd.py
+16-13 files

FreeNAS/freenas 2e23c4bsrc/middlewared/middlewared/alert/source zfs_tier.py, src/middlewared/middlewared/plugins/failover_ event.py

Ensure truenas_zfstierd state on failover

Regenerate the truenas_zfstierd config on the promoted node: the
service now owns its etc group and vrrp_master restarts it, so an
enabled database no longer surfaces a spurious tiering-disabled
error to clients.

Materialize enum_jobs() in the tier alert source so per-job
get_info() no longer opens a second read transaction inside the
live iterator (MDB_BAD_RSLOT; LMDB allows one read txn per thread).

(cherry picked from commit 41b32455f38864877a1576100c139461d0df6c3d)
DeltaFile
+9-0src/middlewared/middlewared/plugins/failover_/event.py
+6-1src/middlewared/middlewared/alert/source/zfs_tier.py
+1-0src/middlewared/middlewared/plugins/service_/services/truenas_zfstierd.py
+16-13 files

FreeNAS/freenas 8090f0asrc/middlewared/middlewared/alert/source zfs_tier.py, src/middlewared/middlewared/plugins/failover_ event.py

NAS-141711 / 27.0.0-BETA.1 / Ensure truenas_zfstierd state on failover (#19280)

Regenerate the truenas_zfstierd config on the promoted node.

Materialize enum_jobs() in the tier alert source so per-job get_info()
no longer opens a second read transaction inside the live iterator
(MDB_BAD_RSLOT; LMDB allows one read txn per thread per db).
DeltaFile
+9-0src/middlewared/middlewared/plugins/failover_/event.py
+6-1src/middlewared/middlewared/alert/source/zfs_tier.py
+1-0src/middlewared/middlewared/plugins/service/services/truenas_zfstierd.py
+16-13 files