From fd0a88140f7114ef90fabdfb5436cff42ec0cd2c Mon Sep 17 00:00:00 2001 From: chenjiankun Date: Mon, 17 Apr 2017 15:49:17 +0000 Subject: Test Case: OPNFV_YARDSTICK_TC023: VM availability during live migration JIRA: YARDSTICK-174 This live migration test case is based on share storage, default share storage is enabled. This test case will do some config work. And do live migration and calculate the migration time and downtime. Change-Id: I6601601edebdd0ac6434ba632b1eba9e9bd4fda0 Signed-off-by: chenjiankun --- .gitignore | 1 + ansible/inventory.ini | 5 +- ansible/migrate_pinning_setup.yaml | 49 ++++++ ansible/migrate_pinning_teardown.yaml | 31 ++++ ansible/roles/backup_nova_conf/tasks/main.yaml | 18 +++ ansible/roles/create_flavor/tasks/main.yaml | 16 ++ ansible/roles/delete_flavor/tasks/main.yaml | 13 ++ ansible/roles/recover_nova_conf/tasks/main.yaml | 18 +++ ansible/roles/restart_nova_service/tasks/main.yaml | 23 +++ ansible/roles/set_flavor_property/tasks/main.yaml | 14 ++ ansible/roles/set_nova_conf/tasks/main.yaml | 17 ++ etc/yardstick/nodes/compass_sclab_virtual/pod.yaml | 5 + requirements.txt | 1 + tests/opnfv/test_cases/opnfv_yardstick_tc023.yaml | 176 +++++++++++++++++++++ tests/unit/benchmark/scenarios/lib/__init__.py | 0 .../scenarios/lib/test_add_memory_load.py | 65 ++++++++ .../scenarios/lib/test_check_numa_info.py | 84 ++++++++++ .../benchmark/scenarios/lib/test_check_value.py | 46 ++++++ .../scenarios/lib/test_get_migrate_target_host.py | 51 ++++++ .../benchmark/scenarios/lib/test_get_numa_info.py | 106 +++++++++++++ .../benchmark/scenarios/lib/test_get_server.py | 50 ++++++ .../benchmark/scenarios/lib/test_get_server_ip.py | 41 +++++ yardstick/benchmark/scenarios/base.py | 12 ++ yardstick/benchmark/scenarios/lib/__init__.py | 0 .../benchmark/scenarios/lib/add_memory_load.py | 57 +++++++ .../benchmark/scenarios/lib/check_numa_info.py | 61 +++++++ yardstick/benchmark/scenarios/lib/check_value.py | 58 +++++++ .../scenarios/lib/get_migrate_target_host.py | 56 +++++++ yardstick/benchmark/scenarios/lib/get_numa_info.py | 79 +++++++++ yardstick/benchmark/scenarios/lib/get_server.py | 83 ++++++++++ yardstick/benchmark/scenarios/lib/get_server_ip.py | 38 +++++ yardstick/benchmark/scenarios/lib/migrate.py | 155 ++++++++++++++++++ 32 files changed, 1428 insertions(+), 1 deletion(-) create mode 100644 ansible/migrate_pinning_setup.yaml create mode 100644 ansible/migrate_pinning_teardown.yaml create mode 100644 ansible/roles/backup_nova_conf/tasks/main.yaml create mode 100644 ansible/roles/create_flavor/tasks/main.yaml create mode 100644 ansible/roles/delete_flavor/tasks/main.yaml create mode 100644 ansible/roles/recover_nova_conf/tasks/main.yaml create mode 100644 ansible/roles/restart_nova_service/tasks/main.yaml create mode 100644 ansible/roles/set_flavor_property/tasks/main.yaml create mode 100644 ansible/roles/set_nova_conf/tasks/main.yaml create mode 100644 tests/opnfv/test_cases/opnfv_yardstick_tc023.yaml create mode 100644 tests/unit/benchmark/scenarios/lib/__init__.py create mode 100644 tests/unit/benchmark/scenarios/lib/test_add_memory_load.py create mode 100644 tests/unit/benchmark/scenarios/lib/test_check_numa_info.py create mode 100644 tests/unit/benchmark/scenarios/lib/test_check_value.py create mode 100644 tests/unit/benchmark/scenarios/lib/test_get_migrate_target_host.py create mode 100644 tests/unit/benchmark/scenarios/lib/test_get_numa_info.py create mode 100644 tests/unit/benchmark/scenarios/lib/test_get_server.py create mode 100644 tests/unit/benchmark/scenarios/lib/test_get_server_ip.py create mode 100644 yardstick/benchmark/scenarios/lib/__init__.py create mode 100644 yardstick/benchmark/scenarios/lib/add_memory_load.py create mode 100644 yardstick/benchmark/scenarios/lib/check_numa_info.py create mode 100644 yardstick/benchmark/scenarios/lib/check_value.py create mode 100644 yardstick/benchmark/scenarios/lib/get_migrate_target_host.py create mode 100644 yardstick/benchmark/scenarios/lib/get_numa_info.py create mode 100644 yardstick/benchmark/scenarios/lib/get_server.py create mode 100644 yardstick/benchmark/scenarios/lib/get_server_ip.py create mode 100644 yardstick/benchmark/scenarios/lib/migrate.py diff --git a/.gitignore b/.gitignore index a53b53390..d98b4a070 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ build htmlcov .agignore .coverage +*.retry Session*.vim .tags* .coverage.* diff --git a/ansible/inventory.ini b/ansible/inventory.ini index 440e625bd..79a6ee0aa 100644 --- a/ansible/inventory.ini +++ b/ansible/inventory.ini @@ -1,8 +1,11 @@ [controller] host1 ansible_host=10.1.0.50 ansible_user=root ansible_ssh_pass=root host2 ansible_host=10.1.0.51 ansible_user=root ansible_ssh_pass=root -host3 ansible_host=10.1.0.52 ansible_user=root ansible_ssh_pass=root [compute] host4 ansible_host=10.1.0.53 ansible_user=root ansible_ssh_pass=root host5 ansible_host=10.1.0.54 ansible_user=root ansible_ssh_pass=root + +[nodes:children] +controller +compute diff --git a/ansible/migrate_pinning_setup.yaml b/ansible/migrate_pinning_setup.yaml new file mode 100644 index 000000000..ee5eef3ff --- /dev/null +++ b/ansible/migrate_pinning_setup.yaml @@ -0,0 +1,49 @@ +--- +############################################################################## +# Copyright (c) 2017 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 +############################################################################## + +- hosts: localhost + roles: + - create_flavor + - role: set_flavor_property + key: "hw:cpu_policy" + value: "dedicated" + - role: set_flavor_property + key: "hw:numa_nodes" + value: "1" + +- hosts: nodes + roles: + - backup_nova_conf + - role: set_nova_conf + section: "DEFAULT" + key: "live_migration_flag" + value: "VIR_MIGRATE_UNDEFINE_SOURCE,VIR_MIGRATE_PEER2PEER,VIR_MIGRATE_LIVE,VIR_MIGRATE_TUNNELLED" + - role: set_nova_conf + section: "DEFAULT" + key: "vncserver_listen" + value: "0.0.0.0" + +- hosts: controller + roles: + - role: set_nova_conf + section: "DEFAULT" + key: "scheduler_default_filters" + value: "NUMATopologyFilter" + - role: restart_nova_service + service: "nova-scheduler" + +- hosts: compute + roles: + - role: set_nova_conf + section: "DEFAULT" + key: "vcpu_pin_set" + value: "{{ cpu_set }}" + - role: restart_nova_service + service: "nova-compute" diff --git a/ansible/migrate_pinning_teardown.yaml b/ansible/migrate_pinning_teardown.yaml new file mode 100644 index 000000000..13dd6113c --- /dev/null +++ b/ansible/migrate_pinning_teardown.yaml @@ -0,0 +1,31 @@ +--- +############################################################################## +# Copyright (c) 2017 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 +############################################################################## + +- hosts: localhost + roles: + - delete_flavor + +- hosts: nodes + roles: + - recover_nova_conf + +- hosts: controller + roles: + - role: restart_nova_service + service: "nova-scheduler" + - role: restart_nova_service + service: "nova-api" + - role: restart_nova_service + service: "nova-conductor" + +- hosts: compute + roles: + - role: restart_nova_service + service: "nova-compute" diff --git a/ansible/roles/backup_nova_conf/tasks/main.yaml b/ansible/roles/backup_nova_conf/tasks/main.yaml new file mode 100644 index 000000000..ca95bac59 --- /dev/null +++ b/ansible/roles/backup_nova_conf/tasks/main.yaml @@ -0,0 +1,18 @@ +--- +############################################################################## +# Copyright (c) 2017 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 +############################################################################## + +- name: backup nova.conf file + copy: + src: /etc/nova/nova.conf + dest: /tmp/nova.conf + owner: nova + group: nova + remote_src: True + become: true diff --git a/ansible/roles/create_flavor/tasks/main.yaml b/ansible/roles/create_flavor/tasks/main.yaml new file mode 100644 index 000000000..9b776c694 --- /dev/null +++ b/ansible/roles/create_flavor/tasks/main.yaml @@ -0,0 +1,16 @@ +############################################################################## +# Copyright (c) 2017 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 +############################################################################## +- name: create flavor {{ flavor }} + os_nova_flavor: + cloud: opnfv + state: present + name: "{{ flavor }}" + ram: "{{ ram }}" + vcpus: "{{ vcpus }}" + disk: "{{ disk }}" diff --git a/ansible/roles/delete_flavor/tasks/main.yaml b/ansible/roles/delete_flavor/tasks/main.yaml new file mode 100644 index 000000000..dc9fc88ce --- /dev/null +++ b/ansible/roles/delete_flavor/tasks/main.yaml @@ -0,0 +1,13 @@ +############################################################################## +# Copyright (c) 2017 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 +############################################################################## +- name: delete flavor {{ flavor }} + os_nova_flavor: + cloud: opnfv + state: absent + name: "{{ flavor }}" diff --git a/ansible/roles/recover_nova_conf/tasks/main.yaml b/ansible/roles/recover_nova_conf/tasks/main.yaml new file mode 100644 index 000000000..44919d2ae --- /dev/null +++ b/ansible/roles/recover_nova_conf/tasks/main.yaml @@ -0,0 +1,18 @@ +--- +############################################################################## +# Copyright (c) 2017 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 +############################################################################## + +- name: recover nova.conf file + copy: + src: /tmp/nova.conf + dest: /etc/nova/nova.conf + owner: nova + group: nova + remote_src: True + become: true diff --git a/ansible/roles/restart_nova_service/tasks/main.yaml b/ansible/roles/restart_nova_service/tasks/main.yaml new file mode 100644 index 000000000..2bdce652d --- /dev/null +++ b/ansible/roles/restart_nova_service/tasks/main.yaml @@ -0,0 +1,23 @@ +--- +############################################################################## +# Copyright (c) 2017 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 +############################################################################## + +- name: restart "{{ service }}" service + service: + name: "{{ service }}" + state: restarted + become: true + when: ansible_os_family == "Debian" + +- name: restart "openstack-{{ service }}" service + service: + name: "openstack-{{ service }}" + state: restarted + become: true + when: ansible_os_family == "RedHat" diff --git a/ansible/roles/set_flavor_property/tasks/main.yaml b/ansible/roles/set_flavor_property/tasks/main.yaml new file mode 100644 index 000000000..f98988783 --- /dev/null +++ b/ansible/roles/set_flavor_property/tasks/main.yaml @@ -0,0 +1,14 @@ +############################################################################## +# Copyright (c) 2017 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 +############################################################################## +- name: set flavor "{{ flavor }}" property {{ key }} = {{ value }} + shell: + source /etc/yardstick/openstack.creds; + openstack flavor set --property {{ key }}={{ value }} {{ flavor }}; + args: + executable: /bin/bash diff --git a/ansible/roles/set_nova_conf/tasks/main.yaml b/ansible/roles/set_nova_conf/tasks/main.yaml new file mode 100644 index 000000000..ae665c5d0 --- /dev/null +++ b/ansible/roles/set_nova_conf/tasks/main.yaml @@ -0,0 +1,17 @@ +--- +############################################################################## +# Copyright (c) 2017 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 +############################################################################## + +- name: set "{{ key }}" value + ini_file: + dest: /etc/nova/nova.conf + section: "{{ section }}" + option: "{{ key }}" + value: "{{ value }}" + become: true diff --git a/etc/yardstick/nodes/compass_sclab_virtual/pod.yaml b/etc/yardstick/nodes/compass_sclab_virtual/pod.yaml index 5c5574005..e306d0d94 100644 --- a/etc/yardstick/nodes/compass_sclab_virtual/pod.yaml +++ b/etc/yardstick/nodes/compass_sclab_virtual/pod.yaml @@ -20,30 +20,35 @@ nodes: - name: node1 + host_name: host1 role: Controller ip: 10.1.0.50 user: root password: root - name: node2 + host_name: host2 role: Controller ip: 10.1.0.51 user: root password: root - name: node3 + host_name: host3 role: Controller ip: 10.1.0.52 user: root password: root - name: node4 + host_name: host4 role: Compute ip: 10.1.0.53 user: root password: root - name: node5 + host_name: host5 role: Compute ip: 10.1.0.54 user: root diff --git a/requirements.txt b/requirements.txt index 3a4cbce0c..2bcc4dfa7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -59,6 +59,7 @@ oslo.utils==3.22.0 paramiko==2.1.1 pbr==1.10.0 pep8==1.7.0 +ping==0.2; python_version <= '2.7' pika==0.10.0 positional==1.1.1 prettytable==0.7.2 diff --git a/tests/opnfv/test_cases/opnfv_yardstick_tc023.yaml b/tests/opnfv/test_cases/opnfv_yardstick_tc023.yaml new file mode 100644 index 000000000..2804f25a2 --- /dev/null +++ b/tests/opnfv/test_cases/opnfv_yardstick_tc023.yaml @@ -0,0 +1,176 @@ +############################################################################## +# Copyright (c) 2017 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 +############################################################################## +--- + +schema: "yardstick:task:0.1" + +{% set file = file or "etc/yardstick/nodes/compass_sclab_virtual/pod.yaml" %} +{% set cpu_set = cpu_set or "0,1,2,3" %} +{% set memory_load = memory_load or 0 %} + +{% set flavor = flavor or "yardstick-migrate-flavor" %} +{% set ram = ram or "2048" %} +{% set vcpus = vcpus or "2" %} +{% set disk = disk or "3" %} + +scenarios: +- + type: GetServer + + output: status server + + host: server.migrate + + runner: + type: Iteration + iteration: 1 +- + type: GetNumaInfo + + options: + server: $server + file: {{ file }} + + output: origin_numa_info + + host: server.migrate + + runner: + type: Iteration + iteration: 1 +- + type: GetMigrateTargetHost + + options: + server: $server + output: target_host + + runner: + type: Iteration + iteration: 1 +- + type: GetServerIp + + options: + server: $server + + output: server_ip + + runner: + type: Iteration + iteration: 1 +- + type: AddMemoryLoad + + options: + memory_load: {{ memory_load }} + + host: server.migrate + + runner: + type: Iteration + iteration: 1 +- + type: Migrate + + options: + server: $server + host: $target_host + server_ip: $server_ip + + output: status migrate_time1 downtime1 + + runner: + type: Iteration + iteration: 1 +- + type: CheckValue + + options: + value1: $status + value2: 0 + operator: eq + + runner: + type: Iteration + iteration: 1 +- + type: GetServer + + output: status server + + host: server.migrate + + runner: + type: Iteration + iteration: 1 +- + type: GetNumaInfo + + options: + server: $server + file: {{ file }} + + output: new_numa_info + + host: server.migrate + + runner: + type: Iteration + iteration: 1 +- + type: CheckNumaInfo + + options: + info1: $origin_numa_info + info2: $new_numa_info + cpu_set: {{ cpu_set }} + + output: status + + runner: + type: Iteration + iteration: 1 +- + type: CheckValue + + options: + value1: $status + value2: true + operator: eq + + runner: + type: Iteration + iteration: 1 + + +contexts: +- + type: Node + name: env-prepare + file: {{ file }} + + env: + type: ansible + setup: migrate_pinning_setup.yaml -e "flavor={{ flavor }} ram={{ ram }} vcpus={{ vcpus }} disk={{ disk }} cpu_set={{ cpu_set }}" + teardown: migrate_pinning_teardown.yaml -e "flavor={{ flavor }}" + +- + name: migrate + image: yardstick-image + flavor: {{ flavor }} + user: ubuntu + + servers: + server: + floating_ip: true + + networks: + test: + cidr: '10.0.1.0/24' diff --git a/tests/unit/benchmark/scenarios/lib/__init__.py b/tests/unit/benchmark/scenarios/lib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/benchmark/scenarios/lib/test_add_memory_load.py b/tests/unit/benchmark/scenarios/lib/test_add_memory_load.py new file mode 100644 index 000000000..bda07f723 --- /dev/null +++ b/tests/unit/benchmark/scenarios/lib/test_add_memory_load.py @@ -0,0 +1,65 @@ +############################################################################## +# Copyright (c) 2017 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 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.add_memory_load import AddMemoryLoad + + +class AddMemoryLoadTestCase(unittest.TestCase): + + @mock.patch('yardstick.ssh.SSH.from_node') + def test_add_memory_load_with_load(self, mock_from_node): + scenario_cfg = { + 'options': { + 'memory_load': 0.5 + } + } + context_cfg = { + 'host': {} + } + mock_from_node().execute.return_value = (0, '0 2048 512', '') + obj = AddMemoryLoad(scenario_cfg, context_cfg) + obj.run({}) + self.assertTrue(mock_from_node.called) + + @mock.patch('yardstick.ssh.SSH.from_node') + def test_add_memory_load_without_load(self, mock_from_node): + scenario_cfg = { + 'options': { + 'memory_load': 0 + } + } + context_cfg = { + 'host': {} + } + obj = AddMemoryLoad(scenario_cfg, context_cfg) + obj.run({}) + self.assertTrue(mock_from_node.called) + + @mock.patch('yardstick.ssh.SSH.from_node') + def test_add_memory_load_without_args(self, mock_from_node): + scenario_cfg = { + 'options': { + } + } + context_cfg = { + 'host': {} + } + obj = AddMemoryLoad(scenario_cfg, context_cfg) + obj.run({}) + self.assertTrue(mock_from_node.called) + + +def main(): + unittest.main() + + +if __name__ == '__main__': + main() diff --git a/tests/unit/benchmark/scenarios/lib/test_check_numa_info.py b/tests/unit/benchmark/scenarios/lib/test_check_numa_info.py new file mode 100644 index 000000000..bdf1e66e5 --- /dev/null +++ b/tests/unit/benchmark/scenarios/lib/test_check_numa_info.py @@ -0,0 +1,84 @@ +############################################################################## +# Copyright (c) 2017 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 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.check_numa_info import CheckNumaInfo + + +class CheckNumaInfoTestCase(unittest.TestCase): + + @mock.patch('yardstick.benchmark.scenarios.lib.check_numa_info.CheckNumaInfo._check_vm2_status') + def test_check_numa_info(self, mock_check_vm2): + scenario_cfg = {'info1': {}, 'info2': {}} + obj = CheckNumaInfo(scenario_cfg, {}) + obj.run({}) + self.assertTrue(mock_check_vm2.called) + + def test_check_vm2_status_length_eq_1(self): + info1 = { + 'pinning': [0], + 'vcpupin': [{ + 'cpuset': '1,2' + }] + } + info2 = { + 'pinning': [0], + 'vcpupin': [{ + 'cpuset': '1,2' + }] + } + scenario_cfg = {'info1': info1, 'info2': info2} + obj = CheckNumaInfo(scenario_cfg, {}) + status = obj._check_vm2_status(info1, info2) + self.assertEqual(status, True) + + def test_check_vm2_status_length_gt_1(self): + info1 = { + 'pinning': [0, 1], + 'vcpupin': [{ + 'cpuset': '1,2' + }] + } + info2 = { + 'pinning': [0, 1], + 'vcpupin': [{ + 'cpuset': '1,2' + }] + } + scenario_cfg = {'info1': info1, 'info2': info2} + obj = CheckNumaInfo(scenario_cfg, {}) + status = obj._check_vm2_status(info1, info2) + self.assertEqual(status, False) + + def test_check_vm2_status_length_not_in_set(self): + info1 = { + 'pinning': [0], + 'vcpupin': [{ + 'cpuset': '1,7' + }] + } + info2 = { + 'pinning': [0], + 'vcpupin': [{ + 'cpuset': '1,7' + }] + } + scenario_cfg = {'info1': info1, 'info2': info2} + obj = CheckNumaInfo(scenario_cfg, {}) + status = obj._check_vm2_status(info1, info2) + self.assertEqual(status, False) + + +def main(): + unittest.main() + + +if __name__ == '__main__': + main() diff --git a/tests/unit/benchmark/scenarios/lib/test_check_value.py b/tests/unit/benchmark/scenarios/lib/test_check_value.py new file mode 100644 index 000000000..21e83f830 --- /dev/null +++ b/tests/unit/benchmark/scenarios/lib/test_check_value.py @@ -0,0 +1,46 @@ +############################################################################## +# Copyright (c) 2017 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 +############################################################################## +import unittest + +from yardstick.benchmark.scenarios.lib.check_value import CheckValue + + +class CheckValueTestCase(unittest.TestCase): + + def test_check_value_eq(self): + scenario_cfg = {'options': {'operator': 'eq', 'value1': 1, 'value2': 2}} + obj = CheckValue(scenario_cfg, {}) + try: + obj.run({}) + except Exception as e: + self.assertIsInstance(e, AssertionError) + + def test_check_value_eq_pass(self): + scenario_cfg = {'options': {'operator': 'eq', 'value1': 1, 'value2': 1}} + obj = CheckValue(scenario_cfg, {}) + try: + obj.run({}) + except Exception as e: + self.assertIsInstance(e, AssertionError) + + def test_check_value_ne(self): + scenario_cfg = {'options': {'operator': 'ne', 'value1': 1, 'value2': 1}} + obj = CheckValue(scenario_cfg, {}) + try: + obj.run({}) + except Exception as e: + self.assertIsInstance(e, AssertionError) + + +def main(): + unittest.main() + + +if __name__ == '__main__': + main() diff --git a/tests/unit/benchmark/scenarios/lib/test_get_migrate_target_host.py b/tests/unit/benchmark/scenarios/lib/test_get_migrate_target_host.py new file mode 100644 index 000000000..f046c92ea --- /dev/null +++ b/tests/unit/benchmark/scenarios/lib/test_get_migrate_target_host.py @@ -0,0 +1,51 @@ +############################################################################## +# Copyright (c) 2017 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 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.get_migrate_target_host import GetMigrateTargetHost + +BASE = 'yardstick.benchmark.scenarios.lib.get_migrate_target_host' + + +class GetMigrateTargetHostTestCase(unittest.TestCase): + + @mock.patch('{}.openstack_utils.get_nova_client'.format(BASE)) + @mock.patch('{}.GetMigrateTargetHost._get_migrate_host'.format(BASE)) + @mock.patch('{}.GetMigrateTargetHost._get_current_host_name'.format(BASE)) + def test_get_migrate_target_host(self, + mock_get_current_host_name, + mock_get_migrate_host, + mock_get_nova_client): + obj = GetMigrateTargetHost({}, {}) + obj.run({}) + self.assertTrue(mock_get_nova_client.called) + self.assertTrue(mock_get_current_host_name.called) + self.assertTrue(mock_get_migrate_host.called) + + @mock.patch('{}.openstack_utils.get_nova_client'.format(BASE)) + def test_get_migrate_host(self, mock_get_nova_client): + class A(object): + def __init__(self, service): + self.service = service + self.host = 'host4' + + mock_get_nova_client().hosts.list_all.return_value = [A('compute')] + obj = GetMigrateTargetHost({}, {}) + host = obj._get_migrate_host('host5') + self.assertTrue(mock_get_nova_client.called) + self.assertEqual(host, 'host4') + + +def main(): + unittest.main() + + +if __name__ == '__main__': + main() diff --git a/tests/unit/benchmark/scenarios/lib/test_get_numa_info.py b/tests/unit/benchmark/scenarios/lib/test_get_numa_info.py new file mode 100644 index 000000000..e7ba3ca73 --- /dev/null +++ b/tests/unit/benchmark/scenarios/lib/test_get_numa_info.py @@ -0,0 +1,106 @@ +############################################################################## +# Copyright (c) 2017 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 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.get_numa_info import GetNumaInfo + +BASE = 'yardstick.benchmark.scenarios.lib.get_numa_info' + + +class GetNumaInfoTestCase(unittest.TestCase): + + @mock.patch('{}.GetNumaInfo._check_numa_node'.format(BASE)) + @mock.patch('{}.GetNumaInfo._get_current_host_name'.format(BASE)) + @mock.patch('yaml.safe_load') + @mock.patch('yardstick.common.task_template.TaskTemplate.render') + def test_get_numa_info(self, + mock_render, + mock_safe_load, + mock_get_current_host_name, + mock_check_numa_node): + scenario_cfg = { + 'options': { + 'server': { + 'id': '1' + }, + 'file': 'yardstick/ssh.py' + }, + 'output': 'numa_info' + } + mock_safe_load.return_value = { + 'nodes': [] + } + obj = GetNumaInfo(scenario_cfg, {}) + obj.run({}) + self.assertTrue(mock_get_current_host_name.called) + self.assertTrue(mock_check_numa_node.called) + + @mock.patch('yardstick.ssh.SSH.from_node') + @mock.patch('{}.GetNumaInfo._get_current_host_name'.format(BASE)) + @mock.patch('yaml.safe_load') + @mock.patch('yardstick.common.task_template.TaskTemplate.render') + def test_check_numa_node(self, + mock_render, + mock_safe_load, + mock_get_current_host_name, + mock_from_node): + scenario_cfg = { + 'options': { + 'server': { + 'id': '1' + }, + 'file': 'yardstick/ssh.py' + }, + 'output': 'numa_info' + } + mock_safe_load.return_value = { + 'nodes': [] + } + data = """ + + + """ + mock_from_node().execute.return_value = (0, data, '') + obj = GetNumaInfo(scenario_cfg, {}) + result = obj._check_numa_node('1', 'host4') + self.assertEqual(result, {'pinning': [], 'vcpupin': []}) + + @mock.patch('{}.change_obj_to_dict'.format(BASE)) + @mock.patch('{}.get_nova_client'.format(BASE)) + @mock.patch('yaml.safe_load') + @mock.patch('yardstick.common.task_template.TaskTemplate.render') + def test_get_current_host_name(self, + mock_render, + mock_safe_load, + mock_get_nova_client, + mock_change_obj_to_dict): + scenario_cfg = { + 'options': { + 'server': { + 'id': '1' + }, + 'file': 'yardstick/ssh.py' + }, + 'output': 'numa_info' + } + mock_get_nova_client().servers.get.return_value = '' + mock_change_obj_to_dict.return_value = {'OS-EXT-SRV-ATTR:host': 'host5'} + + obj = GetNumaInfo(scenario_cfg, {}) + result = obj._get_current_host_name('1') + self.assertEqual(result, 'host5') + + +def main(): + unittest.main() + + +if __name__ == '__main__': + main() diff --git a/tests/unit/benchmark/scenarios/lib/test_get_server.py b/tests/unit/benchmark/scenarios/lib/test_get_server.py new file mode 100644 index 000000000..aebbf5416 --- /dev/null +++ b/tests/unit/benchmark/scenarios/lib/test_get_server.py @@ -0,0 +1,50 @@ +############################################################################## +# Copyright (c) 2017 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 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.get_server import GetServer + + +class GetServerTestCase(unittest.TestCase): + + @mock.patch('yardstick.common.openstack_utils.get_server_by_name') + @mock.patch('yardstick.common.openstack_utils.get_nova_client') + def test_get_server_with_name(self, mock_get_nova_client, mock_get_server_by_name): + scenario_cfg = { + 'options': { + 'server_name': 'yardstick_server' + }, + 'output': 'status server' + } + obj = GetServer(scenario_cfg, {}) + obj.run({}) + self.assertTrue(mock_get_nova_client.called) + self.assertTrue(mock_get_server_by_name.called) + + @mock.patch('yardstick.common.openstack_utils.get_nova_client') + def test_get_server_with_id(self, mock_get_nova_client): + scenario_cfg = { + 'options': { + 'server_id': '1' + }, + 'output': 'status server' + } + mock_get_nova_client().servers.get.return_value = None + obj = GetServer(scenario_cfg, {}) + obj.run({}) + self.assertTrue(mock_get_nova_client.called) + + +def main(): + unittest.main() + + +if __name__ == '__main__': + main() diff --git a/tests/unit/benchmark/scenarios/lib/test_get_server_ip.py b/tests/unit/benchmark/scenarios/lib/test_get_server_ip.py new file mode 100644 index 000000000..3d20d5439 --- /dev/null +++ b/tests/unit/benchmark/scenarios/lib/test_get_server_ip.py @@ -0,0 +1,41 @@ +############################################################################## +# Copyright (c) 2017 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 +############################################################################## +import unittest + +from yardstick.benchmark.scenarios.lib.get_server_ip import GetServerIp + + +class GetServerIpTestCase(unittest.TestCase): + def test_get_server_ip(self): + scenario_cfg = { + 'options': { + 'server': { + 'addresses': { + 'net1': [ + { + 'OS-EXT-IPS:type': 'floating', + 'addr': '127.0.0.1' + } + ] + } + } + }, + 'output': 'ip' + } + obj = GetServerIp(scenario_cfg, {}) + result = obj.run({}) + self.assertEqual(result, {'ip': '127.0.0.1'}) + + +def main(): + unittest.main() + + +if __name__ == '__main__': + main() diff --git a/yardstick/benchmark/scenarios/base.py b/yardstick/benchmark/scenarios/base.py index 5d3c36c38..3cb138dd8 100644 --- a/yardstick/benchmark/scenarios/base.py +++ b/yardstick/benchmark/scenarios/base.py @@ -63,3 +63,15 @@ class Scenario(object): return scenario.__module__ + "." + scenario.__name__ raise RuntimeError("No such scenario type %s" % scenario_type) + + def _push_to_outputs(self, keys, values): + return dict(zip(keys, values)) + + def _change_obj_to_dict(self, obj): + dic = {} + for k, v in vars(obj).items(): + try: + vars(v) + except TypeError: + dic[k] = v + return dic diff --git a/yardstick/benchmark/scenarios/lib/__init__.py b/yardstick/benchmark/scenarios/lib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/yardstick/benchmark/scenarios/lib/add_memory_load.py b/yardstick/benchmark/scenarios/lib/add_memory_load.py new file mode 100644 index 000000000..26cf140d1 --- /dev/null +++ b/yardstick/benchmark/scenarios/lib/add_memory_load.py @@ -0,0 +1,57 @@ +# ############################################################################ +# Copyright (c) 2017 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 __future__ import print_function +from __future__ import absolute_import + +import logging + +import yardstick.ssh as ssh +from yardstick.benchmark.scenarios import base + +LOG = logging.getLogger(__name__) + + +class AddMemoryLoad(base.Scenario): + """Add memory load in server + """ + + __scenario_type__ = "AddMemoryLoad" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + + self.options = scenario_cfg.get('options', {}) + + self.client = ssh.SSH.from_node(self.context_cfg['host']) + self.client.wait(timeout=600) + + def run(self, result): + self._add_load() + + def _add_load(self): + try: + memory_load = self.options['memory_load'] + except KeyError: + LOG.error('memory_load parameter must be provided') + else: + if float(memory_load) == 0: + return + cmd = 'free | awk "/Mem/ {print $2}"' + code, stdout, stderr = self.client.execute(cmd) + total = int(stdout.split()[1]) + used = int(stdout.split()[2]) + remain_memory = total * float(memory_load) - used + if remain_memory > 0: + count = remain_memory / 1024 / 128 + LOG.info('Add %s vm load', count) + if count != 0: + cmd = 'stress -t 10 -m {} --vm-keep'.format(count) + self.client.execute(cmd) diff --git a/yardstick/benchmark/scenarios/lib/check_numa_info.py b/yardstick/benchmark/scenarios/lib/check_numa_info.py new file mode 100644 index 000000000..59a47547e --- /dev/null +++ b/yardstick/benchmark/scenarios/lib/check_numa_info.py @@ -0,0 +1,61 @@ +# ############################################################################ +# Copyright (c) 2017 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 __future__ import print_function +from __future__ import absolute_import + +import logging + +from yardstick.benchmark.scenarios import base + +LOG = logging.getLogger(__name__) + + +class CheckNumaInfo(base.Scenario): + """ + Execute a live migration for two hosts + + """ + + __scenario_type__ = "CheckNumaInfo" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + + self.options = self.scenario_cfg.get('options', {}) + + self.cpu_set = self.options.get('cpu_set', '1,2,3,4,5,6') + + def run(self, result): + info1 = self.options.get('info1') + info2 = self.options.get('info2') + LOG.debug('Origin numa info: %s', info1) + LOG.debug('Current numa info: %s', info2) + status = self._check_vm2_status(info1, info2) + + keys = self.scenario_cfg.get('output', '').split() + values = [status] + return self._push_to_outputs(keys, values) + + def _check_vm2_status(self, info1, info2): + if len(info1['pinning']) != 1 or len(info2['pinning']) != 1: + return False + + for i in info1['vcpupin']: + for j in i['cpuset'].split(','): + if j not in self.cpu_set.split(','): + return False + + for i in info2['vcpupin']: + for j in i['cpuset'].split(','): + if j not in self.cpu_set.split(','): + return False + + return True diff --git a/yardstick/benchmark/scenarios/lib/check_value.py b/yardstick/benchmark/scenarios/lib/check_value.py new file mode 100644 index 000000000..759076068 --- /dev/null +++ b/yardstick/benchmark/scenarios/lib/check_value.py @@ -0,0 +1,58 @@ +############################################################################## +# Copyright (c) 2017 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 __future__ import print_function +from __future__ import absolute_import + +import logging + +from yardstick.benchmark.scenarios import base + +LOG = logging.getLogger(__name__) + + +class CheckValue(base.Scenario): + """Check values between value1 and value2 + + options: + operator: equal(eq) and not equal(ne) + value1: + value2: + output: check_result + """ + + __scenario_type__ = "CheckValue" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + self.options = self.scenario_cfg['options'] + + def run(self, result): + """execute the test""" + + op = self.options.get("operator") + LOG.debug("options=%s", self.options) + value1 = str(self.options.get("value1")) + value2 = str(self.options.get("value2")) + check_result = "PASS" + if op == "eq" and value1 != value2: + LOG.info("value1=%s, value2=%s, error: should equal!!!", value1, + value2) + check_result = "FAIL" + assert value1 == value2, "Error %s!=%s" % (value1, value2) + elif op == "ne" and value1 == value2: + LOG.info("value1=%s, value2=%s, error: should not equal!!!", + value1, value2) + check_result = "FAIL" + assert value1 != value2, "Error %s==%s" % (value1, value2) + LOG.info("Check result is %s", check_result) + keys = self.scenario_cfg.get('output', '').split() + values = [check_result] + return self._push_to_outputs(keys, values) diff --git a/yardstick/benchmark/scenarios/lib/get_migrate_target_host.py b/yardstick/benchmark/scenarios/lib/get_migrate_target_host.py new file mode 100644 index 000000000..c19d96d68 --- /dev/null +++ b/yardstick/benchmark/scenarios/lib/get_migrate_target_host.py @@ -0,0 +1,56 @@ + +# ############################################################################ +# Copyright (c) 2017 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 __future__ import print_function +from __future__ import absolute_import + +import logging + +from yardstick.common import openstack_utils +from yardstick.common.utils import change_obj_to_dict +from yardstick.benchmark.scenarios import base + +LOG = logging.getLogger(__name__) + + +class GetMigrateTargetHost(base.Scenario): + """Get a migrate target host according server + """ + + __scenario_type__ = "GetMigrateTargetHost" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + + self.options = self.scenario_cfg.get('options', {}) + default_instance_id = self.options.get('server', {}).get('id', '') + self.instance_id = self.options.get('server_id', default_instance_id) + + self.nova_client = openstack_utils.get_nova_client() + + def run(self, result): + current_host = self._get_current_host_name(self.instance_id) + target_host = self._get_migrate_host(current_host) + + keys = self.scenario_cfg.get('output', '').split() + values = [target_host] + return self._push_to_outputs(keys, values) + + def _get_current_host_name(self, server_id): + + return change_obj_to_dict(self.nova_client.servers.get(server_id))['OS-EXT-SRV-ATTR:host'] + + def _get_migrate_host(self, current_host): + hosts = self.nova_client.hosts.list_all() + compute_hosts = [a.host for a in hosts if a.service == 'compute'] + for host in compute_hosts: + if host.strip() != current_host.strip(): + return host diff --git a/yardstick/benchmark/scenarios/lib/get_numa_info.py b/yardstick/benchmark/scenarios/lib/get_numa_info.py new file mode 100644 index 000000000..4e4a44d95 --- /dev/null +++ b/yardstick/benchmark/scenarios/lib/get_numa_info.py @@ -0,0 +1,79 @@ +# ############################################################################ +# Copyright (c) 2017 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 __future__ import print_function +from __future__ import absolute_import + +import logging +import os + +import yaml +from xml.etree import ElementTree as ET + +from yardstick import ssh +from yardstick.benchmark.scenarios import base +from yardstick.common import constants as consts +from yardstick.common.utils import change_obj_to_dict +from yardstick.common.openstack_utils import get_nova_client +from yardstick.common.task_template import TaskTemplate + +LOG = logging.getLogger(__name__) + + +class GetNumaInfo(base.Scenario): + """ + Execute a live migration for two hosts + + """ + + __scenario_type__ = "GetNumaInfo" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + self.options = self.scenario_cfg.get('options', {}) + + server = self.options['server'] + self.server_id = server['id'] + self.host = self._get_current_host_name(self.server_id) + + node_file = os.path.join(consts.YARDSTICK_ROOT_PATH, + self.options.get('file')) + + with open(node_file) as f: + nodes = yaml.safe_load(TaskTemplate.render(f.read())) + self.nodes = {a['host_name']: a for a in nodes['nodes']} + + def run(self, result): + numa_info = self._check_numa_node(self.server_id, self.host) + + keys = self.scenario_cfg.get('output', '').split() + values = [numa_info] + return self._push_to_outputs(keys, values) + + def _get_current_host_name(self, server_id): + + return change_obj_to_dict(get_nova_client().servers.get(server_id))['OS-EXT-SRV-ATTR:host'] + + def _get_host_client(self, node_name): + self.host_client = ssh.SSH.from_node(self.nodes.get(node_name)) + self.host_client.wait(timeout=600) + + def _check_numa_node(self, server_id, host): + self._get_host_client(host) + + cmd = "sudo virsh dumpxml %s" % server_id + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.host_client.execute(cmd) + if status: + raise RuntimeError(stderr) + root = ET.fromstring(stdout) + vcpupin = [a.attrib for a in root.iter('vcpupin')] + pinning = [a.attrib for a in root.iter('memnode')] + return {"pinning": pinning, 'vcpupin': vcpupin} diff --git a/yardstick/benchmark/scenarios/lib/get_server.py b/yardstick/benchmark/scenarios/lib/get_server.py new file mode 100644 index 000000000..fcf47c80d --- /dev/null +++ b/yardstick/benchmark/scenarios/lib/get_server.py @@ -0,0 +1,83 @@ +############################################################################## +# Copyright (c) 2017 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 __future__ import print_function +from __future__ import absolute_import + +import logging + +from yardstick.benchmark.scenarios import base +import yardstick.common.openstack_utils as op_utils + +LOG = logging.getLogger(__name__) + + +class GetServer(base.Scenario): + """Get a server instance + + Parameters + server_id - ID of the server + type: string + unit: N/A + default: null + server_name - name of the server + type: string + unit: N/A + default: null + + Either server_id or server_name is required. + + Outputs + rc - response code of getting server instance + 0 for success + 1 for failure + type: int + unit: N/A + server - instance of the server + type: dict + unit: N/A + """ + + __scenario_type__ = "GetServer" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + self.options = self.scenario_cfg.get('options', {}) + + self.server_id = self.options.get("server_id") + if self.server_id: + LOG.debug('Server id is %s', self.server_id) + + default_name = self.scenario_cfg.get('host', + self.scenario_cfg.get('target')) + self.server_name = self.options.get('server_name', default_name) + if self.server_name: + LOG.debug('Server name is %s', self.server_name) + + self.nova_client = op_utils.get_nova_client() + + def run(self, result): + """execute the test""" + + if self.server_id: + server = self.nova_client.servers.get(self.server_id) + else: + server = op_utils.get_server_by_name(self.server_name) + + keys = self.scenario_cfg.get('output', '').split() + + if server: + LOG.info("Get server successful!") + values = [0, self._change_obj_to_dict(server)] + else: + LOG.info("Get server failed!") + values = [1] + + return self._push_to_outputs(keys, values) diff --git a/yardstick/benchmark/scenarios/lib/get_server_ip.py b/yardstick/benchmark/scenarios/lib/get_server_ip.py new file mode 100644 index 000000000..1eeeb7fca --- /dev/null +++ b/yardstick/benchmark/scenarios/lib/get_server_ip.py @@ -0,0 +1,38 @@ +############################################################################## +# Copyright (c) 2017 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 __future__ import print_function +from __future__ import absolute_import + +import logging + +from yardstick.benchmark.scenarios import base + +LOG = logging.getLogger(__name__) + + +class GetServerIp(base.Scenario): + """Get a server by name""" + + __scenario_type__ = "GetServerIp" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + self.options = self.scenario_cfg.get('options', {}) + self.ip_type = self.options.get('ip_type', "floating") + + def run(self, result): + server = self.options.get('server', {}) + ip = next(n['addr'] for k, v in server['addresses'].items() + for n in v if n['OS-EXT-IPS:type'] == self.ip_type) + + keys = self.scenario_cfg.get('output', '').split() + values = [ip] + return self._push_to_outputs(keys, values) diff --git a/yardstick/benchmark/scenarios/lib/migrate.py b/yardstick/benchmark/scenarios/lib/migrate.py new file mode 100644 index 000000000..116bae69e --- /dev/null +++ b/yardstick/benchmark/scenarios/lib/migrate.py @@ -0,0 +1,155 @@ +# ############################################################################ +# Copyright (c) 2017 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 __future__ import print_function +from __future__ import absolute_import + +import logging +import subprocess +import threading +import time + +from datetime import datetime +import ping + +from yardstick.common import openstack_utils +from yardstick.common.utils import change_obj_to_dict +from yardstick.benchmark.scenarios import base + +LOG = logging.getLogger(__name__) + +TIMEOUT = 0.05 +PACKAGE_SIZE = 64 + + +class Migrate(base.Scenario): # pragma: no cover + """ + Execute a live migration for two hosts + + """ + + __scenario_type__ = "Migrate" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + self.options = self.scenario_cfg.get('options', {}) + + self.nova_client = openstack_utils.get_nova_client() + + def run(self, result): + default_instance_id = self.options.get('server', {}).get('id', '') + instance_id = self.options.get('server_id', default_instance_id) + LOG.info('Instance id is %s', instance_id) + + target_host = self.options.get('host') + LOG.info('Target host is %s', target_host) + + instance_ip = self.options.get('server_ip') + if instance_ip: + LOG.info('Instance ip is %s', instance_ip) + + self._ping_until_connected(instance_ip) + LOG.info('Instance is connected') + + LOG.debug('Start to ping instance') + ping_thread = self._do_ping_task(instance_ip) + + keys = self.scenario_cfg.get('output', '').split() + try: + LOG.info('Start to migrate') + self._do_migrate(instance_id, target_host) + except Exception as e: + return self._push_to_outputs(keys, [1, str(e).split('.')[0]]) + else: + migrate_time = self._get_migrate_time(instance_id) + LOG.info('Migration time is %s s', migrate_time) + + current_host = self._get_current_host_name(instance_id) + LOG.info('Current host is %s', current_host) + if current_host.strip() != target_host.strip(): + LOG.error('current_host not equal to target_host') + values = [1, 'current_host not equal to target_host'] + return self._push_to_outputs(keys, values) + + if instance_ip: + ping_thread.flag = False + ping_thread.join() + + downtime = ping_thread.get_delay() + LOG.info('Downtime is %s s', downtime) + + values = [0, migrate_time, downtime] + return self._push_to_outputs(keys, values) + else: + values = [0, migrate_time] + return self._push_to_outputs(keys, values) + + def _do_migrate(self, server_id, target_host): + + cmd = ['nova', 'live-migration', server_id, target_host] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE) + p.communicate() + + def _ping_until_connected(self, instance_ip): + for i in range(3000): + res = ping.do_one(instance_ip, TIMEOUT, PACKAGE_SIZE) + if res: + break + + def _do_ping_task(self, instance_ip): + ping_thread = PingThread(instance_ip) + ping_thread.start() + return ping_thread + + def _get_current_host_name(self, server_id): + + return change_obj_to_dict(self.nova_client.servers.get(server_id))['OS-EXT-SRV-ATTR:host'] + + def _get_migrate_time(self, server_id): + while True: + status = self.nova_client.servers.get(server_id).status.lower() + if status == 'migrating': + start_time = datetime.now() + break + LOG.debug('Instance status change to MIGRATING') + + while True: + status = self.nova_client.servers.get(server_id).status.lower() + if status == 'active': + end_time = datetime.now() + break + if status == 'error': + LOG.error('Instance status is ERROR') + raise RuntimeError('The instance status is error') + LOG.debug('Instance status change to ACTIVE') + + duration = end_time - start_time + return duration.seconds + duration.microseconds * 1.0 / 1e6 + + +class PingThread(threading.Thread): # pragma: no cover + + def __init__(self, target): + super(PingThread, self).__init__() + self.target = target + self.flag = True + self.delay = 0.0 + + def run(self): + count = 0 + while self.flag: + res = ping.do_one(self.target, TIMEOUT, PACKAGE_SIZE) + if not res: + count += 1 + time.sleep(0.01) + self.delay = (TIMEOUT + 0.01) * count + + def get_delay(self): + return self.delay -- cgit 1.2.3-korg