#!/usr/bin/python
from ConfigParser import ConfigParser
from argparse import ArgumentParser
import socket
import errno
import os,sys
config=ConfigParser({"SharedVMs":"/var/cache/vws/shared","AutostartVMs":"/var/cache/vws/auto"})
def find_vm(name):
search_path=[os.environ['HOME']+"/VWs",
config.get("directories","SharedVMs"),
config.get("directories","AutostartVMs")]
for dirname in search_path:
if name in os.listdir(dirname):
return dirname+"/"+name
raise ValueError("Machine "+name+" not found.")
def connect_vm(vm_dir):
sock=socket.socket(socket.AF_UNIX)
sock.connect(vm_dir+"/monitor")
greeting=sock.recv()
return sock
def send_command(sock,command):
fcntl.flock(sock,fcntl.LOCK_EX)
try:
sock.send(command+"\n")
answer=""
while not answer.endswith("(qemu)"):
chunk=sock.recv()
print "Got chunk = ",repr(chunk)
if chunk == '':
raise IOError("Unexpected EOF From monitor")
answer+=sock.recv()
finally:
fcntl.flock(sock,fcntl.LOCK_UN)
return answer
def cmd_spiceurl(options):
output=send_command(options.sock,"info spice")
url=None
for line in output.split("\n"):
if url is not None:
continue
n=line.find("address:")
if n != -1:
url=line[n+9:]
if url.startswith('*:'):
url="localhost"+url[1:]
return "spice://"+url
#
# command implementation
#
def cmd_start(options):
pass
def cmd_stop(options):
send_command(options.sock,'system_powerdown')
def cmd_reset(options):
send_command(options.sock,'system_reset')
def cmd_cdrom(options):
if options.file is None:
answer=send_command(options.sock,"eject "+options.id)
else:
answer=send_command(options.sock, "change %s %s" % (options.id,
options.file))
print answer
raise NotImplementedError
def cmd_usb_insert(options):
raise NotImplementedError
def cmd_usb_list(options):
os.system(lspci)
def cmd_usb_remove(options):
raise NotImplementedError
def cmd_usb_attached(options):
answer=send_command(options.sock,"info usb")
print answer
#
# Utility functions for arg parsing
#
def new_command(cmds,name,**kwargs):
"""
Adds a subparser and adds a machine name argument to it
"""
p=cmds.add_parser(name,**kwargs)
p.add_argument('machine',type=str,help='name of vm to operate on')
return p
#
# arg parsing
#
config=ConfigParser({'SharedVMs':'/var/cache/vws/shared',
'AuthoStartVMs':'/var/cache/vws/autostart'})
config.read(['/etc/vws.conf',os.environ['HOME']+'/.vwsrc'])
args=ArgumentParser()
cmds=args.add_subparsers(dest='command',help="sub-command help")
# Power management
p=new_command(cmds,'start',help='Start VM and connect to console')
p.add_argument('--no-gui',dest='gui',action='store_const',const=False,
default=True,help='do not open console window')
p.add_argument('--cdrom',dest='cdrom',nargs=1,
help='connect specified iso image to VMs cdrom on start')
# Following commands don't need extra args
new_command(cmds,'stop',help='Shut down virtual machine')
new_command(cmds,'save',help='Save VM state and stop emulation')
new_command(cmds,'reset',help='Reboot a guest OS')
# Removable devices management
p=new_command(cmds,'cdrom',help='manage CDROM Drive')
p.add_argument('--id',type=str,default='cdrom0',
help='Identifier of CDROM drive if VM has more than one')
p.add_argument('file',help='ISO image or special file to connect to drive')
p.add_argument('--eject',dest='file',action='store_const',const=None)
usb=cmds.add_parser('usb').add_subparsers(dest='subcommand',help='manage USB devices')
p=new_command(usb,'insert',help='attach device to the virtual machine')
p.add_argument('pattern',help='Pattern of device name to look up in lsusb')
p.add_argument('--address',nargs=1,help='exact address bus:device')
p=new_command(usb,'remove',help='detach connected usb device')
p.add_argument('pattern',help='Pattern of device name to look up in lsusb')
p.add_argument('--address',nargs=1,help='exact address bus:device')
p=new_command(usb,'attached',help='list devices attached to vm')
usb.add_parser('list',help='list devices available in the host system')
# Snapshot management
p=new_command(cmds,'snapshot',help='Create new snapshot')
p=new_command(cmds,'revert',help='Revert to last snapshot')
p=new_command(cmds,'commit',help='Commit snapshot changes into backing file')
p=new_command(cmds,'snapshots',help='List existing snapshots')
# Screenshoits and recording
p=new_command(cmds,'screenshoot',help='take a screenshot')
p.add_argument('filename',help='image filename to write screenshot to')
p=new_command(cmds,'record',help='Record audio output from VM')
p.add_argument('filename',help='wav file to record autdio to')
new_command(cmds,'stoprecord',help='stop recording audio')
# Miscellenia
p=new_command(cmds,'monitor',help='connect stdin/stdout to monitor of VM')
p=new_command(cmds,'spiceuri',help='Output spice URI of machine')
parsed_args=args.parse_args(sys.argv[1:])
stopped_vm_commands = ['start','snapshot','revert','commit','snapshots']
if hasattr(parsed_args,'machine'):
parsed_args.dir=find_vm(parsed_args.machine)
try:
parsed_args.sock=connect_vm(parsed_args.dir)
except IOError as e:
if e.errno == errno.ECONNREFUSED:
# virtal machine is not running
if not parsed_args.command in stopped_vm_commands:
print >>sys.stderr, "Virtual machine %s is not running."%parsed_args.machine
sys.exit(1)
else:
raise e
print repr(parsed_args)
funcname="cmd_"+parsed_args.command
if hasattr(parsed_args."subcommand"):
funcname+="_"+parsed_args.subcommand
try:
func=globals()[funcname]
except KeyError:
print >>sys.stderr,"Operation %s is not implemented"%funcname
sys.exit(3)
func(parsed_args)