ctlplane network attributes in overcloud environment

Set CtlplaneNetworkAttributes parameter in overcloud
environment. The parameter contains a map with network
and subnets data.

CtlplaneNetworkAttributes:
  network:
    dns_domain: ctlplane.localdomain.
    mtu: 1442
    name: ctlplane
    tags: ['192.168.24.0/24', '192.168.25.0/24']
  subnets:
    ctlplane-leaf1:
      cidr: 192.168.25.0/24
      dns_nameservers: ['8.8.8.8', '8.8.4.4']
      gateway_ip: 192.168.25.254
      host_routes:
      - {'destination': '192.168.24.0/24', 'nexthop': '192.168.25.254'}
      ip_version: 4
      name: ctlplane-leaf1

Also set the CtlplaneNetworkAttributes in the undercloud environment
from the data in undercloud.conf.

Also set the CtlplaneNetworkAttributes in the standalone environment.

Change-Id: I12f1ea965d489eb36353e988cc3ec947f72a35ad
This commit is contained in:
Harald Jensås 2020-09-22 00:12:04 +02:00
parent 04e9966b7f
commit 6ced9c71db
9 changed files with 266 additions and 14 deletions

View File

@ -57,7 +57,7 @@ munch==2.1.0
netaddr==0.7.18
netifaces==0.10.4
networkx==1.10
openstacksdk==0.11.2
openstacksdk==0.48.0
os-client-config==1.28.0
os-service-types==1.2.0
osc-lib==1.8.0

View File

@ -18,3 +18,4 @@ tripleo-common>=12.6.0 # Apache-2.0
cryptography>=2.1 # BSD/Apache-2.0
ansible-runner>=1.4.5 # Apache 2.0
validations-libs>=1.0.0
openstacksdk>=0.48.0 # Apache-2.0

View File

@ -108,3 +108,84 @@ class TestDeployOvercloud(fakes.FakePlaybookExecution):
def setUp(self):
super(TestDeployOvercloud, self).setUp(ansible_mock=False)
class FakeNeutronNetwork(dict):
def __init__(self, **attrs):
NETWORK_ATTRS = ['id',
'name',
'status',
'tenant_id',
'is_admin_state_up',
'mtu',
'segments',
'is_shared',
'subnet_ids',
'provider:network_type',
'provider:physical_network',
'provider:segmentation_id',
'router:external',
'availability_zones',
'availability_zone_hints',
'is_default',
'tags']
raw = dict.fromkeys(NETWORK_ATTRS)
raw.update(attrs)
raw.update({
'provider_physical_network': attrs.get(
'provider:physical_network', None),
'provider_network_type': attrs.get(
'provider:network_type', None),
'provider_segmentation_id': attrs.get(
'provider:segmentation_id', None)
})
super(FakeNeutronNetwork, self).__init__(raw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(key)
def __setattr__(self, key, value):
if key in self:
self[key] = value
else:
raise AttributeError(key)
class FakeNeutronSubnet(dict):
def __init__(self, **attrs):
SUBNET_ATTRS = ['id',
'name',
'network_id',
'cidr',
'tenant_id',
'is_dhcp_enabled',
'dns_nameservers',
'allocation_pools',
'host_routes',
'ip_version',
'gateway_ip',
'ipv6_address_mode',
'ipv6_ra_mode',
'subnetpool_id',
'segment_id',
'tags']
raw = dict.fromkeys(SUBNET_ATTRS)
raw.update(attrs)
super(FakeNeutronSubnet, self).__init__(raw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(key)
def __setattr__(self, key, value):
if key in self:
self[key] = value
else:
raise AttributeError(key)

View File

@ -22,6 +22,7 @@ import yaml
from heatclient import exc as hc_exc
import mock
import openstack
from osc_lib import exceptions as oscexc
from osc_lib.tests import utils
from swiftclient.exceptions import ClientException as ObjectClientException
@ -170,6 +171,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
self.cmd._download_missing_files_from_plan = self.real_download_missing
shutil.rmtree = self.real_shutil
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_get_ctlplane_attrs', autospec=True, return_value={})
@mock.patch('tripleoclient.utils.copy_clouds_yaml')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_get_undercloud_host_entry', autospec=True,
@ -189,7 +192,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_create_parameters_env,
mock_breakpoints_cleanup,
mock_events, mock_stack_network_check,
mock_get_undercloud_host_entry, mock_copy):
mock_get_undercloud_host_entry, mock_copy,
mock_get_ctlplane_attrs):
fixture = deployment.DeploymentWorkflowFixture()
self.useFixture(fixture)
clients = self.app.client_manager
@ -241,7 +245,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'SnmpdReadonlyUserPassword': 'PASSWORD',
'StackAction': 'UPDATE',
'UndercloudHostsEntries': [
'192.168.0.1 uc.ctlplane.localhost uc.ctlplane']
'192.168.0.1 uc.ctlplane.localhost uc.ctlplane'],
'CtlplaneNetworkAttributes': {},
}
def _custom_create_params_env(_self, parameters, tht_root,
@ -266,6 +271,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
object_client.put_container.assert_called_once_with(
'overcloud', headers={'x-container-meta-usage-tripleo': 'plan'})
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_get_ctlplane_attrs', autospec=True, return_value={})
@mock.patch('tripleoclient.workflows.deployment.create_overcloudrc',
autospec=True)
@mock.patch('tripleoclient.utils.copy_clouds_yaml')
@ -290,7 +297,7 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_breakpoints_cleanup, mock_postconfig,
mock_invoke_plan_env_wf,
mock_get_undercloud_host_entry,
mock_copy, mock_overcloudrc):
mock_copy, mock_overcloudrc, mock_get_ctlplane_attrs):
fixture = deployment.DeploymentWorkflowFixture()
self.useFixture(fixture)
@ -337,7 +344,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'parameter_defaults': {
'StackAction': 'CREATE',
'UndercloudHostsEntries':
['192.168.0.1 uc.ctlplane.localhost uc.ctlplane']}}
['192.168.0.1 uc.ctlplane.localhost uc.ctlplane'],
'CtlplaneNetworkAttributes': {}}}
mock_open_context = mock.mock_open()
with mock.patch('six.moves.builtins.open', mock_open_context):
@ -378,6 +386,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
object_client = clients.tripleoclient.object_store
object_client.put_object.assert_has_calls(calls)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_get_ctlplane_attrs', autospec=True, return_value={})
@mock.patch('os.chdir')
@mock.patch('tripleoclient.utils.copy_clouds_yaml')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
@ -410,7 +420,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_postconfig, mock_shutil_rmtree,
mock_invoke_plan_env_wf,
mock_stack_network_check,
mock_get_undercloud_host_entry, mock_copy, mock_chdir):
mock_get_undercloud_host_entry, mock_copy, mock_chdir,
mock_get_ctlplane_attrs):
fixture = deployment.DeploymentWorkflowFixture()
self.useFixture(fixture)
plane_management_fixture = deployment.PlanManagementFixture()
@ -468,7 +479,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'StackAction': 'CREATE',
'UndercloudHostsEntries': [
'192.168.0.1 uc.ctlplane.localhost uc.ctlplane'
]
],
'CtlplaneNetworkAttributes': {},
}
testcase = self
@ -1119,6 +1131,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
object_client.put_container.assert_called_once_with(
'overcloud', headers={'x-container-meta-usage-tripleo': 'plan'})
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_get_ctlplane_attrs', autospec=True, return_value={})
@mock.patch('tripleoclient.utils.copy_clouds_yaml')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_get_undercloud_host_entry', autospec=True,
@ -1145,7 +1159,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
mock_breakpoints_cleanup,
mock_deploy_post_config,
mock_stack_network_check,
mock_get_undercloud_host_entry, mock_copy):
mock_get_undercloud_host_entry, mock_copy,
mock_get_ctlplane_attrs):
fixture = deployment.DeploymentWorkflowFixture()
self.useFixture(fixture)
plane_management_fixture = deployment.PlanManagementFixture()
@ -1204,7 +1219,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'NtpServer': 'ntp',
'UndercloudHostsEntries': [
'192.168.0.1 uc.ctlplane.localhost uc.ctlplane'
]
],
'CtlplaneNetworkAttributes': {},
}
def _custom_create_params_env(_self, parameters, tht_root,
@ -1475,6 +1491,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
self.assertTrue(fixture.mock_config_download.called)
mock_copy.assert_called_once()
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_get_ctlplane_attrs', autospec=True, return_value={})
@mock.patch('tripleoclient.utils.copy_clouds_yaml')
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_get_undercloud_host_entry', autospec=True,
@ -1489,7 +1507,8 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'create_plan_from_templates', autospec=True)
def test_config_download_timeout(
self, mock_plan_man, mock_hc, mock_stack_network_check, mock_hd,
mock_overcloudrc, mock_get_undercloud_host_entry, mock_copy):
mock_overcloudrc, mock_get_undercloud_host_entry, mock_copy,
mock_get_ctlplane_attrs):
fixture = deployment.DeploymentWorkflowFixture()
self.useFixture(fixture)
utils_fixture = deployment.UtilsOvercloudFixture()
@ -1512,9 +1531,9 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
self.assertIn(
[mock.call(mock.ANY, mock.ANY, 'overcloud', mock.ANY,
{'StackAction': 'UPDATE', 'UndercloudHostsEntries':
['192.168.0.1 uc.ctlplane.localhost uc.ctlplane']}, {},
451, mock.ANY, {}, False, False, False, None,
deployment_options={})],
['192.168.0.1 uc.ctlplane.localhost uc.ctlplane'],
'CtlplaneNetworkAttributes': {}}, {}, 451, mock.ANY,
{}, False, False, False, None, deployment_options={})],
mock_hd.mock_calls)
self.assertIn(
[mock.call(mock.ANY, mock.ANY, mock.ANY, 'ctlplane', None, None,
@ -1757,6 +1776,67 @@ class TestDeployOvercloud(fakes.TestDeployOvercloud):
'specified.')
mock_warning.assert_called_once_with(expected_message)
@mock.patch('openstack.connect', autospec=True)
def test__get_ctlplane_attrs_no_config(self, mock_connect):
mock_connect.side_effect = openstack.exceptions.ConfigException
function = overcloud_deploy.DeployOvercloud._get_ctlplane_attrs
expected = dict()
self.assertEqual(expected, function(mock.ANY))
@mock.patch('openstack.connect', autospec=True)
@mock.patch.object(openstack.connection, 'Connection', autospec=True)
def test__get_ctlplane_attrs_no_network(self, mock_conn, mock_connect):
mock_connect.return_value = mock_conn
function = overcloud_deploy.DeployOvercloud._get_ctlplane_attrs
mock_conn.network.find_network.return_value = None
expected = dict()
self.assertEqual(expected, function(mock.ANY))
@mock.patch('openstack.connect', autospec=True)
@mock.patch.object(openstack.connection, 'Connection', autospec=True)
def test__get_ctlplane_attrs(self, mock_conn, mock_connect):
mock_connect.return_value = mock_conn
function = overcloud_deploy.DeployOvercloud._get_ctlplane_attrs
fake_network = fakes.FakeNeutronNetwork(
name='net_name',
mtu=1440,
dns_domain='ctlplane.localdomain.',
tags=[],
subnet_ids=['subnet_id'])
fake_subnet = fakes.FakeNeutronSubnet(
id='subnet_id',
name='subnet_name',
cidr='192.168.24.0/24',
gateway_ip='192.168.24.1',
host_routes=[
{'destination': '192.168.25.0/24', 'nexthop': '192.168.24.1'}],
dns_nameservers=['192.168.24.254'],
ip_version=4
)
mock_conn.network.find_network.return_value = fake_network
mock_conn.network.get_subnet.return_value = fake_subnet
expected = {
'network': {
'dns_domain': 'ctlplane.localdomain.',
'mtu': 1440,
'name': 'net_name',
'tags': []},
'subnets': {
'subnet_name': {
'cidr': '192.168.24.0/24',
'dns_nameservers': ['192.168.24.254'],
'gateway_ip': '192.168.24.1',
'host_routes': [{'destination': '192.168.25.0/24',
'nexthop': '192.168.24.1'}],
'ip_version': 4,
'name': 'subnet_name'}
}
}
self.assertEqual(expected, function(mock.ANY))
class TestArgumentValidation(fakes.TestDeployOvercloud):

View File

@ -36,6 +36,8 @@ class TestOvercloudUpdatePrepare(fakes.TestOvercloudUpdatePrepare):
self.mock_uuid4 = uuid4_patcher.start()
self.addCleanup(self.mock_uuid4.stop)
@mock.patch('tripleoclient.v1.overcloud_deploy.DeployOvercloud.'
'_get_ctlplane_attrs', autospec=True, return_value={})
@mock.patch('tripleoclient.utils.ensure_run_as_normal_user')
@mock.patch('tripleoclient.utils.prompt_user_for_confirmation',
return_value=True)
@ -57,7 +59,7 @@ class TestOvercloudUpdatePrepare(fakes.TestOvercloudUpdatePrepare):
def test_update_out(self, mock_deploy, mock_open, mock_copy, mock_yaml,
mock_abspath, mock_update, mock_logger,
mock_get_stack, mock_get_undercloud_host_entry,
mock_confirm, mock_usercheck):
mock_confirm, mock_usercheck, mock_get_ctlplane_attrs):
mock_stack = mock.Mock(parameters={'DeployIdentifier': ''})
mock_stack.stack_name = 'overcloud'
mock_get_stack.return_value = mock_stack

View File

@ -923,6 +923,32 @@ class TestNetworkSettings(TestBaseNetworkSettings):
self.assertRaises(exceptions.DeploymentError,
undercloud_config._generate_inspection_subnets)
def test__env_set_undercloud_ctlplane_networks_attribues(self):
self.conf.config(local_subnet='ctlplane-subnet',
local_mtu=1444,
undercloud_nameservers=['192.168.24.253',
'192.168.24.252'])
self.conf.config(cidr='192.168.24.0/24',
gateway='192.168.24.254',
host_routes=[{'destination': '10.10.10.254/32',
'nexthop': '192.168.24.1'}],
group='ctlplane-subnet')
env = {}
undercloud_config._env_set_undercloud_ctlplane_networks_attribues(env)
expected = {
'CtlplaneNetworkAttributes': {
'network': {'mtu': 1444},
'subnets': {
'ctlplane-subnet': {
'cidr': '192.168.24.0/24',
'dns_nameservers': ['192.168.24.253',
'192.168.24.252'],
'gateway_ip': '192.168.24.254',
'host_routes': [{'destination': '10.10.10.254/32',
'nexthop': '192.168.24.1'}],
'tags': []}}}}
self.assertEqual(expected, env)
class TestChronySettings(TestBaseNetworkSettings):
def test_default(self):

View File

@ -29,6 +29,7 @@ import time
import yaml
from heatclient.common import template_utils
import openstack
from osc_lib import exceptions as oscexc
from osc_lib.i18n import _
from swiftclient.exceptions import ClientException
@ -100,8 +101,44 @@ class DeployOvercloud(command.Command):
parameters[
'UndercloudHostsEntries'] = [self._get_undercloud_host_entry()]
parameters['CtlplaneNetworkAttributes'] = self._get_ctlplane_attrs()
return parameters
def _get_ctlplane_attrs(self):
try:
conn = openstack.connect('undercloud')
except openstack.exceptions.ConfigException:
return dict()
network = conn.network.find_network('ctlplane')
if network is None:
return dict()
net_attributes_map = {'network': dict(), 'subnets': dict()}
net_attributes_map['network'].update({
'name': network.name,
'mtu': network.mtu,
'dns_domain': network.dns_domain,
'tags': network.tags,
})
for subnet_id in network.subnet_ids:
subnet = conn.network.get_subnet(subnet_id)
net_attributes_map['subnets'].update({
subnet.name: {
'name': subnet.name,
'cidr': subnet.cidr,
'gateway_ip': subnet.gateway_ip,
'host_routes': subnet.host_routes,
'dns_nameservers': subnet.dns_nameservers,
'ip_version': subnet.ip_version,
}
})
return net_attributes_map
def _cleanup_host_entry(self, entry):
# remove any tab or space excess
entry_stripped = re.sub('[ \t]+', ' ', str(entry).rstrip())

View File

@ -703,6 +703,18 @@ class Deploy(command.Command):
stack_name=parsed_args.stack,
role_name=self._get_primary_role_name(
roles_file_path, parsed_args.templates)))
tmp_env.update(
{
'CtlplaneNetworkAttributes': {
'subnets': {
'ctlplane-subnet': {
'cidr': str(ip_nw.cidr),
'host_routes': []
}
}
}
}
)
with open(maps_file, 'w') as env_file:
yaml.safe_dump({'parameter_defaults': tmp_env}, env_file,

View File

@ -414,6 +414,18 @@ def _process_network_args(env):
raise exceptions.InvalidConfiguration(msg)
def _env_set_undercloud_ctlplane_networks_attribues(env):
env['CtlplaneNetworkAttributes'] = dict(network=dict(), subnets=dict())
env['CtlplaneNetworkAttributes']['network']['mtu'] = CONF.local_mtu
env['CtlplaneNetworkAttributes']['subnets']['ctlplane-subnet'] = {
'cidr': CONF.get(CONF.local_subnet).cidr,
'gateway_ip': CONF.get(CONF.local_subnet).gateway,
'dns_nameservers': CONF.undercloud_nameservers,
'host_routes': CONF.get(CONF.local_subnet).host_routes,
'tags': [],
}
def _process_chrony_acls(env):
"""Populate ACL rules for chrony to allow ctlplane subnets"""
acl_rules = []
@ -468,6 +480,7 @@ def prepare_undercloud_deploy(upgrade=False, no_validations=True,
# Set up parameters for undercloud networking
_process_network_args(env_data)
_env_set_undercloud_ctlplane_networks_attribues(env_data)
# Setup parameter for Chrony ACL rules
_process_chrony_acls(env_data)