Controlando VirtualBox via Web

Geralmente eu costumo rodar algumas máquinas virtuais no notebook para efetuar testes e configurações personalizadas, mas se eu tento executar mais de 3 VMs ao mesmo tempo meu notebook não aguenta o tranco.
Obviamente que a solução seria trocar de notebook e pegar um mais potente, mas para mim a máquina atual está de bom tamanho, ao invés de trocar de hardware por que não usar o poder de processamento e memória disponíveis em  meu antigo core 2 duo com windows 7 que fica na sala e possuí o virtualbox instalado?

No próprio site do virtualbox é indicado o uso do phpvirtualbox mas eu descartei essa possibilidade pois é necessário ter um webserver instalado com php e isso já basta como um problema! 
Como tenho preguiça de ter que ir até ele toda hora para realizar alguma ação no virtualbox e o mesmo possuí o python instalado, resolvi criar uma simples aplicação web com python3 e bottle para manipular o virtualbox do meu antigo pc pelo navegador, para pelo menos iniciar e salvar o estado da VM. 
Sim eu poderia ter configurado o acesso remoto na maquina mas qual a graça que teria isso ? =D

 O virtualbox possuí uma API para python através de seu sdk, porém pelo que andei vendo pelo web o mesmo não é muito estável e nada fácil de utilizar, por conta disso eu criei um módulo em cima da linha de comando do virtualbox que aparentemente é mais estável e não quebra a cada atualização (apesar de reportar uns erros sem sentido).

Segue abaixo o módulo em torno da linha de comando do virtualbox



""" Simple wrapper around VirtualBox command line
    however this don't support the full command line
    This is a free software, use like you want."""

# Author: rafael@sourcecode.net.br

import sys
import re
from subprocess import Popen, PIPE
from os import path

class VBoxManage(object):
    def __init__(self, PATH):
        # Check OS
        if "win" in sys.platform:
            # Set path for Windows OS
            self.PATH = path.join(PATH, "VBoxManage.exe")
        # Set path for Linux/Unix OS    
        self.PATH = path.join(PATH, "VBoxManage")
        
    def get_vms(self, run=False):
        """Get vms from VBoxManage list vms output and return into a dict.
           if 'run' arg is set True, will return only running vms"""
        # Check run arg
        if run:
            cmd = Popen(["%s" % self.PATH, "list", "runningvms"], stdout=PIPE)
        else:
            # Run list vms command
            cmd = Popen(["%s" % self.PATH, "list", "vms"], stdout=PIPE)
        # Dicty that will receive vms name and uuid
        VMs = {}
        
        # Parse output list and add name and uuid into dict
        for vm in cmd.stdout.readlines():
            name = re.search(r'".*"', str(vm)).group().strip("\"")
            uuid = re.search(r'{.*}', str(vm)).group().strip("{}")
            VMs[name] = uuid
        return VMs
        
    def control_vm(self, name, action):
        """Control vm from the name or uuid given
           You can start|poweroff|savestate|reset"""
        # Check if the action is start with is not on controlvm command
        if action == "start":
            cmd = Popen(["%s" % self.PATH, "startvm", "%s" % name],
                    stdout=PIPE, stderr=PIPE)
        # Control VM with the given action
        cmd = Popen(["%s" % self.PATH, "controlvm", "%s" % name, "%s" % action],
                    stdout=PIPE, stderr=PIPE)
        # check if method poll is None with means that is still running.
        while cmd.poll is None:
            pass
        # Get stdout and stderr
        out, err = cmd.communicate()
        # If something get wrong then return it
        if err:
            return err
        return out
        
    def get_vm_info(self, name):
        """Get Information about given vm"""
        cmd = Popen(["%s" % self.PATH, "showvminfo", "%s" % name],
                stdout=PIPE, stderr=PIPE)
        # check if cmd still running
        while cmd.poll is None:
            pass
        out, err = cmd.communicate()
        if err:
            return err
        return out

Obviamente o módulo não cobre todas as opções disponíveis pela linha de comando, eu apenas acrescentei as que são necessárias para mim, mas acredito que não será nada difícil acrescentar outras opções caso deseje.

