Kubernetes (k8s) support 37/36537/16
authorchenjiankun <chenjiankun1@huawei.com>
Tue, 27 Jun 2017 03:20:08 +0000 (03:20 +0000)
committerchenjiankun <chenjiankun1@huawei.com>
Mon, 17 Jul 2017 00:58:09 +0000 (00:58 +0000)
JIRA: YARDSTICK-682

We decide to support k8s in E release.
We need to discuss with openretriver team and then rewrite the ping
test case under k8s as the first step.

Change-Id: I3f81ebca8de5c1f3a8b7d42581cd7342dc320239
Signed-off-by: chenjiankun <chenjiankun1@huawei.com>
requirements.txt
samples/ping_k8s.yaml [new file with mode: 0644]
tests/unit/benchmark/contexts/test_kubernetes.py [new file with mode: 0644]
tests/unit/common/test_utils.py
tests/unit/orchestrator/test_kubernetes.py [new file with mode: 0644]
yardstick/benchmark/contexts/kubernetes.py [new file with mode: 0644]
yardstick/common/constants.py
yardstick/common/kubernetes_utils.py [new file with mode: 0644]
yardstick/common/utils.py
yardstick/orchestrator/kubernetes.py [new file with mode: 0644]

index 85bd8b3..3a4cbce 100644 (file)
@@ -39,6 +39,7 @@ jsonpatch==1.15
 jsonpointer==1.10
 jsonschema==2.5.1
 keystoneauth1==2.18.0
+kubernetes==3.0.0a1
 linecache2==1.0.0
 lxml==3.7.2
 mccabe==0.4.0
