Support NodeContext type 81/2781/4
authorQiLiang <liangqi1@huawei.com>
Sun, 25 Oct 2015 14:47:37 +0000 (14:47 +0000)
committerQiLiang <liangqi1@huawei.com>
Sun, 1 Nov 2015 12:12:31 +0000 (12:12 +0000)
Initial NodeContext implementation to support BareMetal,
Controller, Compute scenarios.

Usage:

0) install yardstick
1) mkdir -p /etc/yardstick/nodes
2) cp <yardstick_repo>/etc/yardstick/nodes/pod.yaml.sample \
       /etc/yardstick/nodes/pod.yaml
3) edit /etc/yardstick/nodes/pod.yaml (make show ip, username,
       ssh key are configured correctly)
4) yardstick -d task start \
       <yardstick_repo>/samples/ping-node-context.yaml
5) cat /tmp/yardstick.out

Design etherpad link:
    https://etherpad.opnfv.org/p/yardstick_framework

JIRA: YARDSTICK-169

Change-Id: I3f6ade8243e68d88326f23ed213edb32c638ed32
Signed-off-by: QiLiang <liangqi1@huawei.com>
etc/yardstick/nodes/pod.yaml.sample [new file with mode: 0644]
samples/ping-node-context.yaml [new file with mode: 0644]
tests/unit/benchmark/contexts/nodes_duplicate_sample.yaml [new file with mode: 0644]
tests/unit/benchmark/contexts/nodes_sample.yaml [new file with mode: 0644]
tests/unit/benchmark/contexts/test_node.py [new file with mode: 0644]
yardstick/benchmark/contexts/node.py [new file with mode: 0644]

