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