From 9c70924dc338697f2220b0c6f1687bac2963216f Mon Sep 17 00:00:00 2001 From: Sanjay Chari Date: Tue, 4 Oct 2022 15:05:46 +0530 Subject: [PATCH] Refactor stress-ng workload This patch refactors the stress-ng workload code to make it easier to integrate with dynamic workloads. A parameter has been added for nova_api_version as this is required in dynamic workloads to use tags for VMs. The file stress-ng.py has been renamed to stress_ng.py and stress-ng.yml to stress_ng.yml, in order to adhere to Python module naming conventions. Without this change, a syntax error occurs while importing the module. Common code that can be used for dynamic workloads has been moved to a new file called stress_ng_utils.py. Browbeat results and logs : http://perfscale.perf.lab.eng.bos.redhat.com/pub/schari/browbeat_logs/stressng_refactoring/ Change-Id: Ifdcdd1e91658f48d6b19275446f1fb3df4e9575f --- browbeat-config.yaml | 3 +- rally/rally-plugins/workloads/stress_ng.py | 59 +++++++++ .../{stress-ng.yml => stress_ng.yml} | 5 + .../{stress-ng.py => stress_ng_utils.py} | 123 ++++++++---------- 4 files changed, 123 insertions(+), 67 deletions(-) create mode 100644 rally/rally-plugins/workloads/stress_ng.py rename rally/rally-plugins/workloads/{stress-ng.yml => stress_ng.yml} (89%) rename rally/rally-plugins/workloads/{stress-ng.py => stress_ng_utils.py} (73%) diff --git a/browbeat-config.yaml b/browbeat-config.yaml index 0ec7745fb..4be0f19e7 100644 --- a/browbeat-config.yaml +++ b/browbeat-config.yaml @@ -640,6 +640,7 @@ workloads: - name: browbeat-stress-ng enabled: false + nova_api_version: 2.52 username: centos image_name: browbeat-stress-ng flavor_name: m1.small @@ -651,7 +652,7 @@ workloads: vm: 1 vm_bytes: '1G' timeout: '60s' - file: rally/rally-plugins/workloads/stress-ng.yml + file: rally/rally-plugins/workloads/stress_ng.yml - name: dynamic-workloads enabled: false diff --git a/rally/rally-plugins/workloads/stress_ng.py b/rally/rally-plugins/workloads/stress_ng.py new file mode 100644 index 000000000..942ba586b --- /dev/null +++ b/rally/rally-plugins/workloads/stress_ng.py @@ -0,0 +1,59 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from rally_openstack.common import consts +from rally.task import scenario +from rally.task import types +from rally.task import validation +import stress_ng_utils + +@types.convert(image={"type": "glance_image"}, + flavor={"type": "nova_flavor"}) +@validation.add("image_valid_on_flavor", flavor_param="flavor", + image_param="image") +@validation.add("number", param_name="port", minval=1, maxval=65535, + nullable=True, integer_only=True) +@validation.add("external_network_exists", param_name="floating_network") +@validation.add("required_services", services=[consts.Service.NOVA]) +@validation.add("required_param_or_context", + param_name="image", ctx_name="image_command_customizer") +@validation.add("required_platform", platform="openstack", users=True) +@scenario.configure(context={"cleanup@openstack": ["nova", "neutron"], + "keypair@openstack": {}, + "allow_ssh@openstack": None}, + name="BrowbeatPlugin.stress_ng", + platform="openstack") +class BrowbeatStressNg(stress_ng_utils.BrowbeatStressNgUtils): + + def run(self, flavor, username, ssh_timeout, num_clients, + command, image=None, floating_network=None, port=22, + use_floating_ip=True, nova_api_version=2.52, **kwargs): + """Create a jumphost on network with fip and all + other vm's on the same neutron network so that jumphost + can access the other vm's and run the stress tests + + :param flavor: VM flavor name + :param username: ssh username on server + :param ssh_timeout: ssh timeout in seconds + :param num_clients: no.of clients + :param command: command that runs inside the client vm's + :param image: VM image name + :param floating_network: external network name, for floating ip + :param port: ssh port for SSH connection + :param use_floating_ip: bool, floating or fixed IP for SSH connection + :param nova_api_version: api microversion of nova + :param kwargs: optional args to create a VM + """ + self.run_stress_ng_on_clients(flavor, username, ssh_timeout, num_clients, + command, image, floating_network, port, + use_floating_ip, "stressng", + nova_api_version, **kwargs) diff --git a/rally/rally-plugins/workloads/stress-ng.yml b/rally/rally-plugins/workloads/stress_ng.yml similarity index 89% rename from rally/rally-plugins/workloads/stress-ng.yml rename to rally/rally-plugins/workloads/stress_ng.yml index ecceb426d..b2d78281d 100644 --- a/rally/rally-plugins/workloads/stress-ng.yml +++ b/rally/rally-plugins/workloads/stress_ng.yml @@ -1,3 +1,4 @@ +{% set nova_api_version = nova_api_version or 2.52 %} {% set flavor_name = flavor_name or "m1.small" %} {% set image_name = image_name or "browbeat-stress-ng" %} {% set username = username or "centos7" %} @@ -25,6 +26,7 @@ ssh_timeout: {{ssh_timeout}} num_clients: {{num_clients}} command: "stress-ng --cpu {{cpu}} --io {{io}} --vm {{vm}} --vm-bytes {{vm_bytes}} --timeout {{timeout}} --metrics-brief" + nova_api_version: {{ nova_api_version }} runner: type: "constant" times: {{times}} @@ -33,6 +35,9 @@ users: tenants: 3 users_per_tenant: 2 + api_versions: + nova: + version: {{ nova_api_version }} network: {} quotas: neutron: diff --git a/rally/rally-plugins/workloads/stress-ng.py b/rally/rally-plugins/workloads/stress_ng_utils.py similarity index 73% rename from rally/rally-plugins/workloads/stress-ng.py rename to rally/rally-plugins/workloads/stress_ng_utils.py index 2c9f98b6b..24c4648d7 100644 --- a/rally/rally-plugins/workloads/stress-ng.py +++ b/rally/rally-plugins/workloads/stress_ng_utils.py @@ -12,79 +12,15 @@ import logging -from rally_openstack.common import consts from rally_openstack.task.scenarios.vm import utils as vm_utils from rally_openstack.task.scenarios.neutron import utils as neutron_utils from rally.utils import sshutils -from rally.task import scenario -from rally.task import types -from rally.task import validation LOG = logging.getLogger(__name__) -@types.convert(image={"type": "glance_image"}, - flavor={"type": "nova_flavor"}) -@validation.add("image_valid_on_flavor", flavor_param="flavor", - image_param="image") -@validation.add("number", param_name="port", minval=1, maxval=65535, - nullable=True, integer_only=True) -@validation.add("external_network_exists", param_name="floating_network") -@validation.add("required_services", services=[consts.Service.NOVA]) -@validation.add("required_param_or_context", - param_name="image", ctx_name="image_command_customizer") -@validation.add("required_platform", platform="openstack", users=True) -@scenario.configure(context={"cleanup@openstack": ["nova", "neutron"], - "keypair@openstack": {}, - "allow_ssh@openstack": None}, - name="BrowbeatPlugin.stress_ng", - platform="openstack") -class BrowbeatStressNg(vm_utils.VMScenario, neutron_utils.NeutronScenario): - - def run(self, flavor, username, ssh_timeout, num_clients, - command, image=None, floating_network=None, port=22, - use_floating_ip=True, **kwargs): - """Create a jumphost on network with fip and all - other vm's on the same neutron network so that jumphost - can access the other vm's and run the stress tests - - :param flavor: VM flavor name - :param username: ssh username on server - :param ssh_timeout: ssh timeout in seconds - :param num_clients: no.of clients - :param command: command that runs inside the client vm's - :param floating_network: external network name, for floating ip - :param port: ssh port for SSH connection - :param use_floating_ip: bool, floating or fixed IP for SSH connection - :param kwargs: optional args to create a VM - """ - jump_host, fip = self._boot_server_with_fip( - image, flavor, use_floating_ip=use_floating_ip, - floating_network=floating_network, - key_name=self.context["user"]["keypair"]["name"], - **kwargs) - ssh = sshutils.SSH(username, fip["ip"], port=port, pkey=self.context[ - "user"]["keypair"]["private"]) - self._wait_for_ssh(ssh, timeout=ssh_timeout) - - # Write id_rsa to get to guests. - self._run_command_over_ssh(ssh, {'remote_path': "rm -rf ~/.ssh"}) - self._run_command_over_ssh(ssh, {'remote_path': "mkdir ~/.ssh"}) - ssh.run( - "cat > ~/.ssh/id_rsa", - stdin=self.context["user"]["keypair"]["private"]) - ssh.execute("chmod 0600 ~/.ssh/id_rsa") - - _clients = self.create_clients( - ssh, num_clients, image, flavor, username, **kwargs) - - # Run stress test - for sip in _clients: - cmd = " {} 'ssh {}@{}' ".format(command, username, sip) - exitcode, stdout, stderr = ssh.execute(cmd) - if exitcode != 0: - raise Exception(" couldn't run the stress-ng command: {}".format(stderr)) +class BrowbeatStressNgUtils(vm_utils.VMScenario, neutron_utils.NeutronScenario): def create_clients(self, jump_ssh, num_clients, image, flavor, user, **kwargs): """Creates Client VM's @@ -98,7 +34,8 @@ class BrowbeatStressNg(vm_utils.VMScenario, neutron_utils.NeutronScenario): """ _clients = [] for i in range(num_clients): - LOG.info("Launching Client : {}".format(i)) + LOG.info("ITER: {} Launching stress-ng client : {}".format( + self.context["iteration"], i)) server = self._boot_server( image, flavor, @@ -118,3 +55,57 @@ class BrowbeatStressNg(vm_utils.VMScenario, neutron_utils.NeutronScenario): user, sip) s1_exitcode, s1_stdout, s1_stderr = jump_ssh.execute(cmd) return _clients + + def run_stress_ng_on_clients(self, flavor, username, ssh_timeout, num_clients, + command, image=None, floating_network=None, port=22, + use_floating_ip=True, tag=None, nova_api_version=2.52, + **kwargs): + """Create a jumphost on network with fip and all + other vm's on the same neutron network so that jumphost + can access the other vm's and run the stress tests + + :param flavor: VM flavor name + :param username: ssh username on server + :param ssh_timeout: ssh timeout in seconds + :param num_clients: no.of clients + :param command: command that runs inside the client vm's + :param image: VM image name + :param floating_network: external network name, for floating ip + :param port: ssh port for SSH connection + :param use_floating_ip: bool, floating or fixed IP for SSH connection + :param tag: str, tag for the VM + :param nova_api_version: api microversion of nova + :param kwargs: optional args to create a VM + """ + # tags for VMs are available only for nova_api_version >= 2.52 + if nova_api_version >= 2.52: + kwargs["tags"] = ["{}_jumphost".format(tag)] + jump_host, fip = self._boot_server_with_fip( + image, flavor, use_floating_ip=use_floating_ip, + floating_network=floating_network, + key_name=self.context["user"]["keypair"]["name"], + **kwargs) + ssh = sshutils.SSH(username, fip["ip"], port=port, pkey=self.context[ + "user"]["keypair"]["private"]) + self._wait_for_ssh(ssh, timeout=ssh_timeout) + + # Write id_rsa to get to guests. + self._run_command_over_ssh(ssh, {'remote_path': "rm -rf ~/.ssh"}) + self._run_command_over_ssh(ssh, {'remote_path': "mkdir ~/.ssh"}) + ssh.run( + "cat > ~/.ssh/id_rsa", + stdin=self.context["user"]["keypair"]["private"]) + ssh.execute("chmod 0600 ~/.ssh/id_rsa") + + # tags for VMs are available only for nova_api_version >= 2.52 + if nova_api_version >= 2.52: + kwargs["tags"] = ["{}_client".format(tag)] + _clients = self.create_clients( + ssh, num_clients, image, flavor, username, **kwargs) + + # Run stress test + for sip in _clients: + cmd = " {} 'ssh {}@{}' ".format(command, username, sip) + exitcode, stdout, stderr = ssh.execute(cmd) + if exitcode != 0: + raise Exception(" couldn't run the stress-ng command: {}".format(stderr))