diff --git a/samples/ping_k8s.yaml b/samples/ping_k8s.yaml
new file mode 100644 (file)
index 0000000..503fe6a
--- /dev/null
@@ -0,0 +1,46 @@
+##############################################################################
+# Copyright (c) 2017 Huawei AB 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
+##############################################################################
+
+---
+# Sample benchmark task config file
+# measure network latency using ping in container
+
+schema: "yardstick:task:0.1"
+
+scenarios:
+-
+  type: Ping
+  options:
+    packetsize: 200
+
+  host: host-k8s
+  target: target-k8s
+
+  runner:
+    type: Duration
+    duration: 60
+    interval: 1
+
+  sla:
+    max_rtt: 10
+    action: monitor
+
+context:
+  type: Kubernetes
+  name: k8s
+
+  servers:
+    host:
+      image: openretriever/yardstick
+      command: /bin/bash
+      args: ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; service ssh restart;while true ; do sleep 10000; done']
+    target:
+      image: openretriever/yardstick
+      command: /bin/bash
+      args: ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; service ssh restart;while true ; do sleep 10000; done']
diff --git a/tests/unit/benchmark/contexts/test_kubernetes.py b/tests/unit/benchmark/contexts/test_kubernetes.py
new file mode 100644 (file)
index 0000000..f47c07a
--- /dev/null
@@ -0,0 +1,165 @@
+#!/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.kubernetes
+
+from __future__ import absolute_import
+import unittest
+import mock
+
+from yardstick.benchmark.contexts.kubernetes import KubernetesContext
+
+
+context_cfg = {
+    'type': 'Kubernetes',
+    'name': 'k8s',
+    'servers': {
+        'host': {
+            'image': 'openretriever/yardstick',
+            'command': '/bin/bash',
+            'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \
+service ssh restart;while true ; do sleep 10000; done']
+        },
+        'target': {
+            'image': 'openretriever/yardstick',
+            'command': '/bin/bash',
+            'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \
+service ssh restart;while true ; do sleep 10000; done']
+        }
+    }
+}
+
+prefix = 'yardstick.benchmark.contexts.kubernetes'
+
+
+class UndeployTestCase(unittest.TestCase):
+
+    @mock.patch('{}.KubernetesContext._delete_ssh_key'.format(prefix))
+    @mock.patch('{}.KubernetesContext._delete_rcs'.format(prefix))
+    @mock.patch('{}.KubernetesContext._delete_pods'.format(prefix))
+    def test_undeploy(self,
+                      mock_delete_pods,
+                      mock_delete_rcs,
+                      mock_delete_ssh):
+
+        k8s_context = KubernetesContext()
+        k8s_context.init(context_cfg)
+        k8s_context.undeploy()
+        self.assertTrue(mock_delete_ssh.called)
+        self.assertTrue(mock_delete_rcs.called)
+        self.assertTrue(mock_delete_pods.called)
+
+
+class DeployTestCase(unittest.TestCase):
+
+    @mock.patch('{}.KubernetesContext._wait_until_running'.format(prefix))
+    @mock.patch('{}.KubernetesTemplate.get_rc_pods'.format(prefix))
+    @mock.patch('{}.KubernetesContext._create_rcs'.format(prefix))
+    @mock.patch('{}.KubernetesContext._set_ssh_key'.format(prefix))
+    def test_deploy(self,
+                    mock_set_ssh_key,
+                    mock_create_rcs,
+                    mock_get_rc_pods,
+                    mock_wait_until_running):
+
+        k8s_context = KubernetesContext()
+        k8s_context.init(context_cfg)
+        k8s_context.deploy()
+        self.assertTrue(mock_set_ssh_key.called)
+        self.assertTrue(mock_create_rcs.called)
+        self.assertTrue(mock_get_rc_pods.called)
+        self.assertTrue(mock_wait_until_running.called)
+
+
+class SSHKeyTestCase(unittest.TestCase):
+
+    @mock.patch('{}.k8s_utils.delete_config_map'.format(prefix))
+    @mock.patch('{}.k8s_utils.create_config_map'.format(prefix))
+    def test_ssh_key(self, mock_create, mock_delete):
+
+        k8s_context = KubernetesContext()
+        k8s_context.init(context_cfg)
+        k8s_context._set_ssh_key()
+        k8s_context._delete_ssh_key()
+        self.assertTrue(mock_create.called)
+        self.assertTrue(mock_delete.called)
+
+
+class WaitUntilRunningTestCase(unittest.TestCase):
+
+    @mock.patch('{}.k8s_utils.read_pod_status'.format(prefix))
+    def test_wait_until_running(self, mock_read_pod_status):
+
+        k8s_context = KubernetesContext()
+        k8s_context.init(context_cfg)
+        k8s_context.template.pods = ['server']
+        mock_read_pod_status.return_value = 'Running'
+        k8s_context._wait_until_running()
+
+
+class GetServerTestCase(unittest.TestCase):
+
+    @mock.patch('{}.k8s_utils.get_pod_list'.format(prefix))
+    def test_get_server(self, mock_get_pod_list):
+        k8s_context = KubernetesContext()
+        k8s_context.init(context_cfg)
+
+        mock_get_pod_list.return_value.items = []
+        server = k8s_context._get_server('server')
+        self.assertIsNone(server)
+
+
+class CreateRcsTestCase(unittest.TestCase):
+
+    @mock.patch('{}.KubernetesContext._create_rc'.format(prefix))
+    def test_create_rcs(self, mock_create_rc):
+        k8s_context = KubernetesContext()
+        k8s_context.init(context_cfg)
+        k8s_context._create_rcs()
+        self.assertTrue(mock_create_rc.called)
+
+
+class CreateRcTestCase(unittest.TestCase):
+
+    @mock.patch('{}.k8s_utils.create_replication_controller'.format(prefix))
+    def test_create_rc(self, mock_create_replication_controller):
+        k8s_context = KubernetesContext()
+        k8s_context.init(context_cfg)
+        k8s_context._create_rc({})
+        self.assertTrue(mock_create_replication_controller.called)
+
+
+class DeleteRcsTestCases(unittest.TestCase):
+
+    @mock.patch('{}.KubernetesContext._delete_rc'.format(prefix))
+    def test_delete_rcs(self, mock_delete_rc):
+        k8s_context = KubernetesContext()
+        k8s_context.init(context_cfg)
+        k8s_context._delete_rcs()
+        self.assertTrue(mock_delete_rc.called)
+
+
+class DeleteRcTestCase(unittest.TestCase):
+
+    @mock.patch('{}.k8s_utils.delete_replication_controller'.format(prefix))
+    def test_delete_rc(self, mock_delete_replication_controller):
+        k8s_context = KubernetesContext()
+        k8s_context.init(context_cfg)
+        k8s_context._delete_rc({})
+        self.assertTrue(mock_delete_replication_controller.called)
+
+
+def main():
+    unittest.main()
+
+
+if __name__ == '__main__':
+    main()
index 664b38b..e21e5fa 100644 (file)
@@ -176,6 +176,25 @@ class ChangeObjToDictTestCase(unittest.TestCase):
         self.assertEqual(obj_r, obj_s)
 
 
