X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=yardstick%2Forchestrator%2Fkubernetes.py;h=44a333e4bc666f8f1ea6c456aa409cf279b57f64;hb=3fd6e3cb5bfee0c29e2e880b5ec019c4ccb16df5;hp=198eeac6da1265aab87e8ce076793625a8d8c9aa;hpb=c84187fc404d44082826f98b47c28d3d8f6690e5;p=yardstick.git diff --git a/yardstick/orchestrator/kubernetes.py b/yardstick/orchestrator/kubernetes.py index 198eeac6d..44a333e4b 100644 --- a/yardstick/orchestrator/kubernetes.py +++ b/yardstick/orchestrator/kubernetes.py @@ -7,25 +7,79 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -from __future__ import absolute_import -from __future__ import print_function +import copy +from yardstick.common import constants +from yardstick.common import exceptions from yardstick.common import utils from yardstick.common import kubernetes_utils as k8s_utils +class ContainerObject(object): + + SSH_MOUNT_PATH = '/tmp/.ssh/' + IMAGE_DEFAULT = 'openretriever/yardstick' + COMMAND_DEFAULT = '/bin/bash' + + def __init__(self, name, ssh_key, **kwargs): + self._name = name + self._ssh_key = ssh_key + self._image = kwargs.get('image', self.IMAGE_DEFAULT) + self._command = [kwargs.get('command', self.COMMAND_DEFAULT)] + self._args = kwargs.get('args', []) + self._volume_mounts = kwargs.get('volumeMounts', []) + self._security_context = kwargs.get('securityContext') + + def _create_volume_mounts(self): + """Return all "volumeMounts" items per container""" + volume_mounts_items = [self._create_volume_mounts_item(vol) + for vol in self._volume_mounts] + ssh_vol = {'name': self._ssh_key, + 'mountPath': self.SSH_MOUNT_PATH} + volume_mounts_items.append(self._create_volume_mounts_item(ssh_vol)) + return volume_mounts_items + + @staticmethod + def _create_volume_mounts_item(volume_mount): + """Create a "volumeMounts" item""" + return {'name': volume_mount['name'], + 'mountPath': volume_mount['mountPath'], + 'readOnly': volume_mount.get('readOnly', False)} + + def get_container_item(self): + """Create a "container" item""" + container_name = '{}-container'.format(self._name) + container = {'args': self._args, + 'command': self._command, + 'image': self._image, + 'name': container_name, + 'volumeMounts': self._create_volume_mounts()} + if self._security_context: + container['securityContext'] = self._security_context + return container + + class KubernetesObject(object): + SSHKEY_DEFAULT = 'yardstick_key' + def __init__(self, name, **kwargs): super(KubernetesObject, self).__init__() + parameters = copy.deepcopy(kwargs) 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.node_selector = kwargs.get('nodeSelector', {}) - - self.volumes = [] + self.node_selector = parameters.pop('nodeSelector', {}) + self.ssh_key = parameters.pop('ssh_key', self.SSHKEY_DEFAULT) + self._volumes = parameters.pop('volumes', []) + self._security_context = parameters.pop('securityContext', None) + + containers = parameters.pop('containers', None) + if containers: + self._containers = [ + ContainerObject(self.name, self.ssh_key, **container) + for container in containers] + else: + self._containers = [ + ContainerObject(self.name, self.ssh_key, **parameters)] self.template = { "apiVersion": "v1", @@ -37,9 +91,7 @@ class KubernetesObject(object): "replicas": 1, "template": { "metadata": { - "labels": { - "app": name - } + "labels": {"app": name} }, "spec": { "containers": [], @@ -53,8 +105,8 @@ class KubernetesObject(object): self._change_value_according_name(name) self._add_containers() self._add_node_selector() - self._add_ssh_key_volume() self._add_volumes() + self._add_security_context() def get_template(self): return self.template @@ -67,51 +119,50 @@ class KubernetesObject(object): name) def _add_containers(self): - containers = [self._add_container()] + containers = [container.get_container_item() + for container in self._containers] 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_node_selector(self): utils.set_dict_value(self.template, 'spec.template.spec.nodeSelector', self.node_selector) def _add_volumes(self): + """Add "volume" items to container specs, including the SSH one""" + volume_items = [self._create_volume_item(vol) for vol in self._volumes] + volume_items.append(self._create_ssh_key_volume()) 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) + volume_items) + + def _create_ssh_key_volume(self): + """Create a "volume" item of type "configMap" for the SSH key""" + return {'name': self.ssh_key, + 'configMap': {'name': self.ssh_key}} + + @staticmethod + def _create_volume_item(volume): + """Create a "volume" item""" + volume = copy.deepcopy(volume) + name = volume.pop('name') + for key in (k for k in volume if k in k8s_utils.get_volume_types()): + type_name = key + type_data = volume[key] + break + else: + raise exceptions.KubernetesTemplateInvalidVolumeType(volume=volume) + + return {'name': name, + type_name: type_data} + + def _add_security_context(self): + if self._security_context: + utils.set_dict_value(self.template, + 'spec.template.spec.securityContext', + self._security_context) class ServiceObject(object): @@ -143,19 +194,163 @@ class ServiceObject(object): k8s_utils.delete_service(self.name) +class CustomResourceDefinitionObject(object): + + MANDATORY_PARAMETERS = {'name'} + + def __init__(self, ctx_name, **kwargs): + if not self.MANDATORY_PARAMETERS.issubset(kwargs): + missing_parameters = ', '.join( + str(param) for param in + (self.MANDATORY_PARAMETERS - set(kwargs))) + raise exceptions.KubernetesCRDObjectDefinitionError( + missing_parameters=missing_parameters) + + singular = kwargs['name'] + plural = singular + 's' + kind = singular.title() + version = kwargs.get('version', 'v1') + scope = kwargs.get('scope', constants.SCOPE_NAMESPACED) + group = ctx_name + '.com' + self._name = metadata_name = plural + '.' + group + + self._template = { + 'metadata': { + 'name': metadata_name + }, + 'spec': { + 'group': group, + 'version': version, + 'scope': scope, + 'names': {'plural': plural, + 'singular': singular, + 'kind': kind} + } + } + + def create(self): + k8s_utils.create_custom_resource_definition(self._template) + + def delete(self): + 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, template_cfg): + def __init__(self, name, context_cfg): + """KubernetesTemplate object initialization + + :param name: (str) name of the Kubernetes context + :param context_cfg: (dict) context definition + """ + 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) - self.rcs = [self._get_rc_name(rc) for rc in template_cfg] + self.rcs = [self._get_rc_name(rc) for rc in servers_cfg] self.k8s_objs = [KubernetesObject(self._get_rc_name(rc), ssh_key=self.ssh_key, **cfg) - for rc, cfg in template_cfg.items()] + for rc, cfg in servers_cfg.items()] 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):