Merge "Add spec cpu2006 test case"
[yardstick.git] / yardstick / benchmark / scenarios / lib / migrate.py
1 # ############################################################################
2 # Copyright (c) 2017 Huawei Technologies Co.,Ltd and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 # ############################################################################
9
10 from __future__ import print_function
11 from __future__ import absolute_import
12
13 import logging
14 import subprocess
15 import threading
16 import time
17
18 from datetime import datetime
19 import ping
20
21 from yardstick.common import openstack_utils
22 from yardstick.common.utils import change_obj_to_dict
23 from yardstick.benchmark.scenarios import base
24
25 LOG = logging.getLogger(__name__)
26
27 TIMEOUT = 0.05
28 PACKAGE_SIZE = 64
29
30
31 class Migrate(base.Scenario):       # pragma: no cover
32     """
33     Execute a live migration for two hosts
34
35     """
36
37     __scenario_type__ = "Migrate"
38
39     def __init__(self, scenario_cfg, context_cfg):
40         self.scenario_cfg = scenario_cfg
41         self.context_cfg = context_cfg
42         self.options = self.scenario_cfg.get('options', {})
43
44         self.nova_client = openstack_utils.get_nova_client()
45
46     def run(self, result):
47         default_instance_id = self.options.get('server', {}).get('id', '')
48         instance_id = self.options.get('server_id', default_instance_id)
49         LOG.info('Instance id is %s', instance_id)
50
51         target_host = self.options.get('host')
52         LOG.info('Target host is %s', target_host)
53
54         instance_ip = self.options.get('server_ip')
55         if instance_ip:
56             LOG.info('Instance ip is %s', instance_ip)
57
58             self._ping_until_connected(instance_ip)
59             LOG.info('Instance is connected')
60
61             LOG.debug('Start to ping instance')
62             ping_thread = self._do_ping_task(instance_ip)
63
64         keys = self.scenario_cfg.get('output', '').split()
65         try:
66             LOG.info('Start to migrate')
67             self._do_migrate(instance_id, target_host)
68         except Exception as e:
69             return self._push_to_outputs(keys, [1, str(e).split('.')[0]])
70         else:
71             migrate_time = self._get_migrate_time(instance_id)
72             LOG.info('Migration time is %s s', migrate_time)
73
74             current_host = self._get_current_host_name(instance_id)
75             LOG.info('Current host is %s', current_host)
76             if current_host.strip() != target_host.strip():
77                 LOG.error('current_host not equal to target_host')
78                 values = [1, 'current_host not equal to target_host']
79                 return self._push_to_outputs(keys, values)
80
81         if instance_ip:
82             ping_thread.flag = False
83             ping_thread.join()
84
85             downtime = ping_thread.get_delay()
86             LOG.info('Downtime is %s s', downtime)
87
88             values = [0, migrate_time, downtime]
89             return self._push_to_outputs(keys, values)
90         else:
91             values = [0, migrate_time]
92             return self._push_to_outputs(keys, values)
93
94     def _do_migrate(self, server_id, target_host):
95
96         cmd = ['nova', 'live-migration', server_id, target_host]
97         p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
98         p.communicate()
99
100     def _ping_until_connected(self, instance_ip):
101         for i in range(3000):
102             res = ping.do_one(instance_ip, TIMEOUT, PACKAGE_SIZE)
103             if res:
104                 break
105
106     def _do_ping_task(self, instance_ip):
107         ping_thread = PingThread(instance_ip)
108         ping_thread.start()
109         return ping_thread
110
111     def _get_current_host_name(self, server_id):
112
113         return change_obj_to_dict(self.nova_client.servers.get(server_id))['OS-EXT-SRV-ATTR:host']
114
115     def _get_migrate_time(self, server_id):
116         while True:
117             status = self.nova_client.servers.get(server_id).status.lower()
118             if status == 'migrating':
119                 start_time = datetime.now()
120                 break
121         LOG.debug('Instance status change to MIGRATING')
122
123         while True:
124             status = self.nova_client.servers.get(server_id).status.lower()
125             if status == 'active':
126                 end_time = datetime.now()
127                 break
128             if status == 'error':
129                 LOG.error('Instance status is ERROR')
130                 raise RuntimeError('The instance status is error')
131         LOG.debug('Instance status change to ACTIVE')
132
133         duration = end_time - start_time
134         return duration.seconds + duration.microseconds * 1.0 / 1e6
135
136
137 class PingThread(threading.Thread):     # pragma: no cover
138
139     def __init__(self, target):
140         super(PingThread, self).__init__()
141         self.target = target
142         self.flag = True
143         self.delay = 0.0
144
145     def run(self):
146         count = 0
147         while self.flag:
148             res = ping.do_one(self.target, TIMEOUT, PACKAGE_SIZE)
149             if not res:
150                 count += 1
151             time.sleep(0.01)
152         self.delay = (TIMEOUT + 0.01) * count
153
154     def get_delay(self):
155         return self.delay