aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>2018-05-08 16:46:13 +0100
committerRodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>2018-06-14 07:15:41 +0000
commit1042a7077bba049d51022b7f4914048afb521cb1 (patch)
treee68ee712c9d29a0a3e288d047f8c4e16742830ac
parentfa269e3789d19f1335ae9207817203c6ad58cf42 (diff)
Add new Kubernetes resource kind: "Network"
Add a new Kubernetes resource kind: "Network" [1] [2] Kubernetes network plugins (alpha definition): [3] Network definition example in Kubernetes: apiVersion: "kubernetes.com/v1" kind: Network metadata: name: flannel plugin: flannel args: '[ {delegate": {"isDefaultGateway": true}} ]' Proposed Kubernetes context network definition: context: networks: - name: flannel plugin: flannel args: (string) - name: sriov_upload plugin: sriov args: (string) [1]https://builders.intel.com/docs/networkbuilders/multiple-network-interfaces-in-kubernetes-application-note.pdf [2]http://cdn.opensourcecloud.cn/zt/k8s/01.pdf [3]https://kubernetes.io/docs/concepts/cluster-administration/network-plugins/ JIRA: YARDSTICK-1160 Change-Id: I71a49ac14e3d28ded91d2ed3cd9cc527e40303f7 Signed-off-by: Rodolfo Alonso Hernandez <rodolfo.alonso.hernandez@intel.com>
-rw-r--r--yardstick/benchmark/contexts/kubernetes.py12
-rw-r--r--yardstick/common/exceptions.py9
-rw-r--r--yardstick/common/kubernetes_utils.py47
-rw-r--r--yardstick/orchestrator/kubernetes.py99
-rw-r--r--yardstick/tests/unit/common/test_kubernetes_utils.py119
-rw-r--r--yardstick/tests/unit/orchestrator/test_kubernetes.py88
6 files changed, 370 insertions, 4 deletions
diff --git a/yardstick/benchmark/contexts/kubernetes.py b/yardstick/benchmark/contexts/kubernetes.py
index 8cc5dc994..ddfc530cb 100644
--- a/yardstick/benchmark/contexts/kubernetes.py
+++ b/yardstick/benchmark/contexts/kubernetes.py
@@ -49,6 +49,7 @@ class KubernetesContext(Context):
self._set_ssh_key()
self._create_crd()
+ self._create_networks()
LOG.info('Launch containers')
self._create_rcs()
self._create_services()
@@ -62,6 +63,7 @@ class KubernetesContext(Context):
self._delete_rcs()
self._delete_pods()
self._delete_services()
+ self._delete_networks()
self._delete_crd()
super(KubernetesContext, self).undeploy()
@@ -119,6 +121,16 @@ class KubernetesContext(Context):
for crd in self.template.crd:
crd.delete()
+ def _create_networks(self): # pragma: no cover
+ LOG.info('Create Network elements')
+ for net in self.template.network_objs:
+ net.create()
+
+ def _delete_networks(self): # pragma: no cover
+ LOG.info('Create Network elements')
+ for net in self.template.network_objs:
+ net.delete()
+
def _get_key_path(self):
task_id = self.name.split('-')[-1]
k = 'files/yardstick_key-{}'.format(task_id)
diff --git a/yardstick/common/exceptions.py b/yardstick/common/exceptions.py
index e370b92bf..2005bebd2 100644
--- a/yardstick/common/exceptions.py
+++ b/yardstick/common/exceptions.py
@@ -215,6 +215,15 @@ class KubernetesCRDObjectDefinitionError(YardstickException):
'parameters: %(missing_parameters)s')
+class KubernetesNetworkObjectDefinitionError(YardstickException):
+ message = ('Kubernetes Network object definition error, missing '
+ 'parameters: %(missing_parameters)s')
+
+
+class KubernetesNetworkObjectKindMissing(YardstickException):
+ message = 'Kubernetes kind "Network" is not defined'
+
+
class ScenarioCreateNetworkError(YardstickException):
message = 'Create Neutron Network Scenario failed'
diff --git a/yardstick/common/kubernetes_utils.py b/yardstick/common/kubernetes_utils.py
index a472b6de5..42267fc41 100644
--- a/yardstick/common/kubernetes_utils.py
+++ b/yardstick/common/kubernetes_utils.py
@@ -36,6 +36,14 @@ def get_extensions_v1beta_api():
return client.ApiextensionsV1beta1Api()
+def get_custom_objects_api():
+ try:
+ config.load_kube_config(config_file=consts.K8S_CONF_FILE)
+ except IOError:
+ raise exceptions.KubernetesConfigFileNotFound()
+ return client.CustomObjectsApi()
+
+
def get_node_list(**kwargs): # pragma: no cover
core_v1_api = get_core_api()
try:
@@ -220,6 +228,45 @@ def delete_custom_resource_definition(name):
action='delete', resource='CustomResourceDefinition')
+def get_custom_resource_definition(kind):
+ api = get_extensions_v1beta_api()
+ try:
+ crd_list = api.list_custom_resource_definition()
+ for crd_obj in (crd_obj for crd_obj in crd_list.items
+ if crd_obj.spec.names.kind == kind):
+ return crd_obj
+ return None
+ except ApiException:
+ raise exceptions.KubernetesApiException(
+ action='delete', resource='CustomResourceDefinition')
+
+
+def create_network(scope, group, version, plural, body, namespace='default'):
+ api = get_custom_objects_api()
+ try:
+ if scope == consts.SCOPE_CLUSTER:
+ api.create_cluster_custom_object(group, version, plural, body)
+ else:
+ api.create_namespaced_custom_object(
+ group, version, namespace, plural, body)
+ except ApiException:
+ raise exceptions.KubernetesApiException(
+ action='create', resource='Custom Object: Network')
+
+
+def delete_network(scope, group, version, plural, name, namespace='default'):
+ api = get_custom_objects_api()
+ try:
+ if scope == consts.SCOPE_CLUSTER:
+ api.delete_cluster_custom_object(group, version, plural, name, {})
+ else:
+ api.delete_namespaced_custom_object(
+ group, version, namespace, plural, name, {})
+ except ApiException:
+ raise exceptions.KubernetesApiException(
+ action='delete', resource='Custom Object: Network')
+
+
def get_pod_list(namespace='default'): # pragma: no cover
core_v1_api = get_core_api()
try:
diff --git a/yardstick/orchestrator/kubernetes.py b/yardstick/orchestrator/kubernetes.py
index 637abd813..44a333e4b 100644
--- a/yardstick/orchestrator/kubernetes.py
+++ b/yardstick/orchestrator/kubernetes.py
@@ -91,9 +91,7 @@ class KubernetesObject(object):
"replicas": 1,
"template": {
"metadata": {
- "labels": {
- "app": name
- }
+ "labels": {"app": name}
},
"spec": {
"containers": [],
@@ -237,6 +235,98 @@ class CustomResourceDefinitionObject(object):
k8s_utils.delete_custom_resource_definition(self._name)
+class NetworkObject(object):
+
+ MANDATORY_PARAMETERS = {'name', 'plugin', 'args'}
+ KIND = 'Network'
+
+ def __init__(self, **kwargs):
+ if not self.MANDATORY_PARAMETERS.issubset(kwargs):
+ missing_parameters = ', '.join(
+ str(param) for param in
+ (self.MANDATORY_PARAMETERS - set(kwargs)))
+ raise exceptions.KubernetesNetworkObjectDefinitionError(
+ missing_parameters=missing_parameters)
+
+ self._name = kwargs['name']
+ self._plugin = kwargs['plugin']
+ self._args = kwargs['args']
+ self._crd = None
+ self._template = None
+ self._group = None
+ self._version = None
+ self._plural = None
+ self._scope = None
+
+ @property
+ def crd(self):
+ if self._crd:
+ return self._crd
+ crd = k8s_utils.get_custom_resource_definition(self.KIND)
+ if not crd:
+ raise exceptions.KubernetesNetworkObjectKindMissing()
+ self._crd = crd
+ return self._crd
+
+ @property
+ def group(self):
+ if self._group:
+ return self._group
+ self._group = self.crd.spec.group
+ return self._group
+
+ @property
+ def version(self):
+ if self._version:
+ return self._version
+ self._version = self.crd.spec.version
+ return self._version
+
+ @property
+ def plural(self):
+ if self._plural:
+ return self._plural
+ self._plural = self.crd.spec.names.plural
+ return self._plural
+
+ @property
+ def scope(self):
+ if self._scope:
+ return self._scope
+ self._scope = self.crd.spec.scope
+ return self._scope
+
+ @property
+ def template(self):
+ """"Network" object template
+
+ This template can be rendered only once the CRD "Network" is created in
+ Kubernetes. This function call must be delayed until the creation of
+ the CRD "Network".
+ """
+ if self._template:
+ return self._template
+
+ self._template = {
+ 'apiVersion': '{}/{}'.format(self.group, self.version),
+ 'kind': self.KIND,
+ 'metadata': {
+ 'name': self._name
+ },
+ 'plugin': self._plugin,
+ 'args': self._args
+ }
+ return self._template
+
+ def create(self):
+ k8s_utils.create_network(self.scope, self.group, self.version,
+ self.plural, self.template)
+
+ def delete(self):
+ k8s_utils.delete_network(self.scope, self.group, self.version,
+ self.plural, self._name)
+
+
class KubernetesTemplate(object):
def __init__(self, name, context_cfg):
@@ -248,6 +338,7 @@ class KubernetesTemplate(object):
context_cfg = copy.deepcopy(context_cfg)
servers_cfg = context_cfg.pop('servers', {})
crd_cfg = context_cfg.pop('custom_resources', [])
+ networks_cfg = context_cfg.pop('networks', [])
self.name = name
self.ssh_key = '{}-key'.format(name)
@@ -259,7 +350,7 @@ class KubernetesTemplate(object):
self.service_objs = [ServiceObject(s) for s in self.rcs]
self.crd = [CustomResourceDefinitionObject(self.name, **crd)
for crd in crd_cfg]
-
+ self.network_objs = [NetworkObject(**nobj) for nobj in networks_cfg]
self.pods = []
def _get_rc_name(self, rc_name):
diff --git a/yardstick/tests/unit/common/test_kubernetes_utils.py b/yardstick/tests/unit/common/test_kubernetes_utils.py
index e264fd94b..bf9992b57 100644
--- a/yardstick/tests/unit/common/test_kubernetes_utils.py
+++ b/yardstick/tests/unit/common/test_kubernetes_utils.py
@@ -40,6 +40,23 @@ class GetExtensionsV1betaApiTestCase(base.BaseUnitTestCase):
kubernetes_utils.get_extensions_v1beta_api()
+class GetCustomObjectsApiTestCase(base.BaseUnitTestCase):
+
+ @mock.patch.object(client, 'CustomObjectsApi', return_value='api')
+ @mock.patch.object(config, 'load_kube_config')
+ def test_execute_correct(self, mock_load_kube_config, mock_api):
+ self.assertEqual('api', kubernetes_utils.get_custom_objects_api())
+ mock_load_kube_config.assert_called_once_with(
+ config_file=constants.K8S_CONF_FILE)
+ mock_api.assert_called_once()
+
+ @mock.patch.object(config, 'load_kube_config')
+ def test_execute_exception(self, mock_load_kube_config):
+ mock_load_kube_config.side_effect = IOError
+ with self.assertRaises(exceptions.KubernetesConfigFileNotFound):
+ kubernetes_utils.get_custom_objects_api()
+
+
class CreateCustomResourceDefinitionTestCase(base.BaseUnitTestCase):
@mock.patch.object(client, 'V1beta1CustomResourceDefinition',
@@ -103,3 +120,105 @@ class DeleteCustomResourceDefinitionTestCase(base.BaseUnitTestCase):
mock_delobj.assert_called_once()
mock_delete_crd.delete_custom_resource_definition.\
assert_called_once_with('name', 'del_obj')
+
+
+class GetCustomResourceDefinitionTestCase(base.BaseUnitTestCase):
+
+ @mock.patch.object(kubernetes_utils, 'get_extensions_v1beta_api')
+ def test_execute_value(self, mock_get_api):
+ crd_obj = mock.Mock()
+ crd_obj.spec.names.kind = 'some_kind'
+ crd_list = mock.Mock()
+ crd_list.items = [crd_obj]
+ mock_api = mock.Mock()
+ mock_api.list_custom_resource_definition.return_value = crd_list
+ mock_get_api.return_value = mock_api
+ self.assertEqual(
+ crd_obj,
+ kubernetes_utils.get_custom_resource_definition('some_kind'))
+
+ @mock.patch.object(kubernetes_utils, 'get_extensions_v1beta_api')
+ def test_execute_none(self, mock_get_api):
+ crd_obj = mock.Mock()
+ crd_obj.spec.names.kind = 'some_kind'
+ crd_list = mock.Mock()
+ crd_list.items = [crd_obj]
+ mock_api = mock.Mock()
+ mock_api.list_custom_resource_definition.return_value = crd_list
+ mock_get_api.return_value = mock_api
+ self.assertIsNone(
+ kubernetes_utils.get_custom_resource_definition('other_kind'))
+
+ @mock.patch.object(kubernetes_utils, 'get_extensions_v1beta_api')
+ def test_execute_exception(self, mock_get_api):
+ mock_api = mock.Mock()
+ mock_api.list_custom_resource_definition.\
+ side_effect = rest.ApiException
+ mock_get_api.return_value = mock_api
+ with self.assertRaises(exceptions.KubernetesApiException):
+ kubernetes_utils.get_custom_resource_definition('kind')
+
+
+class CreateNetworkTestCase(base.BaseUnitTestCase):
+ @mock.patch.object(kubernetes_utils, 'get_custom_objects_api')
+ def test_execute_correct(self, mock_get_api):
+ mock_api = mock.Mock()
+ mock_get_api.return_value = mock_api
+ group = 'group.com'
+ version = mock.Mock()
+ plural = 'networks'
+ body = mock.Mock()
+
+ kubernetes_utils.create_network(
+ constants.SCOPE_CLUSTER, group, version, plural, body)
+ mock_api.create_cluster_custom_object.assert_called_once_with(
+ group, version, plural, body)
+
+ mock_api.reset_mock()
+ kubernetes_utils.create_network(
+ constants.SCOPE_NAMESPACED, group, version, plural, body)
+ mock_api.create_namespaced_custom_object.assert_called_once_with(
+ group, version, 'default', plural, body)
+
+
+ @mock.patch.object(kubernetes_utils, 'get_custom_objects_api')
+ def test_execute_exception(self, mock_get_api):
+ mock_api = mock.Mock()
+ mock_api.create_cluster_custom_object.side_effect = rest.ApiException
+ mock_get_api.return_value = mock_api
+ with self.assertRaises(exceptions.KubernetesApiException):
+ kubernetes_utils.create_network(
+ constants.SCOPE_CLUSTER, mock.ANY, mock.ANY, mock.ANY,
+ mock.ANY)
+
+
+class DeleteNetworkTestCase(base.BaseUnitTestCase):
+ @mock.patch.object(kubernetes_utils, 'get_custom_objects_api')
+ def test_execute_correct(self, mock_get_api):
+ mock_api = mock.Mock()
+ mock_get_api.return_value = mock_api
+ group = 'group.com'
+ version = mock.Mock()
+ plural = 'networks'
+ name = 'network'
+
+ kubernetes_utils.delete_network(
+ constants.SCOPE_CLUSTER, group, version, plural, name)
+ mock_api.delete_cluster_custom_object.assert_called_once_with(
+ group, version, plural, name, {})
+
+ mock_api.reset_mock()
+ kubernetes_utils.delete_network(
+ constants.SCOPE_NAMESPACED, group, version, plural, name)
+ mock_api.delete_namespaced_custom_object.assert_called_once_with(
+ group, version, 'default', plural, name, {})
+
+ @mock.patch.object(kubernetes_utils, 'get_custom_objects_api')
+ def test_execute_exception(self, mock_get_api):
+ mock_api = mock.Mock()
+ mock_api.delete_cluster_custom_object.side_effect = rest.ApiException
+ mock_get_api.return_value = mock_api
+ with self.assertRaises(exceptions.KubernetesApiException):
+ kubernetes_utils.delete_network(
+ constants.SCOPE_CLUSTER, mock.ANY, mock.ANY, mock.ANY,
+ mock.ANY)
diff --git a/yardstick/tests/unit/orchestrator/test_kubernetes.py b/yardstick/tests/unit/orchestrator/test_kubernetes.py
index 50c6b2773..e45545d6a 100644
--- a/yardstick/tests/unit/orchestrator/test_kubernetes.py
+++ b/yardstick/tests/unit/orchestrator/test_kubernetes.py
@@ -297,3 +297,91 @@ class CustomResourceDefinitionObjectTestCase(base.BaseUnitTestCase):
with self.assertRaises(exceptions.KubernetesCRDObjectDefinitionError):
kubernetes.CustomResourceDefinitionObject('ctx_name',
noname='name')
+
+
+class NetworkObjectTestCase(base.BaseUnitTestCase):
+
+ def setUp(self):
+ self.net_obj = kubernetes.NetworkObject(name='fake_name',
+ plugin='fake_plugin',
+ args='fake_args')
+
+ def test__init_missing_parameter(self):
+ with self.assertRaises(
+ exceptions.KubernetesNetworkObjectDefinitionError):
+ kubernetes.NetworkObject(name='name', plugin='plugin')
+ with self.assertRaises(
+ exceptions.KubernetesNetworkObjectDefinitionError):
+ kubernetes.NetworkObject(name='name', args='args')
+ with self.assertRaises(
+ exceptions.KubernetesNetworkObjectDefinitionError):
+ kubernetes.NetworkObject(args='args', plugin='plugin')
+
+ @mock.patch.object(kubernetes_utils, 'get_custom_resource_definition')
+ def test_crd(self, mock_get_crd):
+ mock_crd = mock.Mock()
+ mock_get_crd.return_value = mock_crd
+ net_obj = copy.deepcopy(self.net_obj)
+ self.assertEqual(mock_crd, net_obj.crd)
+
+ def test_template(self):
+ net_obj = copy.deepcopy(self.net_obj)
+ expected = {'apiVersion': 'group.com/v2',
+ 'kind': kubernetes.NetworkObject.KIND,
+ 'metadata': {
+ 'name': 'fake_name'},
+ 'plugin': 'fake_plugin',
+ 'args': 'fake_args'}
+ crd = mock.Mock()
+ crd.spec.group = 'group.com'
+ crd.spec.version = 'v2'
+ net_obj._crd = crd
+ self.assertEqual(expected, net_obj.template)
+
+ def test_group(self):
+ net_obj = copy.deepcopy(self.net_obj)
+ net_obj._crd = mock.Mock()
+ net_obj._crd.spec.group = 'fake_group'
+ self.assertEqual('fake_group', net_obj.group)
+
+ def test_version(self):
+ net_obj = copy.deepcopy(self.net_obj)
+ net_obj._crd = mock.Mock()
+ net_obj._crd.spec.version = 'version_4'
+ self.assertEqual('version_4', net_obj.version)
+
+ def test_plural(self):
+ net_obj = copy.deepcopy(self.net_obj)
+ net_obj._crd = mock.Mock()
+ net_obj._crd.spec.names.plural = 'name_ending_in_s'
+ self.assertEqual('name_ending_in_s', net_obj.plural)
+
+ def test_scope(self):
+ net_obj = copy.deepcopy(self.net_obj)
+ net_obj._crd = mock.Mock()
+ net_obj._crd.spec.scope = 'Cluster'
+ self.assertEqual('Cluster', net_obj.scope)
+
+ @mock.patch.object(kubernetes_utils, 'create_network')
+ def test_create(self, mock_create_network):
+ net_obj = copy.deepcopy(self.net_obj)
+ net_obj._scope = 'scope'
+ net_obj._group = 'group'
+ net_obj._version = 'version'
+ net_obj._plural = 'plural'
+ net_obj._template = 'template'
+ net_obj.create()
+ mock_create_network.assert_called_once_with(
+ 'scope', 'group', 'version', 'plural', 'template')
+
+ @mock.patch.object(kubernetes_utils, 'delete_network')
+ def test_delete(self, mock_delete_network):
+ net_obj = copy.deepcopy(self.net_obj)
+ net_obj._scope = 'scope'
+ net_obj._group = 'group'
+ net_obj._version = 'version'
+ net_obj._plural = 'plural'
+ net_obj._name = 'name'
+ net_obj.delete()
+ mock_delete_network.assert_called_once_with(
+ 'scope', 'group', 'version', 'plural', 'name')