summaryrefslogtreecommitdiffstats
path: root/vstf/vstf/agent/env/basic/vm_manager.py
diff options
context:
space:
mode:
Diffstat (limited to 'vstf/vstf/agent/env/basic/vm_manager.py')
-rwxr-xr-xvstf/vstf/agent/env/basic/vm_manager.py218
1 files changed, 218 insertions, 0 deletions
diff --git a/vstf/vstf/agent/env/basic/vm_manager.py b/vstf/vstf/agent/env/basic/vm_manager.py
new file mode 100755
index 00000000..e59d6c71
--- /dev/null
+++ b/vstf/vstf/agent/env/basic/vm_manager.py
@@ -0,0 +1,218 @@
+"""
+Created on 2015-8-27
+
+@author: y00228926
+"""
+import os
+import shutil
+import logging
+from vstf.common.utils import check_and_kill, randomMAC, my_mkdir, check_call, check_output, my_sleep
+from vstf.agent.env.basic.vm9pfs import VMConfigBy9pfs
+
+LOG = logging.getLogger(__name__)
+
+
+class VMControlOperation(object):
+ """
+ a libivrt virsh wrapper for creating virtual machine.
+ """
+
+ def __init__(self):
+ """
+ all tmp files will be created under '/tmp/atf_vm_manager'
+
+ """
+ work_dir = '/tmp/atf_vm_manager'
+ shutil.rmtree(work_dir, ignore_errors=True)
+ my_mkdir(work_dir)
+ self.work_dir = work_dir
+ self.vnc_index = 0
+ self.pci_index = 3
+ self.net_index = 0
+ self.vm_9p_controllers = {}
+ self.vm_configs = {}
+ self.image_mgr = None
+
+ @staticmethod
+ def composite_xml(context):
+ """
+ composit a libvirt xml configuration for creating vm from context.
+
+ :param context: a dict containing all necessary options for creating a vm.
+ :return: libvirt xml configuration string
+ """
+ from vm_xml_help import xml_head, xml_disk, xml_ovs, xml_pci, xml_9p, xml_tail, xml_ctrl_br, xml_br
+ xml = ''
+ tmp = xml_head.replace('VM_NAME', context['vm_name'])
+ tmp = tmp.replace('VM_MEMORY', str(context['vm_memory']))
+ tmp = tmp.replace('CPU_NUM', str(context['vm_cpu']))
+ xml += tmp
+ tmp = xml_disk.replace('IMAGE_TYPE', context['image_type'])
+ tmp = tmp.replace('IMAGE_PATH', context['image_path'])
+ xml += tmp
+
+ if context['9p_path']:
+ tmp = xml_9p.replace('9P_PATH', context['9p_path'])
+ xml += tmp
+
+ if context['eth_pci']:
+ for pci in context['eth_pci']:
+ bus = pci[:2]
+ slot = pci[3:5]
+ func = pci[6:7]
+ tmp = xml_pci.replace('BUS', bus)
+ tmp = tmp.replace('SLOT', slot)
+ tmp = tmp.replace('FUNCTION', func)
+ xml += tmp
+
+ if context['ctrl_br']:
+ tmp = xml_ctrl_br.replace('CTRL_BR', context['ctrl_br'])
+ tmp = tmp.replace('CTRL_MAC', context['ctrl_mac'])
+ tmp = tmp.replace('CTRL_MODEL', context['ctrl_model'])
+ xml += tmp
+
+ for tap_cfg in context['taps']:
+ if tap_cfg['br_type'] == "ovs":
+ br_type = "openvswitch"
+ else:
+ br_type = tap_cfg['br_type']
+ if br_type == 'bridge':
+ xml_ovs = xml_br
+ tmp = xml_ovs.replace('BR_TYPE', br_type)
+ tmp = tmp.replace('TAP_MAC', tap_cfg['tap_mac'])
+ tmp = tmp.replace('TAP_NAME', tap_cfg['tap_name'])
+ tmp = tmp.replace('BR_NAME', tap_cfg['br_name'])
+ xml += tmp
+
+ xml += xml_tail
+ return xml
+
+ @staticmethod
+ def check_required_options(context):
+ for key in ('vm_name', 'vm_memory', 'vm_cpu', 'image_path', 'image_type', 'taps'):
+ if not context.has_key(key):
+ raise Exception("vm config error, must set %s option" % key)
+
+ def set_vm_defaults(self, context):
+ vm_9p_path = '%s/%s' % (self.work_dir, context['vm_name'])
+ shutil.rmtree(vm_9p_path, ignore_errors=True)
+ my_mkdir(vm_9p_path)
+ default = {'vm_memory': 4194304,
+ 'vm_cpu': 4,
+ 'image_type': 'qcow2',
+ 'br_type': 'ovs',
+ '9p_path': vm_9p_path,
+ 'eth_pci': None,
+ 'ctrl_br': 'br0',
+ 'ctrl_mac': randomMAC(),
+ 'ctrl_model': 'virtio',
+ 'ctrl_ip_setting': '192.168.100.100/24',
+ 'ctrl_gw': '192.168.100.1'
+ }
+ for k, v in default.items():
+ context.setdefault(k, v)
+
+ def _shutdown_vm(self):
+ out = check_output("virsh list | sed 1,2d | awk '{print $2}'", shell=True)
+ vm_set = set(out.split())
+ for vm in vm_set:
+ check_call("virsh shutdown %s" % vm, shell=True)
+ timeout = 60
+ # wait for gracefully shutdown
+ while timeout > 0:
+ out = check_output("virsh list | sed 1,2d | awk '{print $2}'", shell=True)
+ vm_set = set(out.split())
+ if len(vm_set) == 0:
+ break
+ timeout -= 2
+ my_sleep(2)
+ LOG.info("waiting for vms:%s to shutdown gracefully", vm_set)
+ # destroy by force
+ for vm in vm_set:
+ check_call("virsh destroy %s" % vm, shell=True)
+ # undefine all
+ out = check_output("virsh list --all | sed 1,2d | awk '{print $2}'", shell=True)
+ vm_set = set(out.split())
+ for vm in vm_set:
+ check_call("virsh undefine %s" % vm, shell=True)
+ # kill all qemu
+ check_and_kill('qemu-system-x86_64')
+
+ def clean_all_vms(self):
+ self._shutdown_vm()
+ for _, ctrl in self.vm_9p_controllers.items():
+ LOG.debug("remove vm9pfs dir:%s", ctrl.vm_9p_path)
+ shutil.rmtree(ctrl.vm_9p_path, ignore_errors=True)
+ self.vm_9p_controllers = {}
+ self.vm_configs = {}
+ # shutil.rmtree(self.work_dir, ignore_errors=True)
+ self.vnc_index = 0
+ self.pci_index = 3
+ self.net_index = 0
+ self.vms = []
+ return True
+
+ def create_vm(self, context):
+ self.set_vm_defaults(context)
+ self.check_required_options(context)
+ xml = self.composite_xml(context)
+ vm_name = context['vm_name']
+ file_name = os.path.join(self.work_dir, vm_name + '.xml')
+ with open(file_name, 'w') as f:
+ f.write(xml)
+ check_call('virsh define %s' % file_name, shell=True)
+ check_call('virsh start %s' % vm_name, shell=True)
+ vm_name = context['vm_name']
+ vm_9pfs = context['9p_path']
+ self.vm_9p_controllers[vm_name] = VMConfigBy9pfs(vm_9pfs)
+ self.vm_configs[vm_name] = context
+ LOG.debug("%s's vm_9pfs path:%s", vm_name, vm_9pfs)
+ return True
+
+ def wait_vm(self, vm_name):
+ vm9pctrl = self.vm_9p_controllers[vm_name]
+ ret = vm9pctrl.wait_up()
+ if ret not in (True,):
+ raise Exception('vm running but stuck in boot process, please manully check.')
+ LOG.debug('waitVM %s up ok, ret:%s', vm_name, ret)
+ return True
+
+ def init_config_vm(self, vm_name):
+ """
+ using libvirt 9pfs to config boot up options like network ip/gw.
+
+ :param vm_name: the vm to be config with.
+ :return: True if succeed, Exception if fail.
+ """
+ vm_cfg = self.vm_configs[vm_name]
+ vm9pctrl = self.vm_9p_controllers[vm_name]
+ # print self.vm_9p_controllers
+ init_cfg = vm_cfg['init_config']
+ if "ctrl_ip_setting" in init_cfg:
+ ret = vm9pctrl.config_ip(vm_cfg['ctrl_mac'], init_cfg['ctrl_ip_setting'])
+ assert ret == True
+ LOG.info('initConfigVM config ip ok')
+ if 'ctrl_gw' in init_cfg:
+ ret = vm9pctrl.config_gw(init_cfg['ctrl_gw'])
+ assert ret == True
+ LOG.info('initConfigVM ctrl_gw ok')
+ if "ctrl_ip_setting" in init_cfg and "amqp_server" in init_cfg:
+ identity = init_cfg['ctrl_ip_setting'].split('/')[0]
+ if init_cfg['amqp_id'].strip():
+ identity = init_cfg['amqp_id'].strip()
+ server = init_cfg['amqp_server']
+ port = init_cfg['amqp_port']
+ user = init_cfg['amqp_user']
+ passwd = init_cfg['amqp_passwd']
+ ret = vm9pctrl.config_amqp(identity, server, port, user, passwd)
+ assert ret == True
+ LOG.info('initConfigVM config_amqp ok')
+ if 'tap_pktloop_config' in init_cfg:
+ taps = vm_cfg['taps']
+ macs = []
+ for tap in taps:
+ macs.append(tap['tap_mac'])
+ ret = vm9pctrl.set_pktloop_dpdk(macs)
+ assert ret == True
+ LOG.info('initConfigVM set_pktloop_dpdk ok')
+ return True