#!/usr/bin/python
from ConfigParser import ConfigParser
from argparse import ArgumentParser
import fcntl
import socket
import errno
import re
import os,sys
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(1024)
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(1024)
#print "Got chunk = ",repr(chunk)
if chunk == '':
raise IOError("Unexpected EOF From monitor")
answer+=chunk
finally:
fcntl.flock(sock,fcntl.LOCK_UN)
return answer
def 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_spiceuri(options):
print spiceurl(options)
def cmd_start(options):
if options.stopped:
arg=""
if options.cdrom:
arg=" -cdrom "+options.cdrom
cwd=os.getcwd()
os.chdir(options.dir)
os.system("./start%s" % arg)
os.chdir(cwd)
os.sleep(2)
options.sock = connect_vm(options.dir)
if options.gui:
uri = spiceurl(options)
os.system("remote-viewer %s &"%uri)
elif not options.stopped:
print >>sys.stderr,"VM already running"
def cmd_stop(options):
print send_command(options.sock,'system_powerdown')
def cmd_reset(options):
print send_command(options.sock,'system_reset')
def cmd_cdrom(options):
if options.id is None:
# Search for devices which could be interpreted as CDROM
devlist=send_command(options.sock,"info block")
for dev in re.findall("([-\\w]+): [^\n]+\n Removable device:",devlist):
if re.search("cd",dev):
options.id=dev
break
if options.id is None:
print >>sys.stderr, "No CDROM device found among:\n"+devlist;
sys.exit(1)
if options.file == "":
print >>sys.stderr, "Please specify either --eject or iso image"
sys.exit(1)
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
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
def cmd_list(options):
count = 0
search_path=[os.environ['HOME']+"/VWs",
config.get("directories","SharedVMs"),
config.get("directories","AutostartVMs")]
for dirname in search_path:
if not os.access(dirname+"/.",os.X_OK):
continue
for vmname in os.listdir(dirname):
if os.access(dirname+"/"+vmname+"/start",os.X_OK):
count += 1
print vmname
if not count:
sys.exit(1)
#
# 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',
'AutoStartVMs':'/var/cache/vws/autostart'})
config.add_section('directories')
config.read(['/etc/vws.conf',os.environ['HOME']+'/.vwsrc'])
args=ArgumentParser()
cmds=args.add_subparsers(dest='command',help="sub-command help")
p=cmds.add_parser("list",help="List existing VWs")
p.add_argument("--state",action='store_const',const=True,default=False,
dest='state',help='Show state of the machine')
p.add_argument("--addr",action='store_const',const=True,default=False,
dest='addr',help='Show mac address and spice port')
# 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=None,
help='Identifier of CDROM drive if VM has more than one')
p.add_argument('file',nargs="?",default='',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:])
parsed_args.stopped = False
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:
parsed_args.stopped = True
else:
raise e
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)