Introducing Standalone context for running test in non-managed environment. 11/26611/3
authorDeepak S <deepak.s@linux.intel.com>
Fri, 30 Dec 2016 17:19:34 +0000 (09:19 -0800)
committerDeepak S <deepak.s@linux.intel.com>
Thu, 19 Jan 2017 02:56:35 +0000 (08:26 +0530)
This patch introduces standalone virtualization context to
deploy/undeploy NFVi infrastructure to run the VNF

Supported NFVi Type:
  - vswitch
   - ovs
   - ovs-dpdk
   - sr-iov
   - testpmd
   - linuxbridge

This patches inits the function stubs to enable the standalone context.
Actual deploy/undeploy code will be added in later check-in

v2: Added unit tests to keep test coverage :)

JIRA: YARDSTICK-479
Change-Id: I6ab3ac3335f40eabc4efb0af7d5addc20c122d65
Signed-off-by: Deepak S <deepak.s@linux.intel.com>
tests/unit/benchmark/contexts/standalone_duplicate_sample.yaml [new file with mode: 0644]
tests/unit/benchmark/contexts/standalone_sample.yaml [new file with mode: 0644]
tests/unit/benchmark/contexts/test_standalone.py [new file with mode: 0644]
yardstick/benchmark/contexts/standalone.py [new file with mode: 0644]

