Merge "the recovery action of "baremetal down" should be triggered mandatory"
[yardstick.git] / yardstick / orchestrator / kubernetes.py
index afff5cf..b0b93a3 100644 (file)
@@ -8,8 +8,10 @@
 ##############################################################################
 
 import copy
+import re
 
 from oslo_serialization import jsonutils
+import six
 
 from yardstick.common import constants
 from yardstick.common import exceptions
@@ -21,21 +23,34 @@ class ContainerObject(object):
 
     SSH_MOUNT_PATH = '/tmp/.ssh/'
     IMAGE_DEFAULT = 'openretriever/yardstick'
-    COMMAND_DEFAULT = '/bin/bash'
+    COMMAND_DEFAULT = ['/bin/bash', '-c']
     RESOURCES = ('requests', 'limits')
     PORT_OPTIONS = ('containerPort', 'hostIP', 'hostPort', 'name', 'protocol')
+    IMAGE_PULL_POLICY = ('Always', 'IfNotPresent', 'Never')
 
     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._command = self._parse_commands(
+            kwargs.get('command', self.COMMAND_DEFAULT))
+        self._args = self._parse_commands(kwargs.get('args', []))
         self._volume_mounts = kwargs.get('volumeMounts', [])
         self._security_context = kwargs.get('securityContext')
         self._env = kwargs.get('env', [])
         self._resources = kwargs.get('resources', {})
         self._ports = kwargs.get('ports', [])
+        self._image_pull_policy = kwargs.get('imagePullPolicy')
+        self._tty = kwargs.get('tty')
+        self._stdin = kwargs.get('stdin')
+
+    @staticmethod
+    def _parse_commands(command):
+        if isinstance(command, six.string_types):
+            return [command]
+        elif isinstance(command, list):
+            return command
+        raise exceptions.KubernetesContainerCommandType()
 
     def _create_volume_mounts(self):
         """Return all "volumeMounts" items per container"""
@@ -82,6 +97,14 @@ class ContainerObject(object):
             for res in (res for res in self._resources if
                         res in self.RESOURCES):
                 container['resources'][res] = self._resources[res]
+        if self._image_pull_policy:
+            if self._image_pull_policy not in self.IMAGE_PULL_POLICY:
+                raise exceptions.KubernetesContainerWrongImagePullPolicy()
+            container['imagePullPolicy'] = self._image_pull_policy
+        if self._stdin is not None:
+            container['stdin'] = self._stdin
+        if self._tty is not None:
+            container['tty'] = self._tty
         return container
 
 
@@ -146,6 +169,10 @@ class ReplicationControllerObject(object):
         self._add_networks()
         self._add_tolerations()
 
+    @property
+    def networks(self):
+        return self._networks
+
     def get_template(self):
         return self.template
 
@@ -230,6 +257,9 @@ class ReplicationControllerObject(object):
 
 class ServiceNodePortObject(object):
 
+    MANDATORY_PARAMETERS = {'port', 'name'}
+    NAME_REGEX = re.compile(r'^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')
+
     def __init__(self, name, **kwargs):
         """Service kind "NodePort" object
 
@@ -247,19 +277,27 @@ class ServiceNodePortObject(object):
             }
         }
 
-        self._add_port(22, protocol='TCP')
+        self._add_port(22, 'ssh', protocol='TCP')
         node_ports = copy.deepcopy(kwargs.get('node_ports', []))
         for port in node_ports:
+            if not self.MANDATORY_PARAMETERS.issubset(port.keys()):
+                missing_parameters = ', '.join(
+                    str(param) for param in
+                    (self.MANDATORY_PARAMETERS - set(port.keys())))
+                raise exceptions.KubernetesServiceObjectDefinitionError(
+                    missing_parameters=missing_parameters)
             port_number = port.pop('port')
-            self._add_port(port_number, **port)
+            name = port.pop('name')
+            if not self.NAME_REGEX.match(name):
+                raise exceptions.KubernetesServiceObjectNameError(name=name)
+            self._add_port(port_number, name, **port)
 
-    def _add_port(self, port, protocol=None, name=None, targetPort=None,
+    def _add_port(self, port, name, protocol=None, targetPort=None,
                   nodePort=None):
-        _port = {'port': port}
+        _port = {'port': port,
+                 'name': name}
         if protocol:
             _port['protocol'] = protocol
-        if name:
-            _port['name'] = name
         if targetPort:
             _port['targetPort'] = targetPort
         if nodePort:
@@ -270,7 +308,7 @@ class ServiceNodePortObject(object):
         k8s_utils.create_service(self.template)
 
     def delete(self):
-        k8s_utils.delete_service(self._name)
+        k8s_utils.delete_service(self._name, skip_codes=[404])
 
 
 class CustomResourceDefinitionObject(object):
@@ -311,7 +349,7 @@ class CustomResourceDefinitionObject(object):
         k8s_utils.create_custom_resource_definition(self._template)
 
     def delete(self):
-        k8s_utils.delete_custom_resource_definition(self._name)
+        k8s_utils.delete_custom_resource_definition(self._name, skip_codes=[404])
 
 
 class NetworkObject(object):
@@ -403,7 +441,7 @@ class NetworkObject(object):
 
     def delete(self):
         k8s_utils.delete_network(self.scope, self.group, self.version,
-                                 self.plural, self._name)
+                                 self.plural, self._name, skip_codes=[404])
 
 
 class KubernetesTemplate(object):
@@ -423,7 +461,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 +480,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