From d14f898ed2d77ecffb0a2c03a5d8b3ece5e33f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=A1gr?= Date: Wed, 15 Apr 2015 15:25:29 +0200 Subject: [PATCH] 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 --- docs/packstack.rst | 3 ++ packstack/installer/basedefs.py | 5 +- packstack/modules/common.py | 36 +++++++++++++ packstack/modules/ospluginutils.py | 22 -------- packstack/plugins/neutron_350.py | 8 ++- packstack/plugins/nova_300.py | 25 +++++---- packstack/plugins/prescript_000.py | 51 +++++++++++++----- .../parser/functions/force_interface.rb | 53 +++++++++++++++++++ packstack/puppet/templates/global.pp | 7 +++ .../puppet/templates/neutron_lb_agent.pp | 4 +- .../puppet/templates/neutron_ovs_agent.pp | 8 ++- .../puppet/templates/nova_compute_flat.pp | 6 +++ packstack/puppet/templates/nova_network.pp | 8 +-- 13 files changed, 182 insertions(+), 54 deletions(-) create mode 100644 packstack/puppet/modules/packstack/lib/puppet/parser/functions/force_interface.rb create mode 100644 packstack/puppet/templates/nova_compute_flat.pp diff --git a/docs/packstack.rst b/docs/packstack.rst index dd0da1387..9060016c1 100644 --- a/docs/packstack.rst +++ b/docs/packstack.rst @@ -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 ------------------------- diff --git a/packstack/installer/basedefs.py b/packstack/installer/basedefs.py index bab832a4c..b329332be 100644 --- a/packstack/installer/basedefs.py +++ b/packstack/installer/basedefs.py @@ -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" diff --git a/packstack/modules/common.py b/packstack/modules/common.py index df2aad6ca..1e442e0bf 100644 --- a/packstack/modules/common.py +++ b/packstack/modules/common.py @@ -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) diff --git a/packstack/modules/ospluginutils.py b/packstack/modules/ospluginutils.py index 63cffe054..c13f0bbf2 100644 --- a/packstack/modules/ospluginutils.py +++ b/packstack/modules/ospluginutils.py @@ -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 = [] diff --git a/packstack/plugins/neutron_350.py b/packstack/plugins/neutron_350.py index 11846c9a6..92d954f16 100644 --- a/packstack/plugins/neutron_350.py +++ b/packstack/plugins/neutron_350.py @@ -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) diff --git a/packstack/plugins/nova_300.py b/packstack/plugins/nova_300.py index 7ce66efaa..637cd6afb 100644 --- a/packstack/plugins/nova_300.py +++ b/packstack/plugins/nova_300.py @@ -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)) diff --git a/packstack/plugins/prescript_000.py b/packstack/plugins/prescript_000.py index 3066dc76c..62d8fa2a5 100644 --- a/packstack/plugins/prescript_000.py +++ b/packstack/plugins/prescript_000.py @@ -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']: diff --git a/packstack/puppet/modules/packstack/lib/puppet/parser/functions/force_interface.rb b/packstack/puppet/modules/packstack/lib/puppet/parser/functions/force_interface.rb new file mode 100644 index 000000000..2fbd748f1 --- /dev/null +++ b/packstack/puppet/modules/packstack/lib/puppet/parser/functions/force_interface.rb @@ -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 diff --git a/packstack/puppet/templates/global.pp b/packstack/puppet/templates/global.pp index 58dd4470c..fb623a137 100644 --- a/packstack/puppet/templates/global.pp +++ b/packstack/puppet/templates/global.pp @@ -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') } diff --git a/packstack/puppet/templates/neutron_lb_agent.pp b/packstack/puppet/templates/neutron_lb_agent.pp index ce5224363..7d6c32491 100644 --- a/packstack/puppet/templates/neutron_lb_agent.pp +++ b/packstack/puppet/templates/neutron_lb_agent.pp @@ -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), } diff --git a/packstack/puppet/templates/neutron_ovs_agent.pp b/packstack/puppet/templates/neutron_ovs_agent.pp index 33873f90b..0476a8b68 100644 --- a/packstack/puppet/templates/neutron_ovs_agent.pp +++ b/packstack/puppet/templates/neutron_ovs_agent.pp @@ -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') diff --git a/packstack/puppet/templates/nova_compute_flat.pp b/packstack/puppet/templates/nova_compute_flat.pp new file mode 100644 index 000000000..c4f2c6f84 --- /dev/null +++ b/packstack/puppet/templates/nova_compute_flat.pp @@ -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); +} diff --git a/packstack/puppet/templates/nova_network.pp b/packstack/puppet/templates/nova_network.pp index 279f3abf8..8bced5087 100644 --- a/packstack/puppet/templates/nova_network.pp +++ b/packstack/puppet/templates/nova_network.pp @@ -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, } -