diff --git a/etc/yardstick/nodes/pod.yaml.sample b/etc/yardstick/nodes/pod.yaml.sample
new file mode 100644 (file)
index 0000000..a374596
--- /dev/null
@@ -0,0 +1,24 @@
+---
+# 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: athena
+    role: Controller
+    ip: 10.229.47.137
+    user: root
+    key_filename: /root/yardstick/yardstick/resources/files/yardstick_key
+-
+    name: ares
+    role: Controller
+    ip: 10.229.47.138
+    user: root
+    key_filename: /root/yardstick/yardstick/resources/files/yardstick_key
diff --git a/samples/ping-node-context.yaml b/samples/ping-node-context.yaml
new file mode 100644 (file)
index 0000000..2edc05e
--- /dev/null
@@ -0,0 +1,29 @@
+---
+# Sample benchmark task config file
+# measure network latency using ping
+
+schema: "yardstick:task:0.1"
+
+scenarios:
+-
+  type: Ping
+  options:
+    packetsize: 200
+  host: athena.LF
+  target: ares.LF
+
+  runner:
+    type: Duration
+    duration: 60
+    interval: 1
+
+  sla:
+    max_rtt: 10
+    action: monitor
+
+
+context:
+  type: Node
+  name: LF
+  file: /etc/yardstick/nodes/pod.yaml
+
diff --git a/tests/unit/benchmark/contexts/nodes_duplicate_sample.yaml b/tests/unit/benchmark/contexts/nodes_duplicate_sample.yaml
new file mode 100644 (file)
index 0000000..cdb5138
--- /dev/null
@@ -0,0 +1,13 @@
+nodes:
+-
+    name: node1
+    role: Controller
+    ip: 10.229.47.137
+    user: root
+    key_filename: /root/.yardstick_key
+-
+    name: node1
+    role: Controller
+    ip: 10.229.47.138
+    user: root
+    key_filename: /root/.yardstick_key
diff --git a/tests/unit/benchmark/contexts/nodes_sample.yaml b/tests/unit/benchmark/contexts/nodes_sample.yaml
new file mode 100644 (file)
index 0000000..59b5bb9
--- /dev/null
@@ -0,0 +1,25 @@
+nodes:
+-
+    name: node1
+    role: Controller
+    ip: 10.229.47.137
+    user: root
+    key_filename: /root/.yardstick_key
+-
+    name: node2
+    role: Controller
+    ip: 10.229.47.138
+    user: root
+    key_filename: /root/.yardstick_key
+-
+    name: node3
+    role: Compute
+    ip: 10.229.47.139
+    user: root
+    key_filename: /root/.yardstick_key
+-
+    name: node4
+    role: Baremetal
+    ip: 10.229.47.140
+    user: root
+    key_filename: /root/.yardstick_key
diff --git a/tests/unit/benchmark/contexts/test_node.py b/tests/unit/benchmark/contexts/test_node.py
new file mode 100644 (file)
index 0000000..6939b85
--- /dev/null
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+
+##############################################################################
+# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+# Unittest for yardstick.benchmark.contexts.node
+
+import os
+import unittest
+
+from yardstick.benchmark.contexts import node
+
+
+class NodeContextTestCase(unittest.TestCase):
+
+    NODES_SAMPLE = "nodes_sample.yaml"
+    NODES_DUPLICATE_SAMPLE = "nodes_duplicate_sample.yaml"
+    def setUp(self):
+        self.test_context = node.NodeContext()
+
+    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.controllers, [])
+        self.assertEqual(self.test_context.computes, [])
+        self.assertEqual(self.test_context.baremetals, [])
+
+    def test_unsuccessful_init(self):
+
+        attrs = {
+            'name': 'foo',
+            'file': self._get_file_abspath("error_file")
+        }
+
+        self.assertRaises(SystemExit, 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), 4)
+        self.assertEqual(len(self.test_context.controllers), 2)
+        self.assertEqual(len(self.test_context.computes), 1)
+        self.assertEqual(self.test_context.computes[0]["name"], "node3")
+        self.assertEqual(len(self.test_context.baremetals), 1)
+        self.assertEqual(self.test_context.baremetals[0]["name"], "node4")
+
+    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 = 'node1.foo'
+
+        self.assertRaises(SystemExit, 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'], '10.229.47.137')
+        self.assertEqual(result['name'], 'node1.foo')
+        self.assertEqual(result['user'], 'root')
+        self.assertEqual(result['key_filename'], '/root/.yardstick_key')
+
+    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/node.py b/yardstick/benchmark/contexts/node.py
new file mode 100644 (file)
index 0000000..04c8e7c
--- /dev/null
@@ -0,0 +1,94 @@
+##############################################################################
+# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import sys
+import yaml
+import logging
+
+from yardstick.benchmark.contexts.base import Context
+
+LOG = logging.getLogger(__name__)
+
+
+class NodeContext(Context):
+    '''Class that handle nodes info'''
+
+    __context_type__ = "Node"
+
+    def __init__(self):
+        self.name = None
+        self.file_path = None
+        self.nodes = []
+        self.controllers = []
+        self.computes = []
+        self.baremetals = []
+        super(self.__class__, self).__init__()
+
+    def init(self, attrs):
+        '''initializes itself from the supplied arguments'''
+        self.name = attrs["name"]
+        self.file_path = attrs.get("file", "/etc/yardstick/nodes/pod.yaml")
+
+        LOG.info("Parsing pod file: %s", self.file_path)
+
+        try:
+            with open(self.file_path) as stream:
+                cfg = yaml.load(stream)
+        except IOError as ioerror:
+            sys.exit(ioerror)
+
+        self.nodes.extend(cfg["nodes"])
+        self.controllers.extend([node for node in cfg["nodes"]
+                                if node["role"] == "Controller"])
+        self.computes.extend([node for node in cfg["nodes"]
+                             if node["role"] == "Compute"])
+        self.baremetals.extend([node for node in cfg["nodes"]
+                               if node["role"] == "Baremetal"])
+        LOG.debug("Nodes: %r", self.nodes)
+        LOG.debug("Controllers: %r", self.controllers)
+        LOG.debug("Computes: %r", self.computes)
+        LOG.debug("BareMetals: %r", self.baremetals)
+
+    def deploy(self):
+        '''don't need to deploy'''
+        pass
+
+    def undeploy(self):
+        '''don't need to undeploy'''
+        pass
+
+    def _get_server(self, attr_name):
+        '''lookup server info by name from context
+        attr_name: a name for a server listed in nodes config file
+        '''
+        if type(attr_name) is dict:
+            return None
+
+        if self.name != attr_name.split(".")[1]:
+            return None
+        node_name = attr_name.split(".")[0]
+        nodes = [n for n in self.nodes
+                 if n["name"] == node_name]
+        if len(nodes) == 0:
+            return None
+        elif len(nodes) > 1:
+            LOG.error("Duplicate nodes!!!")
+            LOG.error("Nodes: %r" % nodes)
+            sys.exit(-1)
+
+        node = nodes[0]
+
+        server = {
+            "name": attr_name,
+            "ip": node["ip"],
+            "user": node["user"],
+            "key_filename": node["key_filename"]
+        }
+
+        return server