1 ##############################################################################
2 # Copyright (c) 2017 Huawei Technologies Co.,Ltd.
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
12 from oslo_serialization import jsonutils
14 from yardstick.common import constants
15 from yardstick.common import exceptions
16 from yardstick.common import kubernetes_utils as k8s_utils
17 from yardstick.common import utils
20 class ContainerObject(object):
22 SSH_MOUNT_PATH = '/tmp/.ssh/'
23 IMAGE_DEFAULT = 'openretriever/yardstick'
24 COMMAND_DEFAULT = '/bin/bash'
26 def __init__(self, name, ssh_key, **kwargs):
28 self._ssh_key = ssh_key
29 self._image = kwargs.get('image', self.IMAGE_DEFAULT)
30 self._command = [kwargs.get('command', self.COMMAND_DEFAULT)]
31 self._args = kwargs.get('args', [])
32 self._volume_mounts = kwargs.get('volumeMounts', [])
33 self._security_context = kwargs.get('securityContext')
35 def _create_volume_mounts(self):
36 """Return all "volumeMounts" items per container"""
37 volume_mounts_items = [self._create_volume_mounts_item(vol)
38 for vol in self._volume_mounts]
39 ssh_vol = {'name': self._ssh_key,
40 'mountPath': self.SSH_MOUNT_PATH}
41 volume_mounts_items.append(self._create_volume_mounts_item(ssh_vol))
42 return volume_mounts_items
45 def _create_volume_mounts_item(volume_mount):
46 """Create a "volumeMounts" item"""
47 return {'name': volume_mount['name'],
48 'mountPath': volume_mount['mountPath'],
49 'readOnly': volume_mount.get('readOnly', False)}
51 def get_container_item(self):
52 """Create a "container" item"""
53 container_name = '{}-container'.format(self._name)
54 container = {'args': self._args,
55 'command': self._command,
57 'name': container_name,
58 'volumeMounts': self._create_volume_mounts()}
59 if self._security_context:
60 container['securityContext'] = self._security_context
64 class KubernetesObject(object):
66 SSHKEY_DEFAULT = 'yardstick_key'
68 def __init__(self, name, **kwargs):
69 super(KubernetesObject, self).__init__()
70 parameters = copy.deepcopy(kwargs)
72 self.node_selector = parameters.pop('nodeSelector', {})
73 self.ssh_key = parameters.pop('ssh_key', self.SSHKEY_DEFAULT)
74 self._volumes = parameters.pop('volumes', [])
75 self._security_context = parameters.pop('securityContext', None)
76 self._networks = parameters.pop('networks', [])
78 containers = parameters.pop('containers', None)
81 ContainerObject(self.name, self.ssh_key, **container)
82 for container in containers]
85 ContainerObject(self.name, self.ssh_key, **parameters)]
89 "kind": "ReplicationController",
97 "labels": {"app": name}
108 self._change_value_according_name(name)
109 self._add_containers()
110 self._add_node_selector()
112 self._add_security_context()
115 def get_template(self):
118 def _change_value_according_name(self, name):
119 utils.set_dict_value(self.template, 'metadata.name', name)
121 utils.set_dict_value(self.template,
122 'spec.template.metadata.labels.app',
125 def _add_containers(self):
126 containers = [container.get_container_item()
127 for container in self._containers]
128 utils.set_dict_value(self.template,
129 'spec.template.spec.containers',
132 def _add_node_selector(self):
133 utils.set_dict_value(self.template,
134 'spec.template.spec.nodeSelector',
137 def _add_volumes(self):
138 """Add "volume" items to container specs, including the SSH one"""
139 volume_items = [self._create_volume_item(vol) for vol in self._volumes]
140 volume_items.append(self._create_ssh_key_volume())
141 utils.set_dict_value(self.template,
142 'spec.template.spec.volumes',
145 def _create_ssh_key_volume(self):
146 """Create a "volume" item of type "configMap" for the SSH key"""
147 return {'name': self.ssh_key,
148 'configMap': {'name': self.ssh_key}}
151 def _create_volume_item(volume):
152 """Create a "volume" item"""
153 volume = copy.deepcopy(volume)
154 name = volume.pop('name')
155 for key in (k for k in volume if k in k8s_utils.get_volume_types()):
157 type_data = volume[key]
160 raise exceptions.KubernetesTemplateInvalidVolumeType(volume=volume)
162 return {'name': name,
163 type_name: type_data}
165 def _add_security_context(self):
166 if self._security_context:
167 utils.set_dict_value(self.template,
168 'spec.template.spec.securityContext',
169 self._security_context)
171 def _add_networks(self):
173 for net in self._networks:
174 networks.append({'name': net})
179 annotations = {'networks': jsonutils.dumps(networks)}
180 utils.set_dict_value(self.template,
181 'spec.template.metadata.annotations',
185 class ServiceObject(object):
187 def __init__(self, name):
188 self.name = '{}-service'.format(name)
191 'name': '{}-service'.format(name)
208 k8s_utils.create_service(self.template)
211 k8s_utils.delete_service(self.name)
214 class CustomResourceDefinitionObject(object):
216 MANDATORY_PARAMETERS = {'name'}
218 def __init__(self, ctx_name, **kwargs):
219 if not self.MANDATORY_PARAMETERS.issubset(kwargs):
220 missing_parameters = ', '.join(
221 str(param) for param in
222 (self.MANDATORY_PARAMETERS - set(kwargs)))
223 raise exceptions.KubernetesCRDObjectDefinitionError(
224 missing_parameters=missing_parameters)
226 singular = kwargs['name']
227 plural = singular + 's'
228 kind = singular.title()
229 version = kwargs.get('version', 'v1')
230 scope = kwargs.get('scope', constants.SCOPE_NAMESPACED)
231 group = ctx_name + '.com'
232 self._name = metadata_name = plural + '.' + group
236 'name': metadata_name
242 'names': {'plural': plural,
243 'singular': singular,
249 k8s_utils.create_custom_resource_definition(self._template)
252 k8s_utils.delete_custom_resource_definition(self._name)
255 class NetworkObject(object):
257 MANDATORY_PARAMETERS = {'name', 'plugin', 'args'}
260 def __init__(self, **kwargs):
261 if not self.MANDATORY_PARAMETERS.issubset(kwargs):
262 missing_parameters = ', '.join(
263 str(param) for param in
264 (self.MANDATORY_PARAMETERS - set(kwargs)))
265 raise exceptions.KubernetesNetworkObjectDefinitionError(
266 missing_parameters=missing_parameters)
268 self._name = kwargs['name']
269 self._plugin = kwargs['plugin']
270 self._args = kwargs['args']
272 self._template = None
282 crd = k8s_utils.get_custom_resource_definition(self.KIND)
284 raise exceptions.KubernetesNetworkObjectKindMissing()
292 self._group = self.crd.spec.group
299 self._version = self.crd.spec.version
306 self._plural = self.crd.spec.names.plural
313 self._scope = self.crd.spec.scope
318 """"Network" object template
320 This template can be rendered only once the CRD "Network" is created in
321 Kubernetes. This function call must be delayed until the creation of
325 return self._template
328 'apiVersion': '{}/{}'.format(self.group, self.version),
333 'plugin': self._plugin,
336 return self._template
339 k8s_utils.create_network(self.scope, self.group, self.version,
340 self.plural, self.template)
343 k8s_utils.delete_network(self.scope, self.group, self.version,
344 self.plural, self._name)
347 class KubernetesTemplate(object):
349 def __init__(self, name, context_cfg):
350 """KubernetesTemplate object initialization
352 :param name: (str) name of the Kubernetes context
353 :param context_cfg: (dict) context definition
355 context_cfg = copy.deepcopy(context_cfg)
356 servers_cfg = context_cfg.pop('servers', {})
357 crd_cfg = context_cfg.pop('custom_resources', [])
358 networks_cfg = context_cfg.pop('networks', [])
360 self.ssh_key = '{}-key'.format(name)
362 self.rcs = [self._get_rc_name(rc) for rc in servers_cfg]
363 self.k8s_objs = [KubernetesObject(self._get_rc_name(rc),
364 ssh_key=self.ssh_key,
366 for rc, cfg in servers_cfg.items()]
367 self.service_objs = [ServiceObject(s) for s in self.rcs]
368 self.crd = [CustomResourceDefinitionObject(self.name, **crd)
370 self.network_objs = [NetworkObject(**nobj) for nobj in networks_cfg]
373 def _get_rc_name(self, rc_name):
374 return '{}-{}'.format(rc_name, self.name)
376 def get_rc_pods(self):
377 resp = k8s_utils.get_pod_list()
378 self.pods = [p.metadata.name for p in resp.items for s in self.rcs
379 if p.metadata.name.startswith(s)]