# Copyright 2017 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import serialization from cryptography import x509 from cryptography.x509.oid import NameOID from datetime import datetime from datetime import timedelta import fixtures import mock import os import tempfile import yaml from oslo_config import cfg from oslo_config import fixture as oslo_fixture from tripleo_common.image import kolla_builder from tripleoclient import exceptions from tripleoclient.tests import base from tripleoclient.v1 import undercloud_config class TestProcessDriversAndHardwareTypes(base.TestCase): def setUp(self): super(TestProcessDriversAndHardwareTypes, self).setUp() self.conf = mock.Mock(**{key: getattr(undercloud_config.CONF, key) for key in ('enabled_hardware_types', 'enable_node_discovery', 'discovery_default_driver')}) def test_defaults(self): env = {} undercloud_config._process_drivers_and_hardware_types(self.conf, env) self.assertEqual({ 'IronicEnabledHardwareTypes': ['idrac', 'ilo', 'ipmi', 'redfish'], 'IronicEnabledBootInterfaces': ['ilo-pxe', 'ipxe', 'pxe'], 'IronicEnabledBiosInterfaces': ['ilo', 'no-bios', 'redfish'], 'IronicEnabledInspectInterfaces': ['idrac', 'ilo', 'inspector', 'no-inspect', 'redfish'], 'IronicEnabledManagementInterfaces': ['fake', 'idrac', 'ilo', 'ipmitool', 'noop', 'redfish'], 'IronicEnabledPowerInterfaces': ['fake', 'idrac', 'ilo', 'ipmitool', 'redfish'], 'IronicEnabledRaidInterfaces': ['idrac', 'no-raid'], 'IronicEnabledVendorInterfaces': ['idrac', 'ipmitool', 'no-vendor'] }, env) def test_one_hardware_type_with_discovery(self): env = {} self.conf.enabled_hardware_types = ['redfish'] self.conf.enable_node_discovery = True undercloud_config._process_drivers_and_hardware_types(self.conf, env) self.assertEqual({ # ipmi added because it's the default discovery driver 'IronicEnabledHardwareTypes': ['ipmi', 'redfish'], 'IronicEnabledBootInterfaces': ['ipxe', 'pxe'], 'IronicEnabledBiosInterfaces': ['no-bios', 'redfish'], 'IronicEnabledInspectInterfaces': ['inspector', 'no-inspect', 'redfish'], 'IronicEnabledManagementInterfaces': ['fake', 'ipmitool', 'noop', 'redfish'], 'IronicEnabledPowerInterfaces': ['fake', 'ipmitool', 'redfish'], 'IronicEnabledRaidInterfaces': ['no-raid'], 'IronicEnabledVendorInterfaces': ['ipmitool', 'no-vendor'], 'IronicInspectorDiscoveryDefaultDriver': 'ipmi', 'IronicInspectorEnableNodeDiscovery': True }, env) def test_all_hardware_types(self): env = {} self.conf.enabled_hardware_types = ( self.conf.enabled_hardware_types + ['staging-ovirt', 'snmp', 'irmc', 'cisco-ucs-managed', 'cisco-ucs-standalone', 'xclarity'] ) undercloud_config._process_drivers_and_hardware_types(self.conf, env) self.assertEqual({ 'IronicEnabledHardwareTypes': ['cisco-ucs-managed', 'cisco-ucs-standalone', 'idrac', 'ilo', 'ipmi', 'irmc', 'redfish', 'snmp', 'staging-ovirt', 'xclarity'], 'IronicEnabledBootInterfaces': ['ilo-pxe', 'ipxe', 'irmc-pxe', 'pxe'], 'IronicEnabledBiosInterfaces': ['ilo', 'irmc', 'no-bios', 'redfish'], 'IronicEnabledInspectInterfaces': ['idrac', 'ilo', 'inspector', 'irmc', 'no-inspect', 'redfish'], 'IronicEnabledManagementInterfaces': ['cimc', 'fake', 'idrac', 'ilo', 'ipmitool', 'irmc', 'noop', 'redfish', 'staging-ovirt', 'ucsm', 'xclarity'], 'IronicEnabledPowerInterfaces': ['cimc', 'fake', 'idrac', 'ilo', 'ipmitool', 'irmc', 'redfish', 'snmp', 'staging-ovirt', 'ucsm', 'xclarity'], 'IronicEnabledRaidInterfaces': ['idrac', 'no-raid'], 'IronicEnabledVendorInterfaces': ['idrac', 'ipmitool', 'no-vendor'] }, env) class TestNetworkSettings(base.TestCase): def setUp(self): super(TestNetworkSettings, self).setUp() self.conf = self.useFixture(oslo_fixture.Config(cfg.CONF)) # don't actually load config from ~/undercloud.conf self.mock_config_load = self.useFixture( fixtures.MockPatch('tripleoclient.utils.load_config')) self.conf.config(local_ip='192.168.24.1/24', undercloud_admin_host='192.168.24.3', undercloud_public_host='192.168.24.2') # ctlplane network - config group options self.grp0 = cfg.OptGroup(name='ctlplane-subnet', title='ctlplane-subnet') self.opts = [cfg.StrOpt('cidr'), cfg.ListOpt('dhcp_start'), cfg.ListOpt('dhcp_end'), cfg.ListOpt('dhcp_exclude'), cfg.StrOpt('inspection_iprange'), cfg.StrOpt('gateway'), 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') self.conf.config(cidr='192.168.24.0/24', dhcp_start='192.168.24.5', dhcp_end='192.168.24.24', dhcp_exclude=[], 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): env = {} undercloud_config._process_network_args(env) expected = { 'ControlPlaneStaticRoutes': [], 'DnsServers': '', 'IronicInspectorSubnets': [ {'gateway': '192.168.24.1', 'host_routes': [], 'ip_range': '192.168.24.100,192.168.24.120', 'netmask': '255.255.255.0', 'tag': 'ctlplane-subnet', 'mtu': 1500}], 'MasqueradeNetworks': {}, 'UndercloudCtlplaneSubnets': { '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) def test_nameserver_toomany_fail(self): env = {} self.conf.config(undercloud_nameservers=['1.1.1.1', '1.1.1.2', '1.1.1.3', '1.1.1.4', '1.1.1.5', '1.1.1.6']) self.assertRaises(exceptions.InvalidConfiguration, undercloud_config._process_network_args, env) def test_undercloud_ips_duplicated_fail(self): env = {} # local_ip == undercloud_admin_host self.conf.config(local_ip='192.168.24.1/24', undercloud_admin_host='192.168.24.1', undercloud_public_host='192.168.24.2', generate_service_certificate=True) self.assertRaises(exceptions.InvalidConfiguration, undercloud_config._process_network_args, env) # local_ip == undercloud_public_host self.conf.config(local_ip='192.168.24.1/24', undercloud_admin_host='192.168.24.3', undercloud_public_host='192.168.24.1', generate_service_certificate=True) self.assertRaises(exceptions.InvalidConfiguration, undercloud_config._process_network_args, env) # undercloud_admin_host == undercloud_public_host self.conf.config(local_ip='192.168.24.1/24', undercloud_admin_host='192.168.24.2', undercloud_public_host='192.168.24.2', generate_service_certificate=True) self.assertRaises(exceptions.InvalidConfiguration, undercloud_config._process_network_args, env) # We do not care about ip duplication when ssl is disabled self.conf.config(local_ip='192.168.24.1/24', undercloud_admin_host='192.168.24.1', undercloud_public_host='192.168.24.2', generate_service_certificate=False, undercloud_service_certificate='') undercloud_config._process_network_args(env) def test_start_end_all_addresses(self): self.conf.config(dhcp_start='192.168.24.0', dhcp_end='192.168.24.255', group='ctlplane-subnet') env = {} undercloud_config._process_network_args(env) expected = { 'ControlPlaneStaticRoutes': [], 'DnsServers': '', 'IronicInspectorSubnets': [ {'gateway': '192.168.24.1', 'host_routes': [], 'ip_range': '192.168.24.100,192.168.24.120', 'netmask': '255.255.255.0', 'tag': 'ctlplane-subnet', 'mtu': 1500}], 'MasqueradeNetworks': {}, 'UndercloudCtlplaneSubnets': { 'ctlplane-subnet': { '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) def test_ignore_dhcp_start_end_if_default_but_cidr_not_default(self): self.conf.config(cidr='192.168.10.0/24', inspection_iprange='192.168.10.100,192.168.10.120', gateway='192.168.10.1', group='ctlplane-subnet') env = {} undercloud_config._process_network_args(env) expected = { 'ControlPlaneStaticRoutes': [], 'DnsServers': '', 'IronicInspectorSubnets': [ {'gateway': '192.168.10.1', 'host_routes': [], 'ip_range': '192.168.10.100,192.168.10.120', 'netmask': '255.255.255.0', 'tag': 'ctlplane-subnet', 'mtu': 1500}], 'MasqueradeNetworks': {}, 'UndercloudCtlplaneSubnets': { 'ctlplane-subnet': { '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) def test_dhcp_exclude(self): self.conf.config(cidr='192.168.10.0/24', inspection_iprange='192.168.10.100,192.168.10.120', gateway='192.168.10.1', dhcp_exclude=['192.168.10.50', '192.168.10.80-192.168.10.89'], group='ctlplane-subnet') env = {} undercloud_config._process_network_args(env) expected = { 'ControlPlaneStaticRoutes': [], 'DnsServers': '', 'IronicInspectorSubnets': [ {'gateway': '192.168.10.1', 'host_routes': [], 'ip_range': '192.168.10.100,192.168.10.120', 'netmask': '255.255.255.0', 'tag': 'ctlplane-subnet', 'mtu': 1500}], 'MasqueradeNetworks': {}, 'UndercloudCtlplaneSubnets': { 'ctlplane-subnet': { 'AllocationPools': [ {'start': '192.168.10.2', 'end': '192.168.10.49'}, {'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) def test_no_dhcp_start_no_dhcp_end(self): self.conf.config(dhcp_start=[], dhcp_end=[], group='ctlplane-subnet') env = {} undercloud_config._process_network_args(env) expected = { 'ControlPlaneStaticRoutes': [], 'DnsServers': '', 'IronicInspectorSubnets': [ {'gateway': '192.168.24.1', 'host_routes': [], 'ip_range': '192.168.24.100,192.168.24.120', 'netmask': '255.255.255.0', 'tag': 'ctlplane-subnet', 'mtu': 1500}], 'MasqueradeNetworks': {}, 'UndercloudCtlplaneSubnets': { 'ctlplane-subnet': { '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) def test_dhcp_start_no_dhcp_end(self): self.conf.config(dhcp_start='192.168.24.10', dhcp_end=[], group='ctlplane-subnet') env = {} undercloud_config._process_network_args(env) expected = { 'ControlPlaneStaticRoutes': [], 'DnsServers': '', 'IronicInspectorSubnets': [ {'gateway': '192.168.24.1', 'host_routes': [], 'ip_range': '192.168.24.100,192.168.24.120', 'netmask': '255.255.255.0', 'tag': 'ctlplane-subnet', 'mtu': 1500}], 'MasqueradeNetworks': {}, 'UndercloudCtlplaneSubnets': { 'ctlplane-subnet': { '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'}} } self.assertEqual(expected, env) def test_dhcp_end_no_dhcp_start(self): self.conf.config(dhcp_start=[], dhcp_end='192.168.24.220', group='ctlplane-subnet') env = {} undercloud_config._process_network_args(env) expected = { 'ControlPlaneStaticRoutes': [], 'DnsServers': '', 'IronicInspectorSubnets': [ {'gateway': '192.168.24.1', 'host_routes': [], 'ip_range': '192.168.24.100,192.168.24.120', 'netmask': '255.255.255.0', 'tag': 'ctlplane-subnet', 'mtu': 1500}], 'MasqueradeNetworks': {}, 'UndercloudCtlplaneSubnets': { 'ctlplane-subnet': { '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'}} } self.assertEqual(expected, env) def test_routed_network(self): self.conf.config(subnets=['ctlplane-subnet', 'subnet1', 'subnet2']) self.conf.register_opts(self.opts, group=self.grp1) self.conf.register_opts(self.opts, group=self.grp2) self.conf.config(masquerade=True, group='ctlplane-subnet') 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=[], masquerade=True, 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=[], masquerade=True, 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'}], 'DnsServers': '', 'IronicInspectorSubnets': [ {'gateway': '192.168.24.1', 'host_routes': [], 'ip_range': '192.168.24.100,192.168.24.120', 'netmask': '255.255.255.0', 'tag': 'ctlplane-subnet', 'mtu': 1500}, {'gateway': '192.168.10.254', 'host_routes': [], 'ip_range': '192.168.10.100,192.168.10.189', 'netmask': '255.255.255.0', 'tag': 'subnet1', 'mtu': 1500}, {'gateway': '192.168.20.254', 'host_routes': [], 'ip_range': '192.168.20.100,192.168.20.189', 'netmask': '255.255.255.0', 'tag': 'subnet2', 'mtu': 1500} ], 'MasqueradeNetworks': { '192.168.10.0/24': ['192.168.24.0/24', '192.168.10.0/24', '192.168.20.0/24'], '192.168.20.0/24': ['192.168.24.0/24', '192.168.10.0/24', '192.168.20.0/24'], '192.168.24.0/24': ['192.168.24.0/24', '192.168.10.0/24', '192.168.20.0/24']}, 'UndercloudCtlplaneSubnets': { # The ctlplane-subnet subnet have defaults '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'} } } self.assertEqual(expected, env) def test_routed_network_no_masquerading(self): self.conf.config(subnets=['ctlplane-subnet', 'subnet1', 'subnet2']) 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=[], 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=[], 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'}], 'DnsServers': '', 'IronicInspectorSubnets': [ {'gateway': '192.168.24.1', 'host_routes': [], 'ip_range': '192.168.24.100,192.168.24.120', 'netmask': '255.255.255.0', 'tag': 'ctlplane-subnet', 'mtu': 1500}, {'gateway': '192.168.10.254', 'host_routes': [], 'ip_range': '192.168.10.100,192.168.10.189', 'netmask': '255.255.255.0', 'tag': 'subnet1', 'mtu': 1500}, {'gateway': '192.168.20.254', 'host_routes': [], 'ip_range': '192.168.20.100,192.168.20.189', 'netmask': '255.255.255.0', 'tag': 'subnet2', 'mtu': 1500} ], 'MasqueradeNetworks': {}, 'UndercloudCtlplaneSubnets': { # The ctlplane-subnet subnet have defaults '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'} } } self.assertEqual(expected, env) def test_no_allocation_pool_on_remote_network(self): self.conf.config(subnets=['ctlplane-subnet', 'subnet1']) self.conf.register_opts(self.opts, group=self.grp1) self.conf.config(cidr='192.168.10.0/24', dhcp_exclude=[], inspection_iprange='192.168.10.200,192.168.10.254', gateway='192.168.10.254', host_routes=[], masquerade=False, group='subnet1') env = {} undercloud_config._process_network_args(env) expected = { 'ControlPlaneStaticRoutes': [ {'ip_netmask': '192.168.10.0/24', 'next_hop': '192.168.24.1'}], 'DnsServers': '', 'IronicInspectorSubnets': [ {'gateway': '192.168.24.1', 'host_routes': [], 'ip_range': '192.168.24.100,192.168.24.120', 'netmask': '255.255.255.0', 'tag': 'ctlplane-subnet', 'mtu': 1500}, {'gateway': '192.168.10.254', 'host_routes': [], 'ip_range': '192.168.10.200,192.168.10.254', 'netmask': '255.255.255.0', 'tag': 'subnet1', 'mtu': 1500}, ], 'MasqueradeNetworks': {}, 'UndercloudCtlplaneSubnets': { # The ctlplane-subnet subnet have defaults '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'} } } self.assertEqual(expected, env) def test_no_allocation_pool_on_remote_network_three_pools(self): self.conf.config(subnets=['ctlplane-subnet', 'subnet1']) self.conf.register_opts(self.opts, group=self.grp1) self.conf.config(cidr='192.168.10.0/24', dhcp_exclude=[], inspection_iprange='192.168.10.100,192.168.10.199', gateway='192.168.10.222', host_routes=[], masquerade=False, group='subnet1') env = {} undercloud_config._process_network_args(env) expected = { 'ControlPlaneStaticRoutes': [ {'ip_netmask': '192.168.10.0/24', 'next_hop': '192.168.24.1'}], 'DnsServers': '', 'IronicInspectorSubnets': [ {'gateway': '192.168.24.1', 'host_routes': [], 'ip_range': '192.168.24.100,192.168.24.120', 'netmask': '255.255.255.0', 'tag': 'ctlplane-subnet', 'mtu': 1500}, {'gateway': '192.168.10.222', 'host_routes': [], 'ip_range': '192.168.10.100,192.168.10.199', 'netmask': '255.255.255.0', 'tag': 'subnet1', 'mtu': 1500}, ], 'MasqueradeNetworks': {}, 'UndercloudCtlplaneSubnets': { # The ctlplane-subnet subnet have defaults '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.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', 'host_routes': [{'destination': '10.10.10.254/32', 'nexthop': '192.168.24.1'}], 'ip_range': '192.168.24.100,192.168.24.120', 'netmask': '255.255.255.0', 'tag': 'ctlplane-subnet', 'mtu': 1500}, {'gateway': '192.168.10.254', 'host_routes': [{'destination': '10.10.10.254/32', 'nexthop': '192.168.10.254'}], 'ip_range': '192.168.10.100,192.168.10.189', 'netmask': '255.255.255.0', 'tag': 'subnet1', 'mtu': 1500}, {'gateway': '192.168.20.254', 'host_routes': [{'destination': '10.10.10.254/32', 'nexthop': '192.168.20.254'}], 'ip_range': '192.168.20.100,192.168.20.189', 'netmask': '255.255.255.0', 'tag': 'subnet2', 'mtu': 1500} ], '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): expected_env_file = os.path.join( undercloud_config.THT_HOME, "environments/ssl/tls-endpoints-public-ip.yaml") resulting_env_file1 = undercloud_config._get_tls_endpoint_environment( '127.0.0.1', undercloud_config.THT_HOME) self.assertEqual(expected_env_file, resulting_env_file1) resulting_env_file2 = undercloud_config._get_tls_endpoint_environment( '192.168.1.1', undercloud_config.THT_HOME) self.assertEqual(expected_env_file, resulting_env_file2) def test_public_host_with_fqdn_should_give_dns_endpoint_environment(self): expected_env_file = os.path.join( undercloud_config.THT_HOME, "environments/ssl/tls-endpoints-public-dns.yaml") resulting_env_file1 = undercloud_config._get_tls_endpoint_environment( 'controller-1', undercloud_config.THT_HOME) self.assertEqual(expected_env_file, resulting_env_file1) resulting_env_file2 = undercloud_config._get_tls_endpoint_environment( 'controller-1.tripleodomain.com', undercloud_config.THT_HOME) self.assertEqual(expected_env_file, resulting_env_file2) def get_certificate_and_private_key(self): private_key = rsa.generate_private_key(public_exponent=3, key_size=1024, backend=default_backend()) issuer = x509.Name([ x509.NameAttribute(NameOID.COUNTRY_NAME, u"FI"), x509.NameAttribute(NameOID.LOCALITY_NAME, u"Helsinki"), x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"Some Company"), x509.NameAttribute(NameOID.COMMON_NAME, u"Test Certificate"), ]) cert_builder = x509.CertificateBuilder( issuer_name=issuer, subject_name=issuer, public_key=private_key.public_key(), serial_number=x509.random_serial_number(), not_valid_before=datetime.utcnow(), not_valid_after=datetime.utcnow() + timedelta(days=10) ) cert = cert_builder.sign(private_key, hashes.SHA256(), default_backend()) cert_pem = cert.public_bytes(encoding=serialization.Encoding.PEM) key_pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption()) return cert_pem, key_pem def test_get_dict_with_cert_and_key_from_bundled_pem(self): cert_pem, key_pem = self.get_certificate_and_private_key() with tempfile.NamedTemporaryFile() as tempbundle: tempbundle.write(cert_pem) tempbundle.write(key_pem) tempbundle.seek(0) tls_parameters = undercloud_config._get_public_tls_parameters( tempbundle.name) self.assertEqual(cert_pem, tls_parameters['SSLCertificate']) self.assertEqual(key_pem, tls_parameters['SSLKey']) def test_get_tls_parameters_fails_cause_of_missing_cert(self): _, key_pem = self.get_certificate_and_private_key() with tempfile.NamedTemporaryFile() as tempbundle: tempbundle.write(key_pem) tempbundle.seek(0) self.assertRaises(ValueError, undercloud_config._get_public_tls_parameters, tempbundle.name) def test_get_tls_parameters_fails_cause_of_missing_key(self): cert_pem, _ = self.get_certificate_and_private_key() with tempfile.NamedTemporaryFile() as tempbundle: tempbundle.write(cert_pem) tempbundle.seek(0) self.assertRaises(ValueError, undercloud_config._get_public_tls_parameters, tempbundle.name) def test_get_tls_parameters_fails_cause_of_unexistent_file(self): self.assertRaises(IOError, undercloud_config._get_public_tls_parameters, '/tmp/unexistent-file-12345.pem') class TestContainerImageConfig(base.TestCase): def setUp(self): super(TestContainerImageConfig, self).setUp() conf_keys = ( 'container_images_file', ) self.conf = mock.Mock(**{key: getattr(undercloud_config.CONF, key) for key in conf_keys}) @mock.patch('shutil.copy') def test_defaults(self, mock_copy): env = {} deploy_args = [] cip_default = getattr(kolla_builder, 'CONTAINER_IMAGE_PREPARE_PARAM', None) self.addCleanup(setattr, kolla_builder, 'CONTAINER_IMAGE_PREPARE_PARAM', cip_default) setattr(kolla_builder, 'CONTAINER_IMAGE_PREPARE_PARAM', [{ 'set': { 'namespace': 'one', 'name_prefix': 'two', 'name_suffix': 'three', 'tag': 'four', }, 'tag_from_label': 'five', }]) undercloud_config._container_images_config(self.conf, deploy_args, env, None) self.assertEqual([], deploy_args) cip = env['ContainerImagePrepare'][0] set = cip['set'] self.assertEqual( 'one', set['namespace']) self.assertEqual( 'two', set['name_prefix']) self.assertEqual( 'three', set['name_suffix']) self.assertEqual( 'four', set['tag']) self.assertEqual( 'five', cip['tag_from_label']) @mock.patch('shutil.copy') def test_container_images_file(self, mock_copy): env = {} deploy_args = [] self.conf.container_images_file = '/tmp/container_images_file.yaml' undercloud_config._container_images_config(self.conf, deploy_args, env, None) self.assertEqual(['-e', '/tmp/container_images_file.yaml'], deploy_args) self.assertEqual({}, env) @mock.patch('shutil.copy') def test_custom(self, mock_copy): env = {} deploy_args = [] with tempfile.NamedTemporaryFile(mode='w') as f: yaml.dump({ 'parameter_defaults': {'ContainerImagePrepare': [{ 'set': { 'namespace': 'one', 'name_prefix': 'two', 'name_suffix': 'three', 'tag': 'four', }, 'tag_from_label': 'five', }]} }, f) self.conf.container_images_file = f.name cif_name = f.name undercloud_config._container_images_config( self.conf, deploy_args, env, None) self.assertEqual(['-e', cif_name], deploy_args)