Merge lp:~blake-rouse/maas/fix-1559398 into lp:~maas-committers/maas/trunk
- fix-1559398
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Blake Rouse | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 4815 | ||||
Proposed branch: | lp:~blake-rouse/maas/fix-1559398 | ||||
Merge into: | lp:~maas-committers/maas/trunk | ||||
Diff against target: |
407 lines (+240/-82) 7 files modified
etc/maas/templates/dhcp/dhcpd.conf.template (+3/-3) scripts/maas-dhcp-helper (+128/-0) setup.py (+2/-0) src/provisioningserver/__main__.py (+0/-2) src/provisioningserver/dhcp/notifier.py (+0/-73) src/provisioningserver/dhcp/tests/test_helper_clean.py (+103/-0) src/provisioningserver/dhcp/tests/test_helper_notify.py (+4/-4) |
||||
To merge this branch: | bzr merge lp:~blake-rouse/maas/fix-1559398 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike Pontillo (community) | Approve | ||
Review via email: mp+289712@code.launchpad.net |
Commit message
Add maas-dhcp-notify wrapper that forks maas-rack dhcp-notify and returns. This fixes the issue where if a execute action in isc-dhcp-server takes to long it affects the performance of isc-dhcp-server.
Description of the change
I have QA'd this and previously commissioning with just 40 nodes would result only half getting an actual lease. This change allowed all to get a lease and commission successfully.
Packaging change is also required: https:/
Mike Pontillo (mpontillo) wrote : | # |
One other data point: I wrote a simple script to accept connections on a UNIX datagram socket:
https:/
... with a "processing delay" of one second after reading each datagram.
Using the Python client, I can easily get ~10 packets ahead of the server while it takes its time dequeuing the packets, using a command similar to:
bin/maas-rack dhcp-notify --action commit --mac 74:d4:35:89:bd:a9 --ip-family ipv4 --ip 192.168.10.60 --lease-time 43200 --hostname test-$SEQ --socket /tmp/test-socket
Using netcat, I can send in 500+ packets before it starts blocking on write, using a command similar to:
echo -n "{\"ip\": \"192.168.10.60\", \"mac\": \"74:d4:
Mike Pontillo (mpontillo) wrote : | # |
Upon further testing, I don't think the fork approach is viable. I was able to cripple my system after ~150-200 processes forked, which could easily happen in MAAS if someone brings a rack online.
Mike Pontillo (mpontillo) wrote : | # |
It looks like something we're doing to set up the notification process is very slow (on the order of half a second, or ~100 notifications in a little less than a minute). Since we don't want to pull in a dependency on netcat, I looked at other ways to send the lease notification message faster.
If I use the following test code to write to the socket, it's plenty fast (~100 notifications in ~1.2 seconds on my system):
python -c "import socket; conn = socket.
Something we're doing to set up the command framework and parse the arguments may be what's making it slow. (I already tried eliminating the dependency on the JSON module, and that didn't make any difference.)
Mike Pontillo (mpontillo) wrote : | # |
Looks much better; thanks for the fixes.
I see you've included the host-cleaning functionality in this utility as well. (I didn't look at it in too much detail, but it seems a little scary to do this with a regular expression; you might want to verify that it can't ever match the word "host" inside, for example, a lease block.) So a couple of comments on that:
- It might be safer to use the ISC parser than a regular expression.
- I hope we're going to ensure that dhcpd is disabled while we clean the file, since it could be large, and in the time it takes to rewrite the file another lease could easily have been allocated.
- Could we possibly read the file to determine which host entries we want to remove, and use OMAPI to remove them?
Mike Pontillo (mpontillo) wrote : | # |
One other comment. Since you've added the 'clean' functionality to this code, you could probably make the socket notifier portion faster if you move "import re" to the clean function, that way it isn't imported if we aren't cleaning the leases.
Blake Rouse (blake-rouse) wrote : | # |
Good idea. Moved the imports to each sub command so that they run faster.
Also the clean is called specifically from systemd when the the service before it is started. So its 100% not running when that is called. This ensures that its not being modified at the same time, as its not even running.
The test includes hostname in the lease entry that my previous regex was matching. This regex is specific to a host block not any host in the lease section.
Preview Diff
1 | === modified file 'etc/maas/templates/dhcp/dhcpd.conf.template' | |||
2 | --- etc/maas/templates/dhcp/dhcpd.conf.template 2016-03-17 19:09:25 +0000 | |||
3 | +++ etc/maas/templates/dhcp/dhcpd.conf.template 2016-03-22 17:04:41 +0000 | |||
4 | @@ -89,7 +89,7 @@ | |||
5 | 89 | set cllt = binary-to-ascii(10, 32, "", encode-int(lease-time, 32)); | 89 | set cllt = binary-to-ascii(10, 32, "", encode-int(lease-time, 32)); |
6 | 90 | set clht = pick-first-value(option host-name, "(none)"); | 90 | set clht = pick-first-value(option host-name, "(none)"); |
7 | 91 | execute( | 91 | execute( |
9 | 92 | "/usr/sbin/maas-rack", "dhcp-notify", | 92 | "/usr/sbin/maas-dhcp-helper", "notify", |
10 | 93 | "--action", "commit", "--mac", clhw, | 93 | "--action", "commit", "--mac", clhw, |
11 | 94 | "--ip-family", "ipv4", "--ip", clip, | 94 | "--ip-family", "ipv4", "--ip", clip, |
12 | 95 | "--lease-time", cllt, "--hostname", clht); | 95 | "--lease-time", cllt, "--hostname", clht); |
13 | @@ -98,7 +98,7 @@ | |||
14 | 98 | set clhw = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6)); | 98 | set clhw = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6)); |
15 | 99 | set clip = binary-to-ascii(10, 8, ".", leased-address); | 99 | set clip = binary-to-ascii(10, 8, ".", leased-address); |
16 | 100 | execute( | 100 | execute( |
18 | 101 | "/usr/sbin/maas-rack", "dhcp-notify", | 101 | "/usr/sbin/maas-dhcp-helper", "notify", |
19 | 102 | "--action", "expiry", "--mac", clhw, | 102 | "--action", "expiry", "--mac", clhw, |
20 | 103 | "--ip-family", "ipv4", "--ip", clip); | 103 | "--ip-family", "ipv4", "--ip", clip); |
21 | 104 | } | 104 | } |
22 | @@ -106,7 +106,7 @@ | |||
23 | 106 | set clhw = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6)); | 106 | set clhw = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6)); |
24 | 107 | set clip = binary-to-ascii(10, 8, ".", leased-address); | 107 | set clip = binary-to-ascii(10, 8, ".", leased-address); |
25 | 108 | execute( | 108 | execute( |
27 | 109 | "/usr/sbin/maas-rack", "dhcp-notify", | 109 | "/usr/sbin/maas-dhcp-helper", "notify", |
28 | 110 | "--action", "release", "--mac", clhw, | 110 | "--action", "release", "--mac", clhw, |
29 | 111 | "--ip-family", "ipv4", "--ip", clip); | 111 | "--ip-family", "ipv4", "--ip", clip); |
30 | 112 | } | 112 | } |
31 | 113 | 113 | ||
32 | === added file 'scripts/maas-dhcp-helper' | |||
33 | --- scripts/maas-dhcp-helper 1970-01-01 00:00:00 +0000 | |||
34 | +++ scripts/maas-dhcp-helper 2016-03-22 17:04:41 +0000 | |||
35 | @@ -0,0 +1,128 @@ | |||
36 | 1 | #!/usr/bin/env python3 | ||
37 | 2 | # -*- mode: python -*- | ||
38 | 3 | # Copyright 2016 Canonical Ltd. This software is licensed under the | ||
39 | 4 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
40 | 5 | |||
41 | 6 | """Utility script used by maas-dhcpd and maas-dhcpd6. | ||
42 | 7 | |||
43 | 8 | `notify` is called by maas-dhcpd and maas-dhcpd6 with the on execute command | ||
44 | 9 | from isc-dhcp-server when a lease is committed, released, or expired. Sends | ||
45 | 10 | a JSON encoded message over the `dhcpd.sock` to the running rackd process. | ||
46 | 11 | |||
47 | 12 | `clean` is called by maas-dhcpd and maas-dhcpd6 when either service starts | ||
48 | 13 | to remove any host entries from the passed leases file. This is used to make | ||
49 | 14 | sure that when maas-dhcpd and maas-dhcpd6 start that only the host entries | ||
50 | 15 | from the dhcpd.conf or dhcpd6.conf are used. | ||
51 | 16 | """ | ||
52 | 17 | |||
53 | 18 | import argparse | ||
54 | 19 | from contextlib import closing | ||
55 | 20 | import json | ||
56 | 21 | import re | ||
57 | 22 | import socket | ||
58 | 23 | import time | ||
59 | 24 | |||
60 | 25 | |||
61 | 26 | def notify_add_arguments(parser): | ||
62 | 27 | """Initialise options for sending DHCP notification over the dhcpd.sock. | ||
63 | 28 | |||
64 | 29 | :param parser: An instance of :class:`ArgumentParser`. | ||
65 | 30 | """ | ||
66 | 31 | parser.add_argument( | ||
67 | 32 | "--action", action="store", required=True, | ||
68 | 33 | choices=['commit', 'expiry', 'release'], help=( | ||
69 | 34 | "Action taken by DHCP server for the lease.")) | ||
70 | 35 | parser.add_argument( | ||
71 | 36 | "--mac", action="store", required=True, help=( | ||
72 | 37 | "MAC address for lease.")) | ||
73 | 38 | parser.add_argument( | ||
74 | 39 | "--ip-family", action="store", required=True, choices=['ipv4', 'ipv6'], | ||
75 | 40 | help="IP address family for lease.") | ||
76 | 41 | parser.add_argument( | ||
77 | 42 | "--ip", action="store", required=True, help=( | ||
78 | 43 | "IP address for lease.")) | ||
79 | 44 | parser.add_argument( | ||
80 | 45 | "--lease-time", action="store", type=int, required=False, help=( | ||
81 | 46 | "Length of time before the lease expires.")) | ||
82 | 47 | parser.add_argument( | ||
83 | 48 | "--hostname", action="store", required=False, help=( | ||
84 | 49 | "Hostname of the machine for the lease.")) | ||
85 | 50 | parser.add_argument( | ||
86 | 51 | "--socket", action="store", required=False, | ||
87 | 52 | default="/var/lib/maas/dhcpd.sock", help=argparse.SUPPRESS) | ||
88 | 53 | |||
89 | 54 | |||
90 | 55 | def notify(args): | ||
91 | 56 | """Write DHCP action notification to the `dhcpd.sock`.""" | ||
92 | 57 | notify_packet = { | ||
93 | 58 | "action": args.action, | ||
94 | 59 | "mac": args.mac, | ||
95 | 60 | "ip_family": args.ip_family, | ||
96 | 61 | "ip": args.ip, | ||
97 | 62 | "timestamp": int(time.time()), | ||
98 | 63 | } | ||
99 | 64 | |||
100 | 65 | # Lease time is required by the commit action and hostname is optional. | ||
101 | 66 | if args.action == "commit": | ||
102 | 67 | notify_packet["lease_time"] = args.lease_time | ||
103 | 68 | hostname = args.hostname | ||
104 | 69 | has_hostname = ( | ||
105 | 70 | hostname is not None and | ||
106 | 71 | len(hostname) > 0 and | ||
107 | 72 | not hostname.isspace()) | ||
108 | 73 | if has_hostname: | ||
109 | 74 | notify_packet["hostname"] = hostname | ||
110 | 75 | |||
111 | 76 | # Connect to the socket and send the packet over as JSON. | ||
112 | 77 | payload = json.dumps(notify_packet) | ||
113 | 78 | conn = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) | ||
114 | 79 | conn.connect(args.socket) | ||
115 | 80 | with closing(conn): | ||
116 | 81 | conn.send(payload.encode("utf-8")) | ||
117 | 82 | |||
118 | 83 | |||
119 | 84 | def clean_add_arguments(parser): | ||
120 | 85 | """Initialise options for cleaning the dhcpd.leases file. | ||
121 | 86 | |||
122 | 87 | :param parser: An instance of :class:`ArgumentParser`. | ||
123 | 88 | """ | ||
124 | 89 | parser.add_argument( | ||
125 | 90 | "leases_file", action="store", | ||
126 | 91 | help=("Leases file to remove host entries from.")) | ||
127 | 92 | |||
128 | 93 | |||
129 | 94 | def clean(args): | ||
130 | 95 | """Remove all host entries from leases file.""" | ||
131 | 96 | with open(args.leases_file, "r", encoding="utf-8") as fp: | ||
132 | 97 | content = fp.read() | ||
133 | 98 | cleaned_content = re.sub("host [^{]+{[^}]+}\n", "", content) | ||
134 | 99 | with open(args.leases_file, "w", encoding="utf-8") as fp: | ||
135 | 100 | fp.write(cleaned_content) | ||
136 | 101 | |||
137 | 102 | |||
138 | 103 | def main(): | ||
139 | 104 | """Main entry point for the script.""" | ||
140 | 105 | parser = argparse.ArgumentParser() | ||
141 | 106 | subparsers = parser.add_subparsers(title="command") | ||
142 | 107 | subparsers.required = True | ||
143 | 108 | subparsers.dest = "command" | ||
144 | 109 | |||
145 | 110 | # Notify. | ||
146 | 111 | notify_parser = subparsers.add_parser( | ||
147 | 112 | "notify", help="Write DHCP action notification to the `dhcpd.sock`.") | ||
148 | 113 | notify_parser.set_defaults(handler=notify) | ||
149 | 114 | notify_add_arguments(notify_parser) | ||
150 | 115 | |||
151 | 116 | # Clean. | ||
152 | 117 | clean_parser = subparsers.add_parser( | ||
153 | 118 | "clean", help="Remove all host entries from leases file.") | ||
154 | 119 | clean_parser.set_defaults(handler=clean) | ||
155 | 120 | clean_add_arguments(clean_parser) | ||
156 | 121 | |||
157 | 122 | # Run the parser and the command. | ||
158 | 123 | args = parser.parse_args() | ||
159 | 124 | args.handler(args) | ||
160 | 125 | |||
161 | 126 | |||
162 | 127 | if __name__ == "__main__": | ||
163 | 128 | main() | ||
164 | 0 | 129 | ||
165 | === modified file 'setup.py' | |||
166 | --- setup.py 2016-01-04 16:24:36 +0000 | |||
167 | +++ setup.py 2016-03-22 17:04:41 +0000 | |||
168 | @@ -81,6 +81,8 @@ | |||
169 | 81 | ('/usr/bin', | 81 | ('/usr/bin', |
170 | 82 | ['scripts/maas-generate-winrm-cert', | 82 | ['scripts/maas-generate-winrm-cert', |
171 | 83 | 'scripts/uec2roottar']), | 83 | 'scripts/uec2roottar']), |
172 | 84 | ('/usr/sbin', | ||
173 | 85 | ['scripts/maas-dhcp-notify']), | ||
174 | 84 | ], | 86 | ], |
175 | 85 | 87 | ||
176 | 86 | classifiers=[ | 88 | classifiers=[ |
177 | 87 | 89 | ||
178 | === modified file 'src/provisioningserver/__main__.py' | |||
179 | --- src/provisioningserver/__main__.py 2016-03-17 04:26:14 +0000 | |||
180 | +++ src/provisioningserver/__main__.py 2016-03-22 17:04:41 +0000 | |||
181 | @@ -8,7 +8,6 @@ | |||
182 | 8 | import provisioningserver.boot.install_bootloader | 8 | import provisioningserver.boot.install_bootloader |
183 | 9 | import provisioningserver.boot.install_grub | 9 | import provisioningserver.boot.install_grub |
184 | 10 | import provisioningserver.cluster_config_command | 10 | import provisioningserver.cluster_config_command |
185 | 11 | import provisioningserver.dhcp.notifier | ||
186 | 12 | import provisioningserver.support_dump | 11 | import provisioningserver.support_dump |
187 | 13 | import provisioningserver.upgrade_cluster | 12 | import provisioningserver.upgrade_cluster |
188 | 14 | from provisioningserver.utils.script import ( | 13 | from provisioningserver.utils.script import ( |
189 | @@ -23,7 +22,6 @@ | |||
190 | 23 | 'atomic-delete': AtomicDeleteScript, | 22 | 'atomic-delete': AtomicDeleteScript, |
191 | 24 | 'check-for-shared-secret': security.CheckForSharedSecretScript, | 23 | 'check-for-shared-secret': security.CheckForSharedSecretScript, |
192 | 25 | 'config': provisioningserver.cluster_config_command, | 24 | 'config': provisioningserver.cluster_config_command, |
193 | 26 | 'dhcp-notify': provisioningserver.dhcp.notifier, | ||
194 | 27 | 'install-shared-secret': security.InstallSharedSecretScript, | 25 | 'install-shared-secret': security.InstallSharedSecretScript, |
195 | 28 | 'install-uefi-config': provisioningserver.boot.install_grub, | 26 | 'install-uefi-config': provisioningserver.boot.install_grub, |
196 | 29 | 'support-dump': provisioningserver.support_dump, | 27 | 'support-dump': provisioningserver.support_dump, |
197 | 30 | 28 | ||
198 | === removed file 'src/provisioningserver/dhcp/notifier.py' | |||
199 | --- src/provisioningserver/dhcp/notifier.py 2015-12-23 15:59:21 +0000 | |||
200 | +++ src/provisioningserver/dhcp/notifier.py 1970-01-01 00:00:00 +0000 | |||
201 | @@ -1,73 +0,0 @@ | |||
202 | 1 | # Copyright 2012-2014 Canonical Ltd. This software is licensed under the | ||
203 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
204 | 3 | |||
205 | 4 | """Write DHCP action notification to the `dhcpd.sock`.""" | ||
206 | 5 | |||
207 | 6 | __all__ = [ | ||
208 | 7 | "add_arguments", | ||
209 | 8 | "run", | ||
210 | 9 | ] | ||
211 | 10 | |||
212 | 11 | import argparse | ||
213 | 12 | from contextlib import closing | ||
214 | 13 | import json | ||
215 | 14 | import socket | ||
216 | 15 | import time | ||
217 | 16 | |||
218 | 17 | |||
219 | 18 | def add_arguments(parser): | ||
220 | 19 | """Initialise options for sending DHCP notification over the dhcpd.sock. | ||
221 | 20 | |||
222 | 21 | :param parser: An instance of :class:`ArgumentParser`. | ||
223 | 22 | """ | ||
224 | 23 | parser.add_argument( | ||
225 | 24 | "--action", action="store", required=True, | ||
226 | 25 | choices=['commit', 'expiry', 'release'], help=( | ||
227 | 26 | "Action taken by DHCP server for the lease.")) | ||
228 | 27 | parser.add_argument( | ||
229 | 28 | "--mac", action="store", required=True, help=( | ||
230 | 29 | "MAC address for lease.")) | ||
231 | 30 | parser.add_argument( | ||
232 | 31 | "--ip-family", action="store", required=True, choices=['ipv4', 'ipv6'], | ||
233 | 32 | help="IP address family for lease.") | ||
234 | 33 | parser.add_argument( | ||
235 | 34 | "--ip", action="store", required=True, help=( | ||
236 | 35 | "IP address for lease.")) | ||
237 | 36 | parser.add_argument( | ||
238 | 37 | "--lease-time", action="store", type=int, required=False, help=( | ||
239 | 38 | "Length of time before the lease expires.")) | ||
240 | 39 | parser.add_argument( | ||
241 | 40 | "--hostname", action="store", required=False, help=( | ||
242 | 41 | "Hostname of the machine for the lease.")) | ||
243 | 42 | parser.add_argument( | ||
244 | 43 | "--socket", action="store", required=False, | ||
245 | 44 | default="/var/lib/maas/dhcpd.sock", help=argparse.SUPPRESS) | ||
246 | 45 | |||
247 | 46 | |||
248 | 47 | def run(args): | ||
249 | 48 | """Write DHCP action notification to the `dhcpd.sock`.""" | ||
250 | 49 | notify_packet = { | ||
251 | 50 | "action": args.action, | ||
252 | 51 | "mac": args.mac, | ||
253 | 52 | "ip_family": args.ip_family, | ||
254 | 53 | "ip": args.ip, | ||
255 | 54 | "timestamp": int(time.time()), | ||
256 | 55 | } | ||
257 | 56 | |||
258 | 57 | # Lease time is required by the commit action and hostname is optional. | ||
259 | 58 | if args.action == "commit": | ||
260 | 59 | notify_packet["lease_time"] = args.lease_time | ||
261 | 60 | hostname = args.hostname | ||
262 | 61 | has_hostname = ( | ||
263 | 62 | hostname is not None and | ||
264 | 63 | len(hostname) > 0 and | ||
265 | 64 | not hostname.isspace()) | ||
266 | 65 | if has_hostname: | ||
267 | 66 | notify_packet["hostname"] = hostname | ||
268 | 67 | |||
269 | 68 | # Connect to the socket and send the packet over as JSON. | ||
270 | 69 | payload = json.dumps(notify_packet) | ||
271 | 70 | conn = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) | ||
272 | 71 | conn.connect(args.socket) | ||
273 | 72 | with closing(conn): | ||
274 | 73 | conn.send(payload.encode("utf-8")) | ||
275 | 74 | 0 | ||
276 | === added file 'src/provisioningserver/dhcp/tests/test_helper_clean.py' | |||
277 | --- src/provisioningserver/dhcp/tests/test_helper_clean.py 1970-01-01 00:00:00 +0000 | |||
278 | +++ src/provisioningserver/dhcp/tests/test_helper_clean.py 2016-03-22 17:04:41 +0000 | |||
279 | @@ -0,0 +1,103 @@ | |||
280 | 1 | # Copyright 2016 Canonical Ltd. This software is licensed under the | ||
281 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
282 | 3 | |||
283 | 4 | """Tests for maas-dhcp-support clean command.""" | ||
284 | 5 | |||
285 | 6 | __all__ = [] | ||
286 | 7 | |||
287 | 8 | from maastesting import root | ||
288 | 9 | from maastesting.testcase import MAASTestCase | ||
289 | 10 | from provisioningserver.utils.fs import read_text_file | ||
290 | 11 | from provisioningserver.utils.shell import call_and_check | ||
291 | 12 | |||
292 | 13 | |||
293 | 14 | LEASES_FILE_WITH_HOSTS = """\ | ||
294 | 15 | lease 192.168.10.67 { | ||
295 | 16 | starts 2 2016/03/22 13:44:15; | ||
296 | 17 | ends 3 2016/03/23 01:44:15; | ||
297 | 18 | cltt 2 2016/03/22 13:44:15; | ||
298 | 19 | binding state active; | ||
299 | 20 | next binding state free; | ||
300 | 21 | rewind binding state free; | ||
301 | 22 | hardware ethernet 74:d4:35:89:bc:f2; | ||
302 | 23 | set clht = "zoochemical-carlton"; | ||
303 | 24 | set cllt = "43200"; | ||
304 | 25 | set clip = "192.168.10.67"; | ||
305 | 26 | set clhw = "74:d4:35:89:bc:f2"; | ||
306 | 27 | set vendor-class-identifier = "Linux ipconfig"; | ||
307 | 28 | client-hostname "zoochemical-carlton"; | ||
308 | 29 | } | ||
309 | 30 | host 74-d4-35-89-b9-e8 { | ||
310 | 31 | dynamic; | ||
311 | 32 | hardware ethernet 74:d4:35:89:b9:e8; | ||
312 | 33 | fixed-address 192.168.10.5; | ||
313 | 34 | } | ||
314 | 35 | lease 192.168.10.69 { | ||
315 | 36 | starts 2 2016/03/22 13:44:15; | ||
316 | 37 | ends 3 2016/03/23 01:44:15; | ||
317 | 38 | cltt 2 2016/03/22 13:44:15; | ||
318 | 39 | binding state active; | ||
319 | 40 | next binding state free; | ||
320 | 41 | rewind binding state free; | ||
321 | 42 | hardware ethernet 74:d4:35:89:bd:25; | ||
322 | 43 | set clht = "undetesting-johnetta"; | ||
323 | 44 | set cllt = "43200"; | ||
324 | 45 | set clip = "192.168.10.69"; | ||
325 | 46 | set clhw = "74:d4:35:89:bd:25"; | ||
326 | 47 | set vendor-class-identifier = "Linux ipconfig"; | ||
327 | 48 | } | ||
328 | 49 | host 74-d4-35-89-bc-26 { | ||
329 | 50 | dynamic; | ||
330 | 51 | hardware ethernet 74:d4:35:89:bc:26; | ||
331 | 52 | fixed-address 192.168.10.7; | ||
332 | 53 | } | ||
333 | 54 | host 74-d4-35-89-bc-23 { | ||
334 | 55 | dynamic; | ||
335 | 56 | deleted; | ||
336 | 57 | } | ||
337 | 58 | """ | ||
338 | 59 | |||
339 | 60 | |||
340 | 61 | LEASES_FILE_WITHOUT_HOSTS = """\ | ||
341 | 62 | lease 192.168.10.67 { | ||
342 | 63 | starts 2 2016/03/22 13:44:15; | ||
343 | 64 | ends 3 2016/03/23 01:44:15; | ||
344 | 65 | cltt 2 2016/03/22 13:44:15; | ||
345 | 66 | binding state active; | ||
346 | 67 | next binding state free; | ||
347 | 68 | rewind binding state free; | ||
348 | 69 | hardware ethernet 74:d4:35:89:bc:f2; | ||
349 | 70 | set clht = "zoochemical-carlton"; | ||
350 | 71 | set cllt = "43200"; | ||
351 | 72 | set clip = "192.168.10.67"; | ||
352 | 73 | set clhw = "74:d4:35:89:bc:f2"; | ||
353 | 74 | set vendor-class-identifier = "Linux ipconfig"; | ||
354 | 75 | client-hostname "zoochemical-carlton"; | ||
355 | 76 | } | ||
356 | 77 | lease 192.168.10.69 { | ||
357 | 78 | starts 2 2016/03/22 13:44:15; | ||
358 | 79 | ends 3 2016/03/23 01:44:15; | ||
359 | 80 | cltt 2 2016/03/22 13:44:15; | ||
360 | 81 | binding state active; | ||
361 | 82 | next binding state free; | ||
362 | 83 | rewind binding state free; | ||
363 | 84 | hardware ethernet 74:d4:35:89:bd:25; | ||
364 | 85 | set clht = "undetesting-johnetta"; | ||
365 | 86 | set cllt = "43200"; | ||
366 | 87 | set clip = "192.168.10.69"; | ||
367 | 88 | set clhw = "74:d4:35:89:bd:25"; | ||
368 | 89 | set vendor-class-identifier = "Linux ipconfig"; | ||
369 | 90 | } | ||
370 | 91 | """ | ||
371 | 92 | |||
372 | 93 | |||
373 | 94 | class TestDHCPClean(MAASTestCase): | ||
374 | 95 | |||
375 | 96 | def test_removes_hosts_from_leases_file(self): | ||
376 | 97 | path = self.make_file(contents=LEASES_FILE_WITH_HOSTS) | ||
377 | 98 | call_and_check([ | ||
378 | 99 | "%s/scripts/maas-dhcp-helper" % root, | ||
379 | 100 | "clean", | ||
380 | 101 | path, | ||
381 | 102 | ]) | ||
382 | 103 | self.assertEquals(LEASES_FILE_WITHOUT_HOSTS, read_text_file(path)) | ||
383 | 0 | 104 | ||
384 | === renamed file 'src/provisioningserver/dhcp/tests/test_notifier.py' => 'src/provisioningserver/dhcp/tests/test_helper_notify.py' | |||
385 | --- src/provisioningserver/dhcp/tests/test_notifier.py 2016-03-07 23:20:52 +0000 | |||
386 | +++ src/provisioningserver/dhcp/tests/test_helper_notify.py 2016-03-22 17:04:41 +0000 | |||
387 | @@ -1,7 +1,7 @@ | |||
389 | 1 | # Copyright 2014-2015 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2015-2016 Canonical Ltd. This software is licensed under the |
390 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
391 | 3 | 3 | ||
393 | 4 | """Tests for maas-rack dhcp-notify command.""" | 4 | """Tests for maas-dhcp-support notify command.""" |
394 | 5 | 5 | ||
395 | 6 | __all__ = [] | 6 | __all__ = [] |
396 | 7 | 7 | ||
397 | @@ -67,8 +67,8 @@ | |||
398 | 67 | self.addCleanup(service.stopService) | 67 | self.addCleanup(service.stopService) |
399 | 68 | 68 | ||
400 | 69 | call_and_check([ | 69 | call_and_check([ |
403 | 70 | "%s/bin/maas-rack" % root, | 70 | "%s/scripts/maas-dhcp-helper" % root, |
404 | 71 | "dhcp-notify", | 71 | "notify", |
405 | 72 | '--action', action, | 72 | '--action', action, |
406 | 73 | '--mac', mac, | 73 | '--mac', mac, |
407 | 74 | '--ip-family', ip_family, | 74 | '--ip-family', ip_family, |
See one minor nit below.
My only concern with this branch is that instead of queuing up on a blocking write, we're creating an explosion of processes whenever a large number of DHCP notifications happen.
While this might scale to ~40 nodes, it may not scale to hundreds or thousands; we may fail to create more processes and lose notifications.
Long-term, we should queue up DHCP notifications in memory as fast as we can read them, and a worker should dequeue and process them. (if we still have problems scaling, we can scan the queue for redundant notifications, too.)