aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoss Brattain <ross.b.brattain@intel.com>2017-05-21 21:07:50 -0700
committerEdward MacGillivray <edward.s.macgillivray@intel.com>2017-07-13 16:34:24 -0700
commit7a32c18b2fb3f220f099218871ba29115ef31ee9 (patch)
tree3e87b12bdbd030a43bbd849aee4f231b699768a5
parent49d358550209ab39bf618d8b226f655a0b54d13f (diff)
add network info to topology
we need to know which network each port is connected to so we can find VLAN or VXLAN ID. To do this we implement a new method for Contexts, Context.get_network(). This method is similar to Context.get_server(), it searches for a given network name in all the contexts. From this we generate a context_cfg["networks"] dict that stores all the network info for the nodes in the scenario. Then when we generate the topology for VNFD, we can lookup a given network by the vld_id and get the network_type, segmentation_id, etc. Then if we need to for example generated traffic on a given VLAN or VXLAN, we have this info available. Define default nd_route_tbl for ACL VNF we need default empty nd_route_tbl for IPv6 route. Change-Id: I9f9cfbd6acabeb4ae4675ca7354390efa57b29e7 Signed-off-by: Ross Brattain <ross.b.brattain@intel.com> Signed-off-by: Edward MacGillivray <edward.s.macgillivray@intel.com>
-rw-r--r--tests/unit/benchmark/contexts/test_heat.py111
-rw-r--r--tests/unit/benchmark/contexts/test_node.py44
-rw-r--r--tests/unit/benchmark/contexts/test_standalone.py45
-rw-r--r--tests/unit/benchmark/core/test_task.py67
-rw-r--r--tests/unit/benchmark/scenarios/networking/test_vnf_generic.py497
-rw-r--r--tests/unit/orchestrator/test_heat.py46
-rw-r--r--yardstick/benchmark/contexts/base.py32
-rw-r--r--yardstick/benchmark/contexts/dummy.py3
-rw-r--r--yardstick/benchmark/contexts/heat.py141
-rw-r--r--yardstick/benchmark/contexts/model.py7
-rw-r--r--yardstick/benchmark/contexts/node.py30
-rw-r--r--yardstick/benchmark/contexts/standalone.py32
-rw-r--r--yardstick/benchmark/core/task.py30
-rw-r--r--yardstick/benchmark/scenarios/networking/vnf_generic.py106
-rw-r--r--yardstick/orchestrator/heat.py8
15 files changed, 890 insertions, 309 deletions
diff --git a/tests/unit/benchmark/contexts/test_heat.py b/tests/unit/benchmark/contexts/test_heat.py
index 3dadd48eb..c739f33ff 100644
--- a/tests/unit/benchmark/contexts/test_heat.py
+++ b/tests/unit/benchmark/contexts/test_heat.py
@@ -13,6 +13,7 @@
from __future__ import absolute_import
+import ipaddress
import logging
import os
import unittest
@@ -120,7 +121,8 @@ class HeatContextTestCase(unittest.TestCase):
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_deploy(self, mock_template):
+ @mock.patch('yardstick.benchmark.contexts.heat.get_neutron_client')
+ def test_deploy(self, mock_neutron, mock_template):
self.test_context.name = 'foo'
self.test_context.template_file = '/bar/baz/some-heat-file'
@@ -133,6 +135,59 @@ class HeatContextTestCase(unittest.TestCase):
self.test_context.heat_parameters)
self.assertIsNotNone(self.test_context.stack)
+ def test_add_server_port(self):
+ network1 = mock.MagicMock()
+ network1.vld_id = 'vld111'
+ network2 = mock.MagicMock()
+ network2.vld_id = 'vld777'
+ self.test_context.name = 'foo'
+ self.test_context.stack = mock.MagicMock()
+ self.test_context.networks = {
+ 'a': network1,
+ 'c': network2,
+ }
+ self.test_context.stack.outputs = {
+ 'b': '10.20.30.45',
+ 'b-subnet_id': 1,
+ 'foo-a-subnet-cidr': '10.20.0.0/15',
+ 'foo-a-subnet-gateway_ip': '10.20.30.1',
+ 'b-mac_address': '00:01',
+ 'b-device_id': 'dev21',
+ 'b-network_id': 'net789',
+ 'd': '40.30.20.15',
+ 'd-subnet_id': 2,
+ 'foo-c-subnet-cidr': '40.30.0.0/18',
+ 'foo-c-subnet-gateway_ip': '40.30.20.254',
+ 'd-mac_address': '00:10',
+ 'd-device_id': 'dev43',
+ 'd-network_id': 'net987',
+ }
+ server = mock.MagicMock()
+ server.ports = OrderedDict([
+ ('a', {'stack_name': 'b'}),
+ ('c', {'stack_name': 'd'}),
+ ])
+
+ expected = {
+ "private_ip": '10.20.30.45',
+ "subnet_id": 1,
+ "subnet_cidr": '10.20.0.0/15',
+ "network": '10.20.0.0',
+ "netmask": '255.254.0.0',
+ "gateway_ip": '10.20.30.1',
+ "mac_address": '00:01',
+ "device_id": 'dev21',
+ "network_id": 'net789',
+ "network_name": 'a',
+ "local_mac": '00:01',
+ "local_ip": '10.20.30.45',
+ "vld_id": 'vld111',
+ }
+ self.test_context.add_server_port(server)
+ self.assertEqual(server.private_ip, '10.20.30.45')
+ self.assertEqual(len(server.interfaces), 2)
+ self.assertDictEqual(server.interfaces['a'], expected)
+
@mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate')
def test_undeploy(self, mock_template):
@@ -155,3 +210,57 @@ class HeatContextTestCase(unittest.TestCase):
self.assertEqual(result['ip'], '127.0.0.1')
self.assertEqual(result['private_ip'], '10.0.0.1')
+
+ def test__get_network(self):
+ network1 = mock.MagicMock()
+ network1.name = 'net_1'
+ network1.vld_id = 'vld111'
+ network1.segmentation_id = 'seg54'
+ network1.network_type = 'type_a'
+ network1.physical_network = 'phys'
+
+ network2 = mock.MagicMock()
+ network2.name = 'net_2'
+ network2.vld_id = 'vld999'
+ network2.segmentation_id = 'seg45'
+ network2.network_type = 'type_b'
+ network2.physical_network = 'virt'
+
+ self.test_context.networks = {
+ 'a': network1,
+ 'b': network2,
+ }
+
+ attr_name = None
+ self.assertIsNone(self.test_context._get_network(attr_name))
+
+ attr_name = {}
+ self.assertIsNone(self.test_context._get_network(attr_name))
+
+ attr_name = {'vld_id': 'vld777'}
+ self.assertIsNone(self.test_context._get_network(attr_name))
+
+ attr_name = 'vld777'
+ self.assertIsNone(self.test_context._get_network(attr_name))
+
+ attr_name = {'vld_id': 'vld999'}
+ expected = {
+ "name": 'net_2',
+ "vld_id": 'vld999',
+ "segmentation_id": 'seg45',
+ "network_type": 'type_b',
+ "physical_network": 'virt',
+ }
+ result = self.test_context._get_network(attr_name)
+ self.assertDictEqual(result, expected)
+
+ attr_name = 'a'
+ expected = {
+ "name": 'net_1',
+ "vld_id": 'vld111',
+ "segmentation_id": 'seg54',
+ "network_type": 'type_a',
+ "physical_network": 'phys',
+ }
+ result = self.test_context._get_network(attr_name)
+ self.assertDictEqual(result, expected)
diff --git a/tests/unit/benchmark/contexts/test_node.py b/tests/unit/benchmark/contexts/test_node.py
index 4b35ca421..d5ce8c5cb 100644
--- a/tests/unit/benchmark/contexts/test_node.py
+++ b/tests/unit/benchmark/contexts/test_node.py
@@ -208,6 +208,50 @@ class NodeContextTestCase(unittest.TestCase):
obj._get_client(node_name_args)
self.assertTrue(wait_mock.called)
+ def test__get_network(self):
+ network1 = {
+ 'name': 'net_1',
+ 'vld_id': 'vld111',
+ 'segmentation_id': 'seg54',
+ 'network_type': 'type_a',
+ 'physical_network': 'phys',
+ }
+ network2 = {
+ 'name': 'net_2',
+ 'vld_id': 'vld999',
+ }
+ self.test_context.networks = {
+ 'a': network1,
+ 'b': network2,
+ }
+
+ attr_name = {}
+ self.assertIsNone(self.test_context._get_network(attr_name))
+
+ attr_name = {'vld_id': 'vld777'}
+ self.assertIsNone(self.test_context._get_network(attr_name))
+
+ self.assertIsNone(self.test_context._get_network(None))
+
+ attr_name = 'vld777'
+ self.assertIsNone(self.test_context._get_network(attr_name))
+
+ attr_name = {'vld_id': 'vld999'}
+ expected = {
+ "name": 'net_2',
+ "vld_id": 'vld999',
+ "segmentation_id": None,
+ "network_type": None,
+ "physical_network": None,
+ }
+ result = self.test_context._get_network(attr_name)
+ self.assertDictEqual(result, expected)
+
+ attr_name = 'a'
+ expected = network1
+ result = self.test_context._get_network(attr_name)
+ self.assertDictEqual(result, expected)
+
def main():
unittest.main()
diff --git a/tests/unit/benchmark/contexts/test_standalone.py b/tests/unit/benchmark/contexts/test_standalone.py
index 687ef7305..a6fd776e8 100644
--- a/tests/unit/benchmark/contexts/test_standalone.py
+++ b/tests/unit/benchmark/contexts/test_standalone.py
@@ -129,3 +129,48 @@ class StandaloneContextTestCase(unittest.TestCase):
curr_path = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(curr_path, filename)
return file_path
+
+ def test__get_network(self):
+ network1 = {
+ 'name': 'net_1',
+ 'vld_id': 'vld111',
+ 'segmentation_id': 'seg54',
+ 'network_type': 'type_a',
+ 'physical_network': 'phys',
+ }
+ network2 = {
+ 'name': 'net_2',
+ 'vld_id': 'vld999',
+ }
+ self.test_context.networks = {
+ 'a': network1,
+ 'b': network2,
+ }
+
+ attr_name = None
+ self.assertIsNone(self.test_context._get_network(attr_name))
+
+ attr_name = {}
+ self.assertIsNone(self.test_context._get_network(attr_name))
+
+ attr_name = {'vld_id': 'vld777'}
+ self.assertIsNone(self.test_context._get_network(attr_name))
+
+ attr_name = 'vld777'
+ self.assertIsNone(self.test_context._get_network(attr_name))
+
+ attr_name = {'vld_id': 'vld999'}
+ expected = {
+ "name": 'net_2',
+ "vld_id": 'vld999',
+ "segmentation_id": None,
+ "network_type": None,
+ "physical_network": None,
+ }
+ result = self.test_context._get_network(attr_name)
+ self.assertDictEqual(result, expected)
+
+ attr_name = 'a'
+ expected = network1
+ result = self.test_context._get_network(attr_name)
+ self.assertDictEqual(result, expected)
diff --git a/tests/unit/benchmark/core/test_task.py b/tests/unit/benchmark/core/test_task.py
index b64bb8eed..8d6d963c3 100644
--- a/tests/unit/benchmark/core/test_task.py
+++ b/tests/unit/benchmark/core/test_task.py
@@ -48,6 +48,73 @@ class TaskTestCase(unittest.TestCase):
self.assertEqual(context_cfg["target"], server_info)
@mock.patch('yardstick.benchmark.core.task.Context')
+ def test_parse_networks_from_nodes(self, mock_context):
+ nodes = {
+ 'node1': {
+ 'interfaces': {
+ 'eth0': {
+ 'name': 'mgmt',
+ },
+ 'eth1': {
+ 'name': 'external',
+ 'vld_id': '23',
+ },
+ 'eth10': {
+ 'name': 'internal',
+ 'vld_id': '55',
+ },
+ },
+ },
+ 'node2': {
+ 'interfaces': {
+ 'eth4': {
+ 'name': 'mgmt',
+ },
+ 'eth2': {
+ 'name': 'external',
+ 'vld_id': '32',
+ },
+ 'eth11': {
+ 'name': 'internal',
+ 'vld_id': '55',
+ },
+ },
+ },
+ }
+
+ mock_context.get_network.side_effect = iter([
+ None,
+ {
+ 'name': 'a',
+ 'network_type': 'private',
+ },
+ {},
+ {
+ 'name': 'b',
+ 'vld_id': 'y',
+ 'subnet_cidr': '10.20.0.0/16',
+ },
+ {
+ 'name': 'c',
+ 'vld_id': 'x',
+ },
+ {
+ 'name': 'd',
+ 'vld_id': 'w',
+ },
+ ])
+
+ expected_get_network_calls = 4 # once for each vld_id in the nodes dict
+ expected = {
+ 'a': {'name': 'a', 'network_type': 'private'},
+ 'b': {'name': 'b', 'vld_id': 'y', 'subnet_cidr': '10.20.0.0/16'},
+ }
+
+ networks = task.get_networks_from_nodes(nodes)
+ self.assertEqual(mock_context.get_network.call_count, expected_get_network_calls)
+ self.assertDictEqual(networks, expected)
+
+ @mock.patch('yardstick.benchmark.core.task.Context')
@mock.patch('yardstick.benchmark.core.task.base_runner')
def test_run(self, mock_base_runner, mock_ctx):
scenario = {
diff --git a/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py b/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py
index 111e7812e..c9cd7fed5 100644
--- a/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py
+++ b/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py
@@ -91,68 +91,97 @@ STL_MOCKS = {
'stl.trex_stl_lib.zmq': mock.MagicMock(),
}
-COMPLETE_TREX_VNFD = \
- {'vnfd:vnfd-catalog':
- {'vnfd':
- [{'benchmark':
- {'kpi':
- ['rx_throughput_fps',
- 'tx_throughput_fps',
- 'tx_throughput_mbps',
- 'rx_throughput_mbps',
- 'tx_throughput_pc_linerate',
- 'rx_throughput_pc_linerate',
- 'min_latency',
- 'max_latency',
- 'avg_latency']},
- 'connection-point': [{'name': 'xe0',
- 'type': 'VPORT'},
- {'name': 'xe1',
- 'type': 'VPORT'}],
- 'description': 'TRex stateless traffic generator for RFC2544',
- 'id': 'TrexTrafficGen',
- 'mgmt-interface': {'ip': '1.1.1.1',
- 'password': 'berta',
- 'user': 'berta',
- 'vdu-id': 'trexgen-baremetal'},
- 'name': 'trexgen',
- 'short-name': 'trexgen',
- 'vdu': [{'description': 'TRex stateless traffic generator for RFC2544',
- 'external-interface':
- [{'name': 'xe0',
- 'virtual-interface': {'bandwidth': '10 Gbps',
- 'dst_ip': '1.1.1.1',
- 'dst_mac': '00:01:02:03:04:05',
- 'local_ip': '1.1.1.2',
- 'local_mac': '00:01:02:03:05:05',
- 'type': 'PCI-PASSTHROUGH',
- 'netmask': "255.255.255.0",
- 'driver': 'i40',
- 'vpci': '0000:00:10.2'},
- 'vnfd-connection-point-ref': 'xe0'},
- {'name': 'xe1',
- 'virtual-interface': {'bandwidth': '10 Gbps',
- 'dst_ip': '2.1.1.1',
- 'dst_mac': '00:01:02:03:04:06',
- 'local_ip': '2.1.1.2',
- 'local_mac': '00:01:02:03:05:06',
- 'type': 'PCI-PASSTHROUGH',
- 'netmask': "255.255.255.0",
- 'driver': 'i40',
- 'vpci': '0000:00:10.1'},
- 'vnfd-connection-point-ref': 'xe1'}],
- 'id': 'trexgen-baremetal',
- 'name': 'trexgen-baremetal'}]}]}}
+COMPLETE_TREX_VNFD = {
+ 'vnfd:vnfd-catalog': {
+ 'vnfd': [
+ {
+ 'benchmark': {
+ 'kpi': [
+ 'rx_throughput_fps',
+ 'tx_throughput_fps',
+ 'tx_throughput_mbps',
+ 'rx_throughput_mbps',
+ 'tx_throughput_pc_linerate',
+ 'rx_throughput_pc_linerate',
+ 'min_latency',
+ 'max_latency',
+ 'avg_latency',
+ ],
+ },
+ 'connection-point': [
+ {
+ 'name': 'xe0',
+ 'type': 'VPORT',
+ },
+ {
+ 'name': 'xe1',
+ 'type': 'VPORT',
+ },
+ ],
+ 'description': 'TRex stateless traffic generator for RFC2544',
+ 'id': 'TrexTrafficGen',
+ 'mgmt-interface': {
+ 'ip': '1.1.1.1',
+ 'password': 'berta',
+ 'user': 'berta',
+ 'vdu-id': 'trexgen-baremetal',
+ },
+ 'name': 'trexgen',
+ 'short-name': 'trexgen',
+ 'class-name': 'TrexTrafficGen',
+ 'vdu': [
+ {
+ 'description': 'TRex stateless traffic generator for RFC2544',
+ 'external-interface': [
+ {
+ 'name': 'xe0',
+ 'virtual-interface': {
+ 'bandwidth': '10 Gbps',
+ 'dst_ip': '1.1.1.1',
+ 'dst_mac': '00:01:02:03:04:05',
+ 'local_ip': '1.1.1.2',
+ 'local_mac': '00:01:02:03:05:05',
+ 'type': 'PCI-PASSTHROUGH',
+ 'netmask': "255.255.255.0",
+ 'driver': 'i40',
+ 'vpci': '0000:00:10.2',
+ },
+ 'vnfd-connection-point-ref': 'xe0',
+ },
+ {
+ 'name': 'xe1',
+ 'virtual-interface': {
+ 'bandwidth': '10 Gbps',
+ 'dst_ip': '2.1.1.1',
+ 'dst_mac': '00:01:02:03:04:06',
+ 'local_ip': '2.1.1.2',
+ 'local_mac': '00:01:02:03:05:06',
+ 'type': 'PCI-PASSTHROUGH',
+ 'netmask': "255.255.255.0",
+ 'driver': 'i40',
+ 'vpci': '0000:00:10.1',
+ },
+ 'vnfd-connection-point-ref': 'xe1',
+ },
+ ],
+ 'id': 'trexgen-baremetal',
+ 'name': 'trexgen-baremetal',
+ },
+ ],
+ },
+ ],
+ },
+}
IP_ADDR_SHOW = """
-28: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP """
-"""group default qlen 1000
+28: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP \
+group default qlen 1000
link/ether 90:e2:ba:a7:6a:c8 brd ff:ff:ff:ff:ff:ff
inet 1.1.1.1/8 brd 1.255.255.255 scope global eth1
inet6 fe80::92e2:baff:fea7:6ac8/64 scope link
valid_lft forever preferred_lft forever
-29: eth5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP """
-"""group default qlen 1000
+29: eth5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP \
+group default qlen 1000
link/ether 90:e2:ba:a7:6a:c9 brd ff:ff:ff:ff:ff:ff
inet 2.1.1.1/8 brd 2.255.255.255 scope global eth5
inet6 fe80::92e2:baff:fea7:6ac9/64 scope link tentative
@@ -160,10 +189,10 @@ IP_ADDR_SHOW = """
"""
SYS_CLASS_NET = """
-lrwxrwxrwx 1 root root 0 sie 10 14:16 eth1 -> """
-"""../../devices/pci0000:80/0000:80:02.2/0000:84:00.1/net/eth1
-lrwxrwxrwx 1 root root 0 sie 3 10:37 eth2 -> """
-"""../../devices/pci0000:00/0000:00:01.1/0000:84:00.2/net/eth5
+lrwxrwxrwx 1 root root 0 sie 10 14:16 eth1 -> \
+../../devices/pci0000:80/0000:80:02.2/0000:84:00.1/net/eth1
+lrwxrwxrwx 1 root root 0 sie 3 10:37 eth2 -> \
+../../devices/pci0000:00/0000:00:01.1/0000:84:00.2/net/eth5
"""
TRAFFIC_PROFILE = {
@@ -174,137 +203,195 @@ TRAFFIC_PROFILE = {
"traffic_type": "FixedTraffic",
"frame_rate": 100, # pps
"flow_number": 10,
- "frame_size": 64}}
+ "frame_size": 64,
+ },
+}
class TestNetworkServiceTestCase(unittest.TestCase):
def setUp(self):
- self.context_cfg = \
- {'nodes':
- {'trexgen__1': {'role': 'TrafficGen',
- 'name': 'trafficgen_1.yardstick',
- 'ip': '10.10.10.11',
- 'interfaces':
- {'xe0':
- {'netmask': '255.255.255.0',
- 'local_ip': '152.16.100.20',
- 'local_mac': '00:00:00:00:00:01',
- 'driver': 'i40e',
- 'vpci': '0000:07:00.0',
- 'dpdk_port_num': 0},
- 'xe1':
- {'netmask': '255.255.255.0',
- 'local_ip': '152.16.40.20',
- 'local_mac': '00:00:00:00:00:02',
- 'driver': 'i40e',
- 'vpci': '0000:07:00.1',
- 'dpdk_port_num': 1}},
- 'password': 'r00t',
- 'user': 'root'},
- 'trexvnf__1': {'name': 'vnf.yardstick',
- 'ip': '10.10.10.12',
- 'interfaces':
- {'xe0':
- {'netmask': '255.255.255.0',
- 'local_ip': '152.16.100.19',
- 'local_mac': '00:00:00:00:00:03',
- 'driver': 'i40e',
- 'vpci': '0000:07:00.0',
- 'dpdk_port_num': 0},
- 'xe1': {'netmask': '255.255.255.0',
- 'local_ip': '152.16.40.19',
- 'local_mac': '00:00:00:00:00:04',
- 'driver': 'i40e',
- 'vpci': '0000:07:00.1',
- 'dpdk_port_num': 1}},
- 'routing_table': [{'netmask': '255.255.255.0',
- 'gateway': '152.16.100.20',
- 'network': '152.16.100.20',
- 'if': 'xe0'},
- {'netmask': '255.255.255.0',
- 'gateway': '152.16.40.20',
- 'network': '152.16.40.20',
- 'if': 'xe1'}],
- 'host': '10.223.197.164',
- 'role': 'vnf',
- 'user': 'root',
- 'nd_route_tbl':
- [{'netmask': '112',
- 'gateway': '0064:ff9b:0:0:0:0:9810:6414',
- 'network': '0064:ff9b:0:0:0:0:9810:6414',
- 'if': 'xe0'},
- {'netmask': '112',
- 'gateway': '0064:ff9b:0:0:0:0:9810:2814',
- 'network': '0064:ff9b:0:0:0:0:9810:2814',
- 'if': 'xe1'}],
- 'password': 'r00t'}}}
+ self.trexgen__1 = {
+ 'name': 'trafficgen_1.yardstick',
+ 'ip': '10.10.10.11',
+ 'role': 'TrafficGen',
+ 'user': 'root',
+ 'password': 'r00t',
+ 'interfaces': {
+ 'xe0': {
+ 'netmask': '255.255.255.0',
+ 'local_ip': '152.16.100.20',
+ 'local_mac': '00:00:00:00:00:01',
+ 'driver': 'i40e',
+ 'vpci': '0000:07:00.0',
+ 'dpdk_port_num': 0,
+ },
+ 'xe1': {
+ 'netmask': '255.255.255.0',
+ 'local_ip': '152.16.40.20',
+ 'local_mac': '00:00:00:00:00:02',
+ 'driver': 'i40e',
+ 'vpci': '0000:07:00.1',
+ 'dpdk_port_num': 1,
+ },
+ },
+ }
+
+ self.trexvnf__1 = {
+ 'name': 'vnf.yardstick',
+ 'ip': '10.10.10.12',
+ 'host': '10.223.197.164',
+ 'role': 'vnf',
+ 'user': 'root',
+ 'password': 'r00t',
+ 'interfaces': {
+ 'xe0': {
+ 'netmask': '255.255.255.0',
+ 'local_ip': '152.16.100.19',
+ 'local_mac': '00:00:00:00:00:03',
+ 'driver': 'i40e',
+ 'vpci': '0000:07:00.0',
+ 'dpdk_port_num': 0,
+ },
+ 'xe1': {
+ 'netmask': '255.255.255.0',
+ 'local_ip': '152.16.40.19',
+ 'local_mac': '00:00:00:00:00:04',
+ 'driver': 'i40e',
+ 'vpci': '0000:07:00.1',
+ 'dpdk_port_num': 1,
+ },
+ },
+ 'routing_table': [
+ {
+ 'netmask': '255.255.255.0',
+ 'gateway': '152.16.100.20',
+ 'network': '152.16.100.20',
+ 'if': 'xe0',
+ },
+ {
+ 'netmask': '255.255.255.0',
+ 'gateway': '152.16.40.20',
+ 'network': '152.16.40.20',
+ 'if': 'xe1',
+ },
+ ],
+ 'nd_route_tbl': [
+ {
+ 'netmask': '112',
+ 'gateway': '0064:ff9b:0:0:0:0:9810:6414',
+ 'network': '0064:ff9b:0:0:0:0:9810:6414',
+ 'if': 'xe0',
+ },
+ {
+ 'netmask': '112',
+ 'gateway': '0064:ff9b:0:0:0:0:9810:2814',
+ 'network': '0064:ff9b:0:0:0:0:9810:2814',
+ 'if': 'xe1',
+ },
+ ],
+ }
+
+ self.context_cfg = {
+ 'nodes': {
+ 'trexgen__1': self.trexgen__1,
+ 'trexvnf__1': self.trexvnf__1,
+ },
+ 'networks': {
+ 'private': {
+ 'vld_id': 'private',
+ },
+ 'public': {
+ 'vld_id': 'public',
+ },
+ },
+ }
+
+ self.vld0 = {
+ 'vnfd-connection-point-ref': [
+ {
+ 'vnfd-connection-point-ref': 'xe0',
+ 'member-vnf-index-ref': '1',
+ 'vnfd-id-ref': 'trexgen'
+ },
+ {
+ 'vnfd-connection-point-ref': 'xe0',
+ 'member-vnf-index-ref': '2',
+ 'vnfd-id-ref': 'trexgen'
+ }
+ ],
+ 'type': 'ELAN',
+ 'id': 'private',
+ 'name': 'trexgen__1 to trexvnf__1 link 1'
+ }
+
+ self.vld1 = {
+ 'vnfd-connection-point-ref': [
+ {
+ 'vnfd-connection-point-ref': 'xe1',
+ 'member-vnf-index-ref': '1',
+ 'vnfd-id-ref': 'trexgen'
+ },
+ {
+ 'vnfd-connection-point-ref': 'xe1',
+ 'member-vnf-index-ref': '2',
+ 'vnfd-id-ref': 'trexgen'
+ }
+ ],
+ 'type': 'ELAN',
+ 'id': 'public',
+ 'name': 'trexvnf__1 to trexgen__1 link 2'
+ }
self.topology = {
+ 'id': 'trex-tg-topology',
'short-name': 'trex-tg-topology',
- 'constituent-vnfd':
- [{'member-vnf-index': '1',
- 'VNF model': 'tg_trex_tpl.yaml',
- 'vnfd-id-ref': 'trexgen__1'},
- {'member-vnf-index': '2',
- 'VNF model': 'tg_trex_tpl.yaml',
- 'vnfd-id-ref': 'trexvnf__1'}],
- 'description': 'trex-tg-topology',
'name': 'trex-tg-topology',
- 'vld': [
+ 'description': 'trex-tg-topology',
+ 'constituent-vnfd': [
{
- 'vnfd-connection-point-ref': [
- {
- 'vnfd-connection-point-ref': 'xe0',
- 'member-vnf-index-ref': '1',
- 'vnfd-id-ref': 'trexgen'
- },
- {
- 'vnfd-connection-point-ref': 'xe0',
- 'member-vnf-index-ref': '2',
- 'vnfd-id-ref': 'trexgen'
- }
- ],
- 'type': 'ELAN',
- 'id': 'private',
- 'name': 'trexgen__1 to trexvnf__1 link 1'
+ 'member-vnf-index': '1',
+ 'VNF model': 'tg_trex_tpl.yaml',
+ 'vnfd-id-ref': 'trexgen__1',
},
{
- 'vnfd-connection-point-ref': [
- {
- 'vnfd-connection-point-ref': 'xe1',
- 'member-vnf-index-ref': '1',
- 'vnfd-id-ref': 'trexgen'
- },
- {
- 'vnfd-connection-point-ref': 'xe1',
- 'member-vnf-index-ref': '2',
- 'vnfd-id-ref': 'trexgen'
- }
- ],
- 'type': 'ELAN',
- 'id': 'public',
- 'name': 'trexvnf__1 to trexgen__1 link 2'
- }],
- 'id': 'trex-tg-topology',
+ 'member-vnf-index': '2',
+ 'VNF model': 'tg_trex_tpl.yaml',
+ 'vnfd-id-ref': 'trexvnf__1',
+ },
+ ],
+ 'vld': [self.vld0, self.vld1],
}
self.scenario_cfg = {
'task_path': "",
- 'tc_options': {'rfc2544': {'allowed_drop_rate': '0.8 - 1'}},
+ "topology": self._get_file_abspath("vpe_vnf_topology.yaml"),
'task_id': 'a70bdf4a-8e67-47a3-9dc1-273c14506eb7',
'tc': 'tc_ipv4_1Mflow_64B_packetsize',
- 'runner': {'object': 'NetworkServiceTestCase',
- 'interval': 35,
- 'output_filename': 'yardstick.out',
- 'runner_id': 74476,
- 'duration': 400, 'type': 'Duration'},
'traffic_profile': 'ipv4_throughput_vpe.yaml',
- 'traffic_options': {'flow': 'ipv4_1flow_Packets_vpe.yaml',
- 'imix': 'imix_voice.yaml'}, 'type': 'ISB',
- 'nodes': {'tg__2': 'trafficgen_2.yardstick',
- 'tg__1': 'trafficgen_1.yardstick',
- 'vnf__1': 'vnf.yardstick'},
- "topology": self._get_file_abspath("vpe_vnf_topology.yaml")}
+ 'type': 'ISB',
+ 'tc_options': {
+ 'rfc2544': {
+ 'allowed_drop_rate': '0.8 - 1',
+ },
+ },
+ 'runner': {
+ 'object': 'NetworkServiceTestCase',
+ 'interval': 35,
+ 'output_filename': 'yardstick.out',
+ 'runner_id': 74476,
+ 'duration': 400,
+ 'type': 'Duration',
+ },
+ 'traffic_options': {
+ 'flow': 'ipv4_1flow_Packets_vpe.yaml',
+ 'imix': 'imix_voice.yaml'
+ },
+ 'nodes': {
+ 'tg__2': 'trafficgen_2.yardstick',
+ 'tg__1': 'trafficgen_1.yardstick',
+ 'vnf__1': 'vnf.yardstick',
+ },
+ }
self.s = NetworkServiceTestCase(self.scenario_cfg, self.context_cfg)
@@ -339,10 +426,18 @@ class TestNetworkServiceTestCase(unittest.TestCase):
self.assertEqual({}, self.s._get_traffic_flow(self.scenario_cfg))
def test_get_vnf_imp(self):
- vnfd = COMPLETE_TREX_VNFD['vnfd:vnfd-catalog']['vnfd'][0]
+ vnfd = COMPLETE_TREX_VNFD['vnfd:vnfd-catalog']['vnfd'][0]['class-name']
with mock.patch.dict("sys.modules", STL_MOCKS):
self.assertIsNotNone(self.s.get_vnf_impl(vnfd))
+ with self.assertRaises(IncorrectConfig) as raised:
+ self.s.get_vnf_impl('NonExistentClass')
+
+ exc_str = str(raised.exception)
+ print(exc_str)
+ self.assertIn('No implementation', exc_str)
+ self.assertIn('found in', exc_str)
+
def test_load_vnf_models_invalid(self):
self.context_cfg["nodes"]['trexgen__1']['VNF model'] = \
self._get_file_abspath("tg_trex_tpl.yaml")
@@ -363,10 +458,10 @@ class TestNetworkServiceTestCase(unittest.TestCase):
ssh.from_node.return_value = ssh_mock
self.s.map_topology_to_infrastructure(self.context_cfg,
self.topology)
- self.assertEqual("tg_trex_tpl.yaml",
- self.context_cfg["nodes"]['trexgen__1']['VNF model'])
- self.assertEqual("tg_trex_tpl.yaml",
- self.context_cfg["nodes"]['trexvnf__1']['VNF model'])
+
+ nodes = self.context_cfg["nodes"]
+ self.assertEqual("tg_trex_tpl.yaml", nodes['trexgen__1']['VNF model'])
+ self.assertEqual("tg_trex_tpl.yaml", nodes['trexvnf__1']['VNF model'])
def test_map_topology_to_infrastructure_insufficient_nodes(self):
del self.context_cfg['nodes']['trexvnf__1']
@@ -376,9 +471,8 @@ class TestNetworkServiceTestCase(unittest.TestCase):
mock.Mock(return_value=(1, SYS_CLASS_NET + IP_ADDR_SHOW, ""))
ssh.from_node.return_value = ssh_mock
- self.assertRaises(IncorrectSetup,
- self.s.map_topology_to_infrastructure,
- self.context_cfg, self.topology)
+ with self.assertRaises(IncorrectSetup):
+ self.s.map_topology_to_infrastructure(self.context_cfg, self.topology)
def test_map_topology_to_infrastructure_config_invalid(self):
cfg = dict(self.context_cfg)
@@ -389,9 +483,8 @@ class TestNetworkServiceTestCase(unittest.TestCase):
mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, ""))
ssh.from_node.return_value = ssh_mock
- self.assertRaises(IncorrectConfig,
- self.s.map_topology_to_infrastructure,
- self.context_cfg, self.topology)
+ with self.assertRaises(IncorrectConfig):
+ self.s.map_topology_to_infrastructure(self.context_cfg, self.topology)
def test__resolve_topology_invalid_config(self):
with mock.patch("yardstick.ssh.SSH") as ssh:
@@ -400,14 +493,32 @@ class TestNetworkServiceTestCase(unittest.TestCase):
mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, ""))
ssh.from_node.return_value = ssh_mock
- del self.context_cfg['nodes']
- self.assertRaises(IncorrectConfig, self.s._resolve_topology,
- self.context_cfg, self.topology)
+ # purge an important key from the data structure
+ for interface in self.trexgen__1['interfaces'].values():
+ del interface['local_mac']
+
+ with self.assertRaises(IncorrectConfig) as raised:
+ self.s._resolve_topology(self.context_cfg, self.topology)
+
+ self.assertIn('not found', str(raised.exception))
+
+ # make a connection point ref with 3 points
+ self.vld0['vnfd-connection-point-ref'].append(
+ self.vld0['vnfd-connection-point-ref'][0])
+
+ with self.assertRaises(IncorrectConfig) as raised:
+ self.s._resolve_topology(self.context_cfg, self.topology)
+
+ self.assertIn('wrong number of endpoints', str(raised.exception))
+
+ # make a connection point ref with 1 point
+ self.vld0['vnfd-connection-point-ref'] = \
+ self.vld0['vnfd-connection-point-ref'][:1]
+
+ with self.assertRaises(IncorrectConfig) as raised:
+ self.s._resolve_topology(self.context_cfg, self.topology)
- self.topology['vld'][0]['vnfd-connection-point-ref'].append(
- self.topology['vld'][0]['vnfd-connection-point-ref'])
- self.assertRaises(IncorrectConfig, self.s._resolve_topology,
- self.context_cfg, self.topology)
+ self.assertIn('wrong number of endpoints', str(raised.exception))
def test_run(self):
tgen = mock.Mock(autospec=GenericTrafficGen)
@@ -462,8 +573,8 @@ class TestNetworkServiceTestCase(unittest.TestCase):
def test__get_traffic_profile_exception(self):
cfg = dict(self.scenario_cfg)
cfg["traffic_profile"] = ""
- self.assertRaises(IOError, self.s._get_traffic_profile, cfg,
- self.context_cfg)
+ with self.assertRaises(IOError):
+ self.s._get_traffic_profile(cfg, self.context_cfg)
def test___get_traffic_imix_exception(self):
cfg = dict(self.scenario_cfg)
diff --git a/tests/unit/orchestrator/test_heat.py b/tests/unit/orchestrator/test_heat.py
index 3b3873301..3dc8ad7e7 100644
--- a/tests/unit/orchestrator/test_heat.py
+++ b/tests/unit/orchestrator/test_heat.py
@@ -11,6 +11,7 @@
# Unittest for yardstick.benchmark.orchestrator.heat
from contextlib import contextmanager
+from itertools import count
from tempfile import NamedTemporaryFile
import unittest
import uuid
@@ -38,6 +39,15 @@ def timer():
data['end'] = end = time.time()
data['delta'] = end - start
+
+def index_value_iter(index, index_value, base_value=None):
+ for current_index in count():
+ if current_index == index:
+ yield index_value
+ else:
+ yield base_value
+
+
def get_error_message(error):
try:
# py2
@@ -249,7 +259,7 @@ class HeatTemplateTestCase(unittest.TestCase):
@mock_patch_target_module('op_utils')
@mock_patch_target_module('heatclient.client.Client')
def test_create(self, mock_heat_client_class, mock_op_utils):
- self.template.HEAT_WAIT_LOOP_INTERVAL = interval = 0.2
+ self.template.HEAT_WAIT_LOOP_INTERVAL = 0.2
mock_heat_client = mock_heat_client_class()
# populate attributes of the constructed mock
@@ -270,12 +280,11 @@ class HeatTemplateTestCase(unittest.TestCase):
expected_op_utils_usage = 0
with mock.patch.object(self.template, 'status') as mock_status:
- # no block
- with timer() as time_data:
- self.assertIsInstance(self.template.create(block=False, timeout=2), heat.HeatStack)
+ self.template.name = 'no block test'
+ mock_status.return_value = None
- # ensure runtime is much less than one interval
- self.assertLess(time_data['delta'], interval * 0.2)
+ # no block
+ self.assertIsInstance(self.template.create(block=False, timeout=2), heat.HeatStack)
# ensure op_utils was used
expected_op_utils_usage += 1
@@ -296,12 +305,10 @@ class HeatTemplateTestCase(unittest.TestCase):
self.assertEqual(self.template.outputs, {})
# block with immediate complete
- mock_status.return_value = u'CREATE_COMPLETE'
- with timer() as time_data:
- self.assertIsInstance(self.template.create(block=True, timeout=2), heat.HeatStack)
+ self.template.name = 'block, immediate complete test'
- # ensure runtime is less than one interval
- self.assertLess(time_data['delta'], interval * 0.2)
+ mock_status.return_value = self.template.HEAT_CREATE_COMPLETE_STATUS
+ self.assertIsInstance(self.template.create(block=True, timeout=2), heat.HeatStack)
# ensure existing instance was re-used and op_utils was not used
expected_create_calls += 1
@@ -319,14 +326,12 @@ class HeatTemplateTestCase(unittest.TestCase):
self.template.outputs = None
# block with delayed complete
- mock_status.side_effect = iter([None, None, u'CREATE_COMPLETE'])
- with timer() as time_data:
- self.assertIsInstance(self.template.create(block=True, timeout=2), heat.HeatStack)
+ self.template.name = 'block, delayed complete test'
- # ensure runtime is approximately two intervals
- expected_time_low = interval * 1.8
- expected_time_high = interval * 2.2
- self.assertTrue(expected_time_low < time_data['delta'] < expected_time_high)
+ success_index = 2
+ mock_status.side_effect = index_value_iter(success_index,
+ self.template.HEAT_CREATE_COMPLETE_STATUS)
+ self.assertIsInstance(self.template.create(block=True, timeout=2), heat.HeatStack)
# ensure existing instance was re-used and op_utils was not used
expected_create_calls += 1
@@ -334,7 +339,7 @@ class HeatTemplateTestCase(unittest.TestCase):
self.assertEqual(mock_heat_client.stacks.create.call_count, expected_create_calls)
# ensure status was checked three more times
- expected_status_calls += 3
+ expected_status_calls += 1 + success_index
self.assertEqual(mock_status.call_count, expected_status_calls)
@@ -348,7 +353,8 @@ class HeatStackTestCase(unittest.TestCase):
# call once and then call again if uuid is not none
self.assertGreater(delete_mock.call_count, 1)
- def test_delete_all_calls_delete(self):
+ @mock.patch('yardstick.orchestrator.heat.op_utils')
+ def test_delete_all_calls_delete(self, mock_op):
stack = heat.HeatStack('test')
stack.uuid = 1
with mock.patch.object(stack, "delete") as delete_mock:
diff --git a/yardstick/benchmark/contexts/base.py b/yardstick/benchmark/contexts/base.py
index 0be2eee77..e362c6a3d 100644
--- a/yardstick/benchmark/contexts/base.py
+++ b/yardstick/benchmark/contexts/base.py
@@ -23,7 +23,7 @@ class Context(object):
@abc.abstractmethod
def init(self, attrs):
- "Initiate context."
+ """Initiate context."""
@staticmethod
def get_cls(context_type):
@@ -56,20 +56,34 @@ class Context(object):
"""get server info by name from context
"""
+ @abc.abstractmethod
+ def _get_network(self, attr_name):
+ """get network info by name from context
+ """
+
@staticmethod
def get_server(attr_name):
"""lookup server info by name from context
attr_name: either a name for a server created by yardstick or a dict
with attribute name mapping when using external heat templates
"""
- server = None
- for context in Context.list:
- server = context._get_server(attr_name)
- if server is not None:
- break
-
- if server is None:
+ servers = (context._get_server(attr_name) for context in Context.list)
+ try:
+ return next(s for s in servers if s)
+ except StopIteration:
raise ValueError("context not found for server '%r'" %
attr_name)
- return server
+ @staticmethod
+ def get_network(attr_name):
+ """lookup server info by name from context
+ attr_name: either a name for a server created by yardstick or a dict
+ with attribute name mapping when using external heat templates
+ """
+
+ networks = (context._get_network(attr_name) for context in Context.list)
+ try:
+ return next(n for n in networks if n)
+ except StopIteration:
+ raise ValueError("context not found for server '%r'" %
+ attr_name)
diff --git a/yardstick/benchmark/contexts/dummy.py b/yardstick/benchmark/contexts/dummy.py
index c658d3257..8ae4b65b8 100644
--- a/yardstick/benchmark/contexts/dummy.py
+++ b/yardstick/benchmark/contexts/dummy.py
@@ -37,3 +37,6 @@ class DummyContext(Context):
def _get_server(self, attr_name):
return None
+
+ def _get_network(self, attr_name):
+ return None
diff --git a/yardstick/benchmark/contexts/heat.py b/yardstick/benchmark/contexts/heat.py
index fed8fc342..0a94dd976 100644
--- a/yardstick/benchmark/contexts/heat.py
+++ b/yardstick/benchmark/contexts/heat.py
@@ -25,6 +25,7 @@ from yardstick.benchmark.contexts.model import Network
from yardstick.benchmark.contexts.model import PlacementGroup, ServerGroup
from yardstick.benchmark.contexts.model import Server
from yardstick.benchmark.contexts.model import update_scheduler_hints
+from yardstick.common.openstack_utils import get_neutron_client
from yardstick.orchestrator.heat import HeatTemplate, get_short_key_uuid
from yardstick.common.constants import YARDSTICK_ROOT_PATH
@@ -54,9 +55,11 @@ class HeatContext(Context):
self._user = None
self.template_file = None
self.heat_parameters = None
+ self.neutron_client = None
# generate an uuid to identify yardstick_key
# the first 8 digits of the uuid will be used
self.key_uuid = uuid.uuid4()
+ self.heat_timeout = None
self.key_filename = ''.join(
[YARDSTICK_ROOT_PATH, 'yardstick/resources/files/yardstick_key-',
get_short_key_uuid(self.key_uuid)])
@@ -65,15 +68,16 @@ class HeatContext(Context):
def assign_external_network(self, networks):
sorted_networks = sorted(networks.items())
external_network = os.environ.get("EXTERNAL_NETWORK", "net04_ext")
- have_external_network = [(name, net)
- for name, net in sorted_networks if
- net.get("external_network")]
- # no external net defined, assign it to first network usig os.environ
+
+ have_external_network = any(net.get("external_network") for net in networks.values())
if sorted_networks and not have_external_network:
+ # no external net defined, assign it to first network using os.environ
sorted_networks[0][1]["external_network"] = external_network
- return sorted_networks
- def init(self, attrs): # pragma: no cover
+ self.networks = OrderedDict((name, Network(name, self, attrs))
+ for name, attrs in sorted_networks)
+
+ def init(self, attrs):
"""initializes itself from the supplied arguments"""
self.name = attrs["name"]
@@ -103,11 +107,7 @@ class HeatContext(Context):
# we have to do this first, because we are injecting external_network
# into the dict
- sorted_networks = self.assign_external_network(attrs["networks"])
-
- self.networks = OrderedDict(
- (name, Network(name, self, netattrs)) for name, netattrs in
- sorted_networks)
+ self.assign_external_network(attrs["networks"])
for name, serverattrs in sorted(attrs["servers"].items()):
server = Server(name, self, serverattrs)
@@ -120,7 +120,6 @@ class HeatContext(Context):
with open(self.key_filename + ".pub", "w") as pubkey_file:
pubkey_file.write(
"%s %s\n" % (rsa_key.get_name(), rsa_key.get_base64()))
- del rsa_key
@property
def image(self):
@@ -194,7 +193,7 @@ class HeatContext(Context):
scheduler_hints = {}
for pg in server.placement_groups:
update_scheduler_hints(scheduler_hints, added_servers, pg)
- # workround for openstack nova bug, check JIRA: YARDSTICK-200
+ # workaround for openstack nova bug, check JIRA: YARDSTICK-200
# for details
if len(availability_servers) == 2:
if not scheduler_hints["different_host"]:
@@ -250,6 +249,20 @@ class HeatContext(Context):
list(self.networks.values()),
scheduler_hints)
+ def get_neutron_info(self):
+ if not self.neutron_client:
+ self.neutron_client = get_neutron_client()
+
+ networks = self.neutron_client.list_networks()
+ for network in self.networks.values():
+ for neutron_net in networks['networks']:
+ if neutron_net['name'] == network.stack_name:
+ network.segmentation_id = neutron_net.get('provider:segmentation_id')
+ # we already have physical_network
+ # network.physical_network = neutron_net.get('provider:physical_network')
+ network.network_type = neutron_net.get('provider:network_type')
+ network.neutron_info = neutron_net
+
def deploy(self):
"""deploys template into a stack using cloud"""
print("Deploying context '%s'" % self.name)
@@ -267,20 +280,16 @@ class HeatContext(Context):
raise SystemExit("\nStack create interrupted")
except:
LOG.exception("stack failed")
+ # let the other failures happen, we want stack trace
raise
- # let the other failures happend, we want stack trace
+
+ # TODO: use Neutron to get segementation-id
+ self.get_neutron_info()
# copy some vital stack output into server objects
for server in self.servers:
if server.ports:
- # TODO(hafe) can only handle one internal network for now
- port = next(iter(server.ports.values()))
- server.private_ip = self.stack.outputs[port["stack_name"]]
- server.interfaces = {}
- for network_name, port in server.ports.items():
- self.make_interface_dict(network_name, port['stack_name'],
- server,
- self.stack.outputs)
+ self.add_server_port(server)
if server.floating_ip:
server.public_ip = \
@@ -288,24 +297,36 @@ class HeatContext(Context):
print("Context '%s' deployed" % self.name)
- def make_interface_dict(self, network_name, stack_name, server, outputs):
- server.interfaces[network_name] = {
- "private_ip": outputs[stack_name],
+ def add_server_port(self, server):
+ # TODO(hafe) can only handle one internal network for now
+ port = next(iter(server.ports.values()))
+ server.private_ip = self.stack.outputs[port["stack_name"]]
+ server.interfaces = {}
+ for network_name, port in server.ports.items():
+ server.interfaces[network_name] = self.make_interface_dict(
+ network_name, port['stack_name'], self.stack.outputs)
+
+ def make_interface_dict(self, network_name, stack_name, outputs):
+ private_ip = outputs[stack_name]
+ mac_addr = outputs[stack_name + "-mac_address"]
+ subnet_cidr_key = "-".join([self.name, network_name, 'subnet', 'cidr'])
+ gateway_key = "-".join([self.name, network_name, 'subnet', 'gateway_ip'])
+ subnet_cidr = outputs[subnet_cidr_key]
+ subnet_ip = ipaddress.ip_network(subnet_cidr)
+ return {
+ "private_ip": private_ip,
"subnet_id": outputs[stack_name + "-subnet_id"],
- "subnet_cidr": outputs[
- "{}-{}-subnet-cidr".format(self.name, network_name)],
- "netmask": str(ipaddress.ip_network(
- outputs["{}-{}-subnet-cidr".format(self.name,
- network_name)]).netmask),
- "gateway_ip": outputs[
- "{}-{}-subnet-gateway_ip".format(self.name, network_name)],
- "mac_address": outputs[stack_name + "-mac_address"],
+ "subnet_cidr": subnet_cidr,
+ "network": str(subnet_ip.network_address),
+ "netmask": str(subnet_ip.netmask),
+ "gateway_ip": outputs[gateway_key],
+ "mac_address": mac_addr,
"device_id": outputs[stack_name + "-device_id"],
"network_id": outputs[stack_name + "-network_id"],
"network_name": network_name,
# to match vnf_generic
- "local_mac": outputs[stack_name + "-mac_address"],
- "local_ip": outputs[stack_name],
+ "local_mac": mac_addr,
+ "local_ip": private_ip,
"vld_id": self.networks[network_name].vld_id,
}
@@ -326,6 +347,19 @@ class HeatContext(Context):
super(HeatContext, self).undeploy()
+ @staticmethod
+ def generate_routing_table(server):
+ routes = [
+ {
+ "network": intf["network"],
+ "netmask": intf["netmask"],
+ "if": name,
+ "gateway": intf["gateway_ip"],
+ }
+ for name, intf in server.interfaces.items()
+ ]
+ return routes
+
def _get_server(self, attr_name):
"""lookup server info by name from context
attr_name: either a name for a server created by yardstick or a dict
@@ -335,7 +369,10 @@ class HeatContext(Context):
'yardstick.resources',
'files/yardstick_key-' + get_short_key_uuid(self.key_uuid))
- if isinstance(attr_name, collections.Mapping):
+ if not isinstance(attr_name, collections.Mapping):
+ server = self._server_map.get(attr_name, None)
+
+ else:
cname = attr_name["name"].split(".")[1]
if cname != self.name:
return None
@@ -352,10 +389,6 @@ class HeatContext(Context):
server = Server(attr_name["name"].split(".")[0], self, {})
server.public_ip = public_ip
server.private_ip = private_ip
- else:
- if attr_name not in self._server_map:
- return None
- server = self._server_map[attr_name]
if server is None:
return None
@@ -365,9 +398,37 @@ class HeatContext(Context):
"key_filename": key_filename,
"private_ip": server.private_ip,
"interfaces": server.interfaces,
+ "routing_table": self.generate_routing_table(server),
+ # empty IPv6 routing table
+ "nd_route_tbl": [],
}
# Target server may only have private_ip
if server.public_ip:
result["ip"] = server.public_ip
return result
+
+ def _get_network(self, attr_name):
+ if not isinstance(attr_name, collections.Mapping):
+ network = self.networks.get(attr_name, None)
+
+ else:
+ # Don't generalize too much Just support vld_id
+ vld_id = attr_name.get('vld_id')
+ if vld_id is None:
+ return None
+
+ network = next((n for n in self.networks.values() if
+ getattr(n, "vld_id", None) == vld_id), None)
+
+ if network is None:
+ return None
+
+ result = {
+ "name": network.name,
+ "vld_id": network.vld_id,
+ "segmentation_id": network.segmentation_id,
+ "network_type": network.network_type,
+ "physical_network": network.physical_network,
+ }
+ return result
diff --git a/yardstick/benchmark/contexts/model.py b/yardstick/benchmark/contexts/model.py
index 5077a9786..06538d8a9 100644
--- a/yardstick/benchmark/contexts/model.py
+++ b/yardstick/benchmark/contexts/model.py
@@ -106,13 +106,14 @@ class Network(Object):
self.subnet_cidr = attrs.get('cidr', '10.0.1.0/24')
self.router = None
self.physical_network = attrs.get('physical_network', 'physnet1')
- self.provider = attrs.get('provider', None)
- self.segmentation_id = attrs.get('segmentation_id', None)
+ self.provider = attrs.get('provider')
+ self.segmentation_id = attrs.get('segmentation_id')
+ self.network_type = attrs.get('network_type')
if "external_network" in attrs:
self.router = Router("router", self.name,
context, attrs["external_network"])
- self.vld_id = attrs.get("vld_id", "")
+ self.vld_id = attrs.get("vld_id")
Network.list.append(self)
diff --git a/yardstick/benchmark/contexts/node.py b/yardstick/benchmark/contexts/node.py
index baa1cf5d6..b3f0aca0e 100644
--- a/yardstick/benchmark/contexts/node.py
+++ b/yardstick/benchmark/contexts/node.py
@@ -33,6 +33,7 @@ class NodeContext(Context):
self.name = None
self.file_path = None
self.nodes = []
+ self.networks = {}
self.controllers = []
self.computes = []
self.baremetals = []
@@ -77,6 +78,9 @@ class NodeContext(Context):
self.env = attrs.get('env', {})
LOG.debug("Env: %r", self.env)
+ # add optional static network definition
+ self.networks.update(cfg.get("networks", {}))
+
def deploy(self):
config_type = self.env.get('type', '')
if config_type == 'ansible':
@@ -141,6 +145,32 @@ class NodeContext(Context):
node["name"] = attr_name
return node
+ def _get_network(self, attr_name):
+ if not isinstance(attr_name, collections.Mapping):
+ network = self.networks.get(attr_name)
+
+ else:
+ # Don't generalize too much Just support vld_id
+ vld_id = attr_name.get('vld_id')
+ if vld_id is None:
+ return None
+
+ network = next((n for n in self.networks.values() if
+ n.get("vld_id") == vld_id), None)
+
+ if network is None:
+ return None
+
+ result = {
+ # name is required
+ "name": network["name"],
+ "vld_id": network.get("vld_id"),
+ "segmentation_id": network.get("segmentation_id"),
+ "network_type": network.get("network_type"),
+ "physical_network": network.get("physical_network"),
+ }
+ return result
+
def _execute_script(self, node_name, info):
if node_name == 'local':
self._execute_local_script(info)
diff --git a/yardstick/benchmark/contexts/standalone.py b/yardstick/benchmark/contexts/standalone.py
index 78eaac7ee..8614f0cac 100644
--- a/yardstick/benchmark/contexts/standalone.py
+++ b/yardstick/benchmark/contexts/standalone.py
@@ -36,6 +36,7 @@ class StandaloneContext(Context):
self.name = None
self.file_path = None
self.nodes = []
+ self.networks = {}
self.nfvi_node = []
super(StandaloneContext, self).__init__()
@@ -66,8 +67,11 @@ class StandaloneContext(Context):
self.nodes.extend(cfg["nodes"])
self.nfvi_node.extend([node for node in cfg["nodes"]
if node["role"] == "nfvi_node"])
+ # add optional static network definition
+ self.networks.update(cfg.get("networks", {}))
LOG.debug("Nodes: %r", self.nodes)
LOG.debug("NFVi Node: %r", self.nfvi_node)
+ LOG.debug("Networks: %r", self.networks)
def deploy(self):
"""don't need to deploy"""
@@ -114,3 +118,31 @@ class StandaloneContext(Context):
node["name"] = attr_name
return node
+
+ def _get_network(self, attr_name):
+ if not isinstance(attr_name, collections.Mapping):
+ network = self.networks.get(attr_name)
+
+ else:
+ # Don't generalize too much Just support vld_id
+ vld_id = attr_name.get('vld_id')
+ if vld_id is None:
+ return None
+ try:
+ network = next(n for n in self.networks.values() if
+ n.get("vld_id") == vld_id)
+ except StopIteration:
+ return None
+
+ if network is None:
+ return None
+
+ result = {
+ # name is required
+ "name": network["name"],
+ "vld_id": network.get("vld_id"),
+ "segmentation_id": network.get("segmentation_id"),
+ "network_type": network.get("network_type"),
+ "physical_network": network.get("physical_network"),
+ }
+ return result
diff --git a/yardstick/benchmark/core/task.py b/yardstick/benchmark/core/task.py
index 0e85e6316..b53d6446e 100644
--- a/yardstick/benchmark/core/task.py
+++ b/yardstick/benchmark/core/task.py
@@ -322,6 +322,8 @@ class Task(object): # pragma: no cover
if "nodes" in scenario_cfg:
context_cfg["nodes"] = parse_nodes_with_context(scenario_cfg)
+ context_cfg["networks"] = get_networks_from_nodes(
+ context_cfg["nodes"])
runner = base_runner.Runner.get(runner_cfg)
print("Starting runner of type '%s'" % runner_cfg["type"])
@@ -518,7 +520,7 @@ class TaskParser(object): # pragma: no cover
cfg_schema))
def _check_precondition(self, cfg):
- """Check if the envrionment meet the preconditon"""
+ """Check if the environment meet the precondition"""
if "precondition" in cfg:
precondition = cfg["precondition"]
@@ -573,14 +575,26 @@ def _is_background_scenario(scenario):
def parse_nodes_with_context(scenario_cfg):
- """paras the 'nodes' fields in scenario """
+ """parse the 'nodes' fields in scenario """
nodes = scenario_cfg["nodes"]
-
- nodes_cfg = {}
- for nodename in nodes:
- nodes_cfg[nodename] = Context.get_server(nodes[nodename])
-
- return nodes_cfg
+ return {nodename: Context.get_server(node) for nodename, node in nodes.items()}
+
+
+def get_networks_from_nodes(nodes):
+ """parse the 'nodes' fields in scenario """
+ networks = {}
+ for node in nodes.values():
+ if not node:
+ continue
+ for interface in node['interfaces'].values():
+ vld_id = interface.get('vld_id')
+ # mgmt network doesn't have vld_id
+ if not vld_id:
+ continue
+ network = Context.get_network({"vld_id": vld_id})
+ if network:
+ networks[network['name']] = network
+ return networks
def runner_join(runner):
diff --git a/yardstick/benchmark/scenarios/networking/vnf_generic.py b/yardstick/benchmark/scenarios/networking/vnf_generic.py
index 594edeaa8..9607e3005 100644
--- a/yardstick/benchmark/scenarios/networking/vnf_generic.py
+++ b/yardstick/benchmark/scenarios/networking/vnf_generic.py
@@ -164,38 +164,60 @@ class NetworkServiceTestCase(base.Scenario):
for vnfd in topology["constituent-vnfd"]
if vnf_id == vnfd["member-vnf-index"]), None)
+ @staticmethod
+ def get_vld_networks(networks):
+ return {n['vld_id']: n for n in networks.values()}
+
def _resolve_topology(self, context_cfg, topology):
for vld in topology["vld"]:
- if len(vld["vnfd-connection-point-ref"]) > 2:
+ try:
+ node_0, node_1 = vld["vnfd-connection-point-ref"]
+ except (TypeError, ValueError):
raise IncorrectConfig("Topology file corrupted, "
- "too many endpoint for connection")
-
- node_0, node_1 = vld["vnfd-connection-point-ref"]
+ "wrong number of endpoints for connection")
- node0 = self._find_vnf_name_from_id(topology,
- node_0["member-vnf-index-ref"])
- node1 = self._find_vnf_name_from_id(topology,
- node_1["member-vnf-index-ref"])
+ node_0_name = self._find_vnf_name_from_id(topology,
+ node_0["member-vnf-index-ref"])
+ node_1_name = self._find_vnf_name_from_id(topology,
+ node_1["member-vnf-index-ref"])
- if0 = node_0["vnfd-connection-point-ref"]
- if1 = node_1["vnfd-connection-point-ref"]
+ node_0_ifname = node_0["vnfd-connection-point-ref"]
+ node_1_ifname = node_1["vnfd-connection-point-ref"]
+ node_0_if = context_cfg["nodes"][node_0_name]["interfaces"][node_0_ifname]
+ node_1_if = context_cfg["nodes"][node_1_name]["interfaces"][node_1_ifname]
try:
- nodes = context_cfg["nodes"]
- nodes[node0]["interfaces"][if0]["vld_id"] = vld["id"]
- nodes[node1]["interfaces"][if1]["vld_id"] = vld["id"]
-
- nodes[node0]["interfaces"][if0]["dst_mac"] = \
- nodes[node1]["interfaces"][if1]["local_mac"]
- nodes[node0]["interfaces"][if0]["dst_ip"] = \
- nodes[node1]["interfaces"][if1]["local_ip"]
-
- nodes[node1]["interfaces"][if1]["dst_mac"] = \
- nodes[node0]["interfaces"][if0]["local_mac"]
- nodes[node1]["interfaces"][if1]["dst_ip"] = \
- nodes[node0]["interfaces"][if0]["local_ip"]
+ vld_networks = self.get_vld_networks(context_cfg["networks"])
+
+ node_0_if["vld_id"] = vld["id"]
+ node_1_if["vld_id"] = vld["id"]
+
+ # set peer name
+ node_0_if["peer_name"] = node_1_name
+ node_1_if["peer_name"] = node_0_name
+
+ # set peer interface name
+ node_0_if["peer_ifname"] = node_1_ifname
+ node_1_if["peer_ifname"] = node_0_ifname
+
+ # just load the whole network dict
+ node_0_if["network"] = vld_networks.get(vld["id"], {})
+ node_1_if["network"] = vld_networks.get(vld["id"], {})
+
+ node_0_if["dst_mac"] = node_1_if["local_mac"]
+ node_0_if["dst_ip"] = node_1_if["local_ip"]
+
+ node_1_if["dst_mac"] = node_0_if["local_mac"]
+ node_1_if["dst_ip"] = node_0_if["local_ip"]
+
+ # add peer interface dict, but remove circular link
+ # TODO: don't waste memory
+ node_0_copy = node_0_if.copy()
+ node_1_copy = node_1_if.copy()
+ node_0_if["peer_intf"] = node_1_copy
+ node_1_if["peer_intf"] = node_0_copy
except KeyError:
- raise IncorrectConfig("Required interface not found,"
+ raise IncorrectConfig("Required interface not found, "
"topology file corrupted")
@classmethod
@@ -308,21 +330,36 @@ printf "%s/driver:" $1 ; basename $(readlink -s $1/device/driver); } \
return dict(network_devices)
@classmethod
- def get_vnf_impl(cls, vnf_model):
+ def get_vnf_impl(cls, vnf_model_id):
""" Find the implementing class from vnf_model["vnf"]["name"] field
- :param vnf_model: dictionary containing a parsed vnfd
+ :param vnf_model_id: parsed vnfd model ID field
:return: subclass of GenericVNF
"""
import_modules_from_package(
"yardstick.network_services.vnf_generic.vnf")
- expected_name = vnf_model['id']
- impl = (c for c in itersubclasses(GenericVNF)
- if c.__name__ == expected_name)
+ expected_name = vnf_model_id
+ classes_found = []
+
+ def impl():
+ for name, class_ in ((c.__name__, c) for c in itersubclasses(GenericVNF)):
+ if name == expected_name:
+ yield class_
+ classes_found.append(name)
+
try:
- return next(impl)
+ return next(impl())
except StopIteration:
- raise IncorrectConfig("No implementation for %s", expected_name)
+ pass
+
+ raise IncorrectConfig("No implementation for %s found in %s" %
+ (expected_name, classes_found))
+
+ @staticmethod
+ def update_interfaces_from_node(vnfd, node):
+ for intf in vnfd["vdu"][0]["external-interface"]:
+ node_intf = node['interfaces'][intf['name']]
+ intf['virtual-interface'].update(node_intf)
def load_vnf_models(self, scenario_cfg, context_cfg):
""" Create VNF objects based on YAML descriptors
@@ -339,8 +376,11 @@ printf "%s/driver:" $1 ; basename $(readlink -s $1/device/driver); } \
scenario_cfg['task_path']) as stream:
vnf_model = stream.read()
vnfd = vnfdgen.generate_vnfd(vnf_model, node)
- vnf_impl = self.get_vnf_impl(vnfd["vnfd:vnfd-catalog"]["vnfd"][0])
- vnf_instance = vnf_impl(vnfd["vnfd:vnfd-catalog"]["vnfd"][0])
+ # TODO: here add extra context_cfg["nodes"] regardless of template
+ vnfd = vnfd["vnfd:vnfd-catalog"]["vnfd"][0]
+ self.update_interfaces_from_node(vnfd, node)
+ vnf_impl = self.get_vnf_impl(vnfd['id'])
+ vnf_instance = vnf_impl(vnfd)
vnf_instance.name = node_name
vnfs.append(vnf_instance)
diff --git a/yardstick/orchestrator/heat.py b/yardstick/orchestrator/heat.py
index 7958b1cfb..2a907d124 100644
--- a/yardstick/orchestrator/heat.py
+++ b/yardstick/orchestrator/heat.py
@@ -534,6 +534,7 @@ name (i.e. %s).\
}
HEAT_WAIT_LOOP_INTERVAL = 2
+ HEAT_CREATE_COMPLETE_STATUS = u'CREATE_COMPLETE'
def create(self, block=True, timeout=3600):
"""
@@ -558,10 +559,13 @@ name (i.e. %s).\
if not block:
self.outputs = stack.outputs = {}
+ end_time = time.time()
+ log.info("Created stack '%s' in %.3e secs",
+ self.name, end_time - start_time)
return stack
time_limit = start_time + timeout
- for status in iter(self.status, u'CREATE_COMPLETE'):
+ for status in iter(self.status, self.HEAT_CREATE_COMPLETE_STATUS):
log.debug("stack state %s", status)
if status == u'CREATE_FAILED':
stack_status_reason = heat_client.stacks.get(self.uuid).stack_status_reason
@@ -574,7 +578,7 @@ name (i.e. %s).\
end_time = time.time()
outputs = heat_client.stacks.get(self.uuid).outputs
- log.info("Created stack '%s' in %d secs",
+ log.info("Created stack '%s' in %.3e secs",
self.name, end_time - start_time)
# keep outputs as unicode