Merge ~blake-rouse/maas:custom-syslog-port into maas: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)
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.

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b custom-syslog-port lp:~blake-rouse/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 01867cf6a86575facbb5923b9694c4be53c609eb

review: Approve
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 :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/compose_preseed.py b/src/maasserver/compose_preseed.py
2index 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():
24diff --git a/src/maasserver/forms/settings.py b/src/maasserver/forms/settings.py
25index 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,
87diff --git a/src/maasserver/forms/tests/test_settings.py b/src/maasserver/forms/tests/test_settings.py
88index 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')
114diff --git a/src/maasserver/models/config.py b/src/maasserver/models/config.py
115index 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()),
126diff --git a/src/maasserver/preseed.py b/src/maasserver/preseed.py
127index 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,
146diff --git a/src/maasserver/regiondservices/syslog.py b/src/maasserver/regiondservices/syslog.py
147index 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)
186diff --git a/src/maasserver/regiondservices/tests/test_syslog.py b/src/maasserver/regiondservices/tests/test_syslog.py
187index 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`.
235diff --git a/src/maasserver/rpc/boot.py b/src/maasserver/rpc/boot.py
236index 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:
255diff --git a/src/maasserver/rpc/regionservice.py b/src/maasserver/rpc/regionservice.py
256index 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):
285diff --git a/src/maasserver/rpc/tests/test_boot.py b/src/maasserver/rpc/tests/test_boot.py
286index 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()
332diff --git a/src/maasserver/rpc/tests/test_regionservice_calls.py b/src/maasserver/rpc/tests/test_regionservice_calls.py
333index 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+ }))
375diff --git a/src/maasserver/tests/test_compose_preseed.py b/src/maasserver/tests/test_compose_preseed.py
376index 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(
407diff --git a/src/maasserver/tests/test_preseed.py b/src/maasserver/tests/test_preseed.py
408index 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):
425diff --git a/src/provisioningserver/rackdservices/external.py b/src/provisioningserver/rackdservices/external.py
426index 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
505diff --git a/src/provisioningserver/rackdservices/tests/test_external.py b/src/provisioningserver/rackdservices/tests/test_external.py
506index 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
597diff --git a/src/provisioningserver/rpc/region.py b/src/provisioningserver/rpc/region.py
598index 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+ }
622diff --git a/src/provisioningserver/syslog/config.py b/src/provisioningserver/syslog/config.py
623index 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 []),
643diff --git a/src/provisioningserver/syslog/tests/test_config.py b/src/provisioningserver/syslog/tests/test_config.py
644index 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 {
690diff --git a/src/provisioningserver/templates/syslog/rsyslog.conf.template b/src/provisioningserver/templates/syslog/rsyslog.conf.template
691index 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")

Subscribers

People subscribed via source and target branches