Merge ~blake-rouse/maas:custom-syslog-port into maas:master
- Git
- lp:~blake-rouse/maas
- custom-syslog-port
- Merge into master
Proposed by
Blake Rouse
Status: | Merged |
---|---|
Approved by: | Blake Rouse |
Approved revision: | 56a770116cac0c362a7f7826d2cdaf5e79c19977 |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~blake-rouse/maas:custom-syslog-port |
Merge into: | maas:master |
Diff against target: |
716 lines (+266/-28) 19 files modified
src/maasserver/compose_preseed.py (+7/-4) src/maasserver/forms/settings.py (+27/-1) src/maasserver/forms/tests/test_settings.py (+12/-0) src/maasserver/models/config.py (+1/-0) src/maasserver/preseed.py (+6/-2) src/maasserver/regiondservices/syslog.py (+7/-2) src/maasserver/regiondservices/tests/test_syslog.py (+7/-3) src/maasserver/rpc/boot.py (+5/-1) src/maasserver/rpc/regionservice.py (+19/-0) src/maasserver/rpc/tests/test_boot.py (+36/-0) src/maasserver/rpc/tests/test_regionservice_calls.py (+28/-0) src/maasserver/tests/test_compose_preseed.py (+21/-0) src/maasserver/tests/test_preseed.py (+7/-0) src/provisioningserver/rackdservices/external.py (+15/-2) src/provisioningserver/rackdservices/tests/test_external.py (+17/-9) src/provisioningserver/rpc/region.py (+17/-0) src/provisioningserver/syslog/config.py (+2/-1) src/provisioningserver/syslog/tests/test_config.py (+29/-0) src/provisioningserver/templates/syslog/rsyslog.conf.template (+3/-3) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike Pontillo (community) | Approve | ||
MAAS Lander | Approve | ||
Review via email: mp+354250@code.launchpad.net |
Commit message
Add ability to change the binding port of the internal syslog services that MAAS runs.
This does not expose this setting over the UI, just like the proxy setting is not exposed. This is more of an advanced configuration and really only needs to be changed if absolutely required.
Description of the change
To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote : | # |
Looks good!
One minor nit below about a test case name.
review:
Approve
Revision history for this message
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b custom-syslog-port lp:~blake-rouse/maas/+git/maas into -b master lp:~maas-committers/maas
STATUS: FAILED BUILD
LOG: http://
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/src/maasserver/compose_preseed.py b/src/maasserver/compose_preseed.py |
2 | index 1441a85..ddab084 100644 |
3 | --- a/src/maasserver/compose_preseed.py |
4 | +++ b/src/maasserver/compose_preseed.py |
5 | @@ -266,11 +266,14 @@ def get_cloud_init_reporting(request, node, token): |
6 | |
7 | def get_rsyslog_host_port(request, node): |
8 | """Return the rsyslog host and port to use.""" |
9 | - syslog = Config.objects.get_config('remote_syslog') |
10 | - if syslog: |
11 | - return syslog |
12 | + configs = Config.objects.get_configs(['remote_syslog', 'maas_syslog_port']) |
13 | + if configs['remote_syslog']: |
14 | + return configs['remote_syslog'] |
15 | else: |
16 | - return "%s:%d" % (node.boot_cluster_ip, RSYSLOG_PORT) |
17 | + port = configs['maas_syslog_port'] |
18 | + if not port: |
19 | + port = RSYSLOG_PORT |
20 | + return "%s:%d" % (node.boot_cluster_ip, port) |
21 | |
22 | |
23 | def get_system_info(): |
24 | diff --git a/src/maasserver/forms/settings.py b/src/maasserver/forms/settings.py |
25 | index d99152a..bef07ed 100644 |
26 | --- a/src/maasserver/forms/settings.py |
27 | +++ b/src/maasserver/forms/settings.py |
28 | @@ -76,10 +76,13 @@ def make_default_osystem_field(*args, **kwargs): |
29 | return field |
30 | |
31 | |
32 | -def validate_port(value): |
33 | +def validate_port(value, allow_ports=None): |
34 | """Raise `ValidationError` when the value is set to a port number. that is |
35 | either reserved for known services, or for MAAS services to ensure this |
36 | doesn't break MAAS or other applications.""" |
37 | + if allow_ports and value in allow_ports: |
38 | + # Value is in the allowed ports override. |
39 | + return |
40 | msg = "Unable to change port number" |
41 | if value > 65535 or value <= 0: |
42 | raise ValidationError( |
43 | @@ -96,6 +99,11 @@ def validate_port(value): |
44 | "%s. Port number is reserved for MAAS services." % msg) |
45 | |
46 | |
47 | +def validate_syslog_port(value): |
48 | + """A `validate_port` that allows the internal syslog port.""" |
49 | + return validate_port(value, allow_ports=[5247]) |
50 | + |
51 | + |
52 | def get_default_usable_osystem(default_osystem): |
53 | """Return the osystem from the clusters that matches the default_osystem. |
54 | """ |
55 | @@ -121,6 +129,13 @@ def make_maas_proxy_port_field(*args, **kwargs): |
56 | **kwargs) |
57 | |
58 | |
59 | +def make_maas_syslog_port_field(*args, **kwargs): |
60 | + """Build and return the maas_syslog_port field.""" |
61 | + return forms.IntegerField( |
62 | + validators=[validate_syslog_port], |
63 | + **kwargs) |
64 | + |
65 | + |
66 | def make_default_distro_series_field(*args, **kwargs): |
67 | """Build and return the default_distro_series field.""" |
68 | default_osystem = Config.objects.get_config('default_osystem') |
69 | @@ -435,6 +450,17 @@ CONFIG_ITEMS = { |
70 | """), |
71 | } |
72 | }, |
73 | + 'maas_syslog_port': { |
74 | + 'default': 5247, |
75 | + 'form': make_maas_syslog_port_field, |
76 | + 'form_kwargs': { |
77 | + 'label': "Port to bind the MAAS built-in syslog (default: 5247)", |
78 | + 'required': False, |
79 | + 'help_text': ( |
80 | + "Defines the port used to bind the built-in syslog. The " |
81 | + "default port is 5247.") |
82 | + } |
83 | + }, |
84 | 'network_discovery': { |
85 | 'default': 'enabled', |
86 | 'form': make_network_discovery_field, |
87 | diff --git a/src/maasserver/forms/tests/test_settings.py b/src/maasserver/forms/tests/test_settings.py |
88 | index ed8368f..e459493 100644 |
89 | --- a/src/maasserver/forms/tests/test_settings.py |
90 | +++ b/src/maasserver/forms/tests/test_settings.py |
91 | @@ -6,6 +6,7 @@ |
92 | __all__ = [] |
93 | |
94 | from django import forms |
95 | +from django.core.exceptions import ValidationError |
96 | from maasserver.forms.settings import ( |
97 | CONFIG_ITEMS, |
98 | get_config_doc, |
99 | @@ -80,3 +81,14 @@ class TestRemoteSyslogConfigSettings(MAASServerTestCase): |
100 | def test_wraps_ipv6(self): |
101 | field = get_config_field('remote_syslog') |
102 | self.assertEqual('[::ffff]:514', field.clean('::ffff')) |
103 | + |
104 | + |
105 | +class TestMAASSyslogPortConfigSettings(MAASServerTestCase): |
106 | + |
107 | + def test_allows_port_5247(self): |
108 | + field = get_config_field('maas_syslog_port') |
109 | + self.assertEqual(5247, field.clean('5247')) |
110 | + |
111 | + def test_doesnt_allow_port_5248(self): |
112 | + field = get_config_field('maas_syslog_port') |
113 | + self.assertRaises(ValidationError, field.clean, '5248') |
114 | diff --git a/src/maasserver/models/config.py b/src/maasserver/models/config.py |
115 | index 8551a81..1c63984 100644 |
116 | --- a/src/maasserver/models/config.py |
117 | +++ b/src/maasserver/models/config.py |
118 | @@ -86,6 +86,7 @@ def get_default_config(): |
119 | 'omapi_key': '', |
120 | # Syslog settings |
121 | 'remote_syslog': None, |
122 | + 'maas_syslog_port': 5247, |
123 | # Network discovery. |
124 | 'network_discovery': 'enabled', |
125 | 'active_discovery_interval': int(timedelta(hours=3).total_seconds()), |
126 | diff --git a/src/maasserver/preseed.py b/src/maasserver/preseed.py |
127 | index e795f96..d4a6eb8 100644 |
128 | --- a/src/maasserver/preseed.py |
129 | +++ b/src/maasserver/preseed.py |
130 | @@ -753,9 +753,13 @@ def get_preseed_context( |
131 | rack_controller=rack_controller, default_region_ip=region_ip) |
132 | server_url = request.build_absolute_uri(reverse('machines_handler')) |
133 | metadata_enlist_url = request.build_absolute_uri(reverse('enlist')) |
134 | - syslog = Config.objects.get_config('remote_syslog') |
135 | + configs = Config.objects.get_configs(['remote_syslog', 'maas_syslog_port']) |
136 | + syslog = configs['remote_syslog'] |
137 | if not syslog: |
138 | - syslog = '%s:%d' % (server_host, RSYSLOG_PORT) |
139 | + syslog_port = configs['maas_syslog_port'] |
140 | + if not syslog_port: |
141 | + syslog_port = RSYSLOG_PORT |
142 | + syslog = '%s:%d' % (server_host, syslog_port) |
143 | return { |
144 | 'osystem': osystem, |
145 | 'release': release, |
146 | diff --git a/src/maasserver/regiondservices/syslog.py b/src/maasserver/regiondservices/syslog.py |
147 | index 4da7bc8..6e7ff5b 100644 |
148 | --- a/src/maasserver/regiondservices/syslog.py |
149 | +++ b/src/maasserver/regiondservices/syslog.py |
150 | @@ -10,6 +10,7 @@ __all__ = [ |
151 | from datetime import timedelta |
152 | |
153 | import attr |
154 | +from maasserver.models.config import Config |
155 | from maasserver.models.node import RegionController |
156 | from maasserver.routablepairs import get_routable_address_map |
157 | from maasserver.service_monitor import service_monitor |
158 | @@ -78,7 +79,8 @@ class RegionSyslogService(TimerService): |
159 | # Only a region controller, no need to forward logs. |
160 | peers = frozenset() |
161 | |
162 | - return _Configuration(peers) |
163 | + port = Config.objects.get_config('maas_syslog_port') |
164 | + return _Configuration(port, peers) |
165 | |
166 | def _maybeApplyConfiguration(self, configuration): |
167 | """Reconfigure the syslog server if the configuration changes. |
168 | @@ -116,7 +118,7 @@ class RegionSyslogService(TimerService): |
169 | 'name': hostname, |
170 | } |
171 | for hostname, ip in configuration.peers |
172 | - ]) |
173 | + ], port=configuration.port) |
174 | d.addCallback( |
175 | callOut, service_monitor.restartService, "syslog_region") |
176 | return d |
177 | @@ -134,5 +136,8 @@ class RegionSyslogService(TimerService): |
178 | class _Configuration: |
179 | """Configuration for the region's syslog servers.""" |
180 | |
181 | + # Port syslog binds to. |
182 | + port = attr.ib(converter=int) |
183 | + |
184 | # Addresses of peer region controller hosts. |
185 | peers = attr.ib(converter=frozenset) |
186 | diff --git a/src/maasserver/regiondservices/tests/test_syslog.py b/src/maasserver/regiondservices/tests/test_syslog.py |
187 | index 4f35a71..2cdb0f6 100644 |
188 | --- a/src/maasserver/regiondservices/tests/test_syslog.py |
189 | +++ b/src/maasserver/regiondservices/tests/test_syslog.py |
190 | @@ -6,6 +6,7 @@ |
191 | __all__ = [] |
192 | |
193 | from crochet import wait_for |
194 | +from maasserver.models.config import Config |
195 | from maasserver.regiondservices import syslog |
196 | from maasserver.service_monitor import service_monitor |
197 | from maasserver.testing.factory import factory |
198 | @@ -73,6 +74,9 @@ class TestRegionSyslogService(MAASTransactionServerTestCase): |
199 | |
200 | @transactional |
201 | def make_example_configuration(self): |
202 | + # Set the syslog port. |
203 | + port = factory.pick_port() |
204 | + Config.objects.set_config('maas_syslog_port', port) |
205 | # Populate the database with example peers. |
206 | space = factory.make_Space() |
207 | region, addr4, addr6 = make_region_rack_with_address(space) |
208 | @@ -80,7 +84,7 @@ class TestRegionSyslogService(MAASTransactionServerTestCase): |
209 | peer1, addr1_4, addr1_6 = make_region_rack_with_address(space) |
210 | peer2, addr2_4, addr2_6 = make_region_rack_with_address(space) |
211 | # Return the servers and all possible peer IP addresses. |
212 | - return [ |
213 | + return port, [ |
214 | (peer1, sorted([IPAddress(addr1_4.ip), IPAddress(addr1_6.ip)])[0]), |
215 | (peer2, sorted([IPAddress(addr2_4.ip), IPAddress(addr2_6.ip)])[0]), |
216 | ] |
217 | @@ -89,7 +93,7 @@ class TestRegionSyslogService(MAASTransactionServerTestCase): |
218 | @inlineCallbacks |
219 | def test__tryUpdate_updates_syslog_server(self): |
220 | service = syslog.RegionSyslogService(reactor) |
221 | - peers = yield deferToDatabase(self.make_example_configuration) |
222 | + port, peers = yield deferToDatabase(self.make_example_configuration) |
223 | write_config = self.patch_autospec(syslog, "write_config") |
224 | restartService = self.patch_autospec(service_monitor, "restartService") |
225 | yield service._tryUpdate() |
226 | @@ -101,7 +105,7 @@ class TestRegionSyslogService(MAASTransactionServerTestCase): |
227 | 'name': node.hostname, |
228 | } |
229 | for node, ip in peers |
230 | - ])))) |
231 | + ])), port=port)) |
232 | self.assertThat(restartService, MockCalledOnceWith("syslog_region")) |
233 | # If the configuration has not changed then a second call to |
234 | # `_tryUpdate` does not result in another call to `write_config`. |
235 | diff --git a/src/maasserver/rpc/boot.py b/src/maasserver/rpc/boot.py |
236 | index 7755761..f053b13 100644 |
237 | --- a/src/maasserver/rpc/boot.py |
238 | +++ b/src/maasserver/rpc/boot.py |
239 | @@ -296,10 +296,14 @@ def get_config( |
240 | 'use_rack_proxy', |
241 | 'maas_internal_domain', |
242 | 'remote_syslog', |
243 | + 'maas_syslog_port', |
244 | ]) |
245 | |
246 | # Compute the syslog server. |
247 | - log_host, log_port = local_ip, RSYSLOG_PORT |
248 | + log_host, log_port = local_ip, ( |
249 | + configs['maas_syslog_port'] |
250 | + if configs['maas_syslog_port'] |
251 | + else RSYSLOG_PORT) |
252 | if configs['remote_syslog']: |
253 | log_host, log_port = splithost(configs['remote_syslog']) |
254 | if log_port is None: |
255 | diff --git a/src/maasserver/rpc/regionservice.py b/src/maasserver/rpc/regionservice.py |
256 | index 7d2c065..41c9a74 100644 |
257 | --- a/src/maasserver/rpc/regionservice.py |
258 | +++ b/src/maasserver/rpc/regionservice.py |
259 | @@ -533,6 +533,25 @@ class Region(RPCProtocol): |
260 | |
261 | return deferToDatabase(get_from_db) |
262 | |
263 | + @region.GetSyslogConfiguration.responder |
264 | + def get_syslog_configuration(self, system_id): |
265 | + """Get settings to use for configuring syslog. |
266 | + |
267 | + Implementation of |
268 | + :py:class:`~provisioningserver.rpc.region.GetSyslogConfiguration`. |
269 | + """ |
270 | + # For consistency `system_id` is passed, but at the moment it is not |
271 | + # used to customise the syslog configuration. |
272 | + |
273 | + @transactional |
274 | + def get_from_db(): |
275 | + port = Config.objects.get_config('maas_syslog_port') |
276 | + return { |
277 | + 'port': port, |
278 | + } |
279 | + |
280 | + return deferToDatabase(get_from_db) |
281 | + |
282 | |
283 | @inlineCallbacks |
284 | def isLoopbackURL(url): |
285 | diff --git a/src/maasserver/rpc/tests/test_boot.py b/src/maasserver/rpc/tests/test_boot.py |
286 | index 6bd23bf..29113b6 100644 |
287 | --- a/src/maasserver/rpc/tests/test_boot.py |
288 | +++ b/src/maasserver/rpc/tests/test_boot.py |
289 | @@ -182,6 +182,42 @@ class TestGetConfig(MAASServerTestCase): |
290 | "http_boot": True, |
291 | }, config) |
292 | |
293 | + def test__purpose_local_uses_maas_syslog_port(self): |
294 | + syslog_port = factory.pick_port() |
295 | + Config.objects.set_config('maas_syslog_port', syslog_port) |
296 | + rack_controller = factory.make_RackController() |
297 | + local_ip = factory.make_ip_address() |
298 | + remote_ip = factory.make_ip_address() |
299 | + node = self.make_node_with_extra( |
300 | + status=NODE_STATUS.DEPLOYED, netboot=False) |
301 | + node.boot_cluster_ip = local_ip |
302 | + node.osystem = factory.make_name('osystem') |
303 | + node.distro_series = factory.make_name('distro_series') |
304 | + node.save() |
305 | + mac = node.get_boot_interface().mac_address |
306 | + config = get_config( |
307 | + rack_controller.system_id, local_ip, remote_ip, mac=mac, |
308 | + query_count=7) |
309 | + self.assertEquals({ |
310 | + "system_id": node.system_id, |
311 | + "arch": node.split_arch()[0], |
312 | + "subarch": node.split_arch()[1], |
313 | + "osystem": node.osystem, |
314 | + "release": node.distro_series, |
315 | + "kernel": '', |
316 | + "initrd": '', |
317 | + "boot_dtb": '', |
318 | + "purpose": 'local', |
319 | + "hostname": node.hostname, |
320 | + "domain": node.domain.name, |
321 | + "preseed_url": ANY, |
322 | + "fs_host": local_ip, |
323 | + "log_host": local_ip, |
324 | + "log_port": syslog_port, |
325 | + "extra_opts": '', |
326 | + "http_boot": True, |
327 | + }, config) |
328 | + |
329 | def test__returns_kparams_for_known_node(self): |
330 | rack_controller = factory.make_RackController() |
331 | local_ip = factory.make_ip_address() |
332 | diff --git a/src/maasserver/rpc/tests/test_regionservice_calls.py b/src/maasserver/rpc/tests/test_regionservice_calls.py |
333 | index 07aced1..523623f 100644 |
334 | --- a/src/maasserver/rpc/tests/test_regionservice_calls.py |
335 | +++ b/src/maasserver/rpc/tests/test_regionservice_calls.py |
336 | @@ -84,6 +84,7 @@ from provisioningserver.rpc.region import ( |
337 | GetDNSConfiguration, |
338 | GetProxies, |
339 | GetProxyConfiguration, |
340 | + GetSyslogConfiguration, |
341 | GetTimeConfiguration, |
342 | Identify, |
343 | ListNodePowerParameters, |
344 | @@ -1430,3 +1431,30 @@ class TestRegionProtocol_GetProxyConfiguration(MAASTransactionServerTestCase): |
345 | 'allowed_cidrs': cidrs, |
346 | 'prefer_v4_proxy': prefer_v4_proxy, |
347 | })) |
348 | + |
349 | + |
350 | +class TestRegionProtocol_GetSyslogConfiguration(MAASTransactionServerTestCase): |
351 | + |
352 | + def test_get_proxy_configuration_is_registered(self): |
353 | + protocol = Region() |
354 | + responder = protocol.locateResponder( |
355 | + GetSyslogConfiguration.commandName) |
356 | + self.assertIsNotNone(responder) |
357 | + |
358 | + @wait_for_reactor |
359 | + @inlineCallbacks |
360 | + def test_returns_syslog_configuration(self): |
361 | + |
362 | + def _db_work(): |
363 | + port = random.randint(1000, 8000) |
364 | + Config.objects.set_config('maas_syslog_port', port) |
365 | + return port |
366 | + |
367 | + port = yield deferToDatabase(_db_work) |
368 | + |
369 | + system_id = factory.make_name("id") |
370 | + response = yield call_responder( |
371 | + Region(), GetSyslogConfiguration, {'system_id': system_id}) |
372 | + self.assertThat(response, Equals({ |
373 | + 'port': port, |
374 | + })) |
375 | diff --git a/src/maasserver/tests/test_compose_preseed.py b/src/maasserver/tests/test_compose_preseed.py |
376 | index 4332a1a..19c6d01 100644 |
377 | --- a/src/maasserver/tests/test_compose_preseed.py |
378 | +++ b/src/maasserver/tests/test_compose_preseed.py |
379 | @@ -438,6 +438,27 @@ class TestComposePreseed(MAASServerTestCase): |
380 | remote_syslog, |
381 | preseed['rsyslog']['remotes']['maas']) |
382 | |
383 | + def test_compose_preseed_uses_maas_syslog_port(self): |
384 | + syslog_port = factory.pick_port() |
385 | + Config.objects.set_config('maas_syslog_port', syslog_port) |
386 | + rack_controller = factory.make_RackController(url='') |
387 | + node = factory.make_Node( |
388 | + interface=True, status=NODE_STATUS.COMMISSIONING) |
389 | + nic = node.get_boot_interface() |
390 | + nic.vlan.dhcp_on = True |
391 | + nic.vlan.primary_rack = rack_controller |
392 | + nic.vlan.save() |
393 | + ip_address = factory.make_ipv4_address() |
394 | + node.boot_cluster_ip = ip_address |
395 | + node.save() |
396 | + request = make_HttpRequest() |
397 | + preseed = yaml.safe_load( |
398 | + compose_preseed( |
399 | + request, PRESEED_TYPE.COMMISSIONING, node)) |
400 | + self.assertEqual( |
401 | + '%s:%d' % (ip_address, syslog_port), |
402 | + preseed['rsyslog']['remotes']['maas']) |
403 | + |
404 | def test_compose_preseed_for_rescue_mode_does_not_include_poweroff(self): |
405 | rack_controller = factory.make_RackController() |
406 | node = factory.make_Node( |
407 | diff --git a/src/maasserver/tests/test_preseed.py b/src/maasserver/tests/test_preseed.py |
408 | index d77cff7..fe8c571 100644 |
409 | --- a/src/maasserver/tests/test_preseed.py |
410 | +++ b/src/maasserver/tests/test_preseed.py |
411 | @@ -470,6 +470,13 @@ class TestPreseedContext(MAASServerTestCase): |
412 | context = get_preseed_context(make_HttpRequest()) |
413 | self.assertEquals(remote_syslog, context['syslog_host_port']) |
414 | |
415 | + def test_get_preseed_context_uses_maas_syslog_port(self): |
416 | + syslog_port = factory.pick_port() |
417 | + Config.objects.set_config('maas_syslog_port', syslog_port) |
418 | + context = get_preseed_context(make_HttpRequest()) |
419 | + self.assertTrue( |
420 | + context['syslog_host_port'].endswith(':%d' % syslog_port)) |
421 | + |
422 | |
423 | class TestNodeDeprecatedPreseedContext( |
424 | PreseedRPCMixin, BootImageHelperMixin, MAASTransactionServerTestCase): |
425 | diff --git a/src/provisioningserver/rackdservices/external.py b/src/provisioningserver/rackdservices/external.py |
426 | index 7590b54..b661409 100644 |
427 | --- a/src/provisioningserver/rackdservices/external.py |
428 | +++ b/src/provisioningserver/rackdservices/external.py |
429 | @@ -33,6 +33,7 @@ from provisioningserver.rpc.region import ( |
430 | GetControllerType, |
431 | GetDNSConfiguration, |
432 | GetProxyConfiguration, |
433 | + GetSyslogConfiguration, |
434 | GetTimeConfiguration, |
435 | ) |
436 | from provisioningserver.service_monitor import service_monitor |
437 | @@ -326,12 +327,13 @@ class RackSyslog(RackOnlyExternalService): |
438 | d = maybeDeferred( |
439 | self._getConfiguration, |
440 | config.controller_type, |
441 | + config.syslog_configuration, |
442 | config.connections) |
443 | d.addCallback(self._maybeApplyConfiguration) |
444 | return d |
445 | |
446 | def _getConfiguration( |
447 | - self, controller_type, connections): |
448 | + self, controller_type, syslog_configuration, connections): |
449 | """Return syslog server configuration. |
450 | |
451 | The configuration object returned is comparable with previous and |
452 | @@ -341,6 +343,7 @@ class RackSyslog(RackOnlyExternalService): |
453 | forwarders = list(self._genRegionIps( |
454 | connections, bracket_ipv6=True, include_name=True)) |
455 | return _SyslogConfiguration( |
456 | + port=syslog_configuration['port'], |
457 | forwarders=forwarders, |
458 | is_region=controller_type["is_region"], |
459 | is_rack=controller_type["is_rack"]) |
460 | @@ -375,7 +378,8 @@ class RackSyslog(RackOnlyExternalService): |
461 | } |
462 | for name, ip in dict(configuration.forwarders).items() |
463 | ] |
464 | - syslog_config.write_config(False, forwarders=forwarders) |
465 | + syslog_config.write_config( |
466 | + False, forwarders=forwarders, port=configuration.port) |
467 | |
468 | |
469 | class RackExternalService(TimerService): |
470 | @@ -424,11 +428,14 @@ class RackExternalService(TimerService): |
471 | GetDNSConfiguration, system_id=client.localIdent) |
472 | proxy_configuration = yield client( |
473 | GetProxyConfiguration, system_id=client.localIdent) |
474 | + syslog_configuration = yield client( |
475 | + GetSyslogConfiguration, system_id=client.localIdent) |
476 | return _Configuration( |
477 | controller_type=controller_type, |
478 | time_configuration=time_configuration, |
479 | dns_configuration=dns_configuration, |
480 | proxy_configuration=proxy_configuration, |
481 | + syslog_configuration=syslog_configuration, |
482 | connections=self._rpc_service.connections |
483 | ) |
484 | |
485 | @@ -475,6 +482,9 @@ class _Configuration: |
486 | # Proxy configuration for the controller. |
487 | proxy_configuration = attr.ib(converter=dict) |
488 | |
489 | + # Syslog configuration for the controller. |
490 | + syslog_configuration = attr.ib(converter=dict) |
491 | + |
492 | # Current RPC connections for the controller. |
493 | connections = attr.ib(converter=dict) |
494 | |
495 | @@ -538,6 +548,9 @@ class _ProxyConfiguration: |
496 | class _SyslogConfiguration: |
497 | """Configuration for the rack's syslog server.""" |
498 | |
499 | + # Port to run syslog on. |
500 | + port = attr.ib(converter=int) |
501 | + |
502 | # Forwards to send the logs to. |
503 | forwarders = attr.ib(converter=frozenset) |
504 | |
505 | diff --git a/src/provisioningserver/rackdservices/tests/test_external.py b/src/provisioningserver/rackdservices/tests/test_external.py |
506 | index cc54d83..f703d24 100644 |
507 | --- a/src/provisioningserver/rackdservices/tests/test_external.py |
508 | +++ b/src/provisioningserver/rackdservices/tests/test_external.py |
509 | @@ -48,18 +48,20 @@ def prepareRegion( |
510 | test, *, is_region=False, is_rack=True, |
511 | servers=None, peers=None, trusted_networks=None, |
512 | proxy_enabled=True, proxy_port=8000, proxy_allowed_cidrs=None, |
513 | - proxy_prefer_v4_proxy=False): |
514 | + proxy_prefer_v4_proxy=False, syslog_port=5247): |
515 | """Set up a mock region controller. |
516 | |
517 | It responds to `GetControllerType`, `GetTimeConfiguration`, |
518 | - `GetDNSConfiguration` and `GetProxyConfiguration`. |
519 | + `GetDNSConfiguration`, `GetProxyConfiguration`, and |
520 | + `GetSyslogConfiguration`. |
521 | |
522 | :return: The running RPC service, and the protocol instance. |
523 | """ |
524 | fixture = test.useFixture(MockLiveClusterToRegionRPCFixture()) |
525 | protocol, connecting = fixture.makeEventLoop( |
526 | region.GetControllerType, region.GetTimeConfiguration, |
527 | - region.GetDNSConfiguration, region.GetProxyConfiguration) |
528 | + region.GetDNSConfiguration, region.GetProxyConfiguration, |
529 | + region.GetSyslogConfiguration) |
530 | protocol.RegisterRackController.side_effect = always_succeed_with( |
531 | {"system_id": factory.make_name("maas-id")}) |
532 | protocol.GetControllerType.side_effect = always_succeed_with( |
533 | @@ -80,6 +82,9 @@ def prepareRegion( |
534 | "allowed_cidrs": proxy_allowed_cidrs, |
535 | "prefer_v4_proxy": proxy_prefer_v4_proxy, |
536 | }) |
537 | + protocol.GetSyslogConfiguration.side_effect = always_succeed_with({ |
538 | + "port": syslog_port, |
539 | + }) |
540 | |
541 | def connected(teardown): |
542 | test.addCleanup(teardown) |
543 | @@ -706,9 +711,10 @@ class TestRackSyslog(MAASTestCase): |
544 | |
545 | @inlineCallbacks |
546 | def test__getConfiguration_returns_configuration_object(self): |
547 | + port = factory.pick_port() |
548 | is_region, is_rack = factory.pick_bool(), factory.pick_bool() |
549 | rpc_service, protocol = yield prepareRegion( |
550 | - self, is_region=is_region, is_rack=is_rack) |
551 | + self, is_region=is_region, is_rack=is_rack, syslog_port=port) |
552 | forwarders = self.extract_regions(rpc_service) |
553 | service, syslog = self.make_RackSyslog_ExternalService( |
554 | rpc_service, reactor) |
555 | @@ -717,18 +723,20 @@ class TestRackSyslog(MAASTestCase): |
556 | |
557 | config = yield service._getConfiguration() |
558 | observed = syslog._getConfiguration( |
559 | - config.controller_type, config.connections) |
560 | + config.controller_type, config.syslog_configuration, |
561 | + config.connections) |
562 | |
563 | self.assertThat(observed, IsInstance(external._SyslogConfiguration)) |
564 | self.assertThat( |
565 | observed, MatchesStructure.byEquality( |
566 | - forwarders=forwarders, |
567 | + port=port, forwarders=forwarders, |
568 | is_region=is_region, is_rack=is_rack)) |
569 | |
570 | @inlineCallbacks |
571 | def test__tryUpdate_updates_syslog_server(self): |
572 | self.useFixture(MAASRootFixture()) |
573 | - rpc_service, _ = yield prepareRegion(self) |
574 | + port = factory.pick_port() |
575 | + rpc_service, _ = yield prepareRegion(self, syslog_port=port) |
576 | forwarders = self.extract_regions(rpc_service) |
577 | service, _ = self.make_RackSyslog_ExternalService(rpc_service, reactor) |
578 | |
579 | @@ -751,7 +759,7 @@ class TestRackSyslog(MAASTestCase): |
580 | self.assertThat( |
581 | write_config, |
582 | MockCalledOnceWith( |
583 | - False, forwarders=expected_forwards)) |
584 | + False, forwarders=expected_forwards, port=port)) |
585 | self.assertThat( |
586 | service_monitor.restartService, MockCalledOnceWith("syslog_rack")) |
587 | # If the configuration has not changed then a second call to |
588 | @@ -760,7 +768,7 @@ class TestRackSyslog(MAASTestCase): |
589 | self.assertThat( |
590 | write_config, |
591 | MockCalledOnceWith( |
592 | - False, forwarders=expected_forwards)) |
593 | + False, forwarders=expected_forwards, port=port)) |
594 | self.assertThat( |
595 | service_monitor.restartService, MockCalledOnceWith("syslog_rack")) |
596 | |
597 | diff --git a/src/provisioningserver/rpc/region.py b/src/provisioningserver/rpc/region.py |
598 | index 0d31708..4492f78 100644 |
599 | --- a/src/provisioningserver/rpc/region.py |
600 | +++ b/src/provisioningserver/rpc/region.py |
601 | @@ -657,3 +657,20 @@ class GetProxyConfiguration(amp.Command): |
602 | errors = { |
603 | NoSuchNode: b"NoSuchNode", |
604 | } |
605 | + |
606 | + |
607 | +class GetSyslogConfiguration(amp.Command): |
608 | + """Get settings to use for configuring syslog for a given system identifier. |
609 | + |
610 | + :since: 2.5 |
611 | + """ |
612 | + |
613 | + arguments = [ |
614 | + (b"system_id", amp.Unicode()), |
615 | + ] |
616 | + response = [ |
617 | + (b"port", amp.Integer()), |
618 | + ] |
619 | + errors = { |
620 | + NoSuchNode: b"NoSuchNode", |
621 | + } |
622 | diff --git a/src/provisioningserver/syslog/config.py b/src/provisioningserver/syslog/config.py |
623 | index a90a78f..6fcec86 100644 |
624 | --- a/src/provisioningserver/syslog/config.py |
625 | +++ b/src/provisioningserver/syslog/config.py |
626 | @@ -67,7 +67,7 @@ def is_config_present(): |
627 | |
628 | |
629 | @synchronous |
630 | -def write_config(write_local, forwarders=None): |
631 | +def write_config(write_local, forwarders=None, port=None): |
632 | """Write the syslog configuration.""" |
633 | context = { |
634 | 'user': 'maas', |
635 | @@ -76,6 +76,7 @@ def write_config(write_local, forwarders=None): |
636 | 'work_dir': get_syslog_workdir_path(), |
637 | 'log_dir': get_syslog_log_path(), |
638 | 'write_local': write_local, |
639 | + 'port': port if port else 5247, |
640 | 'forwarders': ( |
641 | sorted(forwarders, key=itemgetter('name')) |
642 | if forwarders is not None else []), |
643 | diff --git a/src/provisioningserver/syslog/tests/test_config.py b/src/provisioningserver/syslog/tests/test_config.py |
644 | index b45e829..e17ab05 100644 |
645 | --- a/src/provisioningserver/syslog/tests/test_config.py |
646 | +++ b/src/provisioningserver/syslog/tests/test_config.py |
647 | @@ -92,6 +92,17 @@ class TestWriteConfig(MAASTestCase): |
648 | "%s/%s" % (self.tmpdir, config.MAAS_SYSLOG_CONF_NAME), |
649 | FileContains(matcher=MatchesAll(matcher_one, matcher_two))) |
650 | |
651 | + def test__udp_and_tcp_both_use_different_port(self): |
652 | + port = factory.pick_port() |
653 | + config.write_config(False, port=port) |
654 | + matcher_one = Contains( |
655 | + 'input(type="imtcp" port="%d")' % port) |
656 | + matcher_two = Contains( |
657 | + 'input(type="imudp" port="%d")' % port) |
658 | + self.assertThat( |
659 | + "%s/%s" % (self.tmpdir, config.MAAS_SYSLOG_CONF_NAME), |
660 | + FileContains(matcher=MatchesAll(matcher_one, matcher_two))) |
661 | + |
662 | def test__adds_tcp_stop(self): |
663 | cidr = factory.make_ipv4_network() |
664 | config.write_config([cidr]) |
665 | @@ -140,6 +151,24 @@ class TestWriteConfig(MAASTestCase): |
666 | self.assertLinesContain( |
667 | 'queue.filename="%s"' % host['name'], lines) |
668 | |
669 | + def test__forwarders_diff_port(self): |
670 | + port = factory.pick_port() |
671 | + forwarders = [ |
672 | + { |
673 | + 'ip': factory.make_ip_address(), |
674 | + 'name': factory.make_name('name') |
675 | + } |
676 | + for _ in range(3) |
677 | + ] |
678 | + config.write_config(False, forwarders, port=port) |
679 | + with self.syslog_path.open() as syslog_file: |
680 | + lines = [line.strip() for line in syslog_file.readlines()] |
681 | + for host in forwarders: |
682 | + self.assertLinesContain( |
683 | + 'target="%s"' % host['ip'], lines) |
684 | + self.assertLinesContain( |
685 | + 'port="%d"' % port, lines) |
686 | + |
687 | def test__write_local_and_forwarders(self): |
688 | forwarders = [ |
689 | { |
690 | diff --git a/src/provisioningserver/templates/syslog/rsyslog.conf.template b/src/provisioningserver/templates/syslog/rsyslog.conf.template |
691 | index 8c43a19..d7a3a1d 100644 |
692 | --- a/src/provisioningserver/templates/syslog/rsyslog.conf.template |
693 | +++ b/src/provisioningserver/templates/syslog/rsyslog.conf.template |
694 | @@ -4,11 +4,11 @@ input(type="imuxsock" socket="{{work_dir}}/log") |
695 | |
696 | # Enable tcp. |
697 | module(load="imtcp") |
698 | -input(type="imtcp" port="5247") |
699 | +input(type="imtcp" port="{{port}}") |
700 | |
701 | # Enable udp. |
702 | module(load="imudp") |
703 | -input(type="imudp" port="5247") |
704 | +input(type="imudp" port="{{port}}") |
705 | |
706 | # Reduce message repetition. |
707 | $RepeatedMsgReduction on |
708 | @@ -55,7 +55,7 @@ if $syslogtag contains "maas" { |
709 | # Forward all UDP messages to all the region controllers. TCP messages will |
710 | # not make it to this point because of the stop above. |
711 | {{for server in forwarders}} |
712 | -*.* action(type="omfwd" target="{{server['ip']}}" port="5247" protocol="tcp" |
713 | +*.* action(type="omfwd" target="{{server['ip']}}" port="{{port}}" protocol="tcp" |
714 | action.resumeRetryCount="-1" |
715 | queue.type="LinkedList" queue.filename="{{server['name']}}" |
716 | queue.saveonshutdown="on") |
UNIT TESTS
-b custom-syslog-port lp:~blake-rouse/maas/+git/maas into -b master lp:~maas-committers/maas
STATUS: SUCCESS acbb5923b9694c4 be53c609eb
COMMIT: 01867cf6a86575f