diff --git a/tests/unit/benchmark/contexts/standalone_duplicate_sample.yaml b/tests/unit/benchmark/contexts/standalone_duplicate_sample.yaml
new file mode 100644 (file)
index 0000000..e468d04
--- /dev/null
@@ -0,0 +1,135 @@
+# Copyright (c) 2016-2017 Intel Corporation
+#
+# 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.
+#
+---
+# Sample config file about the POD information, including the
+# name/IP/user/ssh key of Bare Metal and Controllers/Computes
+#
+# The options of this config file include:
+# name: the name of this node
+# role: node's role, support role: Master/Controller/Comupte/BareMetal
+# ip: the node's IP address
+# user: the username for login
+# key_filename:the path of the private key file for login
+
+nodes:
+-
+    name: node1
+    role: TrafficGen
+    ip: 1.1.1.1
+    user: root
+    password: r00t
+    interfaces:
+        xe0:  # logical name from topology.yaml and vnfd.yaml
+            vpci:      "0000:05:00.0"
+            driver:    i40e
+            dpdk_port_num: 0
+            local_ip: "152.16.100.20"
+            netmask:   "255.255.255.0"
+            local_mac:   "00:00:00:00:00:01"
+        xe1:  # logical name from topology.yaml and vnfd.yaml
+            vpci:      "0000:05:00.1"
+            driver:    i40e
+            dpdk_port_num: 1
+            local_ip: "152.16.100.21"
+            netmask:   "255.255.255.0"
+            local_mac:   "00:00:00:00:00:02"
+-
+    name: node2
+    role: nfvi_node
+    class: OvsDpdk
+    ip: 1.1.1.2
+    user: root
+    password: r00t
+    vports:
+     - dpdkvhostuser0
+     - dpdkvhostuser1
+    vports_mac:
+     - "00:00:00:00:00:03"
+     - "00:00:00:00:00:04"
+    phy_ports: # Physical ports to configure sriov
+     - "0000:05:00.0"
+     - "0000:05:00.1"
+    flow:
+     - ovs-ofctl add-flow br0 in_port=1,action=output:3
+     - ovs-ofctl add-flow br0 in_port=3,action=output:1
+     - ovs-ofctl add-flow br0 in_port=4,action=output:2
+     - ovs-ofctl add-flow br0 in_port=2,action=output:4
+    phy_driver:    i40e # kernel driver
+    images: "/var/lib/libvirt/images/ubuntu.qcow2"
+-
+    name: node2
+    role: nfvi_node
+    class: OvsDpdk
+    ip: 1.1.1.5
+    user: root
+    password: r00t
+    vports:
+     - dpdkvhostuser0
+     - dpdkvhostuser1
+    vports_mac:
+     - "00:00:00:00:00:03"
+     - "00:00:00:00:00:04"
+    phy_ports: # Physical ports to configure sriov
+     - "0000:05:00.0"
+     - "0000:05:00.1"
+    flow:
+     - ovs-ofctl add-flow br0 in_port=1,action=output:3
+     - ovs-ofctl add-flow br0 in_port=3,action=output:1
+     - ovs-ofctl add-flow br0 in_port=4,action=output:2
+     - ovs-ofctl add-flow br0 in_port=2,action=output:4
+    phy_driver:    i40e # kernel driver
+    images: "/var/lib/libvirt/images/ubuntu.qcow2"
+
+-
+    name: node3
+    role: vnf
+    ip: 1.1.1.3
+    user: root
+    password: r00t
+    host: 1.1.1.1
+    interfaces:
+        xe0:  # logical name from topology.yaml and vnfd.yaml
+            vpci:      "0000:00:04.0"
+            driver:    virtio-pci
+            dpdk_port_num: 0
+            local_ip: "152.16.100.19"
+            netmask:   "255.255.255.0"
+            local_mac:   "00:00:00:00:00:05"
+
+        xe1:  # logical name from topology.yaml and vnfd.yaml
+            vpci:      "0000:00:05.0"
+            driver:    virtio-pci
+            dpdk_port_num: 1
+            local_ip: "152.16.40.19"
+            netmask:   "255.255.255.0"
+            local_mac:   "00:00:00:00:00:06"
+    routing_table:
+    - network: "152.16.100.20"
+      netmask: "255.255.255.0"
+      gateway: "152.16.100.20"
+      if: "xe0"
+    - network: "152.16.40.20"
+      netmask: "255.255.255.0"
+      gateway: "152.16.40.20"
+      if: "xe1"
+    nd_route_tbl:
+    - network: "0064:ff9b:0:0:0:0:9810:6414"
+      netmask: "112"
+      gateway: "0064:ff9b:0:0:0:0:9810:6414"
+      if: "xe0"
+    - network: "0064:ff9b:0:0:0:0:9810:2814"
+      netmask: "112"
+      gateway: "0064:ff9b:0:0:0:0:9810:2814"
+      if: "xe1"
diff --git a/tests/unit/benchmark/contexts/standalone_sample.yaml b/tests/unit/benchmark/contexts/standalone_sample.yaml
new file mode 100644 (file)
index 0000000..95e12d6
--- /dev/null
@@ -0,0 +1,112 @@
+# Copyright (c) 2016-2017 Intel Corporation
+#
+# 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.
+#
+---
+# Sample config file about the POD information, including the
+# name/IP/user/ssh key of Bare Metal and Controllers/Computes
+#
+# The options of this config file include:
+# name: the name of this node
+# role: node's role, support role: Master/Controller/Comupte/BareMetal
+# ip: the node's IP address
+# user: the username for login
+# key_filename:the path of the private key file for login
+
+nodes:
+-
+    name: node1
+    role: TrafficGen
+    ip: 1.1.1.1
+    user: root
+    password: r00t
+    interfaces:
+        xe0:  # logical name from topology.yaml and vnfd.yaml
+            vpci:      "0000:05:00.0"
+            driver:    i40e
+            dpdk_port_num: 0
+            local_ip: "152.16.100.20"
+            netmask:   "255.255.255.0"
+            local_mac:   "00:00:00:00:00:01"
+        xe1:  # logical name from topology.yaml and vnfd.yaml
+            vpci:      "0000:05:00.1"
+            driver:    i40e
+            dpdk_port_num: 1
+            local_ip: "152.16.100.21"
+            netmask:   "255.255.255.0"
+            local_mac:   "00:00:00:00:00:02"
+-
+    name: node2
+    role: nfvi_node
+    class: OvsDpdk
+    ip: 1.1.1.2
+    user: root
+    password: r00t
+    vports:
+     - dpdkvhostuser0
+     - dpdkvhostuser1
+    vports_mac:
+     - "00:00:00:00:00:03"
+     - "00:00:00:00:00:04"
+    phy_ports: # Physical ports to configure sriov
+     - "0000:05:00.0"
+     - "0000:05:00.1"
+    flow:
+     - ovs-ofctl add-flow br0 in_port=1,action=output:3
+     - ovs-ofctl add-flow br0 in_port=3,action=output:1
+     - ovs-ofctl add-flow br0 in_port=4,action=output:2
+     - ovs-ofctl add-flow br0 in_port=2,action=output:4
+    phy_driver:    i40e # kernel driver
+    images: "/var/lib/libvirt/images/ubuntu.qcow2"
+
+-
+    name: node3
+    role: vnf
+    ip: 1.1.1.3
+    user: root
+    password: r00t
+    host: 1.1.1.1
+    interfaces:
+        xe0:  # logical name from topology.yaml and vnfd.yaml
+            vpci:      "0000:00:04.0"
+            driver:    virtio-pci
+            dpdk_port_num: 0
+            local_ip: "152.16.100.19"
+            netmask:   "255.255.255.0"
+            local_mac:   "00:00:00:00:00:05"
+
+        xe1:  # logical name from topology.yaml and vnfd.yaml
+            vpci:      "0000:00:05.0"
+            driver:    virtio-pci
+            dpdk_port_num: 1
+            local_ip: "152.16.40.19"
+            netmask:   "255.255.255.0"
+            local_mac:   "00:00:00:00:00:06"
+    routing_table:
+    - network: "152.16.100.20"
+      netmask: "255.255.255.0"
+      gateway: "152.16.100.20"
+      if: "xe0"
+    - network: "152.16.40.20"
+      netmask: "255.255.255.0"
+      gateway: "152.16.40.20"
+      if: "xe1"
+    nd_route_tbl:
+    - network: "0064:ff9b:0:0:0:0:9810:6414"
+      netmask: "112"
+      gateway: "0064:ff9b:0:0:0:0:9810:6414"
+      if: "xe0"
+    - network: "0064:ff9b:0:0:0:0:9810:2814"
+      netmask: "112"
+      gateway: "0064:ff9b:0:0:0:0:9810:2814"
+      if: "xe1"
diff --git a/tests/unit/benchmark/contexts/test_standalone.py b/tests/unit/benchmark/contexts/test_standalone.py
new file mode 100644 (file)
index 0000000..687ef73
--- /dev/null
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2016-2017 Intel Corporation
+#
+# 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.
+#
+
+# Unittest for yardstick.benchmark.contexts.standalone
+
+from __future__ import absolute_import
+import os
+import unittest
+
+from yardstick.benchmark.contexts import standalone
+
+
+class StandaloneContextTestCase(unittest.TestCase):
+
+    NODES_SAMPLE = "standalone_sample.yaml"
+    NODES_DUPLICATE_SAMPLE = "standalone_duplicate_sample.yaml"
+
+    def setUp(self):
+        self.test_context = standalone.StandaloneContext()
+
+    def test_construct(self):
+
+        self.assertIsNone(self.test_context.name)
+        self.assertIsNone(self.test_context.file_path)
+        self.assertEqual(self.test_context.nodes, [])
+        self.assertEqual(self.test_context.nfvi_node, [])
+
+    def test_unsuccessful_init(self):
+
+        attrs = {
+            'name': 'foo',
+            'file': self._get_file_abspath("error_file")
+        }
+
+        self.assertRaises(IOError, self.test_context.init, attrs)
+
+    def test_successful_init(self):
+
+        attrs = {
+            'name': 'foo',
+            'file': self._get_file_abspath(self.NODES_SAMPLE)
+        }
+
+        self.test_context.init(attrs)
+
+        self.assertEqual(self.test_context.name, "foo")
+        self.assertEqual(len(self.test_context.nodes), 3)
+        self.assertEqual(len(self.test_context.nfvi_node), 1)
+        self.assertEqual(self.test_context.nfvi_node[0]["name"], "node2")
+
+    def test__get_server_with_dic_attr_name(self):
+
+        attrs = {
+            'name': 'foo',
+            'file': self._get_file_abspath(self.NODES_SAMPLE)
+        }
+
+        self.test_context.init(attrs)
+
+        attr_name = {'name': 'foo.bar'}
+        result = self.test_context._get_server(attr_name)
+
+        self.assertEqual(result, None)
+
+    def test__get_server_not_found(self):
+
+        attrs = {
+            'name': 'foo',
+            'file': self._get_file_abspath(self.NODES_SAMPLE)
+        }
+
+        self.test_context.init(attrs)
+
+        attr_name = 'bar.foo'
+        result = self.test_context._get_server(attr_name)
+
+        self.assertEqual(result, None)
+
+    def test__get_server_duplicate(self):
+
+        attrs = {
+            'name': 'foo',
+            'file': self._get_file_abspath(self.NODES_DUPLICATE_SAMPLE)
+        }
+
+        self.test_context.init(attrs)
+
+        attr_name = 'node2.foo'
+
+        self.assertRaises(ValueError, self.test_context._get_server, attr_name)
+
+    def test__get_server_found(self):
+
+        attrs = {
+            'name': 'foo',
+            'file': self._get_file_abspath(self.NODES_SAMPLE)
+        }
+
+        self.test_context.init(attrs)
+
+        attr_name = 'node1.foo'
+        result = self.test_context._get_server(attr_name)
+
+        self.assertEqual(result['ip'], '1.1.1.1')
+        self.assertEqual(result['name'], 'node1.foo')
+        self.assertEqual(result['user'], 'root')
+
+    def test_deploy(self):
+        self.assertIsNone(self.test_context.deploy())
+
+    def test_undeploy(self):
+        self.assertIsNone(self.test_context.undeploy())
+
+    def _get_file_abspath(self, filename):
+        curr_path = os.path.dirname(os.path.abspath(__file__))
+        file_path = os.path.join(curr_path, filename)
+        return file_path
diff --git a/yardstick/benchmark/contexts/standalone.py b/yardstick/benchmark/contexts/standalone.py
new file mode 100644 (file)
index 0000000..c1d963f
--- /dev/null
@@ -0,0 +1,116 @@
+# Copyright (c) 2016-2017 Intel Corporation
+#
+# 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.
+"""This module handle non managed standalone virtualization node."""
+
+from __future__ import absolute_import
+import logging
+import errno
+import collections
+import yaml
+
+from yardstick.benchmark.contexts.base import Context
+from yardstick.definitions import YARDSTICK_ROOT_PATH
+
+LOG = logging.getLogger(__name__)
+
+
+class StandaloneContext(Context):
+    """ This class handles standalone nodes - VM running on Non-Managed NFVi
+    Configuration: vswitch, ovs, ovs-dpdk, sr-iov, linuxbridge
+    """
+
+    __context_type__ = "Standalone"
+
+    def __init__(self):
+        self.name = None
+        self.file_path = None
+        self.nodes = []
+        self.nfvi_node = []
+        super(self.__class__, self).__init__()
+
+    def read_config_file(self):
+        """Read from config file"""
+
+        with open(self.file_path) as stream:
+            LOG.info("Parsing pod file: %s", self.file_path)
+            cfg = yaml.load(stream)
+        return cfg
+
+    def init(self, attrs):
+        """initializes itself from the supplied arguments"""
+
+        self.name = attrs["name"]
+        self.file_path = attrs.get("file", "pod.yaml")
+        LOG.info("Parsing pod file: %s", self.file_path)
+
+        try:
+            cfg = self.read_config_file()
+        except IOError as ioerror:
+            if ioerror.errno == errno.ENOENT:
+                self.file_path = YARDSTICK_ROOT_PATH + self.file_path
+                cfg = self.read_config_file()
+            else:
+                raise
+
+        self.nodes.extend(cfg["nodes"])
+        self.nfvi_node.extend([node for node in cfg["nodes"]
+                               if node["role"] == "nfvi_node"])
+        LOG.debug("Nodes: %r", self.nodes)
+        LOG.debug("NFVi Node: %r", self.nfvi_node)
+
+    def deploy(self):
+        """don't need to deploy"""
+
+        # Todo: NFVi deploy (sriov, vswitch, ovs etc) based on the config.
+        pass
+
+    def undeploy(self):
+        """don't need to undeploy"""
+
+        # Todo: NFVi undeploy (sriov, vswitch, ovs etc) based on the config.
+        pass
+
+    def _get_server(self, attr_name):
+        """lookup server info by name from context
+
+        Keyword arguments:
+        attr_name -- A name for a server listed in nodes config file
+        """
+
+        if isinstance(attr_name, collections.Mapping):
+            return None
+
+        if self.name.split("-")[0] != attr_name.split(".")[1]:
+            return None
+
+        node_name = attr_name.split(".")[0]
+        matching_nodes = (n for n in self.nodes if n["name"] == node_name)
+
+        try:
+            # A clone is created in order to avoid affecting the
+            # original one.
+            node = dict(next(matching_nodes))
+        except StopIteration:
+            return None
+
+        try:
+            duplicate = next(matching_nodes)
+        except StopIteration:
+            pass
+        else:
+            raise ValueError("Duplicate nodes!!! Nodes: %s %s",
+                             (matching_nodes, duplicate))
+
+        node["name"] = attr_name
+        return node