Add interface and network information to Kubernetes context 37/59737/4
authorRodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
Wed, 11 Jul 2018 16:32:44 +0000 (17:32 +0100)
committerRodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
Fri, 13 Jul 2018 16:30:51 +0000 (17:30 +0100)
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 <rodolfo.alonso.hernandez@intel.com>
yardstick/benchmark/contexts/kubernetes.py
yardstick/orchestrator/kubernetes.py
yardstick/tests/unit/benchmark/contexts/test_kubernetes.py
yardstick/tests/unit/orchestrator/test_kubernetes.py

index a6b3eba..4ce7cbc 100644 (file)
@@ -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
 
index bb01b33..9883290 100644 (file)
@@ -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
index b070b24..cd5c80f 100644 (file)
@@ -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'))
index f248338..8d351e4 100644 (file)
@@ -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'))