Merge ~mpontillo/maas:alternate-ntp-servers-in-dhcp--bug-1753496 into maas:master

Proposed by Mike Pontillo
Status: Merged
Approved by: Mike Pontillo
Approved revision: f6eb1e941449439156745339969bb0a144983b50
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~mpontillo/maas:alternate-ntp-servers-in-dhcp--bug-1753496
Merge into: maas:master
Diff against target: 183 lines (+80/-28)
2 files modified
src/maasserver/dhcp.py (+49/-26)
src/maasserver/tests/test_dhcp.py (+31/-2)
Reviewer Review Type Date Requested Status
Andres Rodriguez (community) Approve
MAAS Lander Needs Fixing
Review via email: mp+343152@code.launchpad.net

Commit message

LP: #1753496 - Add NTP server for peer rack, if appropriate.

To post a comment you must log in.
Revision history for this message
Paul Gear (paulgear) wrote :

I'm not familiar with how racks, etc. are represented within the MAAS code, but will this produce a list of *all* NTP servers available in the region, or just one additional from a secondary rack?

NTP best practice is to have at least 4 servers, if possible: https://tools.ietf.org/html/draft-ietf-ntp-bcp-06#section-4.1, so if MAAS knows about more NTP servers, I think it should add them all.

Revision history for this message
Andres Rodriguez (andreserl) wrote :

Machines deployed by MAAS only have access to the rack controllers for DHCP (unless they are region/racks). In HA configurations we only have 2 rack controllers, and such we would have 2 NTP servers for NTP.

That said, these changes here are only apply to DHCP. MAAS only relies on DHCP for commissioning or ephemeral environments. For deployments the machines get statically configured from a different code path that gathers all NTP servers in the specific VLAN.

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b alternate-ntp-servers-in-dhcp--bug-1753496 lp:~mpontillo/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/2454/console
COMMIT: b49877a5bf10fbbeb913f8bc7b258a6a5340b56a

review: Needs Fixing
f6eb1e9... by Mike Pontillo

Fix lint.

Revision history for this message
Andres Rodriguez (andreserl) wrote :

