dhcpctl
One interface to DHCP servers: arm a reservation for a client on dnsmasq, ISC Kea, ISC dhcpd or Windows DHCP, then remove it or keep it without its boot options. A server is a URI: its scheme picks the backend and its query says how to reach it. The options to serve belong to each reservation. Documentation: https://jose-pr.github.io/dhcpctl/.
Features
- Four servers, one call —
add,remove,completeon dnsmasq (files, local or oversftp://), Kea (control agent), dhcpd (OMAPI) and Windows DHCP (PowerShell cmdlets or netsh, locally, over ssh or WinRM). - pydhcp's lease — what a client is served is a pydhcp
DHCPLease: its address, itsDHCPOptionsnamed by pydhcp's registry (router,dns,option-<n>) or an alias, each value read by its pydhcp codec before any server sees it, and the BOOTP header fields (next-server,file); each backend translates them, and backend-native text in a reservation'sextrasrides through untouched. - Conditions — options served only to clients that match a test (the iPXE
chainload), as a Windows policy, a dhcpd
ifor group, or a Kea client class; a backend that cannot express one refuses rather than arming without it, and hands over the server-side recipe when it lacks the rights to create it. - Completion that does not reinstall — keep a finished client's reservation and replace only its boot options.
- Values are data, never source — dhcpd statements are escaped, Windows scripts get their parameters as a JSON payload, netsh arguments stay array elements.
- Nothing required — each backend's library is an extra, imported only when its scheme is used; secrets come from the URI or the caller, and a password is masked wherever a server is shown.
- Reservations as documents —
Reservation.to_mapping/from_mappingandto_text/from_text(JSON, YAML, TOML, INI) round-trip a reservation through pydhcp's own option form. - Pluggable — a backend is a
DHCPServersubclass registered for a scheme, directly or through thedhcpctl.backendsentry point.
Installation
pip install pydhcpctl
The distribution is pydhcpctl; the import package is dhcpctl.
| Scheme | Server | Install |
|---|---|---|
dnsmasq:// |
dnsmasq | nothing locally; pydhcpctl[ssh] for sftp:// paths |
kea://, keas:// |
ISC Kea control agent | pydhcpctl[kea] (requests) |
dhcpd:// |
ISC dhcpd over OMAPI | pydhcpctl[dhcpd] (pypureomapi) |
windhcp:// |
Windows DHCP | nothing locally; pydhcpctl[ssh] over ssh, pydhcpctl[winrm] over WinRM |
pydhcpctl[formats] adds YAML, TOML and INI to Reservation.to_text /
from_text (JSON needs nothing), and pydhcpctl[cli] the dhcpctl command.
Python 3.9 or later.
Usage
import dhcpctl
from ipaddress import ip_network
from netimps import MACAddress
server = dhcpctl.connect("kea://kea01:8000/?service=dhcp4") # how to reach it, nothing else
# Options by name, and the BOOTP header fields beside them (not options).
lease = server.codes.lease(
{"router": "10.0.0.1", "file": "undionly.kpxe", "next-server": "10.0.0.2"}
)
reservation = dhcpctl.Reservation(
target=dhcpctl.Target("web01", mac=MACAddress("aa:bb:cc:dd:ee:ff")),
scope=dhcpctl.Scope("lan", network=ip_network("10.0.0.0/24")),
lease=lease.replace(ip="10.0.0.10"),
completion=dhcpctl.Completion(keep=True), # keep the address when done
)
server.add(reservation) # arm
server.complete(reservation) # finished: keep the entry, drop its boot options
A condition serves different options to clients that match a test:
import dataclasses
conditions = dhcpctl.parse_conditions(
{"ipxe": {"match": {"user-class": "iPXE"}, "options": {"file": "boot.ipxe"}}}
)
server.add(dataclasses.replace(reservation, conditions=conditions))
print(server.condition_recipe(reservation, conditions["ipxe"])) # what an admin applies
A secret is the URI's when it carries one, else the caller's, asked for only when needed — so it can stay out of a URI that ends up in config or logs:
server = dhcpctl.connect(
"windhcp://dhcp01/?transport=winrm", secrets={"user": "svc", "password": "..."}
)
Command line
pip install pydhcpctl[cli] adds the dhcpctl command (also python -m dhcpctl). A reservation is given by flags, or as a document with --from
(.json, .yaml, .toml, .ini, or - for JSON on standard input), which
can also carry conditions and a completion:
# What arming web01 would send, contacting nothing:
dhcpctl render 'dhcpd://dhcp01/' -n web01 -m aa:bb:cc:dd:ee:ff --ip 10.0.0.10 \
-o router=10.0.0.1 -o file=undionly.kpxe
dhcpctl add 'kea://kea01/?target=memory' --from web01.json # arm (or re-arm)
dhcpctl complete 'kea://kea01/?target=memory' --from web01.json --keep
dhcpctl remove 'windhcp://dhcp01/?transport=winrm' -n web01 -m aa:bb:cc:dd:ee:ff
dhcpctl recipe 'windhcp://dhcp01/' --from web01.json # what an admin sets up once
dhcpctl backends --json
-o name=value is an option or header field; a comma makes a list
(dns=10.0.0.53,10.0.0.54) and json: a JSON value (lease-time=json:600).
Secrets come from the environment, never a flag: DHCPCTL_USER,
DHCPCTL_PASSWORD, DHCPCTL_KEYNAME, DHCPCTL_OMAPI_KEY. Exit status 0 is
done, 1 the server refused or could not be reached, 2 the invocation or the
reservation was wrong.
Writing a backend
class Recorder(dhcpctl.DHCPServer):
def add(self, reservation): ...
def remove(self, reservation): ...
dhcpctl.register_backend("recorder", Recorder)
dhcpctl.connect("recorder://anything")
or declare it in your package's metadata:
[project.entry-points."dhcpctl.backends"]
recorder = "mypkg.dhcp:Recorder"
A backend is done when it passes the contract the shipped ones pass, which
talks to no server (it needs pytest, and the backend to implement render):
from dhcpctl.testing import BackendContract
class TestRecorder(BackendContract):
uri = "recorder://anything"
License
MIT
Metadata
Release files for pydhcpctl 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pydhcpctl-0.1.0.tar.gz | 131.7 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pydhcpctl-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 215.2 kB
Release files / pydhcpctl-0.1.0.tar.gz
| Download URL | pydhcpctl-0.1.0.tar.gz |
|---|---|
| Size | 131.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b93ec74f9bfde9a8d3882f7cd1d449af7db30e7cf79c6abeacf2abd95f5cd3fc
|
|
BLAKE2b-256 checksum How to use checksums |
efd300208983e7bf05b8a6f3c6df5d132263d44281672b58fd2a714e4e25feb8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logRelease files / pydhcpctl-0.1.0-py3-none-any.whl
| Download URL | pydhcpctl-0.1.0-py3-none-any.whl |
|---|---|
| Size | 83.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
42fbd234040d0dd42b8988eeb8ef8ee7bc6c8376fb1ddabc0604e81de357554b
|
|
BLAKE2b-256 checksum How to use checksums |
70386df4917bac95f1f53bd1c2bf4c301ff2f877cb80d76c48a574577bd950a7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logSecurity for pydhcpctl 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for pydhcpctl-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.