heat: allow overriding Heat/Neutron private IP for DPDK tests 93/45393/12
authorRoss Brattain <ross.b.brattain@intel.com>
Tue, 17 Oct 2017 21:30:37 +0000 (14:30 -0700)
committerRoss Brattain <ross.b.brattain@intel.com>
Mon, 11 Dec 2017 21:00:13 +0000 (13:00 -0800)
For some L2/L3 DPDK testcases we need to use a custom
IP address space different from what Heat provides.

These testcases require port_security_enabled = False so
Neutron should allow for unrestricted L2 traffic.

This will work because we bind the ports to DPDK and thus
don't need DHCP.

  vnf_0:
    floating_ip: true
    placement: "pgrp1"
    network_ports:
      mgmt:
        - mgmt
      uplink_0:
        - xe0:
            local_ip: 10.44.0.20
            netmask: 255.255.255.0
      downlink_0:
        - xe1:
            local_ip: 10.44.0.30
            netmask: 255.255.255.0

Also fixup flake8 errors in unittests

Change-Id: Id29dfffa692f16fb1f526d208db43e476e2f7830
Signed-off-by: Ross Brattain <ross.b.brattain@intel.com>
tests/unit/benchmark/contexts/test_heat.py
tests/unit/benchmark/contexts/test_model.py
yardstick/benchmark/contexts/heat.py
yardstick/benchmark/contexts/model.py

index 223d640..f2e725d 100644 (file)
@@ -119,8 +119,12 @@ class HeatContextTestCase(unittest.TestCase):
             "2f2e4997-0a8e-4eb7-9fa4-f3f8fbbc393b")
         mock_template.add_security_group.assert_called_with("foo-secgroup")
 #        mock_template.add_network.assert_called_with("bar-fool-network", 'physnet1', None)
-        mock_template.add_router.assert_called_with("bar-fool-network-router", netattrs["external_network"], "bar-fool-network-subnet")
-        mock_template.add_router_interface.assert_called_with("bar-fool-network-router-if0", "bar-fool-network-router", "bar-fool-network-subnet")
+        mock_template.add_router.assert_called_with("bar-fool-network-router",
+                                                    netattrs["external_network"],
+                                                    "bar-fool-network-subnet")
+        mock_template.add_router_interface.assert_called_with("bar-fool-network-router-if0",
+                                                              "bar-fool-network-router",
+                                                              "bar-fool-network-subnet")
 
     @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
     def test_attrs_get(self, mock_template):
index 48ee01c..53b035b 100644 (file)
@@ -282,6 +282,178 @@ class ServerTestCase(unittest.TestCase):
             scheduler_hints='hints',
             availability_zone='zone')
 
