author | Tero Marttila <terom@fixme.fi> |
Thu, 02 Apr 2009 20:19:18 +0300 | |
changeset 1 | 2223ade4f259 |
parent 0 | conf_dhcp.py@257003279747 |
child 2 | e66102ab7048 |
permissions | -rw-r--r-- |
0 | 1 |
""" |
2 |
Configuration file output for the ISC DHCP server |
|
3 |
""" |
|
4 |
||
5 |
import conf |
|
6 |
||
7 |
import itertools |
|
8 |
||
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
9 |
class Section (conf.ConfObject) : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
10 |
""" |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
11 |
A section holds a list of params and a list of decls |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
12 |
""" |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
13 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
14 |
def __init__ (self, params=None, decls=None) : |
0 | 15 |
""" |
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
16 |
If params/decls are given, those are the used as the initial contents of this section |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
17 |
""" |
0 | 18 |
|
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
19 |
self.params = params or [] |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
20 |
self.decls = decls or [] |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
21 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
22 |
def add_param (self, param) : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
23 |
""" |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
24 |
Add the given Parameter to the end of this section's params |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
25 |
""" |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
26 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
27 |
self.params.append(param) |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
28 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
29 |
def add_params (self, params) : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
30 |
for param in params : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
31 |
self.add_param(param) |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
32 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
33 |
def add_decl (self, decl) : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
34 |
""" |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
35 |
Add the given Declaration to the end of this section's decls |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
36 |
""" |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
37 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
38 |
self.decls.append(decl) |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
39 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
40 |
def add_decls (self, decls) : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
41 |
for decl in decls : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
42 |
self.add_decl(decl) |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
43 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
44 |
def fmt_lines (self) : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
45 |
""" |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
46 |
Format all of our params and decls, in that order |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
47 |
""" |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
48 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
49 |
# then output each content line |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
50 |
for stmt in itertools.chain(self.params, self.decls) : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
51 |
# skip Nones |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
52 |
if stmt is None : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
53 |
continue |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
54 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
55 |
for line in stmt.fmt_lines() : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
56 |
yield line |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
57 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
58 |
class ConfFile (Section, conf.File) : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
59 |
DEFAULT_NAME = "dhcpd.conf" |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
60 |
DEFAULT_PATH = "/etc/dhcp3/dhcpd.conf" |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
61 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
62 |
def __init__ (self, name=DEFAULT_NAME, path=DEFAULT_PATH, params=None, decls=None) : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
63 |
""" |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
64 |
Initialize the dhcpd config file, but don't open it yet. |
0 | 65 |
""" |
66 |
||
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
67 |
conf.File.__init__(self, name, path) |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
68 |
Section.__init__(self, params, decls) |
0 | 69 |
|
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
70 |
class Statement (conf.ConfObject) : |
0 | 71 |
""" |
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
72 |
A statement is a single line in the config file |
0 | 73 |
""" |
74 |
||
75 |
def __init__ (self, name, *args) : |
|
76 |
""" |
|
77 |
The statement will be formatted like this: |
|
78 |
<name> [ <arg> [ ... ] ] ";" |
|
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
79 |
|
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
80 |
Arguments given as None will be ignored. |
0 | 81 |
""" |
82 |
||
83 |
self.name = name |
|
84 |
self.args = args |
|
85 |
||
86 |
def _fmt_arg (self, arg) : |
|
87 |
""" |
|
88 |
Formats a arg for use in output, the following types are supported: |
|
89 |
||
90 |
list/tuple/iter: results in a comma-and-space separated list of formatted values |
|
91 |
unicode: results in an encoded str |
|
92 |
str: results in the string itself, quoted if needed |
|
93 |
other: attempt to convert to a str, and then format that |
|
94 |
""" |
|
95 |
||
96 |
# format lists specially |
|
97 |
# XXX: iterators? |
|
98 |
if isinstance(arg, (list, tuple)) : |
|
99 |
# recurse as a comma-and-space separated list |
|
100 |
return ', '.join(self._fmt_arg(a) for a in arg) |
|
101 |
||
102 |
elif isinstance(arg, Literal) : |
|
103 |
# use what it specifies |
|
104 |
return arg.fmt_arg() |
|
105 |
||
106 |
elif isinstance(arg, unicode) : |
|
107 |
# recurse with the str version |
|
108 |
# XXX: what encoding to use? |
|
109 |
return self._fmt_arg(arg.encode('utf8')) |
|
110 |
||
111 |
elif isinstance(arg, str) : |
|
112 |
# XXX: quoting |
|
113 |
return arg |
|
114 |
||
115 |
else : |
|
116 |
# try and use it as a string |
|
117 |
return self._fmt_arg(str(arg)) |
|
118 |
||
119 |
def _fmt_data (self) : |
|
120 |
""" |
|
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
121 |
Formats the statement name/params as a single line, ignoring None |
0 | 122 |
""" |
123 |
||
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
124 |
return "%s%s" % (self.name, (' ' + ' '.join(self._fmt_arg(a) for a in self.args if a is not None)) if self.args else '') |
0 | 125 |
|
126 |
class Literal (Statement) : |
|
127 |
""" |
|
128 |
A literal is something that goes into the config file as-is, with no formatting or escaping applied. |
|
129 |
""" |
|
130 |
||
131 |
def __init__ (self, literal) : |
|
132 |
self.literal = literal |
|
133 |
||
134 |
def fmt_arg (self) : |
|
135 |
return self.literal |
|
136 |
||
137 |
def fmt_lines (self) : |
|
138 |
yield self.literal |
|
139 |
||
140 |
class Parameter (Statement) : |
|
141 |
""" |
|
142 |
A parameter is a single statement that configures the behaviour of something. |
|
143 |
||
144 |
Parameters have a name, and optionally, a number of arguments, and are formatted as statements terminated with |
|
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
145 |
a semicolon. For convenience, params/decls that are None are ignored. |
0 | 146 |
""" |
147 |
||
148 |
def fmt_lines (self) : |
|
149 |
""" |
|
150 |
Yields a single ;-terminated line |
|
151 |
""" |
|
152 |
||
153 |
yield "%s;" % self._fmt_data() |
|
154 |
||
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
155 |
class Declaration (Section, Statement) : |
0 | 156 |
""" |
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
157 |
A declaration begins like a statement (with name and args), but then contains a curly-braces-delimited block |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
158 |
that acts like a Section. |
0 | 159 |
|
160 |
<name> [ <args> [ ... ] ] { |
|
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
161 |
[ <Section> ] |
0 | 162 |
} |
163 |
||
164 |
""" |
|
165 |
||
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
166 |
def __init__ (self, name, args=[], params=None, decls=None) : |
0 | 167 |
""" |
168 |
The name/args will be formatted as in Statement, but params should be an iterable of Parameters, and decls |
|
169 |
an iterable of Declarations. |
|
170 |
""" |
|
171 |
||
172 |
# init the statement bit |
|
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
173 |
Section.__init__(self, params, decls) |
0 | 174 |
Statement.__init__(self, name, *args) |
175 |
||
176 |
def fmt_lines (self) : |
|
177 |
""" |
|
178 |
Yields a header line, a series of indented body lines, and the footer line |
|
179 |
""" |
|
180 |
||
181 |
# the header to open the block |
|
182 |
yield "%s {" % self._fmt_data() |
|
183 |
||
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
184 |
# then output the section stuff, indented |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
185 |
for line in Section.fmt_lines(self) : |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
186 |
yield "\t%s" % line |
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
187 |
|
0 | 188 |
# and then close the block |
189 |
yield "}" |
|
190 |
||
191 |
class SharedNetwork (Declaration) : |
|
192 |
""" |
|
193 |
A shared-network declaration is used to define a set of subnets that share the same physical network, |
|
194 |
optionally with some shared params. |
|
195 |
||
196 |
shared-network <name> { |
|
197 |
[ parameters ] |
|
198 |
[ declarations ] |
|
199 |
} |
|
200 |
""" |
|
201 |
||
202 |
def __init__ (self, name, params=[], decls=[]) : |
|
203 |
""" |
|
204 |
@param name the name of the shared-subnet |
|
205 |
@param params optional parameters |
|
206 |
@param decls the iterable of subnets or other declarations in the shared network |
|
207 |
""" |
|
208 |
||
209 |
super(SharedNetwork, self).__init__("shared-network", [name], params, decls) |
|
210 |
||
211 |
class Subnet (Declaration) : |
|
212 |
""" |
|
213 |
A subnet is used to provide the information about a subnet required to identify whether or not an IP address is |
|
214 |
on that subnet, and may also be used to specify parameters/declarations for that subnet. |
|
215 |
||
216 |
subnet <subnet-number> netmask <netmask> { |
|
217 |
[ parameters ] |
|
218 |
[ declarations ] |
|
219 |
} |
|
220 |
""" |
|
221 |
||
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
222 |
def __init__ (self, network, params=None, decls=None) : |
0 | 223 |
""" |
224 |
@param network the addr.Network for the subnet |
|
225 |
@param params optional parameters |
|
226 |
@param decls optional decls, e.g. subnets |
|
227 |
""" |
|
228 |
||
229 |
super(Subnet, self).__init__("subnet", [network.net(), "netmask", network.netmask()], params, decls) |
|
230 |
||
231 |
class Group (Declaration) : |
|
232 |
""" |
|
233 |
A group is simply used to apply a set of parameters to a set of declarations. |
|
234 |
||
235 |
group { |
|
236 |
[ parameters ] |
|
237 |
[ declarations ] |
|
238 |
} |
|
239 |
""" |
|
240 |
||
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
241 |
def __init__ (self, params=None, decls=None) : |
0 | 242 |
super(Group, self).__init__("group", [], params, decls) |
243 |
||
244 |
||
245 |
class Host (Declaration) : |
|
246 |
""" |
|
247 |
A host is used to match a request against specific host, and then apply settings for that host. |
|
248 |
||
249 |
The "hostname" is the DHCP name to identify the host. |
|
250 |
||
251 |
If no dhcp-client-identifier option is specified in the parameters, then the host is matched using the |
|
252 |
"hardware" parameter. |
|
253 |
||
254 |
host <hostname> { |
|
255 |
[ parameters ] |
|
256 |
[ declarations ] |
|
257 |
} |
|
258 |
""" |
|
259 |
||
1
2223ade4f259
continue the overengineering effort, we are now able to generate dhcpd.conf files
Tero Marttila <terom@fixme.fi>
parents:
0
diff
changeset
|
260 |
def __init__ (self, hostname, params=None, decls=None) : |
0 | 261 |
super(Host, self).__init__("host", [hostname], params, decls) |
262 |
||
263 |
class Option (Parameter) : |
|
264 |
""" |
|
265 |
A generic 'option' parameter for a dhcpd.conf file |
|
266 |
""" |
|
267 |
||
268 |
def __init__ (self, name, *args) : |
|
269 |
""" |
|
270 |
Formatted as a Satement with a name of "option <name>". |
|
271 |
""" |
|
272 |
||
273 |
super(Option, self).__init__("option %s" % name, *args) |
|
274 |