A aplicação no bottle é bem simples também, basicamente fará um import do módulo mostrado acima e utilizará o PATH completo para o VBoxManage que é a interface da linha de comando do virtualbox. 
É necessário a informação completa do PATH do VboxManage para que a aplicação funcione corretamente, no meu caso eu estou utilizando o virtualbox instalado no windows 7 em uma partição diferente da padrão, localizado em E:\Rafael\Documentos\VirtualBox onde o padrão geralmente é em C:\Program Files\.
A aplicação funciona no linux também e o path tem que ser informado corretamente, geralmente o VBoxManage no linux se encontra em /usr/bin.

Segue abaixo a aplicação bottle


import bottle
from vbox.VBoxManage import VBoxManage

# VBoxManage instance with full PATH of VirtualBox
manage = VBoxManage("E:\Rafael\Documentos\VirtualBox")

@bottle.route("/", ["GET", "POST"])
def index():
    # Get all available VMs
    vms = manage.get_vms()
    # Set action as None for GET requests work
    action = None
  
    # Check if request method is a POST
    if bottle.request.method == "POST":
        # Get action option and name for some Virtual Machine
        VM, action = bytes.decode(bottle.request.body.read()).split("=")
        # Replace all "+" character with one empty space " "
        VM = VM.replace("+", " ")
        
        # Run action for given VM
        action = manage.control_vm(VM, action)

    return bottle.template("index.html", vms = vms, output = action)
    

@bottle.route("/Running")
def running():
    vms = manage.get_vms(run=True)
    return bottle.template("running.html", vms = vms)
    
@bottle.route("/vm/")
def vm(name):
    info = manage.get_vm_info(name)
    return bottle.template("vm.html", name = name, info = info)
    
    
bottle.debug(True)
bottle.run(host="0.0.0.0", port=8082)

Perceba que na variável manage onde eu obtenho uma instância do VBoxManage eu informo o path da instalação do virtualbox.

Eu não me preocupei muito com a interface, um simples html com os devidos links e botões dentro de um form para realizar as alterações estão de bom tamanho para mim.

Veja a aplicação em execução


Index listando as maquinas existentes




Iniciando uma VM (geralmente o VBoxManage apresenta esse erro estranho
mas a máquina inicia sem problemas)


/running lista as VMs que estão em execução no momento.
/vm/VMname mostra informações a respeito da Maquina Virtual


Salvando o estado da Máquina Virtual

A aplicação cumpre seu papel e executa as operações sem problema, muito embora a linha de comando do virtualbox informe uma mensagem de erro que não interfere com o processo da solicitado para determinada máquina virtual.

Para instalar no windows é simples, assumindo que você tenha o python3.3 instalado, crie um ambiente isolado com o virtualenv



C:\Python33\python.exe -m venv D:\vboxweb

Acesse o diretório criado e ative o ambiente



Scripts\active.bat

Baixe o bottle aqui, descompacte o instale da seguinte forma



python setup.py install

Ou você pode simplismente instalar o bottle através do pip ou easy_install caso os tenha instalado.

Baixe a aplicação aqui e descompacte dentro do diretório criado com o virtualenv

Dentro da pasta da aplicação vbox edite o arquivo vbox_web.py e altere a linha 8 informando o PATH completo da instalação do seu virtualbox



manage = VBoxManage("INFORME O PATH AQUI")

Agora basta executar a aplicação



python vbox_web.py

Se você não alterou a porta de execução basta acessar localhost:8082 ou seuip:8082 para acessar a aplicação.

Para facilitar você pode adicionar o seguinte arquivo .bat na rais do diretório virtual



cd vbox
@echo Running vbox_web.py
..\Scripts\python.exe vbox_web.py

pause

E com um clique nesse aquivo .bat você executa a aplicação.

Na primeira vez que tentar executar o windows perguntará se você deseja liberar a porta em que a aplicação está sendo executada e para que funcione você terá que liberá-la.


run.bat em execução, com informações de requisição a aplicação

Deixe um comentário