diff options
18 files changed, 426 insertions, 176 deletions
diff --git a/ansible/roles/install_yardstick/tasks/main.yml b/ansible/roles/install_yardstick/tasks/main.yml index 973b2b027..203acc3e5 100644 --- a/ansible/roles/install_yardstick/tasks/main.yml +++ b/ansible/roles/install_yardstick/tasks/main.yml @@ -41,7 +41,7 @@ pip: requirements: "{{ yardstick_dir }}/requirements.txt" virtualenv: "{{ yardstick_dir }}/virtualenv" - async: 300 + async: 900 poll: 0 register: pip_installer when: virtual_environment == True @@ -49,7 +49,7 @@ - name: Install Yardstick requirements pip: requirements: "{{ yardstick_dir }}/requirements.txt" - async: 300 + async: 900 poll: 0 register: pip_installer when: virtual_environment == False @@ -59,7 +59,7 @@ jid: "{{ pip_installer.ansible_job_id }}" register: job_result until: job_result.finished - retries: 100 + retries: 180 - name: Install Yardstick code (venv) pip: diff --git a/docker/k8s/Dockerfile b/docker/k8s/Dockerfile new file mode 100644 index 000000000..2f8d9b161 --- /dev/null +++ b/docker/k8s/Dockerfile @@ -0,0 +1,39 @@ +############################################################################## +# Copyright (c) 2018 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +FROM ubuntu:16.04 + +LABEL image=opnfv/yardstick-image-k8s + +ARG BRANCH=master + +# GIT repo directory +ENV CLONE_DEST="/opt/tempT" + +RUN apt-get update && apt-get install -y \ + git bc bonnie++ fio gcc iperf3 ethtool \ + iproute2 linux-tools-common linux-tools-generic \ + lmbench make netperf patch perl rt-tests stress \ + sysstat iputils-ping openssh-server sudo && \ + apt-get -y autoremove && apt-get clean + +RUN rm -rf -- ${CLONE_DEST} +RUN git clone https://github.com/kdlucas/byte-unixbench.git ${CLONE_DEST} +RUN mkdir -p ${CLONE_DEST}/UnixBench/ + +RUN git clone https://github.com/beefyamoeba5/ramspeed.git ${CLONE_DEST}/RAMspeed +WORKDIR ${CLONE_DEST}/RAMspeed/ramspeed-2.6.0 +RUN mkdir -p ${CLONE_DEST}/RAMspeed/ramspeed-2.6.0/temp +RUN bash build.sh + +RUN git clone https://github.com/beefyamoeba5/cachestat.git ${CLONE_DEST}/Cachestat + +WORKDIR / + +CMD /bin/bash diff --git a/docs/testing/developer/devguide/devguide.rst b/docs/testing/developer/devguide/devguide.rst index dbe92b846..91f2c2148 100755 --- a/docs/testing/developer/devguide/devguide.rst +++ b/docs/testing/developer/devguide/devguide.rst @@ -540,6 +540,32 @@ The final step consists in pushing the newly modified commit to Gerrit:: git review +Backporting changes to stable branches +-------------------------------------- +During the release cycle, when master and the ``stable/<release>`` branch have +diverged, it may be necessary to backport (cherry-pick) changes top the +``stable/<release>`` branch once they have merged to master. +These changes should be identified by the committers reviewing the patch. +Changes should be backported **as soon as possible** after merging of the +original code. + +..note:: + Besides the commit and review process below, the Jira tick must be updated to + add dual release versions and indicate that the change is to be backported. + +The process for backporting is as follows: + +* Committer A merges a change to master (process for normal changes). +* Committer A cherry-picks the change to ``stable/<release>`` branch (if the + bug has been identified for backporting). +* The original author should review the code and verify that it still works + (and give a ``+1``). +* Committer B reviews the change, gives a ``+2`` and merges to + ``stable/<release>``. + +A backported change needs a ``+1`` and a ``+2`` from a committer who didn’t +propose the change (i.e. minimum 3 people involved). + Plugins ------- diff --git a/yardstick/benchmark/contexts/kubernetes.py b/yardstick/benchmark/contexts/kubernetes.py index 704c4a022..7534c4ea5 100644 --- a/yardstick/benchmark/contexts/kubernetes.py +++ b/yardstick/benchmark/contexts/kubernetes.py @@ -166,8 +166,8 @@ class KubernetesContext(ctx_base.Context): def _get_server(self, name): node_ports = self._get_service_ports(name) for sn_port in (sn_port for sn_port in node_ports - if sn_port.port == constants.SSH_PORT): - node_port = sn_port.node_port + if sn_port['port'] == constants.SSH_PORT): + node_port = sn_port['node_port'] break else: raise exceptions.KubernetesSSHPortNotDefined() @@ -224,4 +224,11 @@ class KubernetesContext(ctx_base.Context): service = k8s_utils.get_service_by_name(service_name) if not service: raise exceptions.KubernetesServiceObjectNotDefined() - return service.ports + ports = [] + for port in service.ports: + ports.append({'name': port.name, + 'node_port': port.node_port, + 'port': port.port, + 'protocol': port.protocol, + 'target_port': port.target_port}) + return ports diff --git a/yardstick/common/exceptions.py b/yardstick/common/exceptions.py index c25acbaf8..2e6cbdd7b 100644 --- a/yardstick/common/exceptions.py +++ b/yardstick/common/exceptions.py @@ -85,7 +85,6 @@ class InfluxDBConfigurationMissing(YardstickException): class YardstickBannedModuleImported(YardstickException): - # pragma: no cover message = 'Module "%(module)s" cannnot be imported. Reason: "%(reason)s"' @@ -95,7 +94,6 @@ class PayloadMissingAttributes(YardstickException): class HeatTemplateError(YardstickException): - """Error in Heat during the stack deployment""" message = ('Error in Heat during the creation of the OpenStack stack ' '"%(stack_name)s"') @@ -108,6 +106,10 @@ class TrafficProfileNotImplemented(YardstickException): message = 'No implementation for traffic profile %(profile_class)s.' +class TrafficProfileRate(YardstickException): + message = 'Traffic profile rate must be "<number>[fps|%]"' + + class DPDKSetupDriverError(YardstickException): message = '"igb_uio" driver is not loaded' @@ -231,6 +233,16 @@ class KubernetesServiceObjectNotDefined(YardstickException): message = 'ServiceObject is not defined' +class KubernetesServiceObjectDefinitionError(YardstickException): + message = ('Kubernetes Service object definition error, missing ' + 'parameters: %(missing_parameters)s') + + +class KubernetesServiceObjectNameError(YardstickException): + message = ('Kubernetes Service object name "%(name)s" does not comply' + 'naming convention') + + class KubernetesCRDObjectDefinitionError(YardstickException): message = ('Kubernetes Custom Resource Definition Object error, missing ' 'parameters: %(missing_parameters)s') @@ -253,6 +265,14 @@ class KubernetesContainerPortNotDefined(YardstickException): message = 'Container port not defined in "%(port)s"' +class KubernetesContainerWrongImagePullPolicy(YardstickException): + message = 'Image pull policy must be "Always", "IfNotPresent" or "Never"' + + +class KubernetesContainerCommandType(YardstickException): + message = '"args" and "command" must be string or list of strings' + + class ScenarioCreateNetworkError(YardstickException): message = 'Create Neutron Network Scenario failed' diff --git a/yardstick/common/kubernetes_utils.py b/yardstick/common/kubernetes_utils.py index 35e590f2b..f5b0443ea 100644 --- a/yardstick/common/kubernetes_utils.py +++ b/yardstick/common/kubernetes_utils.py @@ -75,15 +75,18 @@ def create_service(template, raise -def delete_service(name, - namespace='default', - **kwargs): # pragma: no cover +def delete_service(name, namespace='default', skip_codes=None, **kwargs): + skip_codes = [] if not skip_codes else skip_codes core_v1_api = get_core_api() try: body = client.V1DeleteOptions() core_v1_api.delete_namespaced_service(name, namespace, body, **kwargs) - except ApiException: - LOG.exception('Delete Service failed') + except ApiException as e: + if e.status in skip_codes: + LOG.info(e.reason) + else: + raise exceptions.KubernetesApiException( + action='delete', resource='Service') def get_service_list(namespace='default', **kwargs): @@ -223,14 +226,18 @@ def create_custom_resource_definition(body): action='create', resource='CustomResourceDefinition') -def delete_custom_resource_definition(name): +def delete_custom_resource_definition(name, skip_codes=None): + skip_codes = [] if not skip_codes else skip_codes api = get_extensions_v1beta_api() body_obj = client.V1DeleteOptions() try: api.delete_custom_resource_definition(name, body_obj) - except ApiException: - raise exceptions.KubernetesApiException( - action='delete', resource='CustomResourceDefinition') + except ApiException as e: + if e.status in skip_codes: + LOG.info(e.reason) + else: + raise exceptions.KubernetesApiException( + action='delete', resource='CustomResourceDefinition') def get_custom_resource_definition(kind): diff --git a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py index 74deeecb5..cee768dba 100644 --- a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py +++ b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py @@ -18,6 +18,7 @@ import IxNetwork from yardstick.common import exceptions from yardstick.common import utils +from yardstick.network_services.traffic_profile import base as tp_base log = logging.getLogger(__name__) @@ -336,8 +337,8 @@ class IxNextgen(object): # pragma: no cover - Duration: in case of traffic_type="fixedDuration", amount of seconds to inject traffic. - Rate: in frames per seconds or percentage. - - Type of rate: "framesPerSecond" ("bitsPerSecond" and - "percentLineRate" no used) + - Type of rate: "framesPerSecond" or "percentLineRate" ("bitsPerSecond" + no used) - Frame size: custom IMIX [1] definition; a list of packet size in bytes and the weight. E.g.: [[64, 64, 10], [128, 128, 15], [512, 512, 5]] @@ -355,7 +356,10 @@ class IxNextgen(object): # pragma: no cover type = traffic_param.get('traffic_type', 'fixedDuration') duration = traffic_param.get('duration', 30) - rate = traffic_param['iload'] + rate = traffic_param['rate'] + rate_unit = ( + 'framesPerSecond' if traffic_param['rate_unit'] == + tp_base.TrafficProfileConfig.RATE_FPS else 'percentLineRate') weighted_range_pairs = self._parse_framesize( traffic_param['outer_l2']['framesize']) srcmac = str(traffic_param.get('srcmac', '00:00:00:00:00:01')) @@ -370,7 +374,7 @@ class IxNextgen(object): # pragma: no cover '-type', type, '-duration', duration) self.ixnet.setMultiAttribute( config_element + '/frameRate', - '-rate', rate, '-type', 'framesPerSecond') + '-rate', rate, '-type', rate_unit) self.ixnet.setMultiAttribute( config_element + '/frameSize', '-type', 'weightedPairs', diff --git a/yardstick/network_services/traffic_profile/base.py b/yardstick/network_services/traffic_profile/base.py index f4b5b178c..a8f950b7b 100644 --- a/yardstick/network_services/traffic_profile/base.py +++ b/yardstick/network_services/traffic_profile/base.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import re + from yardstick.common import exceptions from yardstick.common import utils @@ -21,10 +23,12 @@ class TrafficProfileConfig(object): This object will parse and validate the traffic profile information. """ - DEFAULT_SCHEMA = 'nsb:traffic_profile:0.1' - DEFAULT_FRAME_RATE = 100 + DEFAULT_FRAME_RATE = '100' DEFAULT_DURATION = 30 + RATE_FPS = 'fps' + RATE_PERCENTAGE = '%' + RATE_REGEX = re.compile(r'([0-9]*\.[0-9]+|[0-9]+)\s*(fps|%)*(.*)') def __init__(self, tp_config): self.schema = tp_config.get('schema', self.DEFAULT_SCHEMA) @@ -32,7 +36,8 @@ class TrafficProfileConfig(object): self.description = tp_config.get('description') tprofile = tp_config['traffic_profile'] self.traffic_type = tprofile.get('traffic_type') - self.frame_rate = tprofile.get('frame_rate', self.DEFAULT_FRAME_RATE) + self.frame_rate, self.rate_unit = self._parse_rate( + tprofile.get('frame_rate', self.DEFAULT_FRAME_RATE)) self.test_precision = tprofile.get('test_precision') self.packet_sizes = tprofile.get('packet_sizes') self.duration = tprofile.get('duration', self.DEFAULT_DURATION) @@ -40,6 +45,27 @@ class TrafficProfileConfig(object): self.upper_bound = tprofile.get('upper_bound') self.step_interval = tprofile.get('step_interval') + def _parse_rate(self, rate): + """Parse traffic profile rate + + The line rate can be defined in fps or percentage over the maximum line + rate: + - frame_rate = 5000 (by default, unit is 'fps') + - frame_rate = 5000fps + - frame_rate = 25% + + :param rate: (string, int) line rate in fps or % + :return: (tuple: int, string) line rate number and unit + """ + match = self.RATE_REGEX.match(str(rate)) + if not match: + exceptions.TrafficProfileRate() + rate = float(match.group(1)) + unit = match.group(2) if match.group(2) else self.RATE_FPS + if match.group(3): + raise exceptions.TrafficProfileRate() + return rate, unit + class TrafficProfile(object): """ diff --git a/yardstick/network_services/traffic_profile/ixia_rfc2544.py b/yardstick/network_services/traffic_profile/ixia_rfc2544.py index 39336785e..49bac27e4 100644 --- a/yardstick/network_services/traffic_profile/ixia_rfc2544.py +++ b/yardstick/network_services/traffic_profile/ixia_rfc2544.py @@ -14,25 +14,27 @@ import logging -from yardstick.network_services.traffic_profile.trex_traffic_profile import \ - TrexProfile +from yardstick.network_services.traffic_profile import base as tp_base +from yardstick.network_services.traffic_profile import trex_traffic_profile + LOG = logging.getLogger(__name__) -class IXIARFC2544Profile(TrexProfile): +class IXIARFC2544Profile(trex_traffic_profile.TrexProfile): UPLINK = 'uplink' DOWNLINK = 'downlink' + DROP_PERCENT_ROUND = 6 + RATE_ROUND = 5 def __init__(self, yaml_data): super(IXIARFC2544Profile, self).__init__(yaml_data) self.rate = self.config.frame_rate + self.rate_unit = self.config.rate_unit def _get_ixia_traffic_profile(self, profile_data, mac=None): - if mac is None: - mac = {} - + mac = {} if mac is None else mac result = {} for traffickey, values in profile_data.items(): if not traffickey.startswith((self.UPLINK, self.DOWNLINK)): @@ -58,8 +60,9 @@ class IXIARFC2544Profile(TrexProfile): result[traffickey] = { 'bidir': False, - 'iload': '100', 'id': port_id, + 'rate': self.rate, + 'rate_unit': self.rate_unit, 'outer_l2': { 'framesize': value['outer_l2']['framesize'], 'framesPerSecond': True, @@ -83,9 +86,6 @@ class IXIARFC2544Profile(TrexProfile): return result def _ixia_traffic_generate(self, traffic, ixia_obj): - for key, value in traffic.items(): - if key.startswith((self.UPLINK, self.DOWNLINK)): - value['iload'] = str(self.rate) ixia_obj.update_frame(traffic) ixia_obj.update_ip_packet(traffic) ixia_obj.start_traffic() @@ -114,9 +114,10 @@ class IXIARFC2544Profile(TrexProfile): self.pg_id = 0 self.update_traffic_profile(traffic_generator) self.max_rate = self.rate - self.min_rate = 0 + self.min_rate = 0.0 else: - self.rate = round(float(self.max_rate + self.min_rate) / 2.0, 2) + self.rate = round(float(self.max_rate + self.min_rate) / 2.0, + self.RATE_ROUND) traffic = self._get_ixia_traffic_profile(self.full_profile, mac) self._ixia_traffic_generate(traffic, ixia_obj) @@ -141,7 +142,8 @@ class IXIARFC2544Profile(TrexProfile): try: drop_percent = round( - (packet_drop / float(out_packets_sum)) * 100, 2) + (packet_drop / float(out_packets_sum)) * 100, + self.DROP_PERCENT_ROUND) except ZeroDivisionError: LOG.info('No traffic is flowing') @@ -150,8 +152,10 @@ class IXIARFC2544Profile(TrexProfile): samples['DropPercentage'] = drop_percent if first_run: - self.rate = out_packets_sum / duration / num_ifaces completed = True if drop_percent <= tolerance else False + if (first_run and + self.rate_unit == tp_base.TrafficProfileConfig.RATE_FPS): + self.rate = out_packets_sum / duration / num_ifaces if drop_percent > tolerance: self.max_rate = self.rate diff --git a/yardstick/orchestrator/kubernetes.py b/yardstick/orchestrator/kubernetes.py index 98832908c..7b1450230 100644 --- a/yardstick/orchestrator/kubernetes.py +++ b/yardstick/orchestrator/kubernetes.py @@ -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 @@ -234,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 @@ -251,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: @@ -274,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): @@ -315,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): diff --git a/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py b/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py index bace37653..b526e7cc7 100644 --- a/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py +++ b/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py @@ -57,6 +57,9 @@ class NodePort(object): def __init__(self): self.node_port = 30000 self.port = constants.SSH_PORT + self.name = 'port_name' + self.protocol = 'TCP' + self.target_port = constants.SSH_PORT class Service(object): @@ -152,8 +155,10 @@ class KubernetesTestCase(unittest.TestCase): def test_get_server(self, mock_get_node_ip, mock_get_pod_by_name): mock_get_pod_by_name.return_value = Pod() mock_get_node_ip.return_value = '172.16.10.131' - with mock.patch.object(self.k8s_context, '_get_service_ports', - return_value=[NodePort()]): + with mock.patch.object(self.k8s_context, '_get_service_ports') as \ + mock_get_sports: + mock_get_sports.return_value = [ + {'port': constants.SSH_PORT, 'node_port': 30000}] server = self.k8s_context._get_server('server_name') self.assertEqual('server_name', server['name']) self.assertEqual(30000, server['ssh_port']) @@ -253,7 +258,12 @@ class KubernetesTestCase(unittest.TestCase): name = 'rc_name' service_ports = self.k8s_context._get_service_ports(name) mock_get_service_by_name.assert_called_once_with(name + '-service') - self.assertEqual(30000, service_ports[0].node_port) + expected = {'node_port': 30000, + 'port': constants.SSH_PORT, + 'name': 'port_name', + 'protocol': 'TCP', + 'target_port': constants.SSH_PORT} + self.assertEqual(expected, service_ports[0]) @mock.patch.object(k8s_utils, 'get_service_by_name', return_value=None) diff --git a/yardstick/tests/unit/benchmark/core/test_task.py b/yardstick/tests/unit/benchmark/core/test_task.py index 35236637d..e1414c2ae 100644 --- a/yardstick/tests/unit/benchmark/core/test_task.py +++ b/yardstick/tests/unit/benchmark/core/test_task.py @@ -9,11 +9,13 @@ import copy import io +import logging import os import sys import mock import six +from six.moves import builtins import unittest import uuid @@ -322,9 +324,9 @@ class TaskTestCase(unittest.TestCase): actual_result = t._parse_options(options) self.assertEqual(expected_result, actual_result) - @mock.patch('six.moves.builtins.open', side_effect=mock.mock_open()) + @mock.patch.object(builtins, 'open', side_effect=mock.mock_open()) @mock.patch.object(task, 'utils') - @mock.patch('logging.root') + @mock.patch.object(logging, 'root') def test_set_log(self, mock_logging_root, *args): task_obj = task.Task() task_obj.task_id = 'task_id' @@ -561,7 +563,8 @@ key2: mock_open.assert_has_calls([mock.call('args_file'), mock.call('task_file')]) - def test__render_task_error_arguments(self): + @mock.patch.object(builtins, 'print') + def test__render_task_error_arguments(self, *args): with self.assertRaises(exceptions.TaskRenderArgumentError): task.TaskParser('task_file')._render_task('value1="var3"', None) diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py index db6f9cc89..b305fc93b 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py @@ -20,6 +20,8 @@ import unittest from yardstick import exceptions as y_exc from yardstick.benchmark.scenarios.networking import vsperf_dpdk +from yardstick.common import exceptions as y_exc +from yardstick import ssh class VsperfDPDKTestCase(unittest.TestCase): @@ -56,80 +58,51 @@ class VsperfDPDKTestCase(unittest.TestCase): 'action': 'monitor', } } - - self.scenario = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) - - self._mock_ssh = mock.patch.object(vsperf_dpdk, 'ssh') + self._mock_ssh = mock.patch.object(ssh, 'SSH') self.mock_ssh = self._mock_ssh.start() self._mock_subprocess_call = mock.patch.object(subprocess, 'call') self.mock_subprocess_call = self._mock_subprocess_call.start() + mock_call_obj = mock.Mock() + mock_call_obj.execute.return_value = None + self.mock_subprocess_call.return_value = mock_call_obj + self._mock_log_info = mock.patch.object(vsperf_dpdk.LOG, 'info') self.mock_log_info = self._mock_log_info.start() + self.addCleanup(self._cleanup) + self.scenario = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) + self.scenario.setup() + def _cleanup(self): self._mock_ssh.stop() self._mock_subprocess_call.stop() self._mock_log_info.stop() def test_setup(self): - # setup() specific mocks - self.mock_subprocess_call().execute.return_value = None - - self.scenario.setup() self.assertIsNotNone(self.scenario.client) self.assertTrue(self.scenario.setup_done) def test_teardown(self): - # setup() specific mocks - self.mock_subprocess_call().execute.return_value = None - - self.scenario.setup() - self.assertIsNotNone(self.scenario.client) - self.assertTrue(self.scenario.setup_done) - self.scenario.teardown() self.assertFalse(self.scenario.setup_done) def test_is_dpdk_setup_no(self): - # setup() specific mocks - self.mock_subprocess_call().execute.return_value = None - - self.scenario.setup() - self.assertIsNotNone(self.scenario.client) - self.assertTrue(self.scenario.setup_done) - # is_dpdk_setup() specific mocks - self.mock_ssh.SSH.from_node().execute.return_value = (0, 'dummy', '') + self.mock_ssh.from_node().execute.return_value = (0, 'dummy', '') - result = self.scenario._is_dpdk_setup() - self.assertFalse(result) + self.assertFalse(self.scenario._is_dpdk_setup()) def test_is_dpdk_setup_yes(self): - # setup() specific mocks - self.mock_subprocess_call().execute.return_value = None - - self.scenario.setup() - self.assertIsNotNone(self.scenario.client) - self.assertTrue(self.scenario.setup_done) - # is_dpdk_setup() specific mocks - self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + self.mock_ssh.from_node().execute.return_value = (0, '', '') - result = self.scenario._is_dpdk_setup() - self.assertTrue(result) + self.assertTrue(self.scenario._is_dpdk_setup()) @mock.patch.object(time, 'sleep') def test_dpdk_setup_first(self, *args): - # setup() specific mocks - self.mock_subprocess_call().execute.return_value = None - - self.scenario.setup() - self.assertIsNotNone(self.scenario.client) - self.assertTrue(self.scenario.setup_done) - # is_dpdk_setup() specific mocks - self.mock_ssh.SSH.from_node().execute.return_value = (0, 'dummy', '') + self.mock_ssh.from_node().execute.return_value = (0, 'dummy', '') self.scenario.dpdk_setup() self.assertFalse(self.scenario._is_dpdk_setup()) @@ -137,13 +110,7 @@ class VsperfDPDKTestCase(unittest.TestCase): @mock.patch.object(time, 'sleep') def test_dpdk_setup_next(self, *args): - # setup() specific mocks - self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') - self.mock_subprocess_call().execute.return_value = None - - self.scenario.setup() - self.assertIsNotNone(self.scenario.client) - self.assertTrue(self.scenario.setup_done) + self.mock_ssh.from_node().execute.return_value = (0, '', '') self.scenario.dpdk_setup() self.assertTrue(self.scenario._is_dpdk_setup()) @@ -151,75 +118,38 @@ class VsperfDPDKTestCase(unittest.TestCase): @mock.patch.object(time, 'sleep') def test_dpdk_setup_runtime_error(self, *args): - - # setup specific mocks - self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') - self.mock_subprocess_call().execute.return_value = None - - self.scenario.setup() self.assertIsNotNone(self.scenario.client) - self.mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + self.mock_ssh.from_node().execute.return_value = (1, '', '') self.assertTrue(self.scenario.setup_done) self.assertRaises(RuntimeError, self.scenario.dpdk_setup) + @mock.patch.object(time, 'sleep') @mock.patch.object(subprocess, 'check_output') - @mock.patch('time.sleep') def test_run_ok(self, *args): - # setup() specific mocks - self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') - self.mock_subprocess_call().execute.return_value = None - - self.scenario.setup() - self.assertIsNotNone(self.scenario.client) - self.assertTrue(self.scenario.setup_done) - # run() specific mocks - self.mock_subprocess_call().execute.return_value = None - self.mock_ssh.SSH.from_node().execute.return_value = ( + self.mock_ssh.from_node().execute.return_value = ( 0, 'throughput_rx_fps\r\n14797660.000\r\n', '') result = {} self.scenario.run(result) - self.assertEqual(result['throughput_rx_fps'], '14797660.000') def test_run_failed_vsperf_execution(self): - # setup() specific mocks - self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') - self.mock_subprocess_call().execute.return_value = None + self.mock_ssh.from_node().execute.return_value = (1, '', '') - self.scenario.setup() - self.assertIsNotNone(self.scenario.client) - self.assertTrue(self.scenario.setup_done) - - self.mock_ssh.SSH.from_node().execute.return_value = (1, '', '') - - result = {} - self.assertRaises(RuntimeError, self.scenario.run, result) + self.assertRaises(RuntimeError, self.scenario.run, {}) def test_run_falied_csv_report(self): - # setup() specific mocks - self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') - self.mock_subprocess_call().execute.return_value = None - - self.scenario.setup() - self.assertIsNotNone(self.scenario.client) - self.assertTrue(self.scenario.setup_done) - # run() specific mocks - self.mock_subprocess_call().execute.return_value = None - self.mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + self.mock_ssh.from_node().execute.return_value = (1, '', '') - result = {} - self.assertRaises(RuntimeError, self.scenario.run, result) + self.assertRaises(RuntimeError, self.scenario.run, {}) @mock.patch.object(time, 'sleep') @mock.patch.object(subprocess, 'check_output') def test_vsperf_run_sla_fail(self, *args): - self.scenario.setup() - - self.mock_ssh.SSH.from_node().execute.return_value = ( + self.mock_ssh.from_node().execute.return_value = ( 0, 'throughput_rx_fps\r\n123456.000\r\n', '') with self.assertRaises(y_exc.SLAValidationError) as raised: @@ -232,9 +162,7 @@ class VsperfDPDKTestCase(unittest.TestCase): @mock.patch.object(time, 'sleep') @mock.patch.object(subprocess, 'check_output') def test_vsperf_run_sla_fail_metric_not_collected(self, *args): - self.scenario.setup() - - self.mock_ssh.SSH.from_node().execute.return_value = ( + self.mock_ssh.from_node().execute.return_value = ( 0, 'nonexisting_metric\r\n123456.000\r\n', '') with self.assertRaises(y_exc.SLAValidationError) as raised: @@ -249,7 +177,7 @@ class VsperfDPDKTestCase(unittest.TestCase): del self.scenario.scenario_cfg['sla']['throughput_rx_fps'] self.scenario.setup() - self.mock_ssh.SSH.from_node().execute.return_value = ( + self.mock_ssh.from_node().execute.return_value = ( 0, 'throughput_rx_fps\r\n14797660.000\r\n', '') with self.assertRaises(y_exc.SLAValidationError) as raised: diff --git a/yardstick/tests/unit/common/test_kubernetes_utils.py b/yardstick/tests/unit/common/test_kubernetes_utils.py index bdc2c12d5..42aa9f7e0 100644 --- a/yardstick/tests/unit/common/test_kubernetes_utils.py +++ b/yardstick/tests/unit/common/test_kubernetes_utils.py @@ -121,6 +121,23 @@ class DeleteCustomResourceDefinitionTestCase(base.BaseUnitTestCase): mock_delete_crd.delete_custom_resource_definition.\ assert_called_once_with('name', 'del_obj') + @mock.patch.object(client, 'V1DeleteOptions', return_value='del_obj') + @mock.patch.object(kubernetes_utils, 'get_extensions_v1beta_api') + @mock.patch.object(kubernetes_utils, 'LOG') + def test_execute_skip_exception(self, mock_log, mock_get_api, mock_delobj): + mock_delete_crd = mock.Mock() + mock_delete_crd.delete_custom_resource_definition.side_effect = rest.ApiException( + status=404) + + mock_get_api.return_value = mock_delete_crd + kubernetes_utils.delete_custom_resource_definition('name', skip_codes=[404]) + + mock_delobj.assert_called_once() + mock_delete_crd.delete_custom_resource_definition.assert_called_once_with( + 'name', 'del_obj') + + mock_log.info.assert_called_once() + class GetCustomResourceDefinitionTestCase(base.BaseUnitTestCase): @@ -250,3 +267,34 @@ class DeletePodTestCase(base.BaseUnitTestCase): mock_get_api.return_value = mock_api kubernetes_utils.delete_pod(mock.ANY, skip_codes=[404]) + + +class DeleteServiceTestCase(base.BaseUnitTestCase): + @mock.patch.object(client, "V1DeleteOptions") + @mock.patch.object(kubernetes_utils, 'get_core_api') + def test_execute_correct(self, mock_get_api, mock_options): + mock_api = mock.Mock() + mock_get_api.return_value = mock_api + mock_options.return_value = None + kubernetes_utils.delete_service("name", "default", None) + mock_api.delete_namespaced_service.assert_called_once_with( + "name", 'default', None) + + @mock.patch.object(kubernetes_utils, 'get_core_api') + def test_execute_exception(self, mock_get_api): + mock_api = mock.Mock() + mock_api.delete_namespaced_service.side_effect = rest.ApiException(status=200) + + mock_get_api.return_value = mock_api + with self.assertRaises(exceptions.KubernetesApiException): + kubernetes_utils.delete_service(mock.ANY, skip_codes=[404]) + + @mock.patch.object(kubernetes_utils, 'get_core_api') + @mock.patch.object(kubernetes_utils, 'LOG') + def test_execute_skip_exception(self, mock_log, mock_get_api): + mock_api = mock.Mock() + mock_api.delete_namespaced_service.side_effect = rest.ApiException(status=404) + + mock_get_api.return_value = mock_api + kubernetes_utils.delete_service(mock.ANY, skip_codes=[404]) + mock_log.info.assert_called_once() diff --git a/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py b/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py index 541855aa8..afa4cc3dc 100644 --- a/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py +++ b/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py @@ -28,7 +28,8 @@ TRAFFIC_PARAMETERS = { 'id': 1, 'bidir': 'False', 'duration': 60, - 'iload': '100', + 'rate': 10000.5, + 'rate_unit': 'fps', 'outer_l2': { 'framesize': {'64B': '25', '256B': '75'} }, @@ -65,7 +66,8 @@ TRAFFIC_PARAMETERS = { 'id': 2, 'bidir': 'False', 'duration': 60, - 'iload': '100', + 'rate': 75.2, + 'rate_unit': '%', 'outer_l2': { 'framesize': {'128B': '35', '1024B': '65'} }, @@ -396,6 +398,12 @@ class TestIxNextgen(unittest.TestCase): self.assertEqual(6, len(ixnet_gen.ixnet.setMultiAttribute.mock_calls)) self.assertEqual(4, len(mock_update_frame.mock_calls)) + ixnet_gen.ixnet.setMultiAttribute.assert_has_calls( + [mock.call('cfg_element/frameRate', '-rate', 10000.5, + '-type', 'framesPerSecond'), + mock.call('cfg_element/frameRate', '-rate', 75.2, '-type', + 'percentLineRate')], + any_order=True) def test_update_frame_flow_not_present(self): ixnet_gen = ixnet_api.IxNextgen() diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_base.py b/yardstick/tests/unit/network_services/traffic_profile/test_base.py index 55276af58..2a366fc94 100644 --- a/yardstick/tests/unit/network_services/traffic_profile/test_base.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_base.py @@ -82,7 +82,26 @@ class TrafficProfileConfigTestCase(unittest.TestCase): self.assertEqual({'64B': 100}, tp_config_obj.packet_sizes) self.assertEqual(base.TrafficProfileConfig.DEFAULT_SCHEMA, tp_config_obj.schema) - self.assertEqual(base.TrafficProfileConfig.DEFAULT_FRAME_RATE, + self.assertEqual(float(base.TrafficProfileConfig.DEFAULT_FRAME_RATE), tp_config_obj.frame_rate) self.assertEqual(base.TrafficProfileConfig.DEFAULT_DURATION, tp_config_obj.duration) + + def test__parse_rate(self): + tp_config = {'traffic_profile': {'packet_sizes': {'64B': 100}}} + tp_config_obj = base.TrafficProfileConfig(tp_config) + self.assertEqual((100.0, 'fps'), tp_config_obj._parse_rate('100 ')) + self.assertEqual((200.5, 'fps'), tp_config_obj._parse_rate('200.5')) + self.assertEqual((300.8, 'fps'), tp_config_obj._parse_rate('300.8fps')) + self.assertEqual((400.2, 'fps'), + tp_config_obj._parse_rate('400.2 fps')) + self.assertEqual((500.3, '%'), tp_config_obj._parse_rate('500.3%')) + self.assertEqual((600.1, '%'), tp_config_obj._parse_rate('600.1 %')) + + def test__parse_rate_exception(self): + tp_config = {'traffic_profile': {'packet_sizes': {'64B': 100}}} + tp_config_obj = base.TrafficProfileConfig(tp_config) + with self.assertRaises(exceptions.TrafficProfileRate): + tp_config_obj._parse_rate('100Fps') + with self.assertRaises(exceptions.TrafficProfileRate): + tp_config_obj._parse_rate('100 kbps') diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py b/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py index 3bb8b9192..4ea19a121 100644 --- a/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py @@ -554,7 +554,7 @@ class TestIXIARFC2544Profile(unittest.TestCase): self.assertTrue(completed) self.assertEqual(23.0, samples['TxThroughput']) self.assertEqual(21.0, samples['RxThroughput']) - self.assertEqual(0.1, samples['DropPercentage']) + self.assertEqual(0.099651, samples['DropPercentage']) def test_get_drop_percentage_over_drop_percentage(self): samples = {'iface_name_1': @@ -571,7 +571,7 @@ class TestIXIARFC2544Profile(unittest.TestCase): self.assertFalse(completed) self.assertEqual(23.0, samples['TxThroughput']) self.assertEqual(21.0, samples['RxThroughput']) - self.assertEqual(0.1, samples['DropPercentage']) + self.assertEqual(0.099651, samples['DropPercentage']) self.assertEqual(rfc2544_profile.rate, rfc2544_profile.max_rate) def test_get_drop_percentage_under_drop_percentage(self): @@ -589,7 +589,7 @@ class TestIXIARFC2544Profile(unittest.TestCase): self.assertFalse(completed) self.assertEqual(23.0, samples['TxThroughput']) self.assertEqual(21.0, samples['RxThroughput']) - self.assertEqual(0.1, samples['DropPercentage']) + self.assertEqual(0.099651, samples['DropPercentage']) self.assertEqual(rfc2544_profile.rate, rfc2544_profile.min_rate) @mock.patch.object(ixia_rfc2544.LOG, 'info') @@ -625,5 +625,5 @@ class TestIXIARFC2544Profile(unittest.TestCase): self.assertTrue(completed) self.assertEqual(23.0, samples['TxThroughput']) self.assertEqual(21.0, samples['RxThroughput']) - self.assertEqual(0.1, samples['DropPercentage']) + self.assertEqual(0.099651, samples['DropPercentage']) self.assertEqual(33.45, rfc2544_profile.rate) diff --git a/yardstick/tests/unit/orchestrator/test_kubernetes.py b/yardstick/tests/unit/orchestrator/test_kubernetes.py index 8d351e419..5a6f8c6a0 100644 --- a/yardstick/tests/unit/orchestrator/test_kubernetes.py +++ b/yardstick/tests/unit/orchestrator/test_kubernetes.py @@ -300,7 +300,7 @@ class ContainerObjectTestCase(base.BaseUnitTestCase): 'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount], args=args) expected = {'args': args, - 'command': [kubernetes.ContainerObject.COMMAND_DEFAULT], + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, 'name': 'cname-container', 'volumeMounts': container_obj._create_volume_mounts()} @@ -314,7 +314,7 @@ class ContainerObjectTestCase(base.BaseUnitTestCase): 'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount], args=args, securityContext={'key': 'value'}) expected = {'args': args, - 'command': [kubernetes.ContainerObject.COMMAND_DEFAULT], + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, 'name': 'cname-container', 'volumeMounts': container_obj._create_volume_mounts(), @@ -330,7 +330,7 @@ class ContainerObjectTestCase(base.BaseUnitTestCase): args=args, env=[{'name': 'fake_var_name', 'value': 'fake_var_value'}]) expected = {'args': args, - 'command': [kubernetes.ContainerObject.COMMAND_DEFAULT], + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, 'name': 'cname-container', 'volumeMounts': container_obj._create_volume_mounts(), @@ -351,8 +351,7 @@ class ContainerObjectTestCase(base.BaseUnitTestCase): 'invalid_varible': 'fakeinvalid_varible', 'hostIP': 'fake_port_number'}]) expected = {'args': args, - 'command': [ - kubernetes.ContainerObject.COMMAND_DEFAULT], + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, 'name': 'cname-container', 'volumeMounts': container_obj._create_volume_mounts(), @@ -387,7 +386,7 @@ class ContainerObjectTestCase(base.BaseUnitTestCase): 'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount], args=args, resources=resources) expected = {'args': args, - 'command': [kubernetes.ContainerObject.COMMAND_DEFAULT], + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, 'name': 'cname-container', 'volumeMounts': container_obj._create_volume_mounts(), @@ -395,6 +394,45 @@ class ContainerObjectTestCase(base.BaseUnitTestCase): 'limits': {'key2': 'val2'}}} self.assertEqual(expected, container_obj.get_container_item()) + def test_get_container_item_image_pull_policy(self): + container_obj = kubernetes.ContainerObject( + 'cname', ssh_key='fake_sshkey', imagePullPolicy='Always') + expected = {'args': [], + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, + 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, + 'name': 'cname-container', + 'volumeMounts': container_obj._create_volume_mounts(), + 'imagePullPolicy':'Always'} + self.assertEqual(expected, container_obj.get_container_item()) + + def test_get_container_item_with_tty_stdin(self): + args = ['arg1', 'arg2'] + container_obj = kubernetes.ContainerObject( + 'cname', 'fake_sshkey', args=args, tty=False, stdin=True) + expected = {'args': args, + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, + 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, + 'name': 'cname-container', + 'volumeMounts': container_obj._create_volume_mounts(), + 'tty': False, + 'stdin': True} + self.assertEqual(expected, container_obj.get_container_item()) + + def test__parse_commands_string(self): + container_obj = kubernetes.ContainerObject('cname', 'fake_sshkey') + self.assertEqual(['fake command'], + container_obj._parse_commands('fake command')) + + def test__parse_commands_list(self): + container_obj = kubernetes.ContainerObject('cname', 'fake_sshkey') + self.assertEqual(['cmd1', 'cmd2'], + container_obj._parse_commands(['cmd1', 'cmd2'])) + + def test__parse_commands_exception(self): + container_obj = kubernetes.ContainerObject('cname', 'fake_sshkey') + with self.assertRaises(exceptions.KubernetesContainerCommandType): + container_obj._parse_commands({}) + class CustomResourceDefinitionObjectTestCase(base.BaseUnitTestCase): @@ -513,24 +551,52 @@ class ServiceNodePortObjectTestCase(base.BaseUnitTestCase): def test__init(self): with mock.patch.object(kubernetes.ServiceNodePortObject, '_add_port') \ as mock_add_port: - kubernetes.ServiceNodePortObject('fake_name', - node_ports=[{'port': 80}]) + kubernetes.ServiceNodePortObject( + 'fake_name', node_ports=[{'port': 80, 'name': 'web'}]) + + mock_add_port.assert_has_calls([mock.call(22, 'ssh', protocol='TCP'), + mock.call(80, 'web')]) - mock_add_port.assert_has_calls([mock.call(22, protocol='TCP'), - mock.call(80)]) + @mock.patch.object(kubernetes.ServiceNodePortObject, '_add_port') + def test__init_missing_mandatory_parameters(self, *args): + with self.assertRaises( + exceptions.KubernetesServiceObjectDefinitionError): + kubernetes.ServiceNodePortObject( + 'fake_name', node_ports=[{'port': 80}]) + with self.assertRaises( + exceptions.KubernetesServiceObjectDefinitionError): + kubernetes.ServiceNodePortObject( + 'fake_name', node_ports=[{'name': 'web'}]) + + @mock.patch.object(kubernetes.ServiceNodePortObject, '_add_port') + def test__init_missing_bad_name(self, *args): + with self.assertRaises( + exceptions.KubernetesServiceObjectNameError): + kubernetes.ServiceNodePortObject( + 'fake_name', node_ports=[{'port': 80, 'name': '-web'}]) + with self.assertRaises( + exceptions.KubernetesServiceObjectNameError): + kubernetes.ServiceNodePortObject( + 'fake_name', node_ports=[{'port': 80, 'name': 'Web'}]) + with self.assertRaises( + exceptions.KubernetesServiceObjectNameError): + kubernetes.ServiceNodePortObject( + 'fake_name', node_ports=[{'port': 80, 'name': 'web-'}]) def test__add_port(self): nodeport_object = kubernetes.ServiceNodePortObject('fake_name') - port_ssh = {'port': 22, - 'protocol': 'TCP',} + port_ssh = {'name': 'ssh', + 'port': 22, + 'protocol': 'TCP'} port_definition = {'port': 80, 'protocol': 'TCP', 'name': 'web', 'targetPort': 10080, 'nodePort': 30080} port = copy.deepcopy(port_definition) - port.pop('port') - nodeport_object._add_port(80, **port) + _port = port.pop('port') + name = port.pop('name') + nodeport_object._add_port(_port, name, **port) self.assertEqual([port_ssh, port_definition], nodeport_object.template['spec']['ports']) @@ -545,7 +611,8 @@ class ServiceNodePortObjectTestCase(base.BaseUnitTestCase): def test_delete(self, mock_delete_service): nodeport_object = kubernetes.ServiceNodePortObject('fake_name') nodeport_object.delete() - mock_delete_service.assert_called_once_with('fake_name-service') + mock_delete_service.assert_called_once_with('fake_name-service', + skip_codes=[404]) class KubernetesTemplate(base.BaseUnitTestCase): |