+class SetDictValueTestCase(unittest.TestCase):
+
+    def test_set_dict_value(self):
+        input_dic = {
+            'hello': 'world'
+        }
+        output_dic = utils.set_dict_value(input_dic, 'welcome.to', 'yardstick')
+        self.assertEqual(output_dic.get('welcome', {}).get('to'), 'yardstick')
+
+
+class RemoveFileTestCase(unittest.TestCase):
+
+    def test_remove_file(self):
+        try:
+            utils.remove_file('notexistfile.txt')
+        except Exception as e:
+            self.assertTrue(isinstance(e, OSError))
+
+
 def main():
     unittest.main()
 
diff --git a/tests/unit/orchestrator/test_kubernetes.py b/tests/unit/orchestrator/test_kubernetes.py
new file mode 100644 (file)
index 0000000..51718ab
--- /dev/null
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+
+##############################################################################
+# Copyright (c) 2017 Intel Corporation
+#
+# 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.orchestrator.heat
+import unittest
+import mock
+
+from yardstick.orchestrator.kubernetes import KubernetesObject
+from yardstick.orchestrator.kubernetes import KubernetesTemplate
+
+
+class GetTemplateTestCase(unittest.TestCase):
+
+    def test_get_template(self):
+        output_t = {
+            "apiVersion": "v1",
+            "kind": "ReplicationController",
+            "metadata": {
+                "name": "host-k8s-86096c30"
+            },
+            "spec": {
+                "replicas": 1,
+                "template": {
+                    "metadata": {
+                        "labels": {
+                            "app": "host-k8s-86096c30"
+                        }
+                    },
+                    "spec": {
+                        "containers": [
+                            {
+                                "args": [
+                                    "-c",
+                                    "chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \
+service ssh restart;while true ; do sleep 10000; done"
+                                ],
+                                "command": [
+                                    "/bin/bash"
+                                ],
+                                "image": "openretriever/yardstick",
+                                "name": "host-k8s-86096c30-container",
+                                "volumeMounts": [
+                                    {
+                                        "mountPath": "/root/.ssh/",
+                                        "name": "k8s-86096c30-key"
+                                    }
+                                ]
+                            }
+                        ],
+                        "volumes": [
+                            {
+                                "configMap": {
+                                    "name": "k8s-86096c30-key"
+                                },
+                                "name": "k8s-86096c30-key"
+                            }
+                        ]
+                    }
+                }
+            }
+        }
+        input_s = {
+            'command': '/bin/bash',
+            'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \
+service ssh restart;while true ; do sleep 10000; done'],
+            'ssh_key': 'k8s-86096c30-key'
+        }
+        name = 'host-k8s-86096c30'
+        output_r = KubernetesObject(name, **input_s).get_template()
+        self.assertEqual(output_r, output_t)
+
+
+class GetRcPodsTestCase(unittest.TestCase):
+
+    @mock.patch('yardstick.orchestrator.kubernetes.k8s_utils.get_pod_list')
+    def test_get_rc_pods(self, mock_get_pod_list):
+        servers = {
+            'host': {
+                'image': 'openretriever/yardstick',
+                'command': '/bin/bash',
+                'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \
+service ssh restart;while true ; do sleep 10000; done']
+            },
+            'target': {
+                'image': 'openretriever/yardstick',
+                'command': '/bin/bash',
+                'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \
+service ssh restart;while true ; do sleep 10000; done']
+            }
+        }
+        k8s_template = KubernetesTemplate('k8s-86096c30', servers)
+        mock_get_pod_list.return_value.items = []
+        pods = k8s_template.get_rc_pods()
+        self.assertEqual(pods, [])
+
+
+def main():
+    unittest.main()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/yardstick/benchmark/contexts/kubernetes.py b/yardstick/benchmark/contexts/kubernetes.py
new file mode 100644 (file)
index 0000000..cc3e326
--- /dev/null
@@ -0,0 +1,137 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
+#
+# 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
+##############################################################################
+
+from __future__ import absolute_import
+import logging
+import time
+import pkg_resources
+
+import paramiko
+
+from yardstick.benchmark.contexts.base import Context
+from yardstick.orchestrator.kubernetes import KubernetesTemplate
+from yardstick.common import kubernetes_utils as k8s_utils
+from yardstick.common import utils
+
+LOG = logging.getLogger(__name__)
+BITS_LENGTH = 2048
+
+
+class KubernetesContext(Context):
+    """Class that handle nodes info"""
+
+    __context_type__ = "Kubernetes"
+
+    def __init__(self):
+        self.name = ''
+        self.ssh_key = ''
+        self.key_path = ''
+        self.public_key_path = ''
+        self.template = None
+
+        super(KubernetesContext, self).__init__()
+
+    def init(self, attrs):
+        self.name = attrs.get('name', '')
+
+        template_cfg = attrs.get('servers', {})
+        self.template = KubernetesTemplate(self.name, template_cfg)
+
+        self.ssh_key = '{}-key'.format(self.name)
+
+        self.key_path = self._get_key_path()
+        self.public_key_path = '{}.pub'.format(self.key_path)
+
+    def deploy(self):
+        LOG.info('Creating ssh key')
+        self._set_ssh_key()
+
+        LOG.info('Launch containers')
+        self._create_rcs()
+        time.sleep(1)
+        self.template.get_rc_pods()
+
+        self._wait_until_running()
+
+    def undeploy(self):
+        self._delete_ssh_key()
+        self._delete_rcs()
+        self._delete_pods()
+
+        super(KubernetesContext, self).undeploy()
+
+    def _wait_until_running(self):
+        while not all(self._check_pod_status(p) for p in self.template.pods):
+            time.sleep(1)
+
+    def _check_pod_status(self, pod):
+        status = k8s_utils.read_pod_status(pod)
+        LOG.debug('%s:%s', pod, status)
+        if status == 'Failed':
+            LOG.error('Pod %s status is failed', pod)
+            raise RuntimeError
+        if status != 'Running':
+            return False
+        return True
+
+    def _create_rcs(self):
+        for obj in self.template.k8s_objs:
+            self._create_rc(obj.get_template())
+
+    def _create_rc(self, template):
+        k8s_utils.create_replication_controller(template)
+
+    def _delete_rcs(self):
+        for rc in self.template.rcs:
+            self._delete_rc(rc)
+
+    def _delete_rc(self, rc):
+        k8s_utils.delete_replication_controller(rc)
+
+    def _delete_pods(self):
+        for pod in self.template.pods:
+            self._delete_pod(pod)
+
+    def _delete_pod(self, pod):
+        k8s_utils.delete_pod(pod)
+
+    def _get_key_path(self):
+        task_id = self.name.split('-')[-1]
+        k = 'files/yardstick_key-{}'.format(task_id)
+        return pkg_resources.resource_filename('yardstick.resources', k)
+
+    def _set_ssh_key(self):
+        rsa_key = paramiko.RSAKey.generate(bits=BITS_LENGTH)
+
+        LOG.info('Writing private key')
+        rsa_key.write_private_key_file(self.key_path)
+
+        LOG.info('Writing public key')
+        key = '{} {}\n'.format(rsa_key.get_name(), rsa_key.get_base64())
+        with open(self.public_key_path, 'w') as f:
+            f.write(key)
+
+        LOG.info('Create configmap for ssh key')
+        k8s_utils.create_config_map(self.ssh_key, {'authorized_keys': key})
+
+    def _delete_ssh_key(self):
+        k8s_utils.delete_config_map(self.ssh_key)
+        utils.remove_file(self.key_path)
+        utils.remove_file(self.public_key_path)
+
+    def _get_server(self, name):
+        resp = k8s_utils.get_pod_list()
+        hosts = ({'name': n.metadata.name,
+                  'ip': n.status.pod_ip,
+                  'user': 'root',
+                  'key_filename': self.key_path,
+                  'private_ip': n.status.pod_ip}
+                 for n in resp.items if n.metadata.name.startswith(name))
+
+        return next(hosts, None)
index 5cd34ba..69485a4 100644 (file)
@@ -56,6 +56,7 @@ ETC_HOSTS = get_param('file.etc_hosts', '/etc/hosts')
 CONF_FILE = join(CONF_DIR, 'yardstick.conf')
 POD_FILE = join(CONF_DIR, 'pod.yaml')
 CLOUDS_CONF = join(OPENSTACK_CONF_DIR, 'clouds.yml')
+K8S_CONF_FILE = join(CONF_DIR, 'admin.conf')
 CONF_SAMPLE_FILE = join(CONF_SAMPLE_DIR, 'yardstick.conf.sample')
 FETCH_SCRIPT = get_param('file.fetch_script', 'utils/fetch_os_creds.sh')
 FETCH_SCRIPT = join(RELENG_DIR, FETCH_SCRIPT)
diff --git a/yardstick/common/kubernetes_utils.py b/yardstick/common/kubernetes_utils.py
new file mode 100644 (file)
index 0000000..e4c2328
--- /dev/null
@@ -0,0 +1,137 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
+#
+# 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 logging
+
+from kubernetes import client
+from kubernetes import config
+from kubernetes.client.rest import ApiException
+
+from yardstick.common import constants as consts
+
+LOG = logging.getLogger(__name__)
+LOG.setLevel(logging.DEBUG)
+
+
+def get_core_api():     # pragma: no cover
+    try:
+        config.load_kube_config(config_file=consts.K8S_CONF_FILE)
+    except IOError:
+        LOG.exception('config file not found')
+        raise
+
+    return client.CoreV1Api()
+
+
+def create_replication_controller(template,
+                                  namespace='default',
+                                  wait=False,
+                                  **kwargs):    # pragma: no cover
+
+    core_v1_api = get_core_api()
+    try:
+        core_v1_api.create_namespaced_replication_controller(namespace,
+                                                             template,
+                                                             **kwargs)
+    except ApiException:
+        LOG.exception('Create replication controller failed')
+        raise
+
+
+def delete_replication_controller(name,
+                                  namespace='default',
+                                  wait=False,
+                                  **kwargs):    # pragma: no cover
+
+    core_v1_api = get_core_api()
+    body = kwargs.get('body', client.V1DeleteOptions())
+    kwargs.pop('body', None)
+    try:
+        core_v1_api.delete_namespaced_replication_controller(name,
+                                                             namespace,
+                                                             body,
+                                                             **kwargs)
+    except ApiException:
+        LOG.exception('Delete replication controller failed')
+        raise
+
+
+def delete_pod(name,
+               namespace='default',
+               wait=False,
+               **kwargs):    # pragma: no cover
+
+    core_v1_api = get_core_api()
+    body = kwargs.get('body', client.V1DeleteOptions())
+    kwargs.pop('body', None)
+    try:
+        core_v1_api.delete_namespaced_pod(name,
+                                          namespace,
+                                          body,
+                                          **kwargs)
+    except ApiException:
+        LOG.exception('Delete pod failed')
+        raise
+
+
+def read_pod(name,
+             namespace='default',
+             **kwargs):  # pragma: no cover
+    core_v1_api = get_core_api()
+    try:
+        resp = core_v1_api.read_namespaced_pod(name, namespace, **kwargs)
+    except ApiException:
+        LOG.exception('Read pod failed')
+        raise
+    else:
+        return resp
+
+
+def read_pod_status(name, namespace='default', **kwargs):   # pragma: no cover
+    return read_pod(name).status.phase
+
+
+def create_config_map(name,
+                      data,
+                      namespace='default',
+                      wait=False,
+                      **kwargs):   # pragma: no cover
+    core_v1_api = get_core_api()
+    metadata = client.V1ObjectMeta(name=name)
+    body = client.V1ConfigMap(data=data, metadata=metadata)
+    try:
+        core_v1_api.create_namespaced_config_map(namespace, body, **kwargs)
+    except ApiException:
+        LOG.exception('Create config map failed')
+        raise
+
+
+def delete_config_map(name,
+                      namespace='default',
+                      wait=False,
+                      **kwargs):     # pragma: no cover
+    core_v1_api = get_core_api()
+    body = kwargs.get('body', client.V1DeleteOptions())
+    kwargs.pop('body', None)
+    try:
+        core_v1_api.delete_namespaced_config_map(name,
+                                                 namespace,
+                                                 body,
+                                                 **kwargs)
+    except ApiException:
+        LOG.exception('Delete config map failed')
+        raise
+
+
+def get_pod_list(namespace='default'):      # pragma: no cover
+    core_v1_api = get_core_api()
+    try:
+        return core_v1_api.list_namespaced_pod(namespace=namespace)
+    except ApiException:
+        LOG.exception('Get pod list failed')
+        raise
index 0c0bac9..a4f7b30 100644 (file)
@@ -124,6 +124,14 @@ def makedirs(d):
             raise
 
 
+def remove_file(path):
+    try:
+        os.remove(path)
+    except OSError as e:
+        if e.errno != errno.ENOENT:
+            raise
+
+
 def execute_command(cmd):
     exec_msg = "Executing command: '%s'" % cmd
     logger.debug(exec_msg)
@@ -242,3 +250,16 @@ def change_obj_to_dict(obj):
         except TypeError:
             dic.update({k: v})
     return dic
+
+
+def set_dict_value(dic, keys, value):
+    return_dic = dic
+
+    for key in keys.split('.'):
+
+        return_dic.setdefault(key, {})
+        if key == keys.split('.')[-1]:
+            return_dic[key] = value
+        else:
+            return_dic = return_dic[key]
+    return dic
diff --git a/yardstick/orchestrator/kubernetes.py b/yardstick/orchestrator/kubernetes.py
new file mode 100644 (file)
index 0000000..6d7045f
--- /dev/null
@@ -0,0 +1,130 @@
+##############################################################################
+# Copyright (c) 2017 Huawei Technologies Co.,Ltd.
+#
+# 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
+##############################################################################
+
+from __future__ import absolute_import
+from __future__ import print_function
+
+from yardstick.common import utils
+from yardstick.common import kubernetes_utils as k8s_utils
+
+
+class KubernetesObject(object):
+
+    def __init__(self, name, **kwargs):
+        super(KubernetesObject, self).__init__()
+        self.name = name
+        self.image = kwargs.get('image', 'openretriever/yardstick')
+        self.command = [kwargs.get('command', '/bin/bash')]
+        self.args = kwargs.get('args', [])
+        self.ssh_key = kwargs.get('ssh_key', 'yardstick_key')
+
+        self.volumes = []
+
+        self.template = {
+            "apiVersion": "v1",
+            "kind": "ReplicationController",
+            "metadata": {
+                "name": ""
+            },
+            "spec": {
+                "replicas": 1,
+                "template": {
+                    "metadata": {
+                        "labels": {
+                            "app": ""
+                        }
+                    },
+                    "spec": {
+                        "containers": [],
+                        "volumes": []
+                    }
+                }
+            }
+        }
+
+        self._change_value_according_name(name)
+        self._add_containers()
+        self._add_ssh_key_volume()
+        self._add_volumes()
+
+    def get_template(self):
+        return self.template
+
+    def _change_value_according_name(self, name):
+        utils.set_dict_value(self.template, 'metadata.name', name)
+
+        utils.set_dict_value(self.template,
+                             'spec.template.metadata.labels.app',
+                             name)
+
+    def _add_containers(self):
+        containers = [self._add_container()]
+        utils.set_dict_value(self.template,
+                             'spec.template.spec.containers',
+                             containers)
+
+    def _add_container(self):
+        container_name = '{}-container'.format(self.name)
+        ssh_key_mount_path = "/root/.ssh/"
+
+        container = {
+            "args": self.args,
+            "command": self.command,
+            "image": self.image,
+            "name": container_name,
+            "volumeMounts": [
+                {
+                    "mountPath": ssh_key_mount_path,
+                    "name": self.ssh_key
+                }
+            ]
+        }
+
+        return container
+
+    def _add_volumes(self):
+        utils.set_dict_value(self.template,
+                             'spec.template.spec.volumes',
+                             self.volumes)
+
+    def _add_volume(self, volume):
+        self.volumes.append(volume)
+
+    def _add_ssh_key_volume(self):
+        key_volume = {
+            "configMap": {
+                "name": self.ssh_key
+            },
+            "name": self.ssh_key
+        }
+        self._add_volume(key_volume)
+
+
+class KubernetesTemplate(object):
+
+    def __init__(self, name, template_cfg):
+        self.name = name
+        self.ssh_key = '{}-key'.format(name)
+
+        self.rcs = [self._get_rc_name(rc) for rc in template_cfg]
+        self.k8s_objs = [KubernetesObject(self._get_rc_name(rc),
+                                          ssh_key=self.ssh_key,
+                                          **cfg)
+                         for rc, cfg in template_cfg.items()]
+        self.pods = []
+
+    def _get_rc_name(self, rc_name):
+        return '{}-{}'.format(rc_name, self.name)
+
+    def get_rc_pods(self):
+        resp = k8s_utils.get_pod_list()
+        self.pods = [p.metadata.name for p in resp.items for s in self.rcs
+                     if p.metadata.name.startswith(s)]
+
+        return self.pods