+1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/src/maasserver/dhcp.py b/src/maasserver/dhcp.py
index 1d07cc0..d98134c 100644
--- a/src/maasserver/dhcp.py
+++ b/src/maasserver/dhcp.py
@@ -398,7 +398,7 @@ def make_pools_for_subnet(subnet, failover_peer=None):
398def make_subnet_config(398def make_subnet_config(
399 rack_controller, subnet, default_dns_servers: Optional[list],399 rack_controller, subnet, default_dns_servers: Optional[list],
400 ntp_servers: Union[list, dict], default_domain, search_list=None,400 ntp_servers: Union[list, dict], default_domain, search_list=None,
401 failover_peer=None, subnets_dhcp_snippets: list=None):401 failover_peer=None, subnets_dhcp_snippets: list=None, peer_rack=None):
402 """Return DHCP subnet configuration dict for a rack interface.402 """Return DHCP subnet configuration dict for a rack interface.
403403
404 :param ntp_servers: Either a list of NTP server addresses or hostnames to404 :param ntp_servers: Either a list of NTP server addresses or hostnames to
@@ -416,15 +416,6 @@ def make_subnet_config(
416 if subnets_dhcp_snippets is None:416 if subnets_dhcp_snippets is None:
417 subnets_dhcp_snippets = []417 subnets_dhcp_snippets = []
418418
419 if isinstance(ntp_servers, dict):
420 try:
421 ntp_server = ntp_servers[
422 subnet.vlan.space_id, subnet.get_ip_version()]
423 except KeyError:
424 ntp_servers = []
425 else:
426 ntp_servers = [ntp_server]
427
428 subnet_config = {419 subnet_config = {
429 'subnet': str(ip_network.network),420 'subnet': str(ip_network.network),
430 'subnet_mask': str(ip_network.netmask),421 'subnet_mask': str(ip_network.netmask),
@@ -434,7 +425,7 @@ def make_subnet_config(
434 '' if not subnet.gateway_ip425 '' if not subnet.gateway_ip
435 else str(subnet.gateway_ip)),426 else str(subnet.gateway_ip)),
436 'dns_servers': dns_servers,427 'dns_servers': dns_servers,
437 'ntp_servers': ntp_servers,428 'ntp_servers': get_ntp_servers(ntp_servers, subnet, peer_rack),
438 'domain_name': default_domain.name,429 'domain_name': default_domain.name,
439 'pools': make_pools_for_subnet(subnet, failover_peer),430 'pools': make_pools_for_subnet(subnet, failover_peer),
440 'dhcp_snippets': [431 'dhcp_snippets': [
@@ -454,18 +445,47 @@ def make_failover_peer_config(vlan, rack_controller):
454 interface_ip_address = get_ip_address_for_rack_controller(445 interface_ip_address = get_ip_address_for_rack_controller(
455 rack_controller, vlan)446 rack_controller, vlan)
456 if is_primary:447 if is_primary:
457 peer_address = get_ip_address_for_rack_controller(448 peer_rack = vlan.secondary_rack
458 vlan.secondary_rack, vlan)
459 else:449 else:
460 peer_address = get_ip_address_for_rack_controller(450 peer_rack = vlan.primary_rack
461 vlan.primary_rack, vlan)451 peer_address = get_ip_address_for_rack_controller(peer_rack, vlan)
462 name = "failover-vlan-%d" % vlan.id452 name = "failover-vlan-%d" % vlan.id
463 return name, {453 return (
464 "name": name,454 name,
465 "mode": "primary" if is_primary else "secondary",455 {
466 "address": str(interface_ip_address.ip),456 "name": name,
467 "peer_address": str(peer_address.ip),457 "mode": "primary" if is_primary else "secondary",
468 }458 "address": str(interface_ip_address.ip),
459 "peer_address": str(peer_address.ip),
460 },
461 peer_rack
462 )
463
464
465def get_ntp_servers(ntp_servers, subnet, peer_rack):
466 """Return the list of NTP servers, based on the initial input list of
467 servers or dictionary, the subnet the servers will be advertised on,
468 and the peer rack controller (if present).
469 """
470 if isinstance(ntp_servers, dict):
471 # If ntp_servers is a dict, that means it maps each
472 # (space_id, address_family) to the best NTP server for that space.
473 # If it's already a list, that means we're just using the external
474 # NTP server(s).
475 space_address_family = (subnet.vlan.space_id, subnet.get_ip_version())
476 ntp_server = ntp_servers.get(space_address_family)
477 if ntp_server is None:
478 return []
479 else:
480 if peer_rack is not None:
481 alternates = get_ntp_server_addresses_for_rack(peer_rack)
482 alternate_ntp_server = alternates.get(space_address_family)
483 if alternate_ntp_server is not None:
484 return [ntp_server, alternate_ntp_server]
485 return [ntp_server]
486 else:
487 # Return the original input; it was already a list.
488 return ntp_servers
469489
470490
471@typed491@typed
@@ -487,12 +507,14 @@ def get_dhcp_configure_for(
487 rack_controller, vlan, ip_version)507 rack_controller, vlan, ip_version)
488 interface = get_best_interface(interfaces)508 interface = get_best_interface(interfaces)
489509
490 # Generate the failover peer for this VLAN.510 has_secondary = vlan.secondary_rack_id is not None
491 if vlan.secondary_rack_id is not None:511
492 peer_name, peer_config = make_failover_peer_config(512 if has_secondary:
513 # Generate the failover peer for this VLAN.
514 peer_name, peer_config, peer_rack = make_failover_peer_config(
493 vlan, rack_controller)515 vlan, rack_controller)
494 else:516 else:
495 peer_name, peer_config = None, None517 peer_name, peer_config, peer_rack = None, None, None
496518
497 if dhcp_snippets is None:519 if dhcp_snippets is None:
498 dhcp_snippets = []520 dhcp_snippets = []
@@ -510,7 +532,8 @@ def get_dhcp_configure_for(
510 subnet_configs.append(532 subnet_configs.append(
511 make_subnet_config(533 make_subnet_config(
512 rack_controller, subnet, maas_dns_servers, ntp_servers,534 rack_controller, subnet, maas_dns_servers, ntp_servers,
513 domain, search_list, peer_name, subnets_dhcp_snippets))535 domain, search_list, peer_name, subnets_dhcp_snippets,
536 peer_rack))
514537
515 # Generate the hosts for all subnets.538 # Generate the hosts for all subnets.
516 hosts = make_hosts_for_subnets(subnets, nodes_dhcp_snippets)539 hosts = make_hosts_for_subnets(subnets, nodes_dhcp_snippets)
diff --git a/src/maasserver/tests/test_dhcp.py b/src/maasserver/tests/test_dhcp.py
index 5680ae7..ea601bd 100644
--- a/src/maasserver/tests/test_dhcp.py
+++ b/src/maasserver/tests/test_dhcp.py
@@ -897,6 +897,35 @@ class TestMakeSubnetConfig(MAASServerTestCase):
897 rack_controller, subnet, [""], ntp_servers, default_domain)897 rack_controller, subnet, [""], ntp_servers, default_domain)
898 self.expectThat(config['ntp_servers'], Equals([address.ip]))898 self.expectThat(config['ntp_servers'], Equals([address.ip]))
899899
900 def test__sets_ntp_from_dict_argument_with_alternates(self):
901 r1 = factory.make_RackController(interface=False)
902 r2 = factory.make_RackController(interface=False)
903 vlan = factory.make_VLAN(primary_rack=r1, secondary_rack=r2)
904 subnet = factory.make_Subnet(vlan=vlan, dns_servers=[], space=None)
905 r1_eth0 = factory.make_Interface(
906 INTERFACE_TYPE.PHYSICAL, vlan=vlan, node=r1)
907 r2_eth0 = factory.make_Interface(
908 INTERFACE_TYPE.PHYSICAL, vlan=vlan, node=r2)
909 a1 = factory.make_StaticIPAddress(
910 interface=r1_eth0, subnet=subnet,
911 alloc_type=IPADDRESS_TYPE.STICKY)
912 a2 = factory.make_StaticIPAddress(
913 interface=r2_eth0, subnet=subnet,
914 alloc_type=IPADDRESS_TYPE.STICKY)
915 r1_ntp_servers = {
916 (vlan.space_id, subnet.get_ipnetwork().version): a1.ip,
917 }
918 r2_ntp_servers = {
919 (vlan.space_id, subnet.get_ipnetwork().version): a2.ip,
920 }
921 default_domain = Domain.objects.get_default_domain()
922 config = dhcp.make_subnet_config(
923 r1, subnet, [""], r1_ntp_servers, default_domain, peer_rack=r2)
924 self.expectThat(config['ntp_servers'], Equals([a1.ip, a2.ip]))
925 config = dhcp.make_subnet_config(
926 r2, subnet, [""], r2_ntp_servers, default_domain, peer_rack=r1)
927 self.expectThat(config['ntp_servers'], Equals([a2.ip, a1.ip]))
928
900 def test__overrides_ipv4_dns_from_subnet(self):929 def test__overrides_ipv4_dns_from_subnet(self):
901 rack_controller = factory.make_RackController(interface=False)930 rack_controller = factory.make_RackController(interface=False)
902 vlan = factory.make_VLAN()931 vlan = factory.make_VLAN()
@@ -1351,7 +1380,7 @@ class TestMakeFailoverPeerConfig(MAASServerTestCase):
1351 "mode": "primary",1380 "mode": "primary",
1352 "address": str(primary_ip.ip),1381 "address": str(primary_ip.ip),
1353 "peer_address": str(secondary_ip.ip),1382 "peer_address": str(secondary_ip.ip),
1354 }), dhcp.make_failover_peer_config(vlan, primary_rack))1383 }, secondary_rack), dhcp.make_failover_peer_config(vlan, primary_rack))
13551384
1356 def test__renders_config_for_secondary(self):1385 def test__renders_config_for_secondary(self):
1357 primary_rack = factory.make_RackController()1386 primary_rack = factory.make_RackController()
@@ -1376,7 +1405,7 @@ class TestMakeFailoverPeerConfig(MAASServerTestCase):
1376 "mode": "secondary",1405 "mode": "secondary",
1377 "address": str(secondary_ip.ip),1406 "address": str(secondary_ip.ip),
1378 "peer_address": str(primary_ip.ip),1407 "peer_address": str(primary_ip.ip),
1379 }), dhcp.make_failover_peer_config(vlan, secondary_rack))1408 }, primary_rack), dhcp.make_failover_peer_config(vlan, secondary_rack))
13801409
13811410
1382class TestGetDHCPConfigureFor(MAASServerTestCase):1411class TestGetDHCPConfigureFor(MAASServerTestCase):

Subscribers

People subscribed via source and target branches