Allow CIDR instead of iterface name

This patch implements translation from CIDR to interface name,
which should allow deployments to heretogenous infrastructure.

Package openstack-packstack-puppet will need new require,
which is rubygem-ippaddress.

Workaround for rhbz#1200604

Change-Id: Id27881f616781e5a24a1bdb1e169915b7619eebd
This commit is contained in:
Martin Mágr 2015-04-15 15:25:29 +02:00
parent 29b18bc93b
commit d14f898ed2
13 changed files with 182 additions and 54 deletions

View File

@ -101,6 +101,9 @@ Global Options
**CONFIG_UNSUPPORTED**
Specify 'y' if you want to use unsupported parameters. This should be used only if you know what you are doing. Issues caused by using unsupported options will not be fixed before the next major release. ['y', 'n']
**CONFIG_USE_SUBNETS**
Specify 'y' if you want to use subnet addresses (in CIDR format) instead of interface names in following options: CONFIG_NOVA_COMPUTE_PRIVIF, CONFIG_NOVA_NETWORK_PRIVIF, CONFIG_NOVA_NETWORK_PUBIF, CONFIG_NEUTRON_OVS_BRIDGE_IFACES, CONFIG_NEUTRON_LB_INTERFACE_MAPPINGS, CONFIG_NEUTRON_OVS_TUNNEL_IF. This is useful for cases when interface names are not same on all installation hosts.
vCenter Config Parameters
-------------------------

View File

@ -68,7 +68,10 @@ HIERADATA_FILE_RELATIVE = "hieradata"
HIERADATA_DIR = os.path.join(VAR_DIR, HIERADATA_FILE_RELATIVE)
PUPPET_DEPENDENCIES = ['puppet', 'hiera', 'openssh-clients', 'tar', 'nc']
PUPPET_MODULES_PKG = 'openstack-puppet-modules'
PUPPET_MODULES_PKGS = [
'openstack-puppet-modules',
'openstack-packstack-puppet'
]
FILE_INSTALLER_LOG = "setup.log"

View File

@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import netaddr
from ..installer import utils
@ -49,3 +51,37 @@ def is_all_in_one(config):
# with them when checking all-in-one. MariaDB host should however be
# omitted if we are not installing MariaDB.
return len(filtered_hosts(config, exclude=False, dbhost=True)) == 1
def cidr_to_ifname(cidr, host, config):
"""
Returns appropriate host's interface name from given CIDR subnet. Passed
config dict has to contain discovered hosts details.
"""
if not config or not config['HOST_DETAILS'] or '/' not in cidr:
raise ValueError(
'Cannot translate CIDR to interface, invalid parameters '
'were given.'
)
info = config['HOST_DETAILS'][host]
result = []
for item in cidr.split(','):
translated = []
for fragment in item.split(':'):
try:
subnet_a = netaddr.IPNetwork(fragment)
except netaddr.AddrFormatError:
translated.append(fragment)
continue
for interface in info['interfaces'].split(','):
interface = interface.strip()
ipaddr = info['ipaddress_{}'.format(interface)]
netmask = info['netmask_{}'.format(interface)]
subnet_b = netaddr.IPNetwork('{ipaddr}/{netmask}'.format(**locals()))
if subnet_a == subnet_b:
translated.append(interface)
break
result.append(':'.join(translated))
return ','.join(result)

View File

@ -24,28 +24,6 @@ PUPPET_TEMPLATE_DIR = os.path.join(PUPPET_DIR, "templates")
HIERA_DEFAULTS_YAML = os.path.join(basedefs.HIERADATA_DIR, "defaults.yaml")
class NovaConfig(object):
"""
Helper class to create puppet manifest entries for nova_config
"""
def __init__(self):
self.options = {}
def addOption(self, n, v):
self.options[n] = v
def getManifestEntry(self):
entry = ""
if not self.options:
return entry
entry += "nova_config{\n"
for k, v in self.options.items():
entry += ' "%s": value => "%s";\n' % (k, v)
entry += "}"
return entry
class ManifestFiles(object):
def __init__(self):
self.filelist = []

View File

@ -23,7 +23,7 @@ from packstack.installer import processors
from packstack.installer import output_messages
from packstack.installer.utils import split_hosts
from packstack.modules.common import filtered_hosts
from packstack.modules import common
from packstack.modules.documentation import update_params_usage
from packstack.modules.shortcuts import get_mq
from packstack.modules.ospluginutils import appendManifestFile
@ -739,6 +739,10 @@ def create_l2_agent_manifests(config, messages):
(host in network_hosts and tunnel_types)
or 'vlan' in ovs_type)
):
if config['CONFIG_USE_SUBNETS'] == 'y':
iface_arr = [
common.cidr_to_ifname(i, host, config) for i in iface_arr
]
config["CONFIG_NEUTRON_OVS_BRIDGE_IFACES"] = iface_arr
manifestdata += getManifestTemplate(template_name)
appendManifestFile(manifestfile, manifestdata + "\n")
@ -761,7 +765,7 @@ def create_metadata_manifests(config, messages):
def check_nm_status(config, messages):
hosts_with_nm = []
for host in filtered_hosts(config):
for host in common.filtered_hosts(config):
server = utils.ScriptRunner(host)
server.append("systemctl")
rc, out = server.execute(can_fail=False)

View File

@ -26,13 +26,13 @@ from packstack.installer import utils
from packstack.installer import validators
from packstack.installer.exceptions import ScriptRuntimeError
from packstack.modules import common
from packstack.modules.documentation import update_params_usage
from packstack.modules.shortcuts import get_mq
from packstack.modules.ospluginutils import appendManifestFile
from packstack.modules.ospluginutils import createFirewallResources
from packstack.modules.ospluginutils import getManifestTemplate
from packstack.modules.ospluginutils import manifestfiles
from packstack.modules.ospluginutils import NovaConfig
# ------------- Nova Packstack Plugin Initialization --------------
@ -532,16 +532,19 @@ def create_compute_manifest(config, messages):
manifestdata += getManifestTemplate("nova_nfs")
manifestfile = "%s_nova.pp" % host
nova_config_options = NovaConfig()
if config['CONFIG_NEUTRON_INSTALL'] != 'y':
if host not in network_hosts:
nova_config_options.addOption(
"DEFAULT/flat_interface",
config['CONFIG_NOVA_COMPUTE_PRIVIF']
manifestdata += getManifestTemplate('nova_compute_flat')
if config['CONFIG_USE_SUBNETS'] == 'y':
netface = common.cidr_to_ifname(
config['CONFIG_NOVA_COMPUTE_PRIVIF'], host, config
)
check_ifcfg(host, config['CONFIG_NOVA_COMPUTE_PRIVIF'])
else:
netface = config['CONFIG_NOVA_COMPUTE_PRIVIF']
check_ifcfg(host, netface)
try:
bring_up_ifcfg(host, config['CONFIG_NOVA_COMPUTE_PRIVIF'])
bring_up_ifcfg(host, netface)
except ScriptRuntimeError as ex:
# just warn user to do it by himself
messages.append(str(ex))
@ -564,7 +567,6 @@ def create_compute_manifest(config, messages):
manifestdata += "\n" + createFirewallResources(
'FIREWALL_NOVA_COMPUTE_RULES'
)
manifestdata += "\n" + nova_config_options.getManifestEntry()
manifestdata += "\n" + ssh_hostkeys
appendManifestFile(manifestfile, manifestdata)
@ -585,9 +587,12 @@ def create_network_manifest(config, messages):
config['CONFIG_NOVA_NETWORK_MULTIHOST'] = multihost and 'true' or 'false'
for host in network_hosts:
for i in ('CONFIG_NOVA_NETWORK_PRIVIF', 'CONFIG_NOVA_NETWORK_PUBIF'):
check_ifcfg(host, config[i])
netface = config[i]
if config['CONFIG_USE_SUBNETS'] == 'y':
netface = common.cidr_to_ifname(netface, host, config)
check_ifcfg(host, netface)
try:
bring_up_ifcfg(host, config[i])
bring_up_ifcfg(host, netface)
except ScriptRuntimeError as ex:
# just warn user to do it by himself
messages.append(str(ex))

View File

@ -43,7 +43,7 @@ PLUGIN_NAME_COLORED = utils.color_text(PLUGIN_NAME, 'blue')
def initConfig(controller):
default_ssh_key = os.path.join(os.environ["HOME"], ".ssh/*.pub")
default_ssh_key = os.path.expanduser('~/.ssh/*.pub')
default_ssh_key = (glob.glob(default_ssh_key) + [""])[0]
params = {
"GLOBAL": [
@ -437,6 +437,19 @@ def initConfig(controller):
"USE_DEFAULT": False,
"NEED_CONFIRM": False,
"CONDITION": False},
{"CMD_OPTION": "use-subnets",
"PROMPT": ("Should interface names be automatically recognized "
"based on subnet CIDR"),
"OPTION_LIST": ['y', 'n'],
"VALIDATORS": [validators.validate_options],
"DEFAULT_VALUE": "n",
"MASK_INPUT": False,
"LOOSE_VALIDATION": False,
"CONF_NAME": "CONFIG_USE_SUBNETS",
"USE_DEFAULT": False,
"NEED_CONFIRM": False,
"CONDITION": False},
],
"VMWARE": [
@ -884,7 +897,12 @@ def is_rhel():
def detect_os_and_version(host):
server = utils.ScriptRunner(host)
server.append('python -c "import platform; print platform.linux_distribution(full_distribution_name=0)[0]+\',\'+platform.linux_distribution()[1]"')
server.append(
'python -c "import platform; '
'print platform.linux_distribution(full_distribution_name=0)[0]'
'+\',\'+'
'platform.linux_distribution()[1]"'
)
try:
rc, out = server.execute()
out = out.split(",")
@ -1169,20 +1187,25 @@ def preinstall_and_discover(config, messages):
"""
config['HOST_LIST'] = list(filtered_hosts(config))
local = utils.ScriptRunner()
local.append('rpm -q --requires %s | egrep -v "^(rpmlib|\/|perl)"'
% basedefs.PUPPET_MODULES_PKG)
# this can fail if there are no dependencies other than those
# filtered out by the egrep expression.
rc, modules_deps = local.execute(can_fail=False)
all_deps = ''
for pkg in basedefs.PUPPET_MODULES_PKGS:
local = utils.ScriptRunner()
local.append(
'rpm -q --requires %s | egrep -v "^(rpmlib|\/|perl)"' % pkg
)
# this can fail if there are no dependencies other than those
# filtered out by the egrep expression.
rc, pkg_deps = local.execute(can_fail=False)
errmsg = '%s is not installed' % pkg
if errmsg in pkg_deps:
# modules package might not be installed if we are running
# from source; in this case we assume user knows what (s)he's
# doing and we don't install modules dependencies
continue
all_deps += ' ' + pkg_deps.strip()
# modules package might not be installed if we are running from source;
# in this case we assume user knows what (s)he's doing and we don't
# install modules dependencies
errmsg = '%s is not installed' % basedefs.PUPPET_MODULES_PKG
deps = list(basedefs.PUPPET_DEPENDENCIES)
if errmsg not in modules_deps:
deps.extend([i.strip() for i in modules_deps.split() if i.strip()])
deps.extend([i.strip() for i in all_deps.split() if i.strip()])
details = {}
for hostname in config['HOST_LIST']:

View File

@ -0,0 +1,53 @@
require 'ipaddress'
# Returns value
module Puppet::Parser::Functions
newfunction(:force_interface, :type => :rvalue) do |args|
if args.size < 2
raise(
Puppet::ParseError,
"force_interface(): Wrong number of arguments given (#{args.size} for 2)"
)
end
value = args[0]
allow = args[1]
was_array = value.kind_of?(Array)
if not was_array
value = [value]
end
result = []
if allow
value.each do |val|
translated = []
val.split(':').each do |fragment|
if fragment.include?('/') # this is CIDR, so translate it
cidr = IPAddress fragment
lookupvar('interfaces').split(',').each do |interface|
interface.strip!
ifaddr = lookupvar("ipaddress_#{interface}")
ifmask = lookupvar("netmask_#{interface}")
ifcidr = IPAddress "#{ifaddr}/#{ifmask}"
if cidr.network == ifcidr.network
translated.push(interface)
end
end
else
translated.push(fragment)
end
end
result.push(translated.join(':'))
end
else
result = value
end
if not was_array
result = result[0]
end
result
end
end

View File

@ -1 +1,8 @@
$use_subnets_value = hiera('CONFIG_USE_SUBNETS')
$use_subnets = $use_subnets_value ? {
'y' => true,
default => false,
}
Exec { timeout => hiera('DEFAULT_EXEC_TIMEOUT') }

View File

@ -1,3 +1,5 @@
$neutron_lb_interface_mappings = hiera('CONFIG_NEUTRON_LB_INTERFACE_MAPPINGS')
class { '::neutron::agents::linuxbridge':
physical_interface_mappings => hiera('CONFIG_NEUTRON_LB_INTERFACE_MAPPINGS'),
physical_interface_mappings => force_interface($neutron_lb_interface_mappings, $use_subnets),
}

View File

@ -1,4 +1,10 @@
$ovs_agent_vxlan_cfg_neut_ovs_tun_if = hiera('CONFIG_NEUTRON_OVS_TUNNEL_IF',undef)
$neutron_ovs_tunnel_if = hiera('CONFIG_NEUTRON_OVS_TUNNEL_IF', undef)
if $neutron_ovs_tunnel_if {
$ovs_agent_vxlan_cfg_neut_ovs_tun_if = force_interface($neutron_ovs_tunnel_if, $use_subnets)
} else {
$ovs_agent_vxlan_cfg_neut_ovs_tun_if = undef
}
if $ovs_agent_vxlan_cfg_neut_ovs_tun_if != '' {
$iface = regsubst($ovs_agent_vxlan_cfg_neut_ovs_tun_if, '[\.\-\:]', '_', 'G')

View File

@ -0,0 +1,6 @@
$nova_compute_privif = hiera('CONFIG_NOVA_COMPUTE_PRIVIF')
nova_config {
'DEFAULT/flat_interface': value => force_interface($nova_compute_privif, $use_subnets);
}

View File

@ -35,13 +35,16 @@ if $manager == 'nova.network.manager.VlanManager' {
$net_num = 1
}
$nova_network_privif = hiera('CONFIG_NOVA_NETWORK_PRIVIF')
$nova_network_pubif = hiera('CONFIG_NOVA_NETWORK_PUBIF')
class { '::nova::network':
enabled => true,
network_manager => $manager,
num_networks => $net_num ,
network_size => $net_size,
private_interface => hiera('CONFIG_NOVA_NETWORK_PRIVIF'),
public_interface => hiera('CONFIG_NOVA_NETWORK_PUBIF'),
private_interface => force_interface($nova_network_privif, $use_subnets),
public_interface => force_interface($nova_network_pubif, $use_subnets),
fixed_range => hiera('CONFIG_NOVA_NETWORK_FIXEDRANGE'),
floating_range => hiera('CONFIG_NOVA_NETWORK_FLOATRANGE'),
config_overrides => $overrides,
@ -50,4 +53,3 @@ class { '::nova::network':
package { 'dnsmasq':
ensure => present,
}