Aug 302013
 

As I mentioned – I’m migrating my sysadm tasks from Perl to Python, and Windows is just one of them.

This is a real case, I like VirtualBox as a neat visualization container, and spending lot of time on using it, actually there are always 2~3 VMs running on my laptop while another 4~5 lying on HDD. However, I have different requirements for different types of VMs, like for Windows VM, I used to launch them in GUI mode so that I can use another Windows environment, but for Linux and other *nix machines I used to launch them in headless mode and use putty (another neat tool ๐Ÿ™‚ ) to access.

The problem is that VirtualBox’s console does not support starting VMs in headless mode, so I created some BAT files to do it, but this makes things complicated that I have to go to two places to do actually one thing.

Now here comes Python, plus VirtualBox guys kindly released a Python SDK. It was for Python 2.x, but no wonder some smart guy convert it to work with Python 3. I was thinking of using win32 binding, but later on found Tkinter is good enough for this purpose. Codes posted below, feel free to take it as long as you can mention my name and link back to this post (I hate GPL … you can guess I’m a fan of BSD).

I’ll do some more enhancement if I have to, but so far it can show button status based on VM state, and start/stop/reset the VMs, that’s pretty all I want at this moment. ๐Ÿ˜›
[python]
import sys
import tkinter
from tkinter import messagebox
import vboxapi
import time

mgr = vboxapi.VirtualBoxManager(None, None)

def list_vms():
global mgr
vm_list = []
for vm in mgr.getArray(mgr.vbox, ‘machines’):
vm_list.append(vm)
vm_list = sorted(vm_list, key = lambda k: k.name)
return vm_list

def start_vm(vm_id):
global mgr, root, button_list
root.config(cursor=’clock’)
session = mgr.mgr.getSessionObject(mgr.vbox)
try:
# start the VM
machine = mgr.vbox.FindMachine(vm_id)
if mgr.vbox.getGuestOSType(machine.OSTypeId).familyId == ‘Windows’:
start_type = ‘gui’
else:
start_type = ‘headless’
progress = machine.launchVMProcess(session, start_type, ”)
progress.waitForCompletion(-1)

# change UI
button_list[vm_id][‘start’].config(state=tkinter.DISABLED)
button_list[vm_id][‘stop’].config(state=tkinter.NORMAL)
except Exception as e:
messagebox.showerror(‘Error Happened’, e)
finally:
if session.state == mgr.constants.SessionState_Locked:
session.unlockMachine()
root.config(cursor=”)

def stop_vm(vm_id):
global mgr, root
root.config(cursor=’clock’)
session = mgr.mgr.getSessionObject(mgr.vbox)
try:
# stop (savestate) the VM
mgr.vbox.FindMachine(vm_id).lockMachine(session, mgr.constants.LockType_Shared)
session.console.saveState()

# change UI
button_list[vm_id][‘start’].config(state=tkinter.NORMAL)
button_list[vm_id][‘stop’].config(state=tkinter.DISABLED)
except Exception as e:
messagebox.showerror(‘Error Happened’, e)
finally:
if session.state == mgr.constants.SessionState_Locked:
session.unlockMachine()
root.config(cursor=”)

def reset_vm(vm_id):
global mgr, root
if not messagebox.askyesno(‘Reset VM’, ‘Are you sure?’, default=’no’):
return

root.config(cursor=’clock’)
session = mgr.mgr.getSessionObject(mgr.vbox)
try:
# stop (savestate) the VM
mgr.vbox.FindMachine(vm_id).lockMachine(session, mgr.constants.LockType_Shared)
session.console.reset()

# change UI
button_list[vm_id][‘start’].config(state=tkinter.DISABLED)
button_list[vm_id][‘stop’].config(state=tkinter.NORMAL)
except Exception as e:
messagebox.showerror(‘Error Happened’, e)
finally:
if session.state == mgr.constants.SessionState_Locked:
session.unlockMachine()
root.config(cursor=”)

def timer():
pass
global mgr, vm_list, button_list, timer_lbl, root

# update button status according to VM state
for index in range(0, len(vm_list)):
if vm_list[index].state == mgr.constants.MachineState_Running:
# VM is running
button_list[vm_list[index].id][‘start’].config(state=tkinter.DISABLED)
button_list[vm_list[index].id][‘stop’].config(state=tkinter.NORMAL)
button_list[vm_list[index].id][‘reset’].config(state=tkinter.NORMAL)
elif vm_list[index].state == mgr.constants.MachineState_Saved or vm_list[index].state == mgr.constants.MachineState_PoweredOff or vm_list[index].state == mgr.constants.MachineState_Aborted:
# VM is not running
button_list[vm_list[index].id][‘start’].config(state=tkinter.NORMAL)
button_list[vm_list[index].id][‘stop’].config(state=tkinter.DISABLED)
button_list[vm_list[index].id][‘reset’].config(state=tkinter.DISABLED)
else:
# VM is in interim state
button_list[vm_list[index].id][‘start’].config(state=tkinter.DISABLED)
button_list[vm_list[index].id][‘stop’].config(state=tkinter.DISABLED)
button_list[vm_list[index].id][‘reset’].config(state=tkinter.DISABLED)

# update clock
timer_lbl.configure(text=time.strftime(“%Y-%m-%d %H:%M:%S”))
root.after(1000, timer)

vm_list = list_vms()
button_list = {}
root = tkinter.Tk()
timer_lbl = tkinter.Label(root, text=”, justify=tkinter.RIGHT)
timer_lbl.grid(row=0, columnspan=4, sticky=tkinter.N, padx=10, pady=10)
for index in range(0, len(vm_list)):
if vm_list[index].state == mgr.constants.MachineState_Running:
start_btn_state = tkinter.DISABLED
stop_btn_state = tkinter.NORMAL
else:
start_btn_state = tkinter.NORMAL
stop_btn_state = tkinter.DISABLED

# VM label
lbl = tkinter.Label(root, text=vm_list[index].name)
lbl.grid(row=index+1, sticky=tkinter.W, padx=5, pady=5)

# start button
start_btn = tkinter.Button(root, text=’Start’, state=start_btn_state)
start_btn[‘command’] = lambda vm_id = vm_list[index].id: start_vm(vm_id)
start_btn.grid(row=index+1, column=1, padx=5, pady=5)

# stop (save state) button
stop_btn = tkinter.Button(root, text=’Stop’, state=stop_btn_state)
stop_btn[‘command’] = lambda vm_id = vm_list[index].id: stop_vm(vm_id)
stop_btn.grid(row=index+1, column=2, padx=5, pady=5)

# reset (power recycle) button
reset_btn = tkinter.Button(root, text=’Reset’, state=stop_btn_state)
reset_btn[‘command’] = lambda vm_id = vm_list[index].id: reset_vm(vm_id)
reset_btn.grid(row=index+1, column=3, padx=5, pady=5)

button_list[vm_list[index].id] = {‘start’: start_btn, ‘stop’: stop_btn, ‘reset’: reset_btn}

timer()
root.bind(‘‘, lambda event: root.quit())
root.mainloop()
[/python]

  One Response to “Play with Python on Windows”

  1. Another task is that I need to find a better way to manage putty sessions – I used to have 10~20 sessions open at the same time, currently I’m using SuperPutty, but it sometimes cause problem in switching between sessions, let’s see if I can make it.

Sorry, the comment form is closed at this time.