From: Francis Giraldeau <[email protected]> The Dhcpd lens is able to parse the configuration of ISC DHCP server, according to the manual.
* Most statements are supported * Sections can be nested * Options values are handled as list * Automatic quoting and unquoting of values There is basic support for dhcp-eval statements. --- lenses/dhcpd.aug | 399 +++++++++++++++++++++++++++++++++++++++++++ lenses/tests/test_dhcpd.aug | 372 ++++++++++++++++++++++++++++++++++++++++ tests/Makefile.am | 1 + 3 files changed, 772 insertions(+), 0 deletions(-) create mode 100644 lenses/dhcpd.aug create mode 100644 lenses/tests/test_dhcpd.aug diff --git a/lenses/dhcpd.aug b/lenses/dhcpd.aug new file mode 100644 index 0000000..75d020a --- /dev/null +++ b/lenses/dhcpd.aug @@ -0,0 +1,399 @@ +(* +Module: Dhcpd + BIND dhcp 3 server configuration module for Augeas + +Author: Francis Giraldeau <[email protected]> + +About: Reference + Reference: manual of dhcpd.conf and dhcp-eval + Follow dhclient module for tree structure + +About: License + This file is licensed under the GPL. + +About: Lens Usage + Sample usage of this lens in augtool + + Directive without argument. + Set this dhcpd server authoritative on the domain. + > clear /files/etc/dhcp3/dhcpd.conf/authoritative + + Directives with integer or string argument. + Set max-lease-time to one hour: + > set /files/etc/dhcp3/dhcpd.conf/max-lease-time 3600 + + Options are declared as a list, even for single values. + Set the domain of the network: + > set /files/etc/dhcp3/dhcpd.conf/option/domain-name/1 example.org + Set two name server: + > set /files/etc/dhcp3/dhcpd.conf/option/domain-name-servers/1 foo.example.org + > set /files/etc/dhcp3/dhcpd.conf/option/domain-name-servers/2 bar.example.org + + Create the subnet 172.16.0.1 with 10 addresses: + > clear /files/etc/dhcp3/dhcpd.conf/subnet[last() + 1] + > set /files/etc/dhcp3/dhcpd.conf/subnet[last()]/network 172.16.0.0 + > set /files/etc/dhcp3/dhcpd.conf/subnet[last()]/netmask 255.255.255.0 + > set /files/etc/dhcp3/dhcpd.conf/subnet[last()]/range/from 172.16.0.10 + > set /files/etc/dhcp3/dhcpd.conf/subnet[last()]/range/to 172.16.0.20 + + Create a new group "foo" with one static host. Nodes type and address are ordered. + > ins group after /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/*[last()] + > set /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/group[last()]/host foo + > set /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/group[host='foo']/host/hardware/type "ethernet" + > set /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/group[host='foo']/host/hardware/address "00:00:00:aa:bb:cc" + > set /files/etc/dhcp3/dhcpd.conf/subnet[network='172.16.0.0']/group[host='foo']/host/fixed-address 172.16.0.100 + +About: Configuration files + This lens applies to /etc/dhcpd3/dhcpd.conf. See <filter>. +*) + +module Dhcpd = + +autoload xfm + +(************************************************************************ + * USEFUL PRIMITIVES + *************************************************************************) +let dels (s:string) = del s s +let eol = Util.eol +let comment = Util.comment +let empty = Util.empty +let indent = Util.indent +let eos = comment? + +(* Define separators *) +let sep_spc = del /[ \t]+/ " " +let sep_osp = del /[ \t]*/ "" +let sep_scl = del /[ \t]*;([ \t]*\n)*/ ";\n" +let sep_obr = del /[ \t]*\{([ \t]*\n)*/ " {\n" +let sep_cbr = del /[ \t]*\}([ \t]*\n)*/ "}\n" +let sep_com = del /[ \t\n]*,[ \t\n]*/ ", " +let sep_slh = del "\/" "/" +let sep_col = del ":" ":" +let sep_eq = del /[ \t]*=[ \t]*/ "=" +let scl = del ";" ";" + +(* Define basic types *) +let word = /[A-Za-z0-9_.-]+(\[[0-9]+\])?/ +let ip = Rx.ipv4 + +(* Define fields *) + +(* borrowed from sysconfig.aug *) + (* Chars allowed in a bare string *) + let bchar = /[^ \t\n\"'\\{\}#,\(\)]|\\\\./ + let qchar = /["']/ (* " *) + + (* We split the handling of right hand sides into a few cases: + * bare - strings that contain no spaces, optionally enclosed in + * single or double quotes + * dquot - strings that contain at least one space or apostrophe, + * which must be enclosed in double quotes + * squot - strings that contain an unescaped double quote + *) + let bare = del qchar? "" . store (bchar+) . del qchar? "" + let dquot = + del qchar "\"" . store (bchar* . /[ \t']/ . bchar*)+ . del qchar "\"" + let squot = + dels "'" . store ((bchar|/[ \t]/)* . "\"" . (bchar|/[ \t]/)*)+ . dels "'" + +let sto_to_spc = store /[^\\#,;\{\}" \t\n]+|"[^\\#"\n]+"/ +let sto_to_scl = store /[^ \t;][^;\n=]+[^ \t;]|[^ \t;=]+/ + +let sto_number = store /[0-9][0-9]*/ + +(************************************************************************ + * NO ARG STATEMENTS + *************************************************************************) + +let stmt_noarg_re = "authoritative" + | "primary" + | "secondary" + +let stmt_noarg = [ indent + . key stmt_noarg_re + . sep_scl + . eos ] + +(************************************************************************ + * INT ARG STATEMENTS + *************************************************************************) + +let stmt_integer_re = "default-lease-time" + | "max-lease-time" + | "min-lease-time" + | /lease[ ]+limit/ + | "port" + | /peer[ ]+port/ + | "max-response-delay" + | "max-unacked-updates" + | "mclt" + | "split" + | /load[ ]+balance[ ]+max[ ]+seconds/ + | "max-lease-misbalance" + | "max-lease-ownership" + | "min-balance" + | "max-balance" + | "adaptive-lease-time-threshold" + | "dynamic-bootp-lease-length" + | "local-port" + | "min-sec" + | "omapi-port" + | "ping-timeout" + | "remote-port" + +let stmt_integer = [ indent + . key stmt_integer_re + . sep_spc + . sto_number + . sep_scl + . eos ] + +(************************************************************************ + * STRING ARG STATEMENTS + *************************************************************************) + +let stmt_string_re = "ddns-update-style" + | "ddns-updates" + | "ddns-hostname" + | "ddns-domainname" + | "ddns-rev-domainname" + | "log-facility" + | "filename" + | "server-name" + | "fixed-address" + | /failover[ ]+peer/ + | "use-host-decl-names" + | "next-server" + | "address" + | /peer[ ]+address/ + | "type" + | "file" + | "algorithm" + | "secret" + | "key" + | "include" + | "hba" + | "boot-unknown-clients" + | "db-time-format" + | "do-forward-updates" + | "dynamic-bootp-lease-cutoff" + | "get-lease-hostnames" + | "infinite-is-reserved" + | "lease-file-name" + | "local-address" + | "one-lease-per-client" + | "pid-file-name" + | "ping-check" + | "server-identifier" + | "site-option-space" + | "stash-agent-options" + | "update-conflict-detection" + | "update-optimization" + | "update-static-leases" + | "use-host-decl-names" + | "use-lease-addr-for-default-route" + | "vendor-option-space" + +let stmt_string_tpl (l:lens) = [ indent + . key stmt_string_re + . sep_spc + . l + . sep_scl + . eos ] + +let stmt_string = stmt_string_tpl bare |stmt_string_tpl squot | stmt_string_tpl dquot + +(************************************************************************ + * RANGE STATEMENTS + *************************************************************************) + +let stmt_range = [ indent + . key "range" + . sep_spc + . [ label "flag" . store /dynamic-bootp/ . sep_spc ]? + . [ label "from" . store ip . sep_spc ]? + . [ label "to" . store ip ] + . sep_scl + . eos ] + +(************************************************************************ + * HARDWARE STATEMENTS + *************************************************************************) + +let stmt_hardware = [ indent + . key "hardware" + . sep_spc + . [ label "type" . store /ethernet|tokenring/ ] + . sep_spc + . [ label "address" . store /[a-fA-F0-9:-]+/ ] + . sep_scl + . eos ] + +(************************************************************************ + * OPTION STATEMENTS + *************************************************************************) +(* The general case is considering options as a list *) + +let stmt_option_code = [ label "label" . store word . sep_spc ] + . [ key "code" . sep_spc . store word ] + . sep_eq + . [ label "type" . store word ] + + +let stmt_option_list = counter "id" + . ([ seq "id" . bare ] | [ seq "id" . dquot ] | [ seq "id" . squot ]) + . ( sep_com . ([ seq "id" . bare ] | [ seq "id" . dquot ] | [ seq "id" . squot ]))* + +let stmt_option_basic = [ key word . sep_spc . stmt_option_list ] +let stmt_option_extra = [ key word . sep_spc . store /true|false/ . sep_spc . stmt_option_list ] + +let stmt_option_body = stmt_option_basic | stmt_option_extra + +let stmt_option1 = [ indent + . key "option" + . sep_spc + . stmt_option_body + . sep_scl + . eos ] + +let stmt_option2 = [ indent + . dels "option" . label "rfc-code" + . sep_spc + . stmt_option_code + . sep_scl + . eos ] + +let stmt_option = stmt_option1 | stmt_option2 + +(************************************************************************ + * SUBCLASS STATEMENTS + *************************************************************************) +(* this statement is not well documented in the manual dhcpd.conf + we support basic use case *) + +let stmt_subclass = [ indent . key "subclass" . sep_spc . + ([ label "name" . dquot ]| + [ label "name" . squot ]| + [ label "name" . bare ]) . sep_spc . + [ label "value" . bare ] . sep_scl . eos ] + +(************************************************************************ + * ALLOW/DENY STATEMENTS + *************************************************************************) +(* We have to use special key for allow/deny members of + to avoid ambiguity in the put direction *) + +let allow_deny_re = "unknown-clients" + | /dynamic[ ]+bootp[ ]+clients/ + | /authenticated[ ]+clients/ + | /unauthenticated[ ]+clients/ + +let stmt_secu_re = "allow" + | "deny" + +let del_allow = del /allow[ ]+members[ ]+of/ "allow members of" +let del_deny = del /deny[ \t]+members[ \t]+of/ "deny members of" + +let stmt_secu_tpl (l:lens) (s:string) = + [ indent . l . sep_spc . label s . bare . sep_scl . eos ] | + [ indent . l . sep_spc . label s . squot . sep_scl . eos ] | + [ indent . l . sep_spc . label s . dquot . sep_scl . eos ] + +let stmt_secu = [ indent . key stmt_secu_re . sep_spc . + store allow_deny_re . sep_scl . eos ] | + stmt_secu_tpl del_allow "allow-members-of" | + stmt_secu_tpl del_deny "deny-members-of" + +(************************************************************************ + * MATCH STATEMENTS + *************************************************************************) + +let sto_fct = store (word . /[ \t]*\([^)]*\)/) +let sto_option = store (/option[ ]+/ . word) +let sto_com = /[^ \t\n,\(\)][^,\(\)]*[^ \t\n,\(\)]|[^ \t\n,\(\)]+/ | word . /[ \t]*\([^)]*\)/ +let fct_re = "substring" | "binary-to-ascii" + +let fct_args = [ label "args" . dels "(" . counter "args" . sep_osp . + ([ seq "args" . store sto_com ] . [ seq "args" . sep_com . store sto_com ]+) . + sep_osp . dels ")" ] + +let stmt_match_if = [ dels "if" . sep_spc . store fct_re . sep_osp . label "function" . fct_args ] . + sep_eq . ([ label "value" . bare ]|[ label "value" . squot ]|[ label "value" . dquot ]) + +let stmt_match_pfv = [ label "function" . store "pick-first-value" . sep_spc . + dels "(" . sep_osp . + [ counter "opt" . label "args" . + [ seq "opt" . store sto_com ] . + [ sep_com . seq "opt" . store sto_com ]+ ] . + dels ")" ] + +let stmt_match_tpl (l:lens) = [ indent . key "match" . sep_spc . l . sep_scl . eos ] + +let stmt_match = stmt_match_tpl (stmt_match_if | stmt_match_pfv ) + +(************************************************************************ + * BLOCK STATEMENTS + *************************************************************************) +(* Blocks doesn't support comments at the end of the closing bracket *) + +let stmt_entry = stmt_secu + | stmt_option + | stmt_hardware + | stmt_range + | stmt_string + | stmt_integer + | stmt_noarg + | stmt_match + | stmt_subclass + | empty + | comment + +let stmt_block_noarg_re = "pool" + | "group" + | "allow-update" + +let stmt_block_noarg (body:lens) + = [ indent + . key stmt_block_noarg_re + . sep_obr + . body* + . sep_cbr ] + +let stmt_block_arg_re = "host" + | "class" + | "shared-network" + | /failover[ ]+peer/ + | "zone" + | "key" + +let stmt_block_arg (body:lens) + = [ indent + . key stmt_block_arg_re + . sep_spc + . sto_to_spc + . sep_obr + . body* + . sep_cbr ] + +let stmt_block_subnet (body:lens) + = [ indent + . key "subnet" + . sep_spc + . [ label "network" . store ip ] + . sep_spc + . [ key "netmask" . sep_spc . store ip ] + . sep_obr + . body* + . sep_cbr ] + +let all_block (body:lens) = + let lns1 = stmt_block_subnet body in + let lns2 = stmt_block_arg body in + let lns3 = stmt_block_noarg body in + (lns1 | lns2 | lns3 | stmt_entry) + +let rec lns_staging = stmt_entry|all_block lns_staging +let lns = (lns_staging)* + +let xfm = transform lns (incl "/etc/dhcp3/dhcpd.conf") diff --git a/lenses/tests/test_dhcpd.aug b/lenses/tests/test_dhcpd.aug new file mode 100644 index 0000000..01f7e0c --- /dev/null +++ b/lenses/tests/test_dhcpd.aug @@ -0,0 +1,372 @@ +module Test_dhcpd = + +let lns = Dhcpd.lns + +let conf = "# +# Sample configuration file for ISC dhcpd for Debian +# +# Attention: If /etc/ltsp/dhcpd.conf exists, that will be used as +# configuration file instead of this file. +# +# $Id: dhcpd.conf,v 1.1.1.1 2002/05/21 00:07:44 peloy Exp $ +# + +# The ddns-updates-style parameter controls whether or not the server will +# attempt to do a DNS update when a lease is confirmed. We default to the +# behavior of the version 2 packages ('none', since DHCP v2 didn't +# have support for DDNS.) +ddns-update-style none; + +# option definitions common to all supported networks... +option domain-name \"example.org\"; +option domain-name-servers ns1.example.org, ns2.example.org; + +default-lease-time 600; +max-lease-time 7200; + +# If this DHCP server is the official DHCP server for the local +# network, the authoritative directive should be uncommented. +authoritative; + +# Use this to send dhcp log messages to a different log file (you also +# have to hack syslog.conf to complete the redirection). +log-facility local7; + +# No service will be given on this subnet, but declaring it helps the +# DHCP server to understand the network topology. + +subnet 10.152.187.0 netmask 255.255.255.0 { +} + +# This is a very basic subnet declaration. + +subnet 10.254.239.0 netmask 255.255.255.224 { + range 10.254.239.10 10.254.239.20; + option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org; +} + +# This declaration allows BOOTP clients to get dynamic addresses, +# which we don't really recommend. + +subnet 10.254.239.32 netmask 255.255.255.224 { + range dynamic-bootp 10.254.239.40 10.254.239.60; + option broadcast-address 10.254.239.31; + option routers rtr-239-32-1.example.org; +} + +# A slightly different configuration for an internal subnet. +subnet 10.5.5.0 netmask 255.255.255.224 { + range 10.5.5.26 10.5.5.30; + option domain-name-servers ns1.internal.example.org; + option domain-name \"internal.example.org\"; + option routers 10.5.5.1; + option broadcast-address 10.5.5.31; + default-lease-time 600; + max-lease-time 7200; +} + +# Hosts which require special configuration options can be listed in +# host statements. If no address is specified, the address will be +# allocated dynamically (if possible), but the host-specific information +# will still come from the host declaration. + +host passacaglia { + hardware ethernet 0:0:c0:5d:bd:95; + filename \"vmunix.passacaglia\"; + server-name \"toccata.fugue.com\"; +} + +# Fixed IP addresses can also be specified for hosts. These addresses +# should not also be listed as being available for dynamic assignment. +# Hosts for which fixed IP addresses have been specified can boot using +# BOOTP or DHCP. Hosts for which no fixed address is specified can only +# be booted with DHCP, unless there is an address range on the subnet +# to which a BOOTP client is connected which has the dynamic-bootp flag +# set. +host fantasia { + hardware ethernet 08:00:07:26:c0:a5; + fixed-address fantasia.fugue.com; +} + +# You can declare a class of clients and then do address allocation +# based on that. The example below shows a case where all clients +# in a certain class get addresses on the 10.17.224/24 subnet, and all +# other clients get addresses on the 10.0.29/24 subnet. + +#class \"foo\" { +# match if substring (option vendor-class-identifier, 0, 4) = \"SUNW\"; +#} + +shared-network 224-29 { + subnet 10.17.224.0 netmask 255.255.255.0 { + option routers rtr-224.example.org; + } + subnet 10.0.29.0 netmask 255.255.255.0 { + option routers rtr-29.example.org; + } + pool { + allow members of \"foo\"; + range 10.17.224.10 10.17.224.250; + } + pool { + deny members of \"foo\"; + range 10.0.29.10 10.0.29.230; + } +} +" + +test lns get "authoritative;" = { "authoritative" } +test lns get "ddns-update-style none;" = { "ddns-update-style" = "none" } +test lns get "option domain-name \"example.org\";" = + { "option" + { "domain-name" + { "1" = "example.org" } + } + } + +test lns get "option domain-name-servers ns1.example.org, ns2.example.org;" = + { "option" + { "domain-name-servers" + { "1" = "ns1.example.org" } + { "2" = "ns2.example.org" } + } + } + +test lns get "default-lease-time 600;" = { "default-lease-time" = "600" } +test lns get "range 10.254.239.60;" = +{ "range" + { "to" = "10.254.239.60" } + } + +test lns get "range dynamic-bootp 10.254.239.60;" = + { "range" + { "flag" = "dynamic-bootp" } + { "to" = "10.254.239.60" } + } + +test lns get "range dynamic-bootp 10.254.239.40 10.254.239.60;" = + { "range" + { "flag" = "dynamic-bootp" } + { "from" = "10.254.239.40" } + { "to" = "10.254.239.60" } + } + +test lns get "subnet 10.152.187.0 netmask 255.255.255.0 {}\n" = + { "subnet" + { "network" = "10.152.187.0" } + { "netmask" = "255.255.255.0" } + } + +test lns get " pool { + pool { + + } +} +" = + { "pool" + { "pool" } + } + +test lns get "group { host some-host {hardware ethernet 00:00:aa:bb:cc:dd; +fixed-address 10.1.1.1;}}" = + { "group" + { "host" = "some-host" + { "hardware" + { "type" = "ethernet" } + { "address" = "00:00:aa:bb:cc:dd" } + } + { "fixed-address" = "10.1.1.1" } + } + } + +test Dhcpd.stmt_secu get "allow members of \"foo\";" = { "allow-members-of" = "foo" } +test Dhcpd.stmt_option get "option voip-boot-server code 66 = string;" = + { "rfc-code" + { "label" = "voip-boot-server" } + { "code" = "66" } + { "type" = "string" } + } + +test Dhcpd.lns get "authoritative; +log-facility local7; +ddns-update-style none; +default-lease-time 21600; +max-lease-time 43200; + +# Additional options for VOIP +option voip-boot-server code 66 = string; +option voip-vlan-id code 128 = string; +" = + { "authoritative" } + { "log-facility" = "local7" } + { "ddns-update-style" = "none" } + { "default-lease-time" = "21600" } + { "max-lease-time" = "43200" + { "#comment" = "Additional options for VOIP" } + } + { "rfc-code" + { "label" = "voip-boot-server" } + { "code" = "66" } + { "type" = "string" } + } + { "rfc-code" + { "label" = "voip-vlan-id" } + { "code" = "128" } + { "type" = "string" } + } + + +test Dhcpd.lns get " +option domain-name-servers 10.1.1.1, 10.11.2.1, 10.1.3.1; +next-server 10.1.1.1; + +failover peer \"redondance01\" { + primary; + address 10.1.1.1; + port 647; + peer address 10.1.1.1; + peer port 647; + max-response-delay 20; + max-unacked-updates 10; + mclt 3600; #comment. + split 128; #comment. + load balance max seconds 3; + } +" = + { } + { "option" + { "domain-name-servers" + { "1" = "10.1.1.1" } + { "2" = "10.11.2.1" } + { "3" = "10.1.3.1" } + } + } + { "next-server" = "10.1.1.1" } + { "failover peer" = "\"redondance01\"" + { "primary" } + { "address" = "10.1.1.1" } + { "port" = "647" } + { "peer address" = "10.1.1.1" } + { "peer port" = "647" } + { "max-response-delay" = "20" } + { "max-unacked-updates" = "10" } + { "mclt" = "3600" + { "#comment" = "comment." } + } + { "split" = "128" + { "#comment" = "comment." } + } + { "load balance max seconds" = "3" } + } + +test Dhcpd.lns get " +option CallManager code 150 = ip-address; +option slp-directory-agent true 10.1.1.1, 10.2.2.2; +option slp-service-scope true \"SLP-GLOBAL\"; +option nds-context \"EXAMPLE\"; +option nds-tree-name \"EXAMPLE\"; +" = + { } + { "rfc-code" + { "label" = "CallManager" } + { "code" = "150" } + { "type" = "ip-address" } + } + { "option" + { "slp-directory-agent" = "true" + { "1" = "10.1.1.1" } + { "2" = "10.2.2.2" } + } + } + { "option" + { "slp-service-scope" = "true" + { "1" = "SLP-GLOBAL" } + } + } + { "option" + { "nds-context" + { "1" = "EXAMPLE" } + } + } + { "option" + { "nds-tree-name" + { "1" = "EXAMPLE" } + } + } + + +test Dhcpd.lns get "option voip-vlan-id \"VLAN=1234;\";" = + { "option" + { "voip-vlan-id" + { "1" = "VLAN=1234;" } + } + } + +test Dhcpd.lns get "option domain-name \"x.example.com y.example.com z.example.com\";" = + { "option" + { "domain-name" + { "1" = "x.example.com y.example.com z.example.com" } + } + } + +test Dhcpd.lns get "include \"/etc/dhcpd.master\";" = + { "include" = "/etc/dhcpd.master" } + +test Dhcpd.fct_args get "(option dhcp-client-identifier, 1, 3)" = + { "args" + { "1" = "option dhcp-client-identifier" } + { "2" = "1" } + { "3" = "3" } + } + +test Dhcpd.stmt_match get "match if substring (option dhcp-client-identifier, 1, 3) = \"RAS\";" = + { "match" + { "function" = "substring" + { "args" + { "1" = "option dhcp-client-identifier" } + { "2" = "1" } + { "3" = "3" } + } + } + { "value" = "RAS" } + } + +test Dhcpd.lns get "match pick-first-value (option dhcp-client-identifier, hardware);" = + { "match" + { "function" = "pick-first-value" + { "args" + { "1" = "option dhcp-client-identifier" } + { "2" = "hardware" } + } + } + } + +test Dhcpd.fct_args get "(16, 32, \"\", substring(hardware, 0, 4))" = + { "args" + { "1" = "16" } + { "2" = "32" } + { "3" = "\"\"" } + { "4" = "substring(hardware, 0, 4)" } + } + +test Dhcpd.stmt_match get "match if binary-to-ascii(16, 32, \"\", substring(hardware, 0, 4)) = \"1525400\";" = + { "match" + { "function" = "binary-to-ascii" + { "args" + { "1" = "16" } + { "2" = "32" } + { "3" = "\"\"" } + { "4" = "substring(hardware, 0, 4)" } + } + } + { "value" = "1525400" } + } + +test Dhcpd.lns get "subclass \"allocation-class-1\" 1:8:0:2b:4c:39:ad;" = + { "subclass" + { "name" = "allocation-class-1" } + { "value" = "1:8:0:2b:4c:39:ad" } + } + +(* overall test *) +test Dhcpd.lns put conf after rm "/x" = conf diff --git a/tests/Makefile.am b/tests/Makefile.am index db4172f..fdb0d9f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -27,6 +27,7 @@ lens_tests = \ lens-debctrl.sh \ lens-device_map.sh \ lens-dhclient.sh \ + lens-dhcpd.sh \ lens-dnsmasq.sh \ lens-dpkg.sh \ lens-dput.sh \ -- 1.7.1 _______________________________________________ augeas-devel mailing list [email protected] https://www.redhat.com/mailman/listinfo/augeas-devel
