Handle multiple network interfaces
Support public and private network in kolla-mesos configuration and Vagrant. Change-Id: Ic8df2b608a018dfd59640002c897fd21341bef27 Closes-Bug: #1523448
This commit is contained in:
parent
df9f8a304b
commit
83d8341993
@ -10,20 +10,41 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import fcntl
|
import netifaces
|
||||||
import socket
|
from oslo_config import cfg
|
||||||
import struct
|
|
||||||
|
|
||||||
|
|
||||||
# TODO(nihilifer): Autodetect networks and find a way to share commons between
|
CONF = cfg.CONF
|
||||||
# kolla_mesos and kolla_mesos_start.py.
|
CONF.import_group('network', 'kolla_mesos.config.network')
|
||||||
def get_ip_address(ifname='eth0'):
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
try:
|
def _get_localhost():
|
||||||
return socket.inet_ntoa(fcntl.ioctl(
|
"""Get localhost IP address regarding the IP protocol version"""
|
||||||
s.fileno(),
|
if CONF.network.ipv6:
|
||||||
0x8915, # SIOCGIFADDR
|
return '::1'
|
||||||
struct.pack('256s', ifname[:15])
|
return '127.0.0.1'
|
||||||
)[20:24])
|
|
||||||
except IOError:
|
|
||||||
return '127.0.0.1'
|
def get_ip_address(public=True):
|
||||||
|
"""Get IP address of the interface connected to the public network.
|
||||||
|
|
||||||
|
If there is no such an interface, then localhost is returned.
|
||||||
|
"""
|
||||||
|
if public:
|
||||||
|
iface = CONF.network.public_interface
|
||||||
|
else:
|
||||||
|
iface = CONF.network.private_interface
|
||||||
|
|
||||||
|
if iface not in netifaces.interfaces():
|
||||||
|
return _get_localhost()
|
||||||
|
|
||||||
|
if CONF.network.ipv6:
|
||||||
|
address_family = netifaces.AF_INET6
|
||||||
|
else:
|
||||||
|
address_family = netifaces.AF_INET
|
||||||
|
|
||||||
|
for ifaddress in netifaces.ifaddresses(iface)[address_family]:
|
||||||
|
if 'addr' in ifaddress:
|
||||||
|
return ifaddress['addr']
|
||||||
|
|
||||||
|
return _get_localhost()
|
||||||
|
32
kolla_mesos/config/network.py
Normal file
32
kolla_mesos/config/network.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
network_opts = [
|
||||||
|
cfg.StrOpt('private-interface',
|
||||||
|
default='eth1',
|
||||||
|
help='NIC connected to the private network'),
|
||||||
|
cfg.StrOpt('public-interface',
|
||||||
|
default='eth2',
|
||||||
|
help='NIC connected to the public network'),
|
||||||
|
cfg.BoolOpt('ipv6',
|
||||||
|
default=False,
|
||||||
|
help='Use IPv6 protocol')
|
||||||
|
]
|
||||||
|
network_opt_group = cfg.OptGroup(name='network',
|
||||||
|
title='Options for network interfaces')
|
||||||
|
CONF.register_group(network_opt_group)
|
||||||
|
CONF.register_cli_opts(network_opts, network_opt_group)
|
||||||
|
CONF.register_opts(network_opts, network_opt_group)
|
@ -13,6 +13,7 @@
|
|||||||
from kolla_mesos.config import chronos
|
from kolla_mesos.config import chronos
|
||||||
from kolla_mesos.config import kolla
|
from kolla_mesos.config import kolla
|
||||||
from kolla_mesos.config import marathon
|
from kolla_mesos.config import marathon
|
||||||
|
from kolla_mesos.config import network
|
||||||
from kolla_mesos.config import profiles
|
from kolla_mesos.config import profiles
|
||||||
from kolla_mesos.config import zookeeper
|
from kolla_mesos.config import zookeeper
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ def list_opts():
|
|||||||
('chronos', chronos.chronos_opts),
|
('chronos', chronos.chronos_opts),
|
||||||
('kolla', kolla.kolla_opts),
|
('kolla', kolla.kolla_opts),
|
||||||
('marathon', marathon.marathon_opts),
|
('marathon', marathon.marathon_opts),
|
||||||
|
('network', network.network_opts),
|
||||||
('profiles', profiles.profiles_opts),
|
('profiles', profiles.profiles_opts),
|
||||||
('zookeeper', zookeeper.zookeeper_opts)
|
('zookeeper', zookeeper.zookeeper_opts)
|
||||||
]
|
]
|
||||||
|
82
kolla_mesos/tests/common/test_network_utils.py
Normal file
82
kolla_mesos/tests/common/test_network_utils.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# 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 netifaces
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from kolla_mesos.common import network_utils
|
||||||
|
from kolla_mesos.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class TestNetworkUtils(base.BaseTestCase):
|
||||||
|
|
||||||
|
def test_get_localhost_ipv4(self):
|
||||||
|
CONF.set_override('ipv6', False, group='network')
|
||||||
|
localhost = network_utils._get_localhost()
|
||||||
|
self.assertEqual(localhost, '127.0.0.1')
|
||||||
|
|
||||||
|
def test_get_localhost_ipv6(self):
|
||||||
|
CONF.set_override('ipv6', True, group='network')
|
||||||
|
localhost = network_utils._get_localhost()
|
||||||
|
self.assertEqual(localhost, '::1')
|
||||||
|
|
||||||
|
@mock.patch('netifaces.interfaces')
|
||||||
|
@mock.patch('netifaces.ifaddresses')
|
||||||
|
def test_get_ip_address_public_ipv4(self, ifaddresses_mock,
|
||||||
|
interfaces_mock):
|
||||||
|
CONF.set_override('ipv6', False, group='network')
|
||||||
|
interfaces_mock.return_value = ['eth2']
|
||||||
|
ifaddresses_mock.return_value = {
|
||||||
|
netifaces.AF_INET: [{'addr': '10.0.0.1'}]
|
||||||
|
}
|
||||||
|
ip_address = network_utils.get_ip_address()
|
||||||
|
self.assertEqual(ip_address, '10.0.0.1')
|
||||||
|
|
||||||
|
@mock.patch('netifaces.interfaces')
|
||||||
|
@mock.patch('netifaces.ifaddresses')
|
||||||
|
def test_get_ip_address_private_ipv4(self, ifaddresses_mock,
|
||||||
|
interfaces_mock):
|
||||||
|
CONF.set_override('ipv6', False, group='network')
|
||||||
|
interfaces_mock.return_value = ['eth1']
|
||||||
|
ifaddresses_mock.return_value = {
|
||||||
|
netifaces.AF_INET: [{'addr': '10.0.0.1'}]
|
||||||
|
}
|
||||||
|
ip_address = network_utils.get_ip_address(public=False)
|
||||||
|
self.assertEqual(ip_address, '10.0.0.1')
|
||||||
|
|
||||||
|
@mock.patch('netifaces.interfaces')
|
||||||
|
@mock.patch('netifaces.ifaddresses')
|
||||||
|
def test_get_ip_address_public_ipv6(self, ifaddresses_mock,
|
||||||
|
interfaces_mock):
|
||||||
|
CONF.set_override('ipv6', True, group='network')
|
||||||
|
interfaces_mock.return_value = ['eth2']
|
||||||
|
ifaddresses_mock.return_value = {
|
||||||
|
netifaces.AF_INET6: [{'addr': 'fe80::5054:ff:fe80:e42b%eth2'}]
|
||||||
|
}
|
||||||
|
ip_address = network_utils.get_ip_address()
|
||||||
|
self.assertEqual(ip_address, 'fe80::5054:ff:fe80:e42b%eth2')
|
||||||
|
|
||||||
|
@mock.patch('netifaces.interfaces')
|
||||||
|
@mock.patch('netifaces.ifaddresses')
|
||||||
|
def test_get_ip_address_private_ipv6(self, ifaddresses_mock,
|
||||||
|
interfaces_mock):
|
||||||
|
CONF.set_override('ipv6', True, group='network')
|
||||||
|
interfaces_mock.return_value = ['eth1']
|
||||||
|
ifaddresses_mock.return_value = {
|
||||||
|
netifaces.AF_INET6: [{'addr': 'fe80::5054:ff:fefc:273e%eth1'}]
|
||||||
|
}
|
||||||
|
ip_address = network_utils.get_ip_address(public=False)
|
||||||
|
self.assertEqual(ip_address, 'fe80::5054:ff:fefc:273e%eth1')
|
45
kolla_mesos/tests/config/test_network_config.py
Normal file
45
kolla_mesos/tests/config/test_network_config.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from kolla_mesos.tests import base
|
||||||
|
from kolla_mesos.tests.fakes import config_file as fake_config_file
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
CONF.import_group('network', 'kolla_mesos.config.network')
|
||||||
|
|
||||||
|
|
||||||
|
NETWORK_TEXT_CONFIG = """
|
||||||
|
[network]
|
||||||
|
private_interface = test_priv_iface
|
||||||
|
public_interface = test_pub_iface
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class TestNetworkConfig(base.BaseTestCase):
|
||||||
|
|
||||||
|
def _asserts(self):
|
||||||
|
self.assertEqual(CONF.network.private_interface, 'test_priv_iface')
|
||||||
|
self.assertEqual(CONF.network.public_interface, 'test_pub_iface')
|
||||||
|
|
||||||
|
def test_cli_config(self):
|
||||||
|
argv = ['--network-private-interface', 'test_priv_iface',
|
||||||
|
'--network-public-interface', 'test_pub_iface']
|
||||||
|
CONF(argv, project='kolla-mesos')
|
||||||
|
self._asserts()
|
||||||
|
|
||||||
|
@fake_config_file.FakeConfigFile(NETWORK_TEXT_CONFIG)
|
||||||
|
def test_file_config(self):
|
||||||
|
CONF([], project='kolla-mesos', default_config_files=['/dev/null'])
|
||||||
|
self._asserts()
|
@ -7,5 +7,6 @@ Babel>=1.3
|
|||||||
dcos>=0.1.3 # Apache-2.0
|
dcos>=0.1.3 # Apache-2.0
|
||||||
Jinja2>=2.8 # BSD License (3 clause)
|
Jinja2>=2.8 # BSD License (3 clause)
|
||||||
kazoo>=2.2
|
kazoo>=2.2
|
||||||
|
netifaces>=0.10.4
|
||||||
oslo.config>=2.7.0 # Apache-2.0
|
oslo.config>=2.7.0 # Apache-2.0
|
||||||
PyYAML>=3.1.0
|
PyYAML>=3.1.0
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
HOST_IP=$(ip addr show eth0 | grep -Po 'inet \K[\d.]+')
|
NET_IFACE=eth2
|
||||||
|
|
||||||
|
function get_host_ip {
|
||||||
|
echo $(ip addr show $NET_IFACE | grep -Po 'inet \K[\d.]+')
|
||||||
|
}
|
||||||
|
|
||||||
|
HOST_IP=$(get_host_ip)
|
||||||
|
|
||||||
function infra_start {
|
function infra_start {
|
||||||
docker run -d \
|
docker run -d \
|
||||||
@ -73,6 +79,7 @@ Usage: $0 COMMAND [options]
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
--host, -i <host_ip> Specify path to host ip
|
--host, -i <host_ip> Specify path to host ip
|
||||||
|
--net-iface, -n <nic> Specify NIC to use for host ip lookup
|
||||||
--help, -h Show this usage information
|
--help, -h Show this usage information
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
@ -81,7 +88,7 @@ Commands:
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
ARGS=$(getopt -o hi: -l help,host: --name "$0" -- "$@") || { usage >&2; exit 2; }
|
ARGS=$(getopt -o hin: -l help,host,net-iface: --name "$0" -- "$@") || { usage >&2; exit 2; }
|
||||||
eval set -- "$ARGS"
|
eval set -- "$ARGS"
|
||||||
|
|
||||||
while [ "$#" -gt 0 ]; do
|
while [ "$#" -gt 0 ]; do
|
||||||
@ -91,6 +98,12 @@ while [ "$#" -gt 0 ]; do
|
|||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
(--net-iface|-n)
|
||||||
|
NET_IFACE="$2"
|
||||||
|
HOST_IP=$(get_host_ip)
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
|
||||||
(--help|-h)
|
(--help|-h)
|
||||||
usage
|
usage
|
||||||
shift
|
shift
|
||||||
|
6
vagrant/Vagrantfile
vendored
6
vagrant/Vagrantfile
vendored
@ -25,10 +25,8 @@ end
|
|||||||
Vagrant.configure(2) do |config|
|
Vagrant.configure(2) do |config|
|
||||||
config.vm.box = get_default(:base_image)
|
config.vm.box = get_default(:base_image)
|
||||||
|
|
||||||
config.vm.network "forwarded_port", guest: 4400, host: 4400
|
config.vm.network "private_network", type: "dhcp"
|
||||||
config.vm.network "forwarded_port", guest: 5050, host: 5050
|
config.vm.network "public_network", dev: get_default(:bridge_interface), mode: 'bridge', type: 'bridge'
|
||||||
config.vm.network "forwarded_port", guest: 5051, host: 5051
|
|
||||||
config.vm.network "forwarded_port", guest: 8080, host: 8080
|
|
||||||
|
|
||||||
config.vm.synced_folder "..", "/home/vagrant/kolla-mesos", type: get_default(:sync_method)
|
config.vm.synced_folder "..", "/home/vagrant/kolla-mesos", type: get_default(:sync_method)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user