# Copyright 2015 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. # import mock import os import tempfile import yaml from tripleoclient.tests.v1.overcloud_netenv_validate import fakes from tripleoclient.v1 import overcloud_netenv_validate EMPTY_NETENV = """resource_registry: OS::TripleO::BlockStorage::Net::SoftwareConfig: /tmp/foo parameter_defaults: NeutronExternalNetworkBridge: "''" """ class TestValidateOvercloudNetenv(fakes.TestValidateOvercloudNetenv): def setUp(self): super(TestValidateOvercloudNetenv, self).setUp() # Get the command object to test self.cmd = overcloud_netenv_validate.ValidateOvercloudNetenv( self.app, None) def temporary_nic_config_file(self, bridges): nic_config = { 'resources': { 'OsNetConfigImpl': { 'properties': { 'config': { 'os_net_config': { 'network_config': bridges, } } } } } } tmp = tempfile.NamedTemporaryFile(mode='w', delete=False) yaml.dump(nic_config, tmp) tmp.close() return tmp.name def test_cidr_no_overlapping_networks(self): networks = [ '172.17.0.0/24', '172.16.0.0/24', '172.17.1.0/24', '172.17.2.0/24', '10.1.2.0/24', ] self.cmd.error_count = 0 self.cmd.check_cidr_overlap(networks) self.assertEqual(0, self.cmd.error_count) def test_cidr_overlapping_networks(self): networks = [ '172.17.1.0/24', '172.17.1.0/24', '10.1.2.0/24', ] self.cmd.error_count = 0 self.cmd.check_cidr_overlap(networks) self.assertEqual(1, self.cmd.error_count) def test_cidr_nonnumerical_address(self): networks = [ 'nonsense', ] self.cmd.error_count = 0 self.cmd.check_cidr_overlap(networks) self.assertEqual(1, self.cmd.error_count) def test_cidr_address_outside_of_range(self): networks = [ '172.17.0.278/24', ] self.cmd.error_count = 0 self.cmd.check_cidr_overlap(networks) self.assertEqual(1, self.cmd.error_count) def test_vlan_ids_unique(self): vlans = { 'InternalApiNetworkVlanID': 201, 'StorageNetworkVlanID': 202, 'StorageMgmtNetworkVlanID': 203, 'TenantNetworkVlanID': 204, 'ExternalNetworkVlanID': 100, } self.cmd.error_count = 0 self.cmd.check_vlan_ids(vlans) self.assertEqual(0, self.cmd.error_count) def test_vlan_ids_duplicate(self): vlans = { 'InternalApiNetworkVlanID': 201, 'StorageNetworkVlanID': 202, 'StorageMgmtNetworkVlanID': 203, 'TenantNetworkVlanID': 202, # conflicts with StorageNetworkVlanID 'ExternalNetworkVlanID': 100, } self.cmd.error_count = 0 self.cmd.check_vlan_ids(vlans) self.assertEqual(1, self.cmd.error_count) def test_allocation_pools_pairing_no_overlap(self): filedata = { 'InternalApiNetCidr': '172.17.0.0/24', 'StorageNetCidr': '172.18.0.0/24', 'InternalApiAllocationPools': [ {'start': '172.17.0.10', 'end': '172.17.0.200'}], 'StorageAllocationPools': [ {'start': '172.18.0.10', 'end': '172.18.0.200'}], } pools = { 'InternalApiAllocationPools': [ {'start': '172.17.0.10', 'end': '172.17.0.200'}], 'StorageAllocationPools': [ {'start': '172.18.0.10', 'end': '172.18.0.200'}], } self.cmd.error_count = 0 self.cmd.check_allocation_pools_pairing(filedata, pools) self.assertEqual(0, self.cmd.error_count) def test_allocation_pools_pairing_inverse_range(self): filedata = { 'InternalApiNetCidr': '172.17.0.0/24', 'StorageNetCidr': '172.18.0.0/24', 'InternalApiAllocationPools': [ {'start': '172.17.0.200', 'end': '172.17.0.10'}], 'StorageAllocationPools': [ {'start': '172.18.0.10', 'end': '172.18.0.200'}], } pools = { 'InternalApiAllocationPools': [ {'start': '172.17.0.200', 'end': '172.17.0.10'}], 'StorageAllocationPools': [ {'start': '172.18.0.10', 'end': '172.18.0.200'}], } self.cmd.error_count = 0 self.cmd.check_allocation_pools_pairing(filedata, pools) self.assertEqual(1, self.cmd.error_count) def test_allocation_pools_pairing_pool_outside_subnet(self): filedata = { 'InternalApiNetCidr': '172.17.0.0/24', 'InternalApiAllocationPools': [ {'start': '172.16.0.10', 'end': '172.16.0.200'}], } pools = { 'InternalApiAllocationPools': [ {'start': '172.16.0.10', 'end': '172.16.0.200'}], } self.cmd.error_count = 0 self.cmd.check_allocation_pools_pairing(filedata, pools) self.assertEqual(1, self.cmd.error_count) def test_allocation_pools_pairing_invalid_cidr(self): filedata = { 'InternalApiNetCidr': '172.17.0.298/24', 'InternalApiAllocationPools': [ {'start': '172.17.0.10', 'end': '172.17.0.200'}], } pools = { 'InternalApiAllocationPools': [ {'start': '172.17.0.10', 'end': '172.17.0.200'}], } self.cmd.error_count = 0 self.cmd.check_allocation_pools_pairing(filedata, pools) self.assertEqual(1, self.cmd.error_count) def test_allocation_pools_pairing_invalid_range(self): filedata = { 'InternalApiNetCidr': '172.17.0.0/24', 'InternalApiAllocationPools': [ {'start': '172.17.0.10', 'end': '172.17.0.287'}], } pools = { 'InternalApiAllocationPools': [ {'start': '172.17.0.10', 'end': '172.17.0.287'}], } self.cmd.error_count = 0 self.cmd.check_allocation_pools_pairing(filedata, pools) self.assertEqual(1, self.cmd.error_count) def test_nic_nonexistent_path(self): self.cmd.error_count = 0 self.cmd.NIC_validate('OS::TripleO::Controller::Net::SoftwareConfig', 'this file that not exist') self.assertEqual(1, self.cmd.error_count) def test_nic_valid_file(self): bridges = [{ 'type': 'ovs_bridge', 'name': 'br-storage', 'members': [ {'type': 'interface', 'name': 'eth0'}, {'type': 'interface', 'name': 'eth1'}, {'type': 'ovs_bond', 'name': 'bond1'} ], }] tmp = self.temporary_nic_config_file(bridges) self.cmd.error_count = 0 self.cmd.NIC_validate( 'OS::TripleO::Controller::Net::SoftwareConfig', tmp) os.unlink(tmp) self.assertEqual(0, self.cmd.error_count) def test_nic_no_bond_too_many_interfaces(self): bridges = [{ 'type': 'ovs_bridge', 'name': 'br-storage', 'members': [ {'type': 'interface', 'name': 'eth0'}, {'type': 'interface', 'name': 'eth1'}, ], }] tmp = self.temporary_nic_config_file(bridges) self.cmd.error_count = 0 self.cmd.NIC_validate( 'OS::TripleO::Controller::Net::SoftwareConfig', tmp) os.unlink(tmp) self.assertEqual(1, self.cmd.error_count) def test_nic_two_bonds(self): bridges = [{ 'type': 'ovs_bridge', 'name': 'br-storage', 'members': [ {'type': 'interface', 'name': 'eth0'}, {'type': 'interface', 'name': 'eth1'}, {'type': 'ovs_bond', 'name': 'bond1'}, {'type': 'ovs_bond', 'name': 'bond2'}, ], }] tmp = self.temporary_nic_config_file(bridges) self.cmd.error_count = 0 self.cmd.NIC_validate( 'OS::TripleO::Controller::Net::SoftwareConfig', tmp) os.unlink(tmp) self.assertEqual(1, self.cmd.error_count) @mock.patch('tripleoclient.v1.overcloud_netenv_validate.' 'ValidateOvercloudNetenv.NIC_validate', autospec=True) def test_command(self, mock_nic_validate): """Testing the command with a minimal file that will fail""" with tempfile.NamedTemporaryFile('wt') as net_file: net_file.write(EMPTY_NETENV) net_file.flush() arglist = ['--file', net_file.name] verifylist = [ ('netenv', net_file.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # Validating a minimal file shouldn't raise errors. self.cmd.take_action(parsed_args)