Merge "os-xenapi: Add script for iptable configure"
This commit is contained in:
commit
12065324bc
|
@ -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")
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue