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:
Dmitry Tyzhnenko 2015-11-06 17:18:42 +02:00 committed by Dmitry Tyzhnenko
parent a1ea63bb96
commit f93f526d3d
7 changed files with 296 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

93
samples/block-traffic.py Normal file
View File

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