os-xenapi: Add script for iptable configure

Add a script to do iptable configuration

Change-Id: I0c984bbb1fecebdee6ca9afac238c0c5ba5e5bd8
This commit is contained in:
naichuans 2017-12-26 09:14:29 +00:00
parent e961b45553
commit 60e43c9046
3 changed files with 472 additions and 1 deletions

View File

@ -0,0 +1,309 @@
# 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
from os_xenapi.client import exception
from os_xenapi.client.i18n import _
from os_xenapi.tests import base
from os_xenapi.utils import common_function
from os_xenapi.utils import himn
from os_xenapi.utils import iptables
from os_xenapi.utils import sshclient
class fake_client_exception(exception.OsXenApiException):
msg_fmt = _("Failed to connect to server")
class XenapiIptableTestCase(base.TestCase):
@mock.patch.object(iptables, 'configure_himn_forwards')
@mock.patch.object(iptables, 'configure_dom0_iptables')
def test_config_iptables(self, mock_conf_dom0, mock_himn_forwards):
client = mock.Mock()
client.ip = 'fake_ip'
iptables.config_iptables(client, 'fake_interface')
mock_himn_forwards.assert_called_once_with('fake_interface', 'fake_ip')
mock_conf_dom0.assert_called_once_with(client)
@mock.patch.object(iptables, 'configure_himn_forwards')
@mock.patch.object(iptables, 'configure_dom0_iptables')
def test_config_iptables_without_forwards(self, mock_conf_dom0,
mock_himn_forwards):
client = mock.Mock()
client.ip = 'fake_ip'
iptables.config_iptables(client, None)
mock_himn_forwards.assert_not_called()
mock_conf_dom0.assert_called_once_with(client)
def test_configure_dom0_iptables(self):
client = mock.Mock()
client.ssh.side_effect = [sshclient.SshExecCmdFailure(
command="fake_cmd",
stdout="fake_out",
stderr="fake_err"),
None,
None,
sshclient.SshExecCmdFailure(
command="fake_cmd",
stdout="fake_out",
stderr="fake_err"),
None,
sshclient.SshExecCmdFailure(
command="fake_cmd",
stdout="fake_out",
stderr="fake_err"),
None,
None]
xs_chain = 'XenServer-Neutron-INPUT'
expect_call1 = mock.call('iptables -t filter -L %s' % xs_chain)
expect_call2 = mock.call('iptables -t filter --new %s' % xs_chain)
expect_call3 = mock.call('iptables -t filter -I INPUT -j %s'
% xs_chain)
expect_call4 = mock.call('iptables -t filter -C %s -p tcp -m '
'tcp --dport 6640 -j ACCEPT' % xs_chain)
expect_call5 = mock.call('iptables -t filter -I %s -p tcp -m '
'tcp --dport 6640 -j ACCEPT' % xs_chain)
expect_call6 = mock.call('iptables -t filter -C %s -p udp -m '
'multiport --dport 4789 -j ACCEPT' % xs_chain)
expect_call7 = mock.call('iptables -t filter -I %s -p udp -m '
'multiport --dport 4789 -j ACCEPT' % xs_chain)
expect_call8 = mock.call("service iptables save")
expect_calls = [expect_call1, expect_call2, expect_call3, expect_call4,
expect_call5, expect_call6, expect_call7, expect_call8]
iptables.configure_dom0_iptables(client)
client.ssh.assert_has_calls(expect_calls)
@mock.patch.object(himn, 'get_local_himn_eth')
@mock.patch.object(common_function, 'execute')
def test_configure_himn_forwards(self, mock_execute, mock_get_eth):
mock_get_eth.return_value = 'fake_eth'
fack_end_point = ['br-storage', 'br-mgmt']
mock_execute.side_effect = [None,
None,
exception.ExecuteCommandFailed('fake_cmd'),
None,
exception.ExecuteCommandFailed('fake_cmd'),
None,
exception.ExecuteCommandFailed('fake_cmd'),
None,
exception.ExecuteCommandFailed('fake_cmd'),
None,
exception.ExecuteCommandFailed('fake_cmd'),
None,
exception.ExecuteCommandFailed('fake_cmd'),
None,
exception.ExecuteCommandFailed('fake_cmd'),
None,
None,
None,
None]
expect_call1 = mock.call(
'sed',
'-i', 's/.*net\.ipv4\.ip_forward.*=.*/net.ipv4.ip_forward=1/g',
'/etc/sysctl.conf')
expect_call2 = mock.call('sysctl', 'net.ipv4.ip_forward=1')
expect_call3 = mock.call('iptables', '-t', 'nat', '-C', 'POSTROUTING',
'-o', fack_end_point[0], '-j', 'MASQUERADE')
expect_call4 = mock.call('iptables', '-t', 'nat', '-I', 'POSTROUTING',
'-o', fack_end_point[0], '-j', 'MASQUERADE')
expect_call5 = mock.call('iptables', '-t', 'nat', '-C', 'POSTROUTING',
'-o', fack_end_point[1], '-j', 'MASQUERADE')
expect_call6 = mock.call('iptables', '-t', 'nat', '-I', 'POSTROUTING',
'-o', fack_end_point[1], '-j', 'MASQUERADE')
expect_call7 = mock.call('iptables', '-t', 'filter', '-C', 'FORWARD',
'-i', fack_end_point[0], '-o', 'fake_eth',
'-m', 'state', '--state',
'RELATED,ESTABLISHED', '-j', 'ACCEPT')
expect_call8 = mock.call('iptables', '-t', 'filter', '-I', 'FORWARD',
'-i', fack_end_point[0], '-o', 'fake_eth',
'-m', 'state', '--state',
'RELATED,ESTABLISHED', '-j', 'ACCEPT')
expect_call9 = mock.call('iptables', '-t', 'filter', '-C', 'FORWARD',
'-i', fack_end_point[1], '-o', 'fake_eth',
'-m', 'state', '--state',
'RELATED,ESTABLISHED', '-j', 'ACCEPT')
expect_call10 = mock.call('iptables', '-t', 'filter', '-I', 'FORWARD',
'-i', fack_end_point[1], '-o', 'fake_eth',
'-m', 'state', '--state',
'RELATED,ESTABLISHED', '-j', 'ACCEPT')
expect_call11 = mock.call('iptables', '-t', 'filter', '-C', 'FORWARD',
'-i', 'fake_eth', '-o', fack_end_point[0],
'-j', 'ACCEPT')
expect_call12 = mock.call('iptables', '-t', 'filter', '-I', 'FORWARD',
'-i', 'fake_eth', '-o', fack_end_point[0],
'-j', 'ACCEPT')
expect_call13 = mock.call('iptables', '-t', 'filter', '-C', 'FORWARD',
'-i', 'fake_eth', '-o', fack_end_point[1],
'-j', 'ACCEPT')
expect_call14 = mock.call('iptables', '-t', 'filter', '-I', 'FORWARD',
'-i', 'fake_eth', '-o', fack_end_point[1],
'-j', 'ACCEPT')
expect_call15 = mock.call('iptables', '-t', 'filter', '-C', 'INPUT',
'-i', 'fake_eth', '-j', 'ACCEPT')
expect_call16 = mock.call('iptables', '-t', 'filter', '-I', 'INPUT',
'-i', 'fake_eth', '-j', 'ACCEPT')
expect_call17 = mock.call('iptables', '-t', 'filter', '-S', 'FORWARD')
expect_call18 = mock.call('iptables', '-t', 'nat', '-S', 'POSTROUTING')
expect_calls = [expect_call1, expect_call2,
expect_call3, expect_call4,
expect_call7, expect_call8,
expect_call11, expect_call12,
expect_call5, expect_call6,
expect_call9, expect_call10,
expect_call13, expect_call14,
expect_call15, expect_call16,
expect_call17, expect_call18]
iptables.configure_himn_forwards(fack_end_point, 'fake_dom0_himn_ip')
mock_get_eth.assert_called_once_with('fake_dom0_himn_ip')
mock_execute.assert_has_calls(expect_calls)
@mock.patch.object(himn, 'get_local_himn_eth')
@mock.patch.object(common_function, 'execute')
def test_configure_himn_forwards_no_eth_exc(self, mock_execute,
mock_get_eth):
mock_get_eth.return_value = None
self.assertRaises(exception.NoNetworkInterfaceInSameSegment,
iptables.configure_himn_forwards,
'fake_end_point', 'fake_dom0_himn_ip')
@mock.patch.object(common_function, 'execute')
def test_execute_local_iptables_cmd(self, mock_execute):
fake_rule_spec = 'fake_rule'
mock_execute.return_value = 'success'
execute_result = iptables.execute_iptables_cmd('fake_table',
'fake_action',
'fake_chain',
fake_rule_spec)
self.assertTrue(execute_result)
mock_execute.assert_called_once_with('iptables', '-t', 'fake_table',
'fake_action', 'fake_chain',
fake_rule_spec)
@mock.patch.object(common_function, 'execute')
def test_execute_local_iptables_cmd_failed(self, mock_execute):
fake_rule_spec = 'fake_rule'
mock_execute.side_effect = [exception.ExecuteCommandFailed('fake_cmd')]
self.assertRaises(exception.ExecuteCommandFailed,
iptables.execute_iptables_cmd,
'fake_table', 'fake_action',
'fake_chain', fake_rule_spec)
mock_execute.assert_called_once_with('iptables', '-t', 'fake_table',
'fake_action', 'fake_chain',
fake_rule_spec)
@mock.patch.object(common_function, 'execute')
def test_execute_local_iptables_cmd_expect_failed(self, mock_execute):
fake_rule_spec = 'fake_rule'
mock_execute.side_effect = [exception.ExecuteCommandFailed('fake_cmd')]
execute_result = iptables.execute_iptables_cmd('fake_table',
'fake_action',
'fake_chain',
fake_rule_spec,
None,
True)
self.assertFalse(execute_result)
mock_execute.assert_called_once_with('iptables', '-t', 'fake_table',
'fake_action', 'fake_chain',
fake_rule_spec)
@mock.patch.object(common_function, 'execute')
def test_execute_local_iptables_cmd_no_rule_spec(self, mock_execute):
mock_execute.return_value = 'success'
execute_result = iptables.execute_iptables_cmd('fake_table',
'fake_action',
'fake_chain',
None)
self.assertTrue(execute_result)
mock_execute.assert_called_once_with('iptables', '-t', 'fake_table',
'fake_action', 'fake_chain')
def test_execute_remote_iptables_cmd(self):
fake_client = mock.Mock()
fake_rule_spec = 'fake_rule'
fake_client.ssh.return_value = 'success'
execute_result = iptables.execute_iptables_cmd('fake_table',
'fake_action',
'fake_chain',
fake_rule_spec,
fake_client)
self.assertTrue(execute_result)
fake_client.ssh.assert_called_once_with('iptables -t fake_table ' +
'fake_action fake_chain ' +
fake_rule_spec)
def test_execute_remote_iptables_cmd_failed(self):
fake_client = mock.Mock()
fake_rule_spec = 'fake_rule'
fake_client.ssh.side_effect = [sshclient.SshExecCmdFailure(
command="fake_cmd",
stdout="fake_out",
stderr="fake_err")]
self.assertRaises(sshclient.SshExecCmdFailure,
iptables.execute_iptables_cmd,
'fake_table', 'fake_action',
'fake_chain', fake_rule_spec,
fake_client)
fake_client.ssh.assert_called_once_with('iptables -t fake_table ' +
'fake_action fake_chain ' +
fake_rule_spec)
def test_execute_remote_iptables_cmd_expect_failed(self):
fake_client = mock.Mock()
fake_rule_spec = 'fake_rule'
fake_client.ssh.side_effect = [sshclient.SshExecCmdFailure(
command="fake_cmd",
stdout="fake_out",
stderr="fake_err")]
execute_result = iptables.execute_iptables_cmd('fake_table',
'fake_action',
'fake_chain',
fake_rule_spec,
fake_client,
True)
self.assertFalse(execute_result)
fake_client.ssh.assert_called_once_with('iptables -t fake_table ' +
'fake_action fake_chain ' +
fake_rule_spec)
def test_execute_remote_iptables_cmd_no_rule_spec(self):
fake_client = mock.Mock()
fake_client.ssh.return_value = 'success'
execute_result = iptables.execute_iptables_cmd('fake_table',
'fake_action',
'fake_chain',
None,
fake_client)
self.assertTrue(execute_result)
fake_client.ssh.assert_called_once_with("iptables -t fake_table "
"fake_action fake_chain")

View File

@ -62,7 +62,7 @@ def detailed_execute(*cmd, **kwargs):
proc.returncode)
else:
LOG.warn('proc.returncode: %s', proc.returncode)
raise exception(err)
raise exception.ExecuteCommandFailed(cmd)
return proc.returncode, out, err

162
os_xenapi/utils/iptables.py Normal file
View File

@ -0,0 +1,162 @@
#!/usr/bin/env python
# Copyright 2017 Citrix Systems
#
# 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.
"""iptable utils
It contains the utilities relative to iptable settings."""
import sys
from os_xenapi.client import exception
from os_xenapi.utils import common_function
from os_xenapi.utils import himn
from os_xenapi.utils import sshclient
OVS_NATIVE_TCP_PORT = '6640'
VXLAN_UDP_PORT = '4789'
def exit_with_error(err_msg):
sys.stderr.write(err_msg)
sys.exit(1)
def configure_dom0_iptables(client):
xs_chain = 'XenServer-Neutron-INPUT'
# Check XenServer specific chain, create if not exist
if not execute_iptables_cmd('filter', '-L', xs_chain, client=client,
expect_exception=True):
execute_iptables_cmd('filter', '--new', xs_chain, client=client)
rule_spec = ('-j %s' % xs_chain)
execute_iptables_cmd('filter', '-I', 'INPUT', rule_spec, client)
# Check XenServer rule for ovs native mode, create if not exist
rule_spec = ('-p tcp -m tcp --dport %s -j ACCEPT'
% OVS_NATIVE_TCP_PORT)
ensure_iptables('filter', xs_chain, rule_spec, client)
# Check XenServer rule for vxlan, create if not exist
rule_spec = ('-p udp -m multiport --dport %s -j ACCEPT'
% VXLAN_UDP_PORT)
ensure_iptables('filter', xs_chain, rule_spec, client)
# Persist iptables rules
client.ssh('service iptables save')
def configure_himn_forwards(forwarding_interfaces, dom0_himn_ip):
# enable forward
# make change to be persistent
common_function.execute(
'sed', '-i', 's/.*net\.ipv4\.ip_forward.*=.*/net.ipv4.ip_forward=1/g',
'/etc/sysctl.conf')
# make it to take effective now.
common_function.execute('sysctl', 'net.ipv4.ip_forward=1')
eth = himn.get_local_himn_eth(dom0_himn_ip)
if not eth:
raise exception.NoNetworkInterfaceInSameSegment(dom0_himn_ip)
for interface in forwarding_interfaces:
# allow traffic from HIMN and forward traffic
rule_spec = '-o ' + interface + ' -j MASQUERADE'
ensure_iptables('nat', 'POSTROUTING', rule_spec)
rule_spec = '-i ' + interface + ' -o ' + eth + ' -m state ' + \
'--state RELATED,ESTABLISHED -j ACCEPT'
ensure_iptables('filter', 'FORWARD', rule_spec)
rule_spec = '-i ' + eth + ' -o ' + interface + ' -j ACCEPT'
ensure_iptables('filter', 'FORWARD', rule_spec)
rule_spec = '-i ' + eth + ' -j ACCEPT'
ensure_iptables('filter', 'INPUT', rule_spec)
execute_iptables_cmd('filter', '-S', 'FORWARD')
execute_iptables_cmd('nat', '-S', 'POSTROUTING')
def ensure_iptables(table, chain, rule_spec, client=None):
if not execute_iptables_cmd(table, '-C', chain, rule_spec, client, True):
execute_iptables_cmd(table, '-I', chain, rule_spec, client)
def execute_iptables_cmd(table, action, chain, rule_spec=None, client=None,
expect_exception=False):
"""This function is used to run iptables command.
Users could run command to configure iptables for remote and local hosts.
If the user want to configure remote host, the session client is needed, or
the command would be run on local host.
:param table: table you want you configure.
:param rule_spec: rule spec you want to apply.
:param client: session client to remote host you want to configure.
:param expect_exception: When you just want to do a rule check, set this
flag to 'True'. Then the reture value would be 'Ture' or 'False'.
:param forwarding_interfaces: network interface list which user want to
forward HIMN packages.
"""
if client:
if not rule_spec:
rule_spec = ''
command = ('iptables -t %(table)s %(action)s %(chain)s %(rule_spec)s'
% {'table': table, 'action': action,
'chain': chain, 'rule_spec': rule_spec})
command = command.strip()
try:
client.ssh(command)
except sshclient.SshExecCmdFailure:
if expect_exception:
return False
else:
raise
else:
if rule_spec:
rule_spec = rule_spec.split()
else:
rule_spec = []
command = ['iptables', '-t', table, action, chain] + rule_spec
try:
common_function.execute(*command)
except exception.ExecuteCommandFailed:
if expect_exception:
return False
else:
raise
return True
def config_iptables(client, forwarding_interfaces=None):
"""This function is used to configure iptables on a XenServer compute node.
:param client: session client with Dom0
:param forwarding_interfaces: network interface list which user want to
forward HIMN packages.
"""
if forwarding_interfaces:
configure_himn_forwards(forwarding_interfaces, client.ip)
configure_dom0_iptables(client)
if __name__ == '__main__':
if len(sys.argv) != 5:
exit_with_error("Wrong parameters input.")
dom0_himn_ip, user_name, password, forwarding_interfaces = sys.argv[1:]
forwarding_interfaces = forwarding_interfaces.split()
try:
client = sshclient.SSHClient(dom0_himn_ip, user_name, password)
except Exception:
exit_with_error("Create connection failed, ip: %(dom0_himn_ip)s,"
" user_name: %(user_name)s" %
{'dom0_himn_ip': dom0_himn_ip, 'user_name': user_name})
config_iptables(client, forwarding_interfaces)