Add blocking traffic in network or interface
Add method to Interface and Nework models for blocking traffic Change-Id: I3e6c00d092a7efdbfb666d319c9b62f2c6cc7518 Closes-bug: #1512773
This commit is contained in:
parent
a1ea63bb96
commit
f93f526d3d
|
@ -299,6 +299,103 @@ class DevopsDriver(object):
|
|||
"""
|
||||
self.conn.networkLookupByUUIDString(network.uuid).create()
|
||||
|
||||
@retry()
|
||||
def network_filter_define(self, network):
|
||||
"""Define network filter"""
|
||||
return self.conn.nwfilterDefineXML(
|
||||
self.xml_builder.build_network_filter(network))
|
||||
|
||||
@retry()
|
||||
def network_filter_undefine(self, network):
|
||||
"""Undefine network filter"""
|
||||
return self.conn.nwfilterLookupByName(
|
||||
"{}_{}".format(network.environment.name, network.name)).undefine()
|
||||
|
||||
@retry()
|
||||
def network_block_status(self, network):
|
||||
"""Return network block status"""
|
||||
filter_xml = self.conn.nwfilterLookupByName(
|
||||
"{}_{}".format(network.environment.name, network.name)).XMLDesc()
|
||||
filter_xml = ET.fromstring(filter_xml)
|
||||
return filter_xml.find('./rule') is not None
|
||||
|
||||
@retry()
|
||||
def network_block(self, network):
|
||||
"""Block all traffic in network"""
|
||||
filter_xml = self.conn.nwfilterLookupByName(
|
||||
"{}_{}".format(network.environment.name, network.name)).XMLDesc()
|
||||
filter_xml = ET.fromstring(filter_xml)
|
||||
rule = ET.Element(
|
||||
'rule',
|
||||
{'action': 'drop', 'direction': "inout", 'priority': '-1000'})
|
||||
rule.append(ET.Element('all'))
|
||||
filter_xml.find('.').append(rule)
|
||||
self.conn.nwfilterDefineXML(ET.tostring(filter_xml))
|
||||
|
||||
@retry()
|
||||
def network_unblock(self, network):
|
||||
"""Unblock all traffic in network"""
|
||||
filter_xml = self.conn.nwfilterLookupByName(
|
||||
"{}_{}".format(network.environment.name, network.name)).XMLDesc()
|
||||
filter_xml = ET.fromstring(filter_xml)
|
||||
filter_xml.find('.').remove(filter_xml.find('./rule'))
|
||||
self.conn.nwfilterDefineXML(ET.tostring(filter_xml))
|
||||
|
||||
@retry()
|
||||
def interface_filter_define(self, interface):
|
||||
self.conn.nwfilterDefineXML(
|
||||
self.xml_builder.build_interface_filter(interface))
|
||||
|
||||
@retry()
|
||||
def interface_filter_undefine(self, interface):
|
||||
"""Undefine interface filter"""
|
||||
self.conn.nwfilterLookupByName(
|
||||
"{}_{}_{}".format(
|
||||
interface.network.environment.name,
|
||||
interface.network.name,
|
||||
interface.mac_address)).undefine()
|
||||
|
||||
@retry()
|
||||
def interface_block_status(self, interface):
|
||||
"""Return block status of interface"""
|
||||
filter_xml = self.conn.nwfilterLookupByName(
|
||||
"{}_{}_{}".format(
|
||||
interface.network.environment.name,
|
||||
interface.network.name,
|
||||
interface.mac_address)).XMLDesc()
|
||||
filter_xml = ET.fromstring(filter_xml)
|
||||
return filter_xml.find('./rule') is not None
|
||||
|
||||
@retry()
|
||||
def interface_block(self, interface):
|
||||
"""Block traffic on interface"""
|
||||
net_filter_name = "{}_{}".format(interface.network.environment.name,
|
||||
interface.network.name)
|
||||
iface_filter_name = "{}_{}".format(net_filter_name,
|
||||
interface.mac_address)
|
||||
filter_xml = self.conn.nwfilterLookupByName(
|
||||
iface_filter_name).XMLDesc()
|
||||
filter_xml = ET.fromstring(filter_xml)
|
||||
rule = ET.Element(
|
||||
'rule',
|
||||
{'action': 'drop', 'direction': "inout", 'priority': '-950'})
|
||||
rule.append(ET.Element('all'))
|
||||
filter_xml.find('.').append(rule)
|
||||
self.conn.nwfilterDefineXML(ET.tostring(filter_xml))
|
||||
|
||||
@retry()
|
||||
def interface_unblock(self, interface):
|
||||
"""Unblock traffic on interface"""
|
||||
net_filter_name = "{}_{}".format(interface.network.environment.name,
|
||||
interface.network.name)
|
||||
iface_filter_name = "{}_{}".format(net_filter_name,
|
||||
interface.mac_address)
|
||||
filter_xml = self.conn.nwfilterLookupByName(
|
||||
iface_filter_name).XMLDesc()
|
||||
filter_xml = ET.fromstring(filter_xml)
|
||||
filter_xml.find('.').remove(filter_xml.find('./rule'))
|
||||
self.conn.nwfilterDefineXML(ET.tostring(filter_xml))
|
||||
|
||||
@retry()
|
||||
def node_define(self, node):
|
||||
"""Define node
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2013 - 2014 Mirantis, Inc.
|
||||
# Copyright 2013 - 2016 Mirantis, Inc.
|
||||
#
|
||||
# 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
|
||||
|
@ -180,6 +180,43 @@ class LibvirtXMLBuilder(object):
|
|||
device_xml.target(dev="fuelnet{0}".format(interface.id))
|
||||
if interface.type is not None:
|
||||
device_xml.model(type=interface.model)
|
||||
device_xml.filterref(filter='{}_{}_{}'.format(
|
||||
interface.network.environment.name,
|
||||
interface.network.name,
|
||||
interface.mac_address))
|
||||
|
||||
def build_network_filter(self, network):
|
||||
"""Generate nwfilter XML for network
|
||||
|
||||
:type network: Network
|
||||
:type state: String accept | drop
|
||||
:rtype : String
|
||||
"""
|
||||
filter_xml = XMLBuilder(
|
||||
'filter',
|
||||
name="{}_{}".format(network.environment.name, network.name))
|
||||
|
||||
return str(filter_xml)
|
||||
|
||||
def build_interface_filter(self, interface):
|
||||
"""Generate nwfilter XML for interface
|
||||
|
||||
:type network: Interface
|
||||
:type state: String accept | drop
|
||||
:rtype : String
|
||||
"""
|
||||
filter_xml = XMLBuilder(
|
||||
'filter',
|
||||
name="{}_{}_{}".format(
|
||||
interface.network.environment.name,
|
||||
interface.network.name,
|
||||
interface.mac_address))
|
||||
|
||||
filter_xml.filterref(filter="{}_{}".format(
|
||||
interface.network.environment.name,
|
||||
interface.network.name))
|
||||
|
||||
return str(filter_xml)
|
||||
|
||||
def build_node_xml(self, node, emulator):
|
||||
"""Generate node XML
|
||||
|
|
|
@ -168,6 +168,11 @@ class Environment(DriverModel):
|
|||
" test should be interrupted")
|
||||
for node in self.get_nodes():
|
||||
node.revert(name, destroy=False)
|
||||
for network in self.get_networks():
|
||||
if network.is_blocked:
|
||||
logger.info("{} network has been "
|
||||
"unblocked".format(network.name))
|
||||
network.unblock()
|
||||
|
||||
@classmethod
|
||||
def synchronize_all(cls):
|
||||
|
|
|
@ -75,6 +75,19 @@ class Network(DriverModel):
|
|||
def default_gw(self):
|
||||
return IPNetwork(self.ip_network)[1]
|
||||
|
||||
@property
|
||||
def is_blocked(self):
|
||||
"""Show state of network"""
|
||||
return self.driver.network_block_status(self)
|
||||
|
||||
def block(self):
|
||||
"""Block all traffic in network"""
|
||||
return self.driver.network_block(self)
|
||||
|
||||
def unblock(self):
|
||||
"""All all traffic in network"""
|
||||
return self.driver.network_unblock(self)
|
||||
|
||||
def next_ip(self):
|
||||
while True:
|
||||
self._iterhosts = self._iterhosts or IPNetwork(
|
||||
|
@ -91,9 +104,16 @@ class Network(DriverModel):
|
|||
return self.driver.network_bridge_name(self)
|
||||
|
||||
def define(self):
|
||||
self.define_filter()
|
||||
self.driver.network_define(self)
|
||||
self.save()
|
||||
|
||||
def define_filter(self):
|
||||
self.driver.network_filter_define(self)
|
||||
|
||||
def undefine_filter(self):
|
||||
self.driver.network_filter_undefine(self)
|
||||
|
||||
def start(self):
|
||||
self.create(verbose=False)
|
||||
|
||||
|
@ -113,6 +133,7 @@ class Network(DriverModel):
|
|||
if self.driver.network_active(self):
|
||||
self.driver.network_destroy(self)
|
||||
self.driver.network_undefine(self)
|
||||
self.undefine_filter()
|
||||
self.delete()
|
||||
|
||||
@classmethod
|
||||
|
@ -255,6 +276,27 @@ class Interface(models.Model):
|
|||
def addresses(self):
|
||||
return self.address_set.all()
|
||||
|
||||
@property
|
||||
def is_blocked(self):
|
||||
"""Show state of interface"""
|
||||
return self.network.driver.interface_block_status(self)
|
||||
|
||||
def block(self):
|
||||
"""Block traffic on interface"""
|
||||
return self.network.driver.interface_block(self)
|
||||
|
||||
def unblock(self):
|
||||
"""Block traffic on interface"""
|
||||
return self.network.driver.interface_unblock(self)
|
||||
|
||||
def define_filter(self):
|
||||
"""Define filter for interface"""
|
||||
return self.network.driver.interface_filter_define(self)
|
||||
|
||||
def undefine_filter(self):
|
||||
"""Undefine interface filter"""
|
||||
return self.network.driver.interface_filter_undefine(self)
|
||||
|
||||
@staticmethod
|
||||
def interface_create(network, node, type='network',
|
||||
mac_address=None, model='virtio',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2013 - 2015 Mirantis, Inc.
|
||||
# Copyright 2013 - 2016 Mirantis, Inc.
|
||||
#
|
||||
# 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
|
||||
|
@ -180,6 +180,9 @@ class Node(DriverModel):
|
|||
timeout=timeout)
|
||||
|
||||
def define(self):
|
||||
"""Define node and interface filter"""
|
||||
for iface in self.interfaces:
|
||||
iface.define_filter()
|
||||
self.driver.node_define(self)
|
||||
self.save()
|
||||
|
||||
|
@ -198,10 +201,13 @@ class Node(DriverModel):
|
|||
self.remove(verbose=False)
|
||||
|
||||
def remove(self, verbose=False):
|
||||
"""Remove node and filters for interfaces"""
|
||||
if verbose or self.uuid:
|
||||
if verbose or self.driver.node_exists(self):
|
||||
self.destroy(verbose=False)
|
||||
self.driver.node_undefine(self, undefine_snapshots=True)
|
||||
for iface in self.interfaces:
|
||||
iface.undefine_filter()
|
||||
self.delete()
|
||||
|
||||
def suspend(self, verbose=False):
|
||||
|
@ -365,6 +371,12 @@ class Node(DriverModel):
|
|||
print('Domain snapshot for {0} node not found: no domain '
|
||||
'snapshot with matching'
|
||||
' name {1}'.format(self.name, name))
|
||||
for iface in self.interfaces:
|
||||
if iface.is_blocked:
|
||||
logger.info("Interface({}) in {} network has "
|
||||
"been unblocked".format(iface.mac_address,
|
||||
iface.network.name))
|
||||
iface.unblock()
|
||||
|
||||
def get_snapshots(self):
|
||||
"""Return full snapshots objects"""
|
||||
|
|
|
@ -448,9 +448,12 @@ class TestNodeXml(BaseTestXMLBuilder):
|
|||
self.assertXMLIn(expected, xml)
|
||||
|
||||
def test_node_interfaces(self):
|
||||
networks = [mock.Mock(uuid=i) for i in range(3)]
|
||||
networks = [mock.Mock(uuid=i,
|
||||
environment=self.node.environment) for i in range(3)]
|
||||
for num, net in enumerate(networks):
|
||||
net.configure_mock(**{'name': 'network_name_mock_{0}'.format(num)})
|
||||
self.node.interfaces = [
|
||||
mock.Mock(type='network'.format(i), mac_address='mac{0}'.format(i),
|
||||
mock.Mock(type='network', mac_address='mac{0}'.format(i),
|
||||
network=networks[i], id='id{0}'.format(i),
|
||||
model='model{0}'.format(i)) for i in range(3)]
|
||||
xml = self.xml_builder.build_node_xml(self.node, 'test_emulator')
|
||||
|
@ -463,18 +466,21 @@ class TestNodeXml(BaseTestXMLBuilder):
|
|||
<source network="network_name_mock" />
|
||||
<target dev="fuelnetid0" />
|
||||
<model type="model0" />
|
||||
<filterref filter="test_env_name_network_name_mock_0_mac0"/>
|
||||
</interface>
|
||||
<interface type="network">
|
||||
<mac address="mac1" />
|
||||
<source network="network_name_mock" />
|
||||
<target dev="fuelnetid1" />
|
||||
<model type="model1" />
|
||||
<filterref filter="test_env_name_network_name_mock_1_mac1"/>
|
||||
</interface>
|
||||
<interface type="network">
|
||||
<mac address="mac2" />
|
||||
<source network="network_name_mock" />
|
||||
<target dev="fuelnetid2" />
|
||||
<model type="model2" />
|
||||
<filterref filter="test_env_name_network_name_mock_2_mac2"/>
|
||||
</interface>
|
||||
<video>
|
||||
<model heads="1" type="vga" vram="9216" />
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
# Copyright 2016 Mirantis, Inc.
|
||||
#
|
||||
# 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 devops.models import DiskDevice
|
||||
from devops.models import Environment
|
||||
from devops.models import Interface
|
||||
from devops.models import Network
|
||||
from devops.models import Node
|
||||
from devops.models import Volume
|
||||
|
||||
from devops.helpers import SSHClient
|
||||
|
||||
from ipaddr import IPNetwork
|
||||
|
||||
|
||||
def block_traffic():
|
||||
environment = Environment.create('test_env8')
|
||||
internal_pool = Network.create_network_pool(
|
||||
networks=[IPNetwork('10.109.0.0/16')], prefix=24
|
||||
)
|
||||
private_pool = Network.create_network_pool(
|
||||
networks=[IPNetwork('10.109.0.0/16')], prefix=24
|
||||
)
|
||||
external_pool = Network.create_network_pool(
|
||||
networks=[IPNetwork('10.109.0.0/16')], prefix=24
|
||||
)
|
||||
internal = Network.network_create(
|
||||
environment=environment, name='internal', pool=internal_pool)
|
||||
external = Network.network_create(
|
||||
environment=environment, name='external', pool=external_pool,
|
||||
forward='nat')
|
||||
private = Network.network_create(
|
||||
environment=environment, name='private', pool=private_pool)
|
||||
nodes = []
|
||||
for i in range(5):
|
||||
node = Node.node_create(
|
||||
name='test_node' + str(i),
|
||||
environment=environment)
|
||||
nodes.append(node)
|
||||
|
||||
Interface.interface_create(node=node, network=internal)
|
||||
Interface.interface_create(node=node, network=external)
|
||||
Interface.interface_create(node=node, network=private)
|
||||
volume = Volume.volume_get_predefined(
|
||||
'/var/lib/libvirt/images/centos63-cobbler-base.qcow2')
|
||||
v3 = Volume.volume_create_child(
|
||||
'test_vp895' + str(i),
|
||||
backing_store=volume,
|
||||
environment=environment)
|
||||
v4 = Volume.volume_create_child(
|
||||
'test_vp896' + str(i),
|
||||
backing_store=volume,
|
||||
environment=environment)
|
||||
DiskDevice.node_attach_volume(node=node, volume=v3)
|
||||
DiskDevice.node_attach_volume(node, v4)
|
||||
environment.define()
|
||||
environment.start()
|
||||
remotes = []
|
||||
for node in environment.get_nodes():
|
||||
node.await('internal')
|
||||
node.remote('internal', 'root', 'r00tme').check_stderr(
|
||||
'ls -la', verbose=True)
|
||||
remotes.append(node.remote('internal', 'root', 'r00tme'))
|
||||
SSHClient.execute_together(remotes, 'ls -la')
|
||||
|
||||
# Block traffic in private network
|
||||
priv_net = [i for i in nodes[0].interfaces if i.network.name == 'private']
|
||||
if not priv_net.is_blocked:
|
||||
priv_net.block()
|
||||
if priv_net.is_blocked:
|
||||
priv_net.unblock()
|
||||
|
||||
# Block traffic per interface
|
||||
iface = nodes[0].interfaces[0]
|
||||
if not iface.is_blocked:
|
||||
iface.block()
|
||||
if iface.is_blocked:
|
||||
iface.unblock()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
block_traffic()
|
Loading…
Reference in New Issue