From 2429ef152b5503d939022fbfd145b88a1df5c23b Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Wed, 11 Jul 2018 17:32:44 +0100 Subject: Add interface and network information to Kubernetes context Add to "Kubernetes" context the "interfaces" information when retrieving a server. This information is needed for NSPerf test cases. The interface information comes from the resource controller network list. Each replication controller will have one port per network defined. JIRA: YARDSTICK-1303 Change-Id: Ifb0e17df295c042a643128c705a93876af999bad Signed-off-by: Rodolfo Alonso Hernandez --- yardstick/benchmark/contexts/kubernetes.py | 50 +++++++++++--- yardstick/orchestrator/kubernetes.py | 11 ++- .../unit/benchmark/contexts/test_kubernetes.py | 78 ++++++++++++++++++---- .../tests/unit/orchestrator/test_kubernetes.py | 27 ++++++++ 4 files changed, 143 insertions(+), 23 deletions(-) diff --git a/yardstick/benchmark/contexts/kubernetes.py b/yardstick/benchmark/contexts/kubernetes.py index a6b3ebad8..4ce7cbc55 100644 --- a/yardstick/benchmark/contexts/kubernetes.py +++ b/yardstick/benchmark/contexts/kubernetes.py @@ -7,25 +7,28 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## +import collections import logging -import time import pkg_resources +import time import paramiko from yardstick.benchmark import contexts -from yardstick.benchmark.contexts.base import Context -from yardstick.orchestrator import kubernetes +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.benchmark.contexts import model from yardstick.common import constants from yardstick.common import exceptions from yardstick.common import kubernetes_utils as k8s_utils from yardstick.common import utils +from yardstick.orchestrator import kubernetes + LOG = logging.getLogger(__name__) BITS_LENGTH = 2048 -class KubernetesContext(Context): +class KubernetesContext(ctx_base.Context): """Class that handle nodes info""" __context_type__ = contexts.CONTEXT_KUBERNETES @@ -40,10 +43,14 @@ class KubernetesContext(Context): def init(self, attrs): super(KubernetesContext, self).init(attrs) + networks = attrs.get('networks', {}) self.template = kubernetes.KubernetesTemplate(self.name, attrs) self.ssh_key = '{}-key'.format(self.name) self.key_path = self._get_key_path() self.public_key_path = '{}.pub'.format(self.key_path) + self._networks = collections.OrderedDict( + (net_name, model.Network(net_name, self, network)) + for net_name, network in networks.items()) def deploy(self): LOG.info('Creating ssh key') @@ -92,7 +99,7 @@ class KubernetesContext(Context): obj.delete() def _create_rcs(self): - for obj in self.template.k8s_objs: + for obj in self.template.rc_objs: self._create_rc(obj.get_template()) def _create_rc(self, template): @@ -175,15 +182,40 @@ class KubernetesContext(Context): 'private_ip': k8s_utils.get_pod_by_name(name).status.pod_ip, 'ssh_port': node_port, 'user': 'root', - 'key_filename': self.key_path + 'key_filename': self.key_path, + 'interfaces': self._get_interfaces(name) } + def _get_network(self, net_name): + """Retrieves the network object, searching by name + + :param net_name: (str) replication controller name + :return: (dict) network information (name) + """ + network = self._networks.get(net_name) + if not network: + return + return {'name': net_name} + + def _get_interfaces(self, rc_name): + """Retrieves the network list of a replication controller + + :param rc_name: (str) replication controller name + :return: (dict) names and information of the networks used in this + replication controller; those networks must be defined in the + Kubernetes cluster + """ + rc = self.template.get_rc_by_name(rc_name) + if not rc: + return {} + return {name: {'network_name': name, + 'local_mac': None, + 'local_ip': None} + for name in rc.networks} + def _get_node_ip(self): return k8s_utils.get_node_list().items[0].status.addresses[0].address - def _get_network(self, attr_name): - return None - def _get_physical_nodes(self): return None diff --git a/yardstick/orchestrator/kubernetes.py b/yardstick/orchestrator/kubernetes.py index bb01b33fa..98832908c 100644 --- a/yardstick/orchestrator/kubernetes.py +++ b/yardstick/orchestrator/kubernetes.py @@ -146,6 +146,10 @@ class ReplicationControllerObject(object): self._add_networks() self._add_tolerations() + @property + def networks(self): + return self._networks + def get_template(self): return self.template @@ -423,7 +427,7 @@ class KubernetesTemplate(object): self.rcs = {self._get_rc_name(rc): cfg for rc, cfg in servers_cfg.items()} - self.k8s_objs = [ReplicationControllerObject( + self.rc_objs = [ReplicationControllerObject( rc, ssh_key=self.ssh_key, **cfg) for rc, cfg in self.rcs.items()] self.service_objs = [ServiceNodePortObject(rc, **cfg) for rc, cfg in self.rcs.items()] @@ -442,3 +446,8 @@ class KubernetesTemplate(object): if p.metadata.name.startswith(s)] return self.pods + + def get_rc_by_name(self, rc_name): + """Returns a ``ReplicationControllerObject``, searching by name""" + for rc in (rc for rc in self.rc_objs if rc.name == rc_name): + return rc diff --git a/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py b/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py index b070b24a9..cd5c80f47 100644 --- a/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py +++ b/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py @@ -7,6 +7,9 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## +import collections +import time + import mock import unittest @@ -34,6 +37,16 @@ CONTEXT_CFG = { 'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; ' 'service ssh restart;while true ; do sleep 10000; done'] } + }, + 'networks': { + 'flannel': { + 'args': 'flannel_args', + 'plugin': 'flannel' + }, + 'sriov01': { + 'args': 'sriov_args', + 'plugin': 'sriov' + }, } } @@ -57,17 +70,19 @@ class KubernetesTestCase(unittest.TestCase): @mock.patch.object(kubernetes.KubernetesContext, '_delete_ssh_key') @mock.patch.object(kubernetes.KubernetesContext, '_delete_rcs') @mock.patch.object(kubernetes.KubernetesContext, '_delete_pods') - def test_undeploy(self, - mock_delete_pods, - mock_delete_rcs, - mock_delete_ssh, - mock_delete_services): + @mock.patch.object(kubernetes.KubernetesContext, '_delete_networks') + @mock.patch.object(kubernetes.KubernetesContext, '_delete_crd') + def test_undeploy(self, mock_delete_pods, mock_delete_rcs, + mock_delete_ssh, mock_delete_services, + mock_delete_networks, mock_delete_crd): self.k8s_context.undeploy() mock_delete_ssh.assert_called_once() mock_delete_rcs.assert_called_once() mock_delete_pods.assert_called_once() mock_delete_services.assert_called_once() + mock_delete_networks.assert_called_once() + mock_delete_crd.assert_called_once() @mock.patch.object(kubernetes.KubernetesContext, '_create_services') @mock.patch.object(kubernetes.KubernetesContext, '_wait_until_running') @@ -75,20 +90,21 @@ class KubernetesTestCase(unittest.TestCase): 'get_rc_pods') @mock.patch.object(kubernetes.KubernetesContext, '_create_rcs') @mock.patch.object(kubernetes.KubernetesContext, '_set_ssh_key') - def test_deploy(self, - mock_set_ssh_key, - mock_create_rcs, - mock_get_rc_pods, - mock_wait_until_running, - mock_create_services): - - with mock.patch("yardstick.benchmark.contexts.kubernetes.time"): + @mock.patch.object(kubernetes.KubernetesContext, '_create_networks') + @mock.patch.object(kubernetes.KubernetesContext, '_create_crd') + def test_deploy(self, mock_set_ssh_key, mock_create_rcs, mock_get_rc_pods, + mock_wait_until_running, mock_create_services, + mock_create_networks, mock_create_crd): + + with mock.patch.object(time, 'sleep'): self.k8s_context.deploy() mock_set_ssh_key.assert_called_once() mock_create_rcs.assert_called_once() mock_create_services.assert_called_once() mock_get_rc_pods.assert_called_once() mock_wait_until_running.assert_called_once() + mock_create_networks.assert_called_once() + mock_create_crd.assert_called_once() @mock.patch.object(kubernetes, 'paramiko', **{"resource_filename.return_value": ""}) @mock.patch.object(kubernetes, 'pkg_resources', **{"resource_filename.return_value": ""}) @@ -185,6 +201,9 @@ class KubernetesTestCase(unittest.TestCase): mock_k8stemplate.assert_called_once_with(self.k8s_context.name, CONTEXT_CFG) self.assertEqual('fake_template', self.k8s_context.template) + self.assertEqual(2, len(self.k8s_context._networks)) + self.assertIn('flannel', self.k8s_context._networks.keys()) + self.assertIn('sriov01', self.k8s_context._networks.keys()) def test__get_physical_nodes(self): result = self.k8s_context._get_physical_nodes() @@ -193,3 +212,36 @@ class KubernetesTestCase(unittest.TestCase): def test__get_physical_node_for_server(self): result = self.k8s_context._get_physical_node_for_server("fake") self.assertIsNone(result) + + def test__get_network(self): + networks = collections.OrderedDict([('n1', 'data1'), ('n2', 'data2')]) + self.k8s_context._networks = networks + self.assertEqual({'name': 'n1'}, self.k8s_context._get_network('n1')) + self.assertEqual({'name': 'n2'}, self.k8s_context._get_network('n2')) + self.assertIsNone(self.k8s_context._get_network('n3')) + + @mock.patch.object(orchestrator_kubernetes.KubernetesTemplate, + 'get_rc_by_name') + def test__get_interfaces(self, mock_get_rc): + rc = orchestrator_kubernetes.ReplicationControllerObject('rc_name') + rc._networks = ['net1', 'net2'] + mock_get_rc.return_value = rc + expected = {'net1': {'network_name': 'net1', + 'local_mac': None, + 'local_ip': None}, + 'net2': {'network_name': 'net2', + 'local_mac': None, + 'local_ip': None}} + self.assertEqual(expected, self.k8s_context._get_interfaces('rc_name')) + + @mock.patch.object(orchestrator_kubernetes.KubernetesTemplate, + 'get_rc_by_name') + def test__get_interfaces_no_networks(self, mock_get_rc): + rc = orchestrator_kubernetes.ReplicationControllerObject('rc_name') + mock_get_rc.return_value = rc + self.assertEqual({}, self.k8s_context._get_interfaces('rc_name')) + + @mock.patch.object(orchestrator_kubernetes.KubernetesTemplate, + 'get_rc_by_name', return_value=None) + def test__get_interfaces_no_rc(self, *args): + self.assertEqual({}, self.k8s_context._get_interfaces('rc_name')) diff --git a/yardstick/tests/unit/orchestrator/test_kubernetes.py b/yardstick/tests/unit/orchestrator/test_kubernetes.py index f248338ee..8d351e419 100644 --- a/yardstick/tests/unit/orchestrator/test_kubernetes.py +++ b/yardstick/tests/unit/orchestrator/test_kubernetes.py @@ -546,3 +546,30 @@ class ServiceNodePortObjectTestCase(base.BaseUnitTestCase): nodeport_object = kubernetes.ServiceNodePortObject('fake_name') nodeport_object.delete() mock_delete_service.assert_called_once_with('fake_name-service') + + +class KubernetesTemplate(base.BaseUnitTestCase): + + def test_get_rc_by_name(self): + ctx_cfg = { + 'servers': { + 'host1': {'args': 'some data'} + } + } + k_template = kubernetes.KubernetesTemplate('k8s_name', ctx_cfg) + rc = k_template.get_rc_by_name('host1-k8s_name') + self.assertTrue(isinstance(rc, kubernetes.ReplicationControllerObject)) + + def test_get_rc_by_name_wrong_name(self): + ctx_cfg = { + 'servers': { + 'host1': {'args': 'some data'} + } + } + k_template = kubernetes.KubernetesTemplate('k8s_name', ctx_cfg) + self.assertIsNone(k_template.get_rc_by_name('wrong_host_name')) + + def test_get_rc_by_name_no_rcs(self): + ctx_cfg = {'servers': {}} + k_template = kubernetes.KubernetesTemplate('k8s_name', ctx_cfg) + self.assertIsNone(k_template.get_rc_by_name('any_host_name')) -- cgit 1.2.3-korg