[Ironic] Add baremetal network for ironic

This change adds:
* new optional baremetal network
* baremetal sections in Neutron L3 configuration

Ironic requires separate baremetal network on infra nodes and neutron flat
network mapped to this physical network.

New baremetal network is optional and depends on Ironic component enabled in
settings.

Implements: blueprint fuel-integrate-ironic (partially)
Change-Id: I742bf69d3f3ac5e4b4ecd39a12e3171c566563e8
This commit is contained in:
Andrey Shestakov 2015-10-15 14:48:30 +03:00
parent f8ba3f9cd4
commit d0bb7c17cf
14 changed files with 236 additions and 61 deletions

View File

@ -94,6 +94,10 @@ NULLABLE_IP_ADDRESS = {
'anyOf': [IP_ADDRESS, NULL]
}
NULLABLE_IP_ADDRESS_RANGE = {
'anyOf': [IP_ADDRESS_RANGE, NULL]
}
NET_ADDRESS = {
'type': 'string',
# check for valid ip address and route prefix

View File

@ -167,7 +167,12 @@ NEUTRON_NETWORK_CONFIGURATION = {
"exclusiveMinimum": False,
"exclusiveMaximum": False
}
}
},
"baremetal_gateway": base_types.NULLABLE_IP_ADDRESS,
"baremetal_range": base_types.NULLABLE_IP_ADDRESS_RANGE,
},
"dependencies": {
"baremetal_range": ["baremetal_gateway"]
}
}
}

View File

@ -59,6 +59,7 @@ NETWORKS = Enum(
# Node networks
'fuelweb_admin',
'storage',
'baremetal',
# internal in terms of fuel
'management',
'public',
@ -101,6 +102,7 @@ DEFAULT_BRIDGES_NAMES = Enum(
'br-ex',
'br-mesh',
'br-aux',
'br-baremetal',
names=(
'br_fw_admin',
'br_storage',
@ -110,7 +112,8 @@ DEFAULT_BRIDGES_NAMES = Enum(
'br_int',
'br_ex',
'br_mesh',
'br_aux'
'br_aux',
'br_baremetal'
)
)

View File

@ -23,6 +23,9 @@ from nailgun.settings import settings
ALLOWED_LOCKS_CHAINS = [
('attributes', 'clusters'),
('attributes', 'clusters', 'ip_addr_ranges'),
('attributes', 'ip_addr_ranges'),
('attributes', 'ip_addrs'),
('attributes', 'ip_addrs', 'network_groups'),
('clusters', 'nodes'),
('tasks', 'clusters'),
('tasks', 'clusters', 'nodes'),

View File

@ -30,6 +30,7 @@ from oslo_serialization import jsonutils
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql as psql
from nailgun.db.sqlalchemy.models.fields import JSON
from nailgun.utils.migration import drop_enum
from nailgun.utils.migration import upgrade_enum
@ -108,9 +109,11 @@ def upgrade():
add_node_discover_error_upgrade()
upgrade_neutron_parameters()
upgrade_cluster_plugins()
upgrade_add_baremetal_net()
def downgrade():
downgrade_add_baremetal_net()
downgrade_cluster_plugins()
downgrade_neutron_parameters()
add_node_discover_error_downgrade()
@ -474,3 +477,17 @@ def downgrade_cluster_plugins():
'cluster_id',
nullable=None
)
def upgrade_add_baremetal_net():
op.add_column('neutron_config',
sa.Column('baremetal_gateway', sa.String(length=25),
nullable=True))
op.add_column('neutron_config',
sa.Column('baremetal_range', JSON(), nullable=True,
server_default='[]'))
def downgrade_add_baremetal_net():
op.drop_column('neutron_config', 'baremetal_gateway')
op.drop_column('neutron_config', 'baremetal_range')

View File

@ -61,6 +61,8 @@ class NeutronConfig(NetworkingConfig):
base_mac = Column(LowercaseString(17), nullable=False)
internal_cidr = Column(String(25))
internal_gateway = Column(String(25))
baremetal_gateway = Column(String(25))
baremetal_range = Column(JSON, default=[])
# Neutron L3 names for default internal / floating networks
# which were previously knows as net04 and net04_ext.

View File

@ -429,6 +429,17 @@
render_addr_mask: null
map_priority: 2
configurable: true
- name: "baremetal"
cidr: "192.168.3.0/24"
ip_range: ["192.168.3.1", "192.168.3.50"]
vlan_start: 104
use_gateway: false
notation: "ip_ranges"
render_type: null
map_priority: 2
configurable: true
restrictions:
- condition: "settings:additional_components.ironic.value == false"
config:
vlan_range: [1000, 1030]
gre_id_range: [2, 65535]
@ -438,6 +449,8 @@
internal_gateway: "192.168.111.1"
floating_name: "admin_floating_net"
floating_ranges: [["172.16.0.130", "172.16.0.254"]]
baremetal_gateway: "192.168.3.51"
baremetal_range: ["192.168.3.52", "192.168.3.254"]
parameters:
amqp:
provider: "rabbitmq"

View File

@ -1089,6 +1089,17 @@ class Cluster(NailgunObject):
consts.CLUSTER_STATUSES.error]
return instance.status not in allowed
@classmethod
def is_component_enabled(cls, instance, component):
"""Checks is specified additional component enabled in cluster
:param instance: nailgun.db.sqlalchemy.models.Cluster instance
:param component: name of additional component
:returns: The result depends on current component status in settings
"""
return bool(instance.attributes.editable['additional_components'].
get((component), {}).get('value'))
class ClusterCollection(NailgunCollection):
"""Cluster collection."""

View File

@ -95,6 +95,14 @@ class NeutronNetworkConfigurationSerializer(NetworkConfigurationSerializer):
'vlan_range',
)
@classmethod
def serialize_network_params(cls, cluster):
"""Overrides default serialization, adds baremetal fields if need"""
fields = cls.network_cfg_fields
if objects.Cluster.is_component_enabled(cluster, 'ironic'):
fields += ('baremetal_gateway', 'baremetal_range')
return BasicSerializer.serialize(cluster.network_config, fields)
@classmethod
def serialize_for_cluster(cls, cluster):
result = cls.serialize_net_groups_and_vips(cluster)

View File

@ -26,9 +26,7 @@ from nailgun import consts
from nailgun.db import db
from nailgun.db.sqlalchemy import models
from nailgun.logger import logger
from nailgun.objects import Cluster
from nailgun.objects import Node
from nailgun.objects import NodeGroupCollection
from nailgun import objects
from nailgun.orchestrator.base_serializers import NetworkDeploymentSerializer
from nailgun.settings import settings
from nailgun import utils
@ -45,7 +43,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
if cluster.mode == 'multinode':
for node in cluster.nodes:
if cls._node_has_role_by_name(node, 'controller'):
net_manager = Cluster.get_network_manager(cluster)
net_manager = objects.Cluster.get_network_manager(cluster)
networks = net_manager.get_node_networks(node)
mgmt_cidr = net_manager.get_network_by_netname(
'management', networks)['ip']
@ -57,7 +55,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
@classmethod
def network_provider_node_attrs(cls, cluster, node):
"""Serialize node, then it will be merged with common attributes."""
nm = Cluster.get_network_manager(cluster)
nm = objects.Cluster.get_network_manager(cluster)
networks = nm.get_node_networks(node)
node_attrs = {
'network_scheme': cls.generate_network_scheme(node, networks),
@ -75,15 +73,15 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
"""
# Get Mellanox data
neutron_mellanox_data = \
Cluster.get_editable_attributes(cluster)\
objects.Cluster.get_editable_attributes(cluster)\
.get('neutron_mellanox', {})
# Get storage data
storage_data = \
Cluster.get_editable_attributes(cluster).get('storage', {})
objects.Cluster.get_editable_attributes(cluster).get('storage', {})
# Get network manager
nm = Cluster.get_network_manager(cluster)
nm = objects.Cluster.get_network_manager(cluster)
# Init mellanox dict
node_attrs['neutron_mellanox'] = {}
@ -192,7 +190,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
if cluster.release.operating_system == 'RHEL':
attrs['amqp'] = {'provider': 'qpid-rh'}
cluster_attrs = Cluster.get_editable_attributes(cluster)
cluster_attrs = objects.Cluster.get_editable_attributes(cluster)
if 'nsx_plugin' in cluster_attrs and \
cluster_attrs['nsx_plugin']['metadata']['enabled']:
attrs['L2']['provider'] = 'nsx'
@ -221,11 +219,11 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
'transformations': []
}
if Node.should_have_public(node):
if objects.Node.should_have_public(node):
attrs['endpoints']['br-ex'] = {}
attrs['roles']['ex'] = 'br-ex'
nm = Cluster.get_network_manager(node.cluster)
nm = objects.Cluster.get_network_manager(node.cluster)
iface_types = consts.NETWORK_INTERFACE_TYPES
# Add a dynamic data to a structure.
@ -282,7 +280,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
# to provide a right ordering of ifdown/ifup operations with
# IP interfaces.
brnames = ['br-ex', 'br-mgmt', 'br-storage', 'br-fw-admin']
if not Node.should_have_public(node):
if not objects.Node.should_have_public(node):
brnames.pop(0)
for brname in brnames:
@ -297,7 +295,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
('management', 'br-mgmt'),
('fuelweb_admin', 'br-fw-admin'),
]
if Node.should_have_public(node):
if objects.Node.should_have_public(node):
netgroup_mapping.append(('public', 'br-ex'))
netgroups = {}
@ -309,7 +307,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
attrs['endpoints'][brname]['IP'] = [netgroup['ip']]
netgroups[ngname] = netgroup
if Node.should_have_public(node):
if objects.Node.should_have_public(node):
attrs['endpoints']['br-ex']['gateway'] = \
netgroups['public']['gateway']
else:
@ -439,7 +437,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
"router_ext": True,
"physnet": "physnet1"
},
"tenant": Cluster.get_creds(cluster)['tenant']['value'],
"tenant": objects.Cluster.get_creds(cluster)['tenant']['value'],
"shared": False
}
@ -460,7 +458,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
"physnet": "physnet2"
if cluster.network_config.segmentation_type == "vlan" else None
},
"tenant": Cluster.get_creds(cluster)['tenant']['value'],
"tenant": objects.Cluster.get_creds(cluster)['tenant']['value'],
"shared": False
}
@ -500,7 +498,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
}
# Set non-default ml2 configurations
attrs = Cluster.get_editable_attributes(cluster)
attrs = objects.Cluster.get_editable_attributes(cluster)
if 'neutron_mellanox' in attrs and \
attrs['neutron_mellanox']['plugin']['value'] == 'ethernet':
res['mechanism_drivers'] = 'mlnx,openvswitch'
@ -515,7 +513,7 @@ class NeutronNetworkDeploymentSerializer(NetworkDeploymentSerializer):
l3 = {
"use_namespaces": True
}
attrs = Cluster.get_editable_attributes(cluster)
attrs = objects.Cluster.get_editable_attributes(cluster)
if 'nsx_plugin' in attrs and \
attrs['nsx_plugin']['metadata']['enabled']:
dhcp_attrs = l3.setdefault('dhcp_agent', {})
@ -564,7 +562,7 @@ class NeutronNetworkDeploymentSerializer60(
# Include information about all subnets that don't belong to this node.
# This is used during deployment to configure routes to all other
# networks in the environment.
nm = Cluster.get_network_manager(node.cluster)
nm = objects.Cluster.get_network_manager(node.cluster)
other_nets = nm.get_networks_not_on_node(node, networks)
netgroup_mapping = [
@ -572,7 +570,7 @@ class NeutronNetworkDeploymentSerializer60(
('management', 'br-mgmt'),
('fuelweb_admin', 'br-fw-admin'),
]
if Node.should_have_public(node):
if objects.Node.should_have_public(node):
netgroup_mapping.append(('public', 'br-ex'))
for ngname, brname in netgroup_mapping:
@ -582,7 +580,7 @@ class NeutronNetworkDeploymentSerializer60(
attrs['endpoints'][brname]['other_nets'] = \
other_nets.get(ngname, [])
if Node.should_have_public(node):
if objects.Node.should_have_public(node):
attrs['endpoints']['br-ex']['default_gateway'] = True
else:
gw = nm.get_default_gateway(node.id)
@ -716,14 +714,14 @@ class NeutronNetworkDeploymentSerializer61(
},
}
is_public = Node.should_have_public(node)
is_public = objects.Node.should_have_public(node)
if is_public:
attrs['endpoints']['br-ex'] = {'IP': 'none'}
attrs['endpoints']['br-floating'] = {'IP': 'none'}
attrs['roles']['ex'] = 'br-ex'
attrs['roles']['neutron/floating'] = 'br-floating'
nm = Cluster.get_network_manager(node.cluster)
nm = objects.Cluster.get_network_manager(node.cluster)
# Populate IP and GW information to endpoints.
netgroup_mapping = [
@ -794,7 +792,7 @@ class NeutronNetworkDeploymentSerializer61(
attrs['transformations'] = cls.generate_transformations(
node, nm, nets_by_ifaces, is_public, prv_base_ep)
if NodeGroupCollection.get_by_cluster_id(
if objects.NodeGroupCollection.get_by_cluster_id(
node.cluster.id).count() > 1:
cls.generate_routes(node, attrs, nm, netgroup_mapping, netgroups,
networks)
@ -868,7 +866,7 @@ class NeutronNetworkDeploymentSerializer70(
@classmethod
def get_node_non_default_networks(cls, node):
"""Returns list of non-default networks assigned to node."""
nm = Cluster.get_network_manager(node.cluster)
nm = objects.Cluster.get_network_manager(node.cluster)
return filter(lambda net: net['name'] not in consts.NETWORKS,
nm.get_node_networks(node))
@ -915,7 +913,7 @@ class NeutronNetworkDeploymentSerializer70(
consts.NETWORKS.management: 'br-mgmt'}
# roles can be assigned to br-ex only in case it has a public IP
if Node.should_have_public_with_ip(node):
if objects.Node.should_have_public_with_ip(node):
mapping[consts.NETWORKS.public] = 'br-ex'
if node.cluster.network_config.segmentation_type in \
@ -928,7 +926,7 @@ class NeutronNetworkDeploymentSerializer70(
@classmethod
def get_network_to_ip_mapping(cls, node):
nm = Cluster.get_network_manager(node.cluster)
nm = objects.Cluster.get_network_manager(node.cluster)
mapping = dict()
networks = nm.get_node_networks(node)
@ -948,7 +946,7 @@ class NeutronNetworkDeploymentSerializer70(
- 'get_network_role_mapping_to_interfaces'.
"""
roles = dict()
for role in Cluster.get_network_roles(node.cluster):
for role in objects.Cluster.get_network_roles(node.cluster):
default_mapping = mapping.get(role['default_mapping'])
if default_mapping:
roles[role['id']] = default_mapping
@ -1005,14 +1003,14 @@ class NeutronNetworkDeploymentSerializer70(
'roles': cls.get_network_role_mapping_to_interfaces(node),
}
is_public = Node.should_have_public(node)
is_public = objects.Node.should_have_public(node)
if is_public:
attrs['endpoints']['br-ex'] = {'IP': 'none'}
attrs['endpoints']['br-floating'] = {'IP': 'none'}
attrs['roles']['ex'] = 'br-ex'
attrs['roles']['neutron/floating'] = 'br-floating'
nm = Cluster.get_network_manager(node.cluster)
nm = objects.Cluster.get_network_manager(node.cluster)
# Populate IP and GW information to endpoints.
netgroup_mapping = (cls.get_network_to_endpoint_mapping(node)
@ -1046,7 +1044,7 @@ class NeutronNetworkDeploymentSerializer70(
})
# Add gateway.
if Node.should_have_public_with_ip(node):
if objects.Node.should_have_public_with_ip(node):
attrs['endpoints']['br-ex']['gateway'] = \
netgroups['public']['gateway']
else:
@ -1084,7 +1082,7 @@ class NeutronNetworkDeploymentSerializer70(
attrs['transformations'] = cls.generate_transformations(
node, nm, nets_by_ifaces, is_public, prv_base_ep)
if NodeGroupCollection.get_by_cluster_id(
if objects.NodeGroupCollection.get_by_cluster_id(
node.cluster.id).count() > 1:
cls.generate_routes(node, attrs, nm, netgroup_mapping, netgroups,
networks)
@ -1120,16 +1118,16 @@ class NeutronNetworkDeploymentSerializer70(
@classmethod
def generate_network_metadata(cls, cluster):
nodes = dict()
nm = Cluster.get_network_manager(cluster)
nm = objects.Cluster.get_network_manager(cluster)
for node in Cluster.get_nodes_not_for_deletion(cluster):
name = Node.get_slave_name(node)
node_roles = Node.all_roles(node)
for node in objects.Cluster.get_nodes_not_for_deletion(cluster):
name = objects.Node.get_slave_name(node)
node_roles = objects.Node.all_roles(node)
network_roles = cls.get_network_role_mapping_to_ip(node)
nodes[name] = {
"uid": node.uid,
"fqdn": Node.get_node_fqdn(node),
"fqdn": objects.Node.get_node_fqdn(node),
"name": name,
"user_node_name": node.name,
"swift_zone": node.uid,
@ -1208,7 +1206,7 @@ class NeutronNetworkTemplateSerializer70(
'roles': roles,
}
nm = Cluster.get_network_manager(node.cluster)
nm = objects.Cluster.get_network_manager(node.cluster)
netgroups = nm.get_node_networks_with_ips(node)
netgroup_mapping = nm.get_node_network_mapping(node)
@ -1251,7 +1249,7 @@ class NeutronNetworkTemplateSerializer70(
attrs['transformations'] = cls.generate_transformations(node)
if NodeGroupCollection.get_by_cluster_id(
if objects.NodeGroupCollection.get_by_cluster_id(
node.cluster.id).count() > 1:
cls.generate_routes(node, attrs, nm, netgroup_mapping, netgroups,
networks)
@ -1262,7 +1260,7 @@ class NeutronNetworkTemplateSerializer70(
@classmethod
def _get_endpoint_to_ip_mapping(cls, node):
nm = Cluster.get_network_manager(node.cluster)
nm = objects.Cluster.get_network_manager(node.cluster)
net_to_ips = nm.get_node_networks_with_ips(node)
mapping = dict()
@ -1294,8 +1292,8 @@ class NeutronNetworkTemplateSerializer70(
This info is deprecated in 7.0 and should be removed in later version.
"""
nm = Cluster.get_network_manager(cluster)
for node in Cluster.get_nodes_not_for_deletion(cluster):
nm = objects.Cluster.get_network_manager(cluster)
for node in objects.Cluster.get_nodes_not_for_deletion(cluster):
netw_data = []
for name, data in six.iteritems(
nm.get_node_networks_with_ips(node)):
@ -1322,6 +1320,41 @@ class NeutronNetworkDeploymentSerializer80(
def render_floating_ranges(cls, floating_ranges):
return [utils.join_range(x) for x in floating_ranges]
@classmethod
def get_network_to_endpoint_mapping(cls, node):
mapping = {
consts.NETWORKS.fuelweb_admin:
consts.DEFAULT_BRIDGES_NAMES.br_fw_admin,
consts.NETWORKS.storage:
consts.DEFAULT_BRIDGES_NAMES.br_storage,
consts.NETWORKS.management:
consts.DEFAULT_BRIDGES_NAMES.br_mgmt}
# roles can be assigned to br-ex only in case it has a public IP
if objects.Node.should_have_public_with_ip(node):
mapping[consts.NETWORKS.public] = \
consts.DEFAULT_BRIDGES_NAMES.br_ex
if node.cluster.network_config.segmentation_type in \
(consts.NEUTRON_SEGMENT_TYPES.gre,
consts.NEUTRON_SEGMENT_TYPES.tun):
mapping[consts.NETWORKS.private] = \
consts.DEFAULT_BRIDGES_NAMES.br_mesh
if objects.Cluster.is_component_enabled(node.cluster, 'ironic'):
mapping[consts.NETWORKS.baremetal] = \
consts.DEFAULT_BRIDGES_NAMES.br_baremetal
mapping.update(cls.get_node_non_default_bridge_mapping(node))
return mapping
@classmethod
def generate_transformations(cls, node, nm, nets_by_ifaces, is_public,
prv_base_ep):
transformations = (super(NeutronNetworkDeploymentSerializer80, cls)
.generate_transformations(node, nm, nets_by_ifaces,
is_public, prv_base_ep))
if objects.Cluster.is_component_enabled(node.cluster, 'ironic'):
transformations.insert(0, cls.add_bridge(
consts.DEFAULT_BRIDGES_NAMES.br_baremetal))
return transformations
class NeutronNetworkTemplateSerializer80(NeutronNetworkTemplateSerializer70):
pass

View File

@ -18,7 +18,9 @@ import copy
from mock import patch
from oslo_serialization import jsonutils
import yaml
from nailgun.objects import Cluster
from nailgun.objects.serializers.network_configuration \
import NeutronNetworkConfigurationSerializer
from nailgun.objects.serializers.network_configuration \
@ -238,3 +240,28 @@ class TestNetworkModels(BaseIntegrationTest):
"admin_internal_net", cluster.network_config.internal_name)
self.assertEqual(
"admin_floating_net", cluster.network_config.floating_name)
def test_neutron_networking_parameters_baremetal(self):
attributes_metadata = """
editable:
additional_components:
ironic:
value: %r
type: "checkbox"
"""
cluster = self.env.create_cluster(
api=False,
net_provider=consts.CLUSTER_NET_PROVIDERS.neutron)
# Ensure baremetal_* fields are not serialized when Ironic disabled
nw_params = NeutronNetworkConfigurationSerializer. \
serialize_network_params(cluster)
self.assertNotIn('baremetal_gateway', nw_params)
self.assertNotIn('baremetal_range', nw_params)
# Ensure baremetal_* fields are serialized when Ironic enabled
Cluster.patch_attributes(
cluster, yaml.load(attributes_metadata % True))
self.db.refresh(cluster)
nw_params = NeutronNetworkConfigurationSerializer. \
serialize_network_params(cluster)
self.assertIn('baremetal_gateway', nw_params)
self.assertIn('baremetal_range', nw_params)

View File

@ -114,11 +114,11 @@ class TestDeploymentAttributesSerialization70(
):
segmentation_type = consts.NEUTRON_SEGMENT_TYPES.vlan
custom_network = {
'name': 'baremetal',
'role': 'ironic/baremetal',
'name': 'custom',
'role': 'plugin/custom',
'cidr': '192.168.3.0/24',
'vlan_start': 50,
'bridge': 'br-baremetal',
'bridge': 'br-custom',
}
plugin_network_roles = yaml.safe_load("""
- id: "{role}"
@ -232,25 +232,25 @@ class TestDeploymentAttributesSerialization70(
transformations = node['network_scheme']['transformations']
node_network_roles = (node['network_metadata']['nodes']
['node-' + node['uid']]['network_roles'])
baremetal_ip = node_network_roles.get(self.custom_network['role'],
'0.0.0.0')
baremetal_brs = filter(lambda t: t.get('name') ==
self.custom_network['bridge'],
transformations)
baremetal_ports = filter(lambda t: t.get('name') ==
("eth0.%s" %
self.custom_network['vlan_start']),
transformations)
custom_ip = node_network_roles.get(self.custom_network['role'],
'0.0.0.0')
custom_brs = filter(lambda t: t.get('name') ==
self.custom_network['bridge'],
transformations)
custom_ports = filter(lambda t: t.get('name') ==
("eth0.%s" %
self.custom_network['vlan_start']),
transformations)
self.assertEqual(roles.get(self.custom_network['role']),
self.custom_network['bridge'])
self.assertEqual(vips.get(self.custom_network['name'],
{}).get('network_role'),
self.custom_network['role'])
self.assertTrue(netaddr.IPAddress(baremetal_ip) in
self.assertTrue(netaddr.IPAddress(custom_ip) in
netaddr.IPNetwork(self.custom_network['cidr']))
self.assertEqual(len(baremetal_brs), 1)
self.assertEqual(len(baremetal_ports), 1)
self.assertEqual(baremetal_ports[0]['bridge'],
self.assertEqual(len(custom_brs), 1)
self.assertEqual(len(custom_ports), 1)
self.assertEqual(custom_ports[0]['bridge'],
self.custom_network['bridge'])
def test_network_scheme(self):

View File

@ -555,3 +555,40 @@ class TestClusterPluginsMigration(base.BaseAlembicMigrationTest):
'weight': 25,
'label': 'label'
})
class TestBaremetalFields(base.BaseAlembicMigrationTest):
def test_baremetal_fields_saving(self):
baremetal_gateway = '192.168.3.51'
baremetal_range = jsonutils.dumps(['192.168.3.52', '192.168.3.254'])
result = db.execute(
self.meta.tables['networking_configs'].insert(),
[{
'cluster_id': None,
'dns_nameservers': ['8.8.8.8'],
'floating_ranges': [],
'configuration_template': None,
}])
db.execute(
self.meta.tables['neutron_config'].insert(),
[{
'id': result.inserted_primary_key[0],
'vlan_range': [],
'gre_id_range': [],
'base_mac': '00:00:00:00:00:00',
'internal_cidr': '10.10.10.00/24',
'internal_gateway': '10.10.10.01',
'internal_name': 'my_internal_name',
'floating_name': 'my_floating_name',
'baremetal_gateway': baremetal_gateway,
'baremetal_range': baremetal_range,
'segmentation_type': 'vlan',
'net_l23_provider': 'ovs'
}])
result = db.execute(
sa.select(
[self.meta.tables['neutron_config'].c.baremetal_gateway,
self.meta.tables['neutron_config'].c.baremetal_range])).\
fetchall()
self.assertIn((baremetal_gateway, baremetal_range), result)

View File

@ -528,7 +528,9 @@ class TestNeutronNetworkConfigurationValidatorProtocol(
"internal_gateway": "192.168.111.1",
"net_l23_provider": consts.NEUTRON_L23_PROVIDERS.ovs,
"segmentation_type": consts.NEUTRON_SEGMENT_TYPES.gre,
"vlan_range": [1000, 1030]
"vlan_range": [1000, 1030],
"baremetal_gateway": "192.168.3.51",
"baremetal_range": ["192.168.3.52", "192.168.3.254"]
}
}
@ -648,6 +650,16 @@ class TestNeutronNetworkConfigurationValidatorProtocol(
self.nc['networking_parameters']['vlan_range'] = [2, 2]
self.assertRaisesNonUnique(self.nc, "[2, 2]")
def test_baremetal_gateway_invalid_type(self):
self.nc['networking_parameters']['baremetal_gateway'] = []
self.assertRaisesInvalidAnyOf(
self.nc, [], "['networking_parameters']['baremetal_gateway']")
def test_baremetal_range_invalid_value(self):
self.nc['networking_parameters']['baremetal_range'] = ["bad"]
self.assertRaisesInvalidAnyOf(
self.nc, ["bad"], "['networking_parameters']['baremetal_range']")
class TestNeutronNetworkConfigurationValidator(base.BaseIntegrationTest):