API reference
Connecting
dhcpctl.connect(uri, *, secrets=None, owner=DEFAULT_OWNER, codes=None)
The backend for uri's scheme, built from uri. Does no I/O.
secrets supplies what the URI must not carry (see DHCPServer.secret);
owner names the entries this server writes (see DHCPServer.owner);
codes is the codemap its option names are read with (an OptionCodes
from option_codes, or a mapping of declarations).
:raises UnknownBackendError: no backend serves the scheme. :raises ConfigError: the URI cannot be used by that backend.
Source code in src/dhcpctl/_registry.py
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | |
dhcpctl.register_backend(scheme, backend, *, replace=False)
Serve scheme with backend (a class, or "module:Class" to import lazily).
:raises ValueError: scheme is already served by another backend and
replace is false.
Source code in src/dhcpctl/_registry.py
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | |
dhcpctl.unregister_backend(scheme)
Stop serving scheme; True if it was registered.
Source code in src/dhcpctl/_registry.py
59 60 61 | |
dhcpctl.backend_names()
Every registered scheme, sorted.
Source code in src/dhcpctl/_registry.py
64 65 66 67 | |
dhcpctl.get_backend(scheme)
The backend class serving scheme, imported if it was registered by name.
:raises UnknownBackendError: no backend serves scheme.
Source code in src/dhcpctl/_registry.py
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | |
The contract
dhcpctl.DHCPServer(uri, *, secrets=None, owner=DEFAULT_OWNER, codes=None)
A DHCP server reached through one backend, described by a URI.
Build one with dhcpctl.connect(uri), which picks the backend by the URI's
scheme. Constructing a backend validates its URI and does no I/O.
A backend overrides add and remove, and keep, ensure_condition,
condition_recipe and remove_condition_member where it supports them.
complete and extras are the same for every backend.
Source code in src/dhcpctl/_server.py
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
redacted_uri
property
uri with any password replaced by ***: what messages and logs show.
add(reservation)
Arm the server for this reservation.
Source code in src/dhcpctl/_server.py
145 146 | |
complete(reservation)
Disarm, honouring reservation.completion.
remove unless the entry is kept; when it is, the client first leaves
every condition (a kept entry still matching one would keep being served
the installer) and then keep replaces its boot options.
Source code in src/dhcpctl/_server.py
189 190 191 192 193 194 195 196 197 198 199 200 | |
condition_recipe(reservation, condition)
What a privileged operator applies for condition, as text, not run.
The same text ConditionMissingError carries.
Source code in src/dhcpctl/_server.py
175 176 177 178 179 180 | |
ensure_condition(reservation, condition)
The name of the construct serving condition, creating it if needed.
The ladder every backend walks: (1) it exists -- use it; (2) it is
absent and can be created -- create it; (3) it cannot be created --
ConditionMissingError carrying condition_recipe(); (4) this backend
has no such concept -- ConditionUnsupportedError, the base behaviour.
Source code in src/dhcpctl/_server.py
162 163 164 165 166 167 168 169 170 171 172 173 | |
extras(reservation, phase='add')
Backend-native fragments to apply with the reservation, verbatim.
reservation.extras[phase]. A subclass may override this to decide
fragments per client (reservation.context is the caller's), calling
super() to keep these. What a fragment is belongs to the backend.
Source code in src/dhcpctl/_server.py
245 246 247 248 249 250 251 252 253 254 | |
keep(reservation)
Keep the entry with reservation.completion's options instead.
The base refuses, so a backend that cannot keep an entry is reported rather than silently ignoring the request.
Source code in src/dhcpctl/_server.py
151 152 153 154 155 156 157 158 159 160 | |
remove(reservation)
Remove this reservation; removing one that is not there succeeds.
Source code in src/dhcpctl/_server.py
148 149 | |
remove_condition_member(reservation, condition)
Detach this client from condition, leaving the construct for others.
Source code in src/dhcpctl/_server.py
182 183 184 185 | |
render(reservation, action='add')
What action would send this server for reservation, contacting
nothing: the backend's own text, one step after another.
action is add, remove, keep or complete. A step that needs an
answer from the server is given the answer "there is nothing yet", so
the text is what a server holding nothing for this client would get.
:raises NotImplementedError: the backend cannot record what it sends.
:raises ValueError: an unknown action.
Source code in src/dhcpctl/_server.py
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | |
secret(key)
The secret named key: the caller's if it gives one, else the URI's.
The caller's secrets override the URI's, so one URI serves several
logins; a caller's None leaves the URI's in place, and "" is an empty
secret. They are asked when the value is needed, not at construction.
None when neither has it.
Source code in src/dhcpctl/_server.py
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | |
Reservations
dhcpctl.Reservation(target, scope=Scope(), lease=_no_address(), conditions=dict(), completion=Completion(), extras=dict(), context=None)
dataclass
One client's reservation, as a backend applies it.
lease is what the server answers the client with: a pydhcp DHCPLease
holding its address (0.0.0.0 for a MAC-only reservation), the fully
merged options, and the BOOTP header fields siaddr (the next server),
sname and file (the boot file). conditions are the named conditions
that apply to it; completion what happens when it completes; extras
per-phase backend-native fragments the backend emits verbatim; context
is the caller's, carried untouched for a backend subclass that decides
fragments per client.
ip_text
property
The address as text, or "" when there is none (0.0.0.0).
from_mapping(data, *, codes=None)
classmethod
A reservation from a mapping in the shape to_mapping writes.
Only target.name is required. lease is read by
DHCPLease.from_mapping with the codes codemap (pydhcp's), its
address optional; conditions and completion as parse_conditions
and parse_completion read them.
:raises ConfigError: an unknown key, or a field that cannot be read.
Source code in src/dhcpctl/_model.py
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |
from_text(text, format, *, codes=None)
classmethod
pydhcp.packet.structured.loads then from_mapping, with their errors.
Source code in src/dhcpctl/_model.py
204 205 206 207 208 209 210 211 212 213 | |
require_ip(what)
The address, or ConfigError saying what needs one.
Source code in src/dhcpctl/_model.py
92 93 94 95 96 97 98 | |
to_mapping()
The reservation as plain data, the form from_mapping reads.
target, scope, lease (DHCPLease.to_mapping's form), and
conditions, completion and extras when there are any. An unset
field is left out. context is the caller's and is not written.
Source code in src/dhcpctl/_model.py
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | |
to_text(format)
to_mapping as a "json", "yaml", "toml" or "ini" document,
written by pydhcp.packet.structured.dumps, with its errors.
Source code in src/dhcpctl/_model.py
199 200 201 202 | |
dhcpctl.Target(name, mac=None, hostname='')
dataclass
The client a reservation is for.
name labels it on the server (the dhcpd host name, the Windows reservation
name, the dnsmasq tag) and in messages. Its address is the lease's.
require_mac(what)
The MAC, or MissingMACError saying what is identified by one.
Source code in src/dhcpctl/_model.py
37 38 39 40 41 42 43 | |
dhcpctl.Scope(name='', network=None, subnet_id=None, scope_id=None)
dataclass
Where the reservation lives on the server.
network identifies a Windows scope and a Kea subnet; subnet_id and
scope_id override what is derived from it. name labels it in messages
and keys per-scope caches.
What is served
dhcpctl.DHCPLease, dhcpctl.DHCPOptions and dhcpctl.DHCPOptionCode are
pydhcp's, re-exported: see pydhcp's reference.
dhcpctl.OptionCodes
Bases: OptionCodeEnum
A codemap: pydhcp's option codes and names, and what a config declares.
Build one with option_codes; PYDHCP_CODES is pydhcp's names alone. As
a pydhcp OptionCodeEnum, each codemap has its own codecs and its own
unnamed members, so a declared type never reaches pydhcp's registry.
Names are read by pydhcp's resolve.
field(name)
classmethod
The lease header field name names (next-server is siaddr), or None.
Source code in src/dhcpctl/_options.py
104 105 106 107 108 109 | |
knows(name)
classmethod
Does name name an option or a header field of this codemap?
Source code in src/dhcpctl/_options.py
111 112 113 114 115 116 117 118 119 120 | |
lease(declared, where='')
classmethod
What a config mapping serves: its options, and the header fields
(next-server, file, ...) among them, as a lease with no address.
A DHCPLease is returned as it is.
:raises ConfigError: an unknown name, or a value its option or field refuses.
Source code in src/dhcpctl/_options.py
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
of(codes)
staticmethod
codes as a codemap: itself, a declarations mapping, or pydhcp's.
Source code in src/dhcpctl/_options.py
194 195 196 197 198 199 | |
options(declared, where='')
classmethod
A bag with this codemap from a mapping of option names to values.
pydhcp's DHCPOptions.from_mapping reads it: a value is what the codec
takes, an enum its member name, octets {"hex": ...} or, for an
opaque option, hex text. A DHCPOptions is copied, keeping its own
codemap.
:raises ConfigError: an unknown name, or a value its option's codec
refuses (pydhcp's message, and (on 'where')).
Source code in src/dhcpctl/_options.py
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | |
over(base)
classmethod
This codemap's declarations, every layer of them, made over base.
Source code in src/dhcpctl/_options.py
186 187 188 189 190 191 192 | |
read_lease(data, where='')
classmethod
A lease from what DHCPLease.to_mapping writes, its address optional.
:raises ConfigError: what pydhcp refuses, with its message.
Source code in src/dhcpctl/_options.py
175 176 177 178 179 180 181 182 183 184 | |
dhcpctl.option_codes(declared=None, *, base=None)
A codemap: base's members (pydhcp's by default) and declared's.
declared maps a name to {code: <n>, type: <pydhcp codec name>} (an
option of the caller's own; type applies to this codemap only), or to a
code, another name or a header field (an alias). Every declaration is
checked here.
:raises ConfigError: an unknown key or type, a code pydhcp refuses, a name
that cannot be one (a header field, option-<n>, a number, not a
Python identifier), a pydhcp name given another code, an alias of
nothing, or a loop.
Source code in src/dhcpctl/_options.py
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | |
dhcpctl.plain_items(options)
(code, value) with each value as plain data (codec.to_json()).
A helper for a backend that writes text: a list option a list of strings
(["10.0.0.1"]), a number an int, an opaque option hex text.
Source code in src/dhcpctl/_options.py
332 333 334 335 336 337 338 | |
Conditions and completion
dhcpctl.Condition(name, match, lease, codes=None)
One named condition: a test, and what to serve when it matches.
name is the construct's name on every backend. match maps a key from
MATCH_KEYS to the value the client must send; several keys mean all of
them. lease is what is served: a DHCPLease with no address (its
options, siaddr, sname, file), or a config mapping read with the
codes codemap (codes.lease).
Source code in src/dhcpctl/_conditions.py
48 49 50 51 52 53 54 55 56 57 | |
option_codes
property
The match as {option code: value}, for a backend that tests by code.
to_mapping()
{match: ..., lease: ...}, lease as DHCPLease.to_mapping writes
it: the entry parse_conditions reads back.
Source code in src/dhcpctl/_conditions.py
73 74 75 76 | |
dhcpctl.parse_conditions(declared, where='', codes=None)
Build conditions from a {name: {match: ..., options: ...}} mapping, or
{name: {match: ..., lease: ...}} as Condition.to_mapping writes it.
where names the declaring object in error messages; option names are read
with the codes codemap (pydhcp's), a config's options by codes.lease.
:raises ConfigError: the mapping cannot be used as given.
Source code in src/dhcpctl/_conditions.py
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | |
dhcpctl.Completion(keep=False, lease=None, codes=None)
What to do with a reservation when provisioning finishes.
keep=False (the default) removes it. keep=True keeps it, with its boot
fields and options replaced by lease's (none: the client falls through to
its disk). lease is a DHCPLease, or a config mapping read with the
codes codemap (codes.lease).
Source code in src/dhcpctl/_completion.py
41 42 43 44 45 46 47 48 | |
__bool__()
True when completion does anything other than remove the entry.
Source code in src/dhcpctl/_completion.py
60 61 62 | |
apply_to(lease)
The lease a kept entry should end up with.
BOOT_OPTIONS are dropped and this completion's options put in their
place, and its siaddr, sname and file replace the lease's --
replaced, not merged. The address and every other option stay.
Source code in src/dhcpctl/_completion.py
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | |
to_mapping()
{keep: false}, or {keep: true, lease: {...}} with lease as
DHCPLease.to_mapping writes it.
Source code in src/dhcpctl/_completion.py
64 65 66 67 68 69 | |
dhcpctl.parse_completion(declared, where='', codes=None)
Build a Completion from {keep: ..., options: ...}, or from
{keep: ..., lease: ...} as Completion.to_mapping writes it.
None stays None (nothing declared). where names the declaring object
in error messages; option names are read with the codes codemap
(pydhcp's), a config's options by codes.lease.
:raises ConfigError: the mapping cannot be used as given.
Source code in src/dhcpctl/_completion.py
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
Errors
dhcpctl.exceptions
The errors dhcpctl raises. Every one derives from DHCPCtlError.
BackendUnavailableError
Bases: DHCPCtlError, ImportError
A backend needs a library that is not installed; the message names the extra.
ConditionMissingError(message, recipe='')
Bases: DHCPCtlError
The condition's construct is absent and this backend cannot create it.
recipe is the text a privileged operator applies once, in the server's own
syntax; it is also appended to the message.
Source code in src/dhcpctl/exceptions.py
52 53 54 | |
ConditionUnsupportedError
Bases: DHCPCtlError
This backend cannot express a condition at all, so nothing is armed with it.
ConfigError
Bases: DHCPCtlError, ValueError
A connection URI, a setting or a value cannot be used as given.
DHCPCtlError
Bases: Exception
Base class of every error dhcpctl raises.
MissingMACError
Bases: DHCPCtlError, LookupError
The target has no MAC address, and the server identifies a reservation by one.
ServerError
Bases: DHCPCtlError
The server, or the program that talks to it, refused or failed.
UnknownBackendError
Bases: ConfigError
No backend is registered for a URI's scheme.