diff --git a/releasenotes/notes/ctlplane-undercloud-conf-host-routes-7084bf696020c39e.yaml b/releasenotes/notes/ctlplane-undercloud-conf-host-routes-7084bf696020c39e.yaml new file mode 100644 index 000000000..53f8785a1 --- /dev/null +++ b/releasenotes/notes/ctlplane-undercloud-conf-host-routes-7084bf696020c39e.yaml @@ -0,0 +1,11 @@ +--- +features: + - | + A new option ``host_routes`` are now available for subnet defenitions in + ``undercloud.conf``. + + - Host routes specified for the *local_subnet* will be added to + the routing table on the Undercloud. + - Host routes for all subnets are passed to tripleo-heat-templates so that + the *host_routes* property of the ctlplane subnets are set accordingly + when installing the Undercloud. diff --git a/tripleoclient/config/undercloud.py b/tripleoclient/config/undercloud.py index 2bb9bf0c3..8030bc5d7 100644 --- a/tripleoclient/config/undercloud.py +++ b/tripleoclient/config/undercloud.py @@ -47,6 +47,10 @@ GATEWAY_HELP_STR = _( 'on this network.') MASQUERADE_HELP_STR = _( 'The network will be masqueraded for external access.') +HOST_ROUTES_HELP_STR = _( + 'Host routes for the Neutron-managed subnet for the Overcloud instances ' + 'on this network. The host routes on the local_subnet will also be ' + 'configured on the undercloud.') # Deprecated options _deprecated_opt_network_gateway = [cfg.DeprecatedOpt( @@ -370,6 +374,13 @@ class UndercloudConfig(StandaloneConfig): cfg.BoolOpt('masquerade', default=False, help=MASQUERADE_HELP_STR), + cfg.ListOpt('host_routes', + item_type=cfg.types.Dict(bounds=True), + bounds=True, + default=[], + sample_default=('[{destination: 10.10.10.0, ' + 'nexthop: 192.168.24.1}]'), + help=HOST_ROUTES_HELP_STR), ] return self.sort_opts(_subnets_opts) @@ -393,6 +404,11 @@ class UndercloudConfig(StandaloneConfig): cfg.BoolOpt('masquerade', default=False, help=MASQUERADE_HELP_STR), + cfg.ListOpt('host_routes', + item_type=cfg.types.Dict(bounds=True), + bounds=True, + default=[], + help=HOST_ROUTES_HELP_STR), ] return self.sort_opts(_subnets_opts) diff --git a/tripleoclient/tests/config/test_config_undercloud.py b/tripleoclient/tests/config/test_config_undercloud.py index 6957f016a..293c2a551 100644 --- a/tripleoclient/tests/config/test_config_undercloud.py +++ b/tripleoclient/tests/config/test_config_undercloud.py @@ -147,6 +147,7 @@ class TestUndercloudConfig(base.TestCase): 'dhcp_exclude', 'dhcp_start', 'gateway', + 'host_routes', 'inspection_iprange', 'masquerade'] diff --git a/tripleoclient/tests/v1/undercloud/test_config.py b/tripleoclient/tests/v1/undercloud/test_config.py index 83c48e2dd..4a46d47ed 100644 --- a/tripleoclient/tests/v1/undercloud/test_config.py +++ b/tripleoclient/tests/v1/undercloud/test_config.py @@ -141,7 +141,10 @@ class TestNetworkSettings(base.TestCase): cfg.ListOpt('dhcp_exclude'), cfg.StrOpt('inspection_iprange'), cfg.StrOpt('gateway'), - cfg.BoolOpt('masquerade')] + cfg.BoolOpt('masquerade'), + cfg.ListOpt('host_routes', + item_type=cfg.types.Dict(bounds=True), + bounds=True,)] self.conf.register_opts(self.opts, group=self.grp0) self.grp1 = cfg.OptGroup(name='subnet1', title='subnet1') self.grp2 = cfg.OptGroup(name='subnet2', title='subnet2') @@ -152,6 +155,7 @@ class TestNetworkSettings(base.TestCase): inspection_iprange='192.168.24.100,192.168.24.120', gateway='192.168.24.1', masquerade=False, + host_routes=[], group='ctlplane-subnet') def test_default(self): @@ -170,6 +174,7 @@ class TestNetworkSettings(base.TestCase): 'ctlplane-subnet': { 'AllocationPools': [ {'start': '192.168.24.5', 'end': '192.168.24.24'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.24.0/24', 'NetworkGateway': '192.168.24.1'}}} self.assertEqual(expected, env) @@ -203,6 +208,7 @@ class TestNetworkSettings(base.TestCase): 'AllocationPools': [ {'start': '192.168.24.4', 'end': '192.168.24.99'}, {'start': '192.168.24.121', 'end': '192.168.24.254'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.24.0/24', 'NetworkGateway': '192.168.24.1'}}} self.assertEqual(expected, env) @@ -228,6 +234,7 @@ class TestNetworkSettings(base.TestCase): 'AllocationPools': [ {'start': '192.168.10.2', 'end': '192.168.10.99'}, {'start': '192.168.10.121', 'end': '192.168.10.254'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.10.0/24', 'NetworkGateway': '192.168.10.1'}}} self.assertEqual(expected, env) @@ -257,6 +264,7 @@ class TestNetworkSettings(base.TestCase): {'start': '192.168.10.51', 'end': '192.168.10.79'}, {'start': '192.168.10.90', 'end': '192.168.10.99'}, {'start': '192.168.10.121', 'end': '192.168.10.254'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.10.0/24', 'NetworkGateway': '192.168.10.1'}}} self.assertEqual(expected, env) @@ -281,6 +289,7 @@ class TestNetworkSettings(base.TestCase): 'AllocationPools': [ {'start': '192.168.24.4', 'end': '192.168.24.99'}, {'start': '192.168.24.121', 'end': '192.168.24.254'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.24.0/24', 'NetworkGateway': '192.168.24.1'}}} self.assertEqual(expected, env) @@ -305,6 +314,7 @@ class TestNetworkSettings(base.TestCase): 'AllocationPools': [ {'start': '192.168.24.10', 'end': '192.168.24.99'}, {'start': '192.168.24.121', 'end': '192.168.24.254'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.24.0/24', 'NetworkGateway': '192.168.24.1'}} } @@ -330,6 +340,7 @@ class TestNetworkSettings(base.TestCase): 'AllocationPools': [ {'start': '192.168.24.4', 'end': '192.168.24.99'}, {'start': '192.168.24.121', 'end': '192.168.24.220'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.24.0/24', 'NetworkGateway': '192.168.24.1'}} } @@ -347,6 +358,7 @@ class TestNetworkSettings(base.TestCase): dhcp_exclude=[], inspection_iprange='192.168.10.100,192.168.10.189', gateway='192.168.10.254', + host_routes=[], masquerade=True, group='subnet1') self.conf.config(cidr='192.168.20.0/24', @@ -355,6 +367,7 @@ class TestNetworkSettings(base.TestCase): dhcp_exclude=[], inspection_iprange='192.168.20.100,192.168.20.189', gateway='192.168.20.254', + host_routes=[], masquerade=True, group='subnet2') env = {} @@ -393,16 +406,19 @@ class TestNetworkSettings(base.TestCase): 'ctlplane-subnet': { 'AllocationPools': [ {'start': '192.168.24.5', 'end': '192.168.24.24'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.24.0/24', 'NetworkGateway': '192.168.24.1'}, 'subnet1': { 'AllocationPools': [ {'start': '192.168.10.10', 'end': '192.168.10.99'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.10.0/24', 'NetworkGateway': '192.168.10.254'}, 'subnet2': { 'AllocationPools': [ {'start': '192.168.20.10', 'end': '192.168.20.99'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.20.0/24', 'NetworkGateway': '192.168.20.254'} } @@ -419,6 +435,7 @@ class TestNetworkSettings(base.TestCase): dhcp_exclude=[], inspection_iprange='192.168.10.100,192.168.10.189', gateway='192.168.10.254', + host_routes=[], group='subnet1') self.conf.config(cidr='192.168.20.0/24', dhcp_start='192.168.20.10', @@ -426,6 +443,7 @@ class TestNetworkSettings(base.TestCase): dhcp_exclude=[], inspection_iprange='192.168.20.100,192.168.20.189', gateway='192.168.20.254', + host_routes=[], group='subnet2') env = {} undercloud_config._process_network_args(env) @@ -454,16 +472,19 @@ class TestNetworkSettings(base.TestCase): 'ctlplane-subnet': { 'AllocationPools': [ {'start': '192.168.24.5', 'end': '192.168.24.24'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.24.0/24', 'NetworkGateway': '192.168.24.1'}, 'subnet1': { 'AllocationPools': [ {'start': '192.168.10.10', 'end': '192.168.10.99'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.10.0/24', 'NetworkGateway': '192.168.10.254'}, 'subnet2': { 'AllocationPools': [ {'start': '192.168.20.10', 'end': '192.168.20.99'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.20.0/24', 'NetworkGateway': '192.168.20.254'} } @@ -477,6 +498,7 @@ class TestNetworkSettings(base.TestCase): dhcp_exclude=[], inspection_iprange='192.168.10.200,192.168.10.254', gateway='192.168.10.254', + host_routes=[], masquerade=False, group='subnet1') env = {} @@ -501,11 +523,13 @@ class TestNetworkSettings(base.TestCase): 'ctlplane-subnet': { 'AllocationPools': [ {'start': '192.168.24.5', 'end': '192.168.24.24'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.24.0/24', 'NetworkGateway': '192.168.24.1'}, 'subnet1': { 'AllocationPools': [ {'start': '192.168.10.1', 'end': '192.168.10.199'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.10.0/24', 'NetworkGateway': '192.168.10.254'} } @@ -519,6 +543,7 @@ class TestNetworkSettings(base.TestCase): dhcp_exclude=[], inspection_iprange='192.168.10.100,192.168.10.199', gateway='192.168.10.222', + host_routes=[], masquerade=False, group='subnet1') env = {} @@ -543,6 +568,7 @@ class TestNetworkSettings(base.TestCase): 'ctlplane-subnet': { 'AllocationPools': [ {'start': '192.168.24.5', 'end': '192.168.24.24'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.24.0/24', 'NetworkGateway': '192.168.24.1'}, 'subnet1': { @@ -550,12 +576,88 @@ class TestNetworkSettings(base.TestCase): {'start': '192.168.10.1', 'end': '192.168.10.99'}, {'start': '192.168.10.200', 'end': '192.168.10.221'}, {'start': '192.168.10.223', 'end': '192.168.10.254'}], + 'HostRoutes': [], 'NetworkCidr': '192.168.10.0/24', 'NetworkGateway': '192.168.10.222'} } } self.assertEqual(expected, env) + def test_additional_host_routes(self): + self.conf.config(subnets=['ctlplane-subnet', 'subnet1', 'subnet2']) + self.conf.config(host_routes=[{'destination': '10.10.10.254/32', + 'nexthop': '192.168.24.1'}], + group='ctlplane-subnet') + self.conf.register_opts(self.opts, group=self.grp1) + self.conf.register_opts(self.opts, group=self.grp2) + self.conf.config(cidr='192.168.10.0/24', + dhcp_start='192.168.10.10', + dhcp_end='192.168.10.99', + dhcp_exclude=[], + inspection_iprange='192.168.10.100,192.168.10.189', + gateway='192.168.10.254', + host_routes=[{'destination': '10.10.10.254/32', + 'nexthop': '192.168.10.254'}], + group='subnet1') + self.conf.config(cidr='192.168.20.0/24', + dhcp_start='192.168.20.10', + dhcp_end='192.168.20.99', + dhcp_exclude=[], + inspection_iprange='192.168.20.100,192.168.20.189', + gateway='192.168.20.254', + host_routes=[{'destination': '10.10.10.254/32', + 'nexthop': '192.168.20.254'}], + group='subnet2') + env = {} + undercloud_config._process_network_args(env) + expected = { + 'ControlPlaneStaticRoutes': [ + {'ip_netmask': '192.168.10.0/24', 'next_hop': '192.168.24.1'}, + {'ip_netmask': '192.168.20.0/24', 'next_hop': '192.168.24.1'}, + {'ip_netmask': '10.10.10.254/32', 'next_hop': '192.168.24.1'}], + 'DnsServers': '', + 'IronicInspectorSubnets': [ + {'gateway': '192.168.24.1', + 'ip_range': '192.168.24.100,192.168.24.120', + 'netmask': '255.255.255.0', + 'tag': 'ctlplane-subnet'}, + {'gateway': '192.168.10.254', + 'ip_range': '192.168.10.100,192.168.10.189', + 'netmask': '255.255.255.0', + 'tag': 'subnet1'}, + {'gateway': '192.168.20.254', + 'ip_range': '192.168.20.100,192.168.20.189', + 'netmask': '255.255.255.0', + 'tag': 'subnet2'} + ], + 'MasqueradeNetworks': {}, + 'UndercloudCtlplaneSubnets': { + # The ctlplane-subnet subnet have defaults + 'ctlplane-subnet': { + 'AllocationPools': [ + {'start': '192.168.24.5', 'end': '192.168.24.24'}], + 'HostRoutes': [{'destination': '10.10.10.254/32', + 'nexthop': '192.168.24.1'}], + 'NetworkCidr': '192.168.24.0/24', + 'NetworkGateway': '192.168.24.1'}, + 'subnet1': { + 'AllocationPools': [ + {'start': '192.168.10.10', 'end': '192.168.10.99'}], + 'HostRoutes': [{'destination': '10.10.10.254/32', + 'nexthop': '192.168.10.254'}], + 'NetworkCidr': '192.168.10.0/24', + 'NetworkGateway': '192.168.10.254'}, + 'subnet2': { + 'AllocationPools': [ + {'start': '192.168.20.10', 'end': '192.168.20.99'}], + 'HostRoutes': [{'destination': '10.10.10.254/32', + 'nexthop': '192.168.20.254'}], + 'NetworkCidr': '192.168.20.0/24', + 'NetworkGateway': '192.168.20.254'} + } + } + self.assertEqual(expected, env) + class TestTLSSettings(base.TestCase): def test_public_host_with_ip_should_give_ip_endpoint_environment(self): diff --git a/tripleoclient/v1/undercloud_config.py b/tripleoclient/v1/undercloud_config.py index f379364e3..8868a4ad7 100644 --- a/tripleoclient/v1/undercloud_config.py +++ b/tripleoclient/v1/undercloud_config.py @@ -78,6 +78,7 @@ PARAMETER_MAPPING = { SUBNET_PARAMETER_MAPPING = { 'cidr': 'NetworkCidr', 'gateway': 'NetworkGateway', + 'host_routes': 'HostRoutes' } THT_HOME = os.environ.get('THT_HOME', @@ -237,6 +238,10 @@ def _generate_subnets_static_routes(): continue s = CONF.get(subnet) env_list.append({'ip_netmask': s.cidr, 'next_hop': local_router}) + for route in CONF.get(CONF.local_subnet).host_routes: + env_list.append({'ip_netmask': route['destination'], + 'next_hop': route['nexthop']}) + return env_list