+    def test_override_ip(self):
+        network_ports = {
+            'mgmt': ['mgmt'],
+            'uplink_0': [
+                {'xe0': {'local_ip': '10.44.0.20', 'netmask': '255.255.255.0'}},
+            ],
+            'downlink_0': [
+                {'xe1': {'local_ip': '10.44.0.30', 'netmask': '255.255.255.0'}},
+            ],
+        }
+        attrs = {
+            'image': 'some-image', 'flavor': 'some-flavor',
+        }
+        test_server = model.Server('foo', self.mock_context, attrs)
+        test_server.interfaces = {
+            "xe0": {
+                "local_ip": "1.2.3.4",
+                "netmask": "255.255.255.0",
+            },
+            "xe1": {
+                "local_ip": "1.2.3.5",
+                "netmask": "255.255.255.0"
+            }
+        }
+        test_server.network_ports = network_ports
+
+        test_server.override_ip("uplink_0", {"port": "xe0"})
+        self.assertEqual(test_server.interfaces["xe0"], network_ports["uplink_0"][0]["xe0"])
+
+    def test_override_ip_multiple(self):
+        network_ports = {
+            'mgmt': ['mgmt'],
+            'uplink_0': [
+                {'xe0': {'local_ip': '10.44.0.20', 'netmask': '255.255.255.0'}},
+                {'xe0': {'local_ip': '10.44.0.21', 'netmask': '255.255.255.0'}},
+            ],
+            'downlink_0': [
+                {'xe1': {'local_ip': '10.44.0.30', 'netmask': '255.255.255.0'}},
+            ],
+        }
+        attrs = {
+            'image': 'some-image', 'flavor': 'some-flavor',
+        }
+        test_server = model.Server('foo', self.mock_context, attrs)
+        test_server.interfaces = {
+            "xe0": {
+                "local_ip": "1.2.3.4",
+                "netmask": "255.255.255.0",
+            },
+            "xe1": {
+                "local_ip": "1.2.3.5",
+                "netmask": "255.255.255.0"
+            }
+        }
+        test_server.network_ports = network_ports
+        test_server.override_ip("uplink_0", {"port": "xe0"})
+        self.assertEqual(test_server.interfaces["xe0"], network_ports["uplink_0"][0]["xe0"])
+
+    def test_override_ip_mixed(self):
+        network_ports = {
+            'mgmt': ['mgmt'],
+            'uplink_0': [
+                'xe0',
+                {'xe0': {'local_ip': '10.44.0.21', 'netmask': '255.255.255.0'}},
+            ],
+            'downlink_0': [
+                {'xe1': {'local_ip': '10.44.0.30', 'netmask': '255.255.255.0'}},
+            ],
+        }
+        attrs = {
+            'image': 'some-image', 'flavor': 'some-flavor',
+        }
+        test_server = model.Server('foo', self.mock_context, attrs)
+        test_server.interfaces = {
+            "xe0": {
+                "local_ip": "1.2.3.4",
+                "netmask": "255.255.255.0",
+            },
+            "xe1": {
+                "local_ip": "1.2.3.5",
+                "netmask": "255.255.255.0"
+            }
+        }
+        test_server.network_ports = network_ports
+        test_server.override_ip("uplink_0", {"port": "xe0"})
+        self.assertEqual(test_server.interfaces["xe0"], network_ports["uplink_0"][1]["xe0"])
+
+    @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
+    def test__add_instance_with_ip_override_invalid_syntax(self, mock_template):
+        network_ports = {
+            'mgmt': ['mgmt'],
+            'uplink_0': 'xe0',
+            'downlink_0': [
+                {'xe1': {'local_ip': '10.44.0.30', 'netmask': '255.255.255.0'}},
+            ],
+        }
+        attrs = {
+            'image': 'some-image', 'flavor': 'some-flavor',
+        }
+        test_server = model.Server('foo', self.mock_context, attrs)
+        test_server.network_ports = network_ports
+        context = type("Context", (object,), {})
+        # can't use Mock because Mock.name is reserved
+        context.name = "context"
+        networks = [model.Network(n, context, {}) for n in network_ports]
+
+        with self.assertRaises(SyntaxError):
+            test_server._add_instance(mock_template, 'some-server',
+                                      networks, 'hints')
+
+    @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
+    def test__add_instance_with_ip_override(self, mock_template):
+        network_ports = {
+            'mgmt': ['mgmt'],
+            'uplink_0': [
+                {'xe0': {'local_ip': '10.44.0.20', 'netmask': '255.255.255.0'}},
+            ],
+            'downlink_0': [
+                {'xe1': {'local_ip': '10.44.0.30', 'netmask': '255.255.255.0'}},
+            ],
+        }
+        attrs = {
+            'image': 'some-image', 'flavor': 'some-flavor',
+        }
+        test_server = model.Server('foo', self.mock_context, attrs)
+        test_server.network_ports = network_ports
+        context = type("Context", (object,), {})
+        # can't use Mock because Mock.name is reserved
+        context.name = "context"
+        networks = [model.Network(n, context, {}) for n in network_ports]
+
+        test_server._add_instance(mock_template, 'some-server',
+                                  networks, 'hints')
+        self.assertEqual(test_server.ports, {
+            'downlink_0': [{'port': 'xe1', 'stack_name': 'some-server-xe1-port'}],
+            'mgmt': [{'port': 'mgmt', 'stack_name': 'some-server-mgmt-port'}],
+            'uplink_0': [{'port': 'xe0', 'stack_name': 'some-server-xe0-port'}]
+        })
+
+    @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
+    def test__add_instance_with_multiple_ip_override(self, mock_template):
+        network_ports = {
+            'mgmt': ['mgmt'],
+            'uplink_0': [
+                {'xe0': {'local_ip': '10.44.0.20', 'netmask': '255.255.255.0'}},
+                {'xe0': {'local_ip': '10.44.0.21', 'netmask': '255.255.255.0'}},
+            ],
+            'downlink_0': [
+                {'xe1': {'local_ip': '10.44.0.30', 'netmask': '255.255.255.0'}},
+            ],
+        }
+        attrs = {
+            'image': 'some-image', 'flavor': 'some-flavor',
+        }
+        test_server = model.Server('foo', self.mock_context, attrs)
+        test_server.network_ports = network_ports
+        context = type("Context", (object,), {})
+        # can't use Mock because Mock.name is reserved
+        context.name = "context"
+        networks = [model.Network(n, context, {}) for n in network_ports]
+
+        test_server._add_instance(mock_template, 'some-server',
+                                  networks, 'hints')
+        self.assertEqual(test_server.ports, {
+            'downlink_0': [{'port': 'xe1', 'stack_name': 'some-server-xe1-port'}],
+            'mgmt': [{'port': 'mgmt', 'stack_name': 'some-server-mgmt-port'}],
+            'uplink_0': [{'port': 'xe0', 'stack_name': 'some-server-xe0-port'},
+                         # this is not an error, we can produce this, it is left to Heat
+                         # to detect duplicate ports and error
+                         {'port': 'xe0', 'stack_name': 'some-server-xe0-port'}]
+        })
+
     @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
     def test__add_instance_with_user_data(self, mock_template):
         user_data = "USER_DATA"
index ff3e5f8..4ba543b 100644 (file)
@@ -348,6 +348,7 @@ class HeatContext(Context):
                                                                            port['port'],
                                                                            port['stack_name'],
                                                                            self.stack.outputs)
+                server.override_ip(network_name, port)
 
     def make_interface_dict(self, network_name, port, stack_name, outputs):
         private_ip = outputs[stack_name]
index d3c03e1..ae56066 100644 (file)
@@ -14,6 +14,8 @@ from __future__ import absolute_import
 
 import six
 import logging
+
+from collections import Mapping
 from six.moves import range
 
 
@@ -241,6 +243,26 @@ class Server(Object):     # pragma: no cover
 
         Server.list.append(self)
 
+    def override_ip(self, network_name, port):
+        def find_port_overrides():
+            for p in ports:
+                # p can be string or dict
+                # we can't just use p[port['port'] in case p is a string
+                # and port['port'] is an int?
+                if isinstance(p, Mapping):
+                    g = p.get(port['port'])
+                    # filter out empty dicts
+                    if g:
+                        yield g
+
+        ports = self.network_ports.get(network_name, [])
+        intf = self.interfaces[port['port']]
+        for override in find_port_overrides():
+            intf['local_ip'] = override.get('local_ip', intf['local_ip'])
+            intf['netmask'] = override.get('netmask', intf['netmask'])
+            # only use the first value
+            break
+
     @property
     def image(self):
         """returns a server's image name"""
@@ -270,9 +292,13 @@ class Server(Object):     # pragma: no cover
                     continue
                 else:
                     if isinstance(ports, six.string_types):
-                        if ports.startswith('-'):
-                            LOG.warning("possible YAML error, port name starts with - '%s", ports)
-                        ports = [ports]
+                        # because strings are iterable we have to check specifically
+                        raise SyntaxError("network_port must be a list '{}'".format(ports))
+                    # convert port subdicts into their just port name
+                    # port subdicts are used to override Heat IP address,
+                    # but we just need the port name
+                    # we allow duplicates here and let Heat raise the error
+                    ports = [next(iter(p)) if isinstance(p, dict) else p for p in ports]
             # otherwise add a port for every network with port name as network name
             else:
                 ports = [network.name]