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:
Michal Rostecki 2015-12-07 12:01:20 +01:00
parent df9f8a304b
commit 83d8341993
8 changed files with 215 additions and 21 deletions

View File

@ -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()

View 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)

View File

@ -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)
] ]

View 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')

View 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()

View File

@ -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

View File

@ -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
View File

@ -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)