Merge "Change PTL informatin in INFO"
[bottlenecks.git] / testsuites / vstf / vstf_scripts / vstf / agent / env / basic / vm_manager.py
1 ##############################################################################
2 # Copyright (c) 2015 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 import os
11 import shutil
12 import logging
13 from vstf.common.utils import check_and_kill, randomMAC, my_mkdir, check_call, check_output, my_sleep
14 from vstf.agent.env.basic.vm9pfs import VMConfigBy9pfs
15
16 LOG = logging.getLogger(__name__)
17
18
19 class VMControlOperation(object):
20     """
21     a libivrt virsh wrapper for creating virtual machine.
22     """
23
24     def __init__(self):
25         """
26         all tmp files will be created under '/tmp/atf_vm_manager'
27
28         """
29         work_dir = '/tmp/atf_vm_manager'
30         shutil.rmtree(work_dir, ignore_errors=True)
31         my_mkdir(work_dir)
32         self.work_dir = work_dir
33         self.vnc_index = 0
34         self.pci_index = 3
35         self.net_index = 0
36         self.vm_9p_controllers = {}
37         self.vm_configs = {}
38         self.image_mgr = None
39
40     @staticmethod
41     def composite_xml(context):
42         """
43         composit a libvirt xml configuration for creating vm from context.
44
45         :param context: a dict containing all necessary options for creating a vm.
46         :return: libvirt xml configuration string
47         """
48         from vm_xml_help import xml_head, xml_disk, xml_ovs, xml_pci, xml_9p, xml_tail, xml_ctrl_br, xml_br
49         xml = ''
50         tmp = xml_head.replace('VM_NAME', context['vm_name'])
51         tmp = tmp.replace('VM_MEMORY', str(context['vm_memory']))
52         tmp = tmp.replace('CPU_NUM', str(context['vm_cpu']))
53         xml += tmp
54         tmp = xml_disk.replace('IMAGE_TYPE', context['image_type'])
55         tmp = tmp.replace('IMAGE_PATH', context['image_path'])
56         xml += tmp
57
58         if context['9p_path']:
59             tmp = xml_9p.replace('9P_PATH', context['9p_path'])
60             xml += tmp
61
62         if context['eth_pci']:
63             for pci in context['eth_pci']:
64                 bus = pci[:2]
65                 slot = pci[3:5]
66                 func = pci[6:7]
67                 tmp = xml_pci.replace('BUS', bus)
68                 tmp = tmp.replace('SLOT', slot)
69                 tmp = tmp.replace('FUNCTION', func)
70                 xml += tmp
71
72         if context['ctrl_br']:
73             tmp = xml_ctrl_br.replace('CTRL_BR', context['ctrl_br'])
74             tmp = tmp.replace('CTRL_MAC', context['ctrl_mac'])
75             tmp = tmp.replace('CTRL_MODEL', context['ctrl_model'])
76             xml += tmp
77
78         for tap_cfg in context['taps']:
79             if tap_cfg['br_type'] == "ovs":
80                 br_type = "openvswitch"
81             else:
82                 br_type = tap_cfg['br_type']
83             if br_type == 'bridge':
84                 xml_ovs = xml_br
85             tmp = xml_ovs.replace('BR_TYPE', br_type)
86             tmp = tmp.replace('TAP_MAC', tap_cfg['tap_mac'])
87             tmp = tmp.replace('TAP_NAME', tap_cfg['tap_name'])
88             tmp = tmp.replace('BR_NAME', tap_cfg['br_name'])
89             xml += tmp
90
91         xml += xml_tail
92         return xml
93
94     @staticmethod
95     def check_required_options(context):
96         for key in (
97             'vm_name',
98             'vm_memory',
99             'vm_cpu',
100             'image_path',
101             'image_type',
102                 'taps'):
103             if key not in context:
104                 raise Exception("vm config error, must set %s option" % key)
105
106     def set_vm_defaults(self, context):
107         vm_9p_path = '%s/%s' % (self.work_dir, context['vm_name'])
108         shutil.rmtree(vm_9p_path, ignore_errors=True)
109         my_mkdir(vm_9p_path)
110         default = {'vm_memory': 4194304,
111                    'vm_cpu': 4,
112                    'image_type': 'qcow2',
113                    'br_type': 'ovs',
114                    '9p_path': vm_9p_path,
115                    'eth_pci': None,
116                    'ctrl_br': 'br0',
117                    'ctrl_mac': randomMAC(),
118                    'ctrl_model': 'virtio',
119                    'ctrl_ip_setting': '192.168.100.100/24',
120                    'ctrl_gw': '192.168.100.1'
121                    }
122         for k, v in default.items():
123             context.setdefault(k, v)
124
125     def _shutdown_vm(self):
126         out = check_output(
127             "virsh list | sed 1,2d | awk '{print $2}'",
128             shell=True)
129         vm_set = set(out.split())
130         for vm in vm_set:
131             check_call("virsh shutdown %s" % vm, shell=True)
132         timeout = 60
133         # wait for gracefully shutdown
134         while timeout > 0:
135             out = check_output(
136                 "virsh list | sed 1,2d | awk '{print $2}'",
137                 shell=True)
138             vm_set = set(out.split())
139             if len(vm_set) == 0:
140                 break
141             timeout -= 2
142             my_sleep(2)
143             LOG.info("waiting for vms:%s to shutdown gracefully", vm_set)
144         # destroy by force
145         for vm in vm_set:
146             check_call("virsh destroy %s" % vm, shell=True)
147         # undefine all
148         out = check_output(
149             "virsh list --all | sed 1,2d | awk '{print $2}'",
150             shell=True)
151         vm_set = set(out.split())
152         for vm in vm_set:
153             check_call("virsh undefine %s" % vm, shell=True)
154         # kill all qemu
155         check_and_kill('qemu-system-x86_64')
156
157     def clean_all_vms(self):
158         self._shutdown_vm()
159         for _, ctrl in self.vm_9p_controllers.items():
160             LOG.debug("remove vm9pfs dir:%s", ctrl.vm_9p_path)
161             shutil.rmtree(ctrl.vm_9p_path, ignore_errors=True)
162         self.vm_9p_controllers = {}
163         self.vm_configs = {}
164         # shutil.rmtree(self.work_dir, ignore_errors=True)
165         self.vnc_index = 0
166         self.pci_index = 3
167         self.net_index = 0
168         self.vms = []
169         return True
170
171     def create_vm(self, context):
172         self.set_vm_defaults(context)
173         self.check_required_options(context)
174         xml = self.composite_xml(context)
175         vm_name = context['vm_name']
176         file_name = os.path.join(self.work_dir, vm_name + '.xml')
177         with open(file_name, 'w') as f:
178             f.write(xml)
179         check_call('virsh define %s' % file_name, shell=True)
180         check_call('virsh start %s' % vm_name, shell=True)
181         vm_name = context['vm_name']
182         vm_9pfs = context['9p_path']
183         self.vm_9p_controllers[vm_name] = VMConfigBy9pfs(vm_9pfs)
184         self.vm_configs[vm_name] = context
185         LOG.debug("%s's vm_9pfs path:%s", vm_name, vm_9pfs)
186         return True
187
188     def wait_vm(self, vm_name):
189         vm9pctrl = self.vm_9p_controllers[vm_name]
190         ret = vm9pctrl.wait_up()
191         if ret not in (True,):
192             raise Exception(
193                 'vm running but stuck in boot process, please manully check.')
194         LOG.debug('waitVM %s up ok, ret:%s', vm_name, ret)
195         return True
196
197     def init_config_vm(self, vm_name):
198         """
199         using libvirt 9pfs to config boot up options like network ip/gw.
200
201         :param vm_name: the vm to be config with.
202         :return: True if succeed, Exception if fail.
203         """
204         vm_cfg = self.vm_configs[vm_name]
205         vm9pctrl = self.vm_9p_controllers[vm_name]
206         # print self.vm_9p_controllers
207         init_cfg = vm_cfg['init_config']
208         if "ctrl_ip_setting" in init_cfg:
209             ret = vm9pctrl.config_ip(
210                 vm_cfg['ctrl_mac'],
211                 init_cfg['ctrl_ip_setting'])
212             assert ret
213             LOG.info('initConfigVM config ip ok')
214         if 'ctrl_gw' in init_cfg:
215             ret = vm9pctrl.config_gw(init_cfg['ctrl_gw'])
216             assert ret
217             LOG.info('initConfigVM ctrl_gw ok')
218         if "ctrl_ip_setting" in init_cfg and "amqp_server" in init_cfg:
219             identity = init_cfg['ctrl_ip_setting'].split('/')[0]
220             if init_cfg['amqp_id'].strip():
221                 identity = init_cfg['amqp_id'].strip()
222             server = init_cfg['amqp_server']
223             port = init_cfg['amqp_port']
224             user = init_cfg['amqp_user']
225             passwd = init_cfg['amqp_passwd']
226             ret = vm9pctrl.config_amqp(identity, server, port, user, passwd)
227             assert ret
228             LOG.info('initConfigVM config_amqp ok')
229         if 'tap_pktloop_config' in init_cfg:
230             taps = vm_cfg['taps']
231             macs = []
232             for tap in taps:
233                 macs.append(tap['tap_mac'])
234             ret = vm9pctrl.set_pktloop_dpdk(macs)
235             assert ret
236             LOG.info('initConfigVM set_pktloop_dpdk ok')
237         return True