[PATCH] VirtualBox power mngt

Erwan MAS erwan at mas.nom.fr
Wed Apr 19 21:56:51 UTC 2017


Hi

Please find in attachement , a patch for maas 2.1 & maas 2.2 .

Power address : login@(ip|name)
Power ID      : name_of_the_virtual_machine

For using this :
  maas user must be allow to ssh to the server and run the command VBoxManage 

Thanks

--
Erwan MAS
-------------- next part --------------
--- /usr/share/maas/web/static/js/angular/controllers/add_hardware.js.old	2016-12-21 07:56:19.000000000 -0500
+++ /usr/share/maas/web/static/js/angular/controllers/add_hardware.js	2017-04-19 14:34:02.297953011 -0400
@@ -175,6 +175,11 @@
                 description: 'Virsh (virtual systems)',
                 fields: virshFields
             },
+	    {
+		name: 'virtualbox',
+		description: 'VirtualBox (virtual systems)',
+		fields: virshFields
+	    },
             {
                 name: 'vmware',
                 description: 'VMWare',
--- /usr/lib/python3/dist-packages/provisioningserver/drivers/power/__init__.py.old	2016-12-21 07:56:19.000000000 -0500
+++ /usr/lib/python3/dist-packages/provisioningserver/drivers/power/__init__.py	2017-04-19 14:14:43.088109118 -0400
@@ -351,6 +351,7 @@
 from provisioningserver.drivers.power.seamicro import SeaMicroPowerDriver
 from provisioningserver.drivers.power.ucsm import UCSMPowerDriver
 from provisioningserver.drivers.power.virsh import VirshPowerDriver
+from provisioningserver.drivers.power.virtualbox import VirtualBoxPowerDriver
 from provisioningserver.drivers.power.vmware import VMwarePowerDriver
 from provisioningserver.drivers.power.nova import NovaPowerDriver
 
@@ -369,6 +370,7 @@
     SeaMicroPowerDriver(),
     UCSMPowerDriver(),
     VirshPowerDriver(),
+    VirtualBoxPowerDriver(),
     VMwarePowerDriver(),
     NovaPowerDriver(),
 ]
--- /usr/lib/python3/dist-packages/provisioningserver/power/__init__.py.old	2017-04-19 14:16:53.808080278 -0400
+++ /usr/lib/python3/dist-packages/provisioningserver/power/__init__.py	2017-04-19 14:15:49.626080011 -0400
@@ -30,6 +30,7 @@
     'sm15k',
     'ucsm',
     'virsh',
+    'virtualbox',
     'vmware',
     'nova',
 ]
--- /usr/lib/python3/dist-packages/provisioningserver/power/schema.py.old	2016-12-21 07:56:19.000000000 -0500
+++ /usr/lib/python3/dist-packages/provisioningserver/power/schema.py	2017-04-19 14:17:39.458579904 -0400
@@ -250,6 +250,19 @@
             'power_address', IP_EXTRACTOR_PATTERNS.URL),
     },
     {
+        'name': 'virtualbox',
+        'description': 'Virtualbox (virtual systems)',
+        'fields': [
+            make_json_field('power_address', "Power address", required=True),
+            make_json_field(
+                'power_id', "Power ID", scope=POWER_PARAMETER_SCOPE.NODE,
+                required=True),
+            make_json_field(
+                'power_pass', "Power password (optional)",
+                required=False, field_type='password'),
+        ],
+    },
+    {
         'name': 'vmware',
         'description': 'VMWare',
         'fields': [
--- /usr/lib/python3/dist-packages/provisioningserver/drivers/hardware/virtualbox.py.old	1969-12-31 19:00:00.000000000 -0500
+++ /usr/lib/python3/dist-packages/provisioningserver/drivers/hardware/virtualbox.py	2017-04-19 14:12:11.188836872 -0400
@@ -0,0 +1,123 @@
+# 
+# VBoxManage   showvminfo  --machinereadable pxe-maas | grep -i ^VMState=
+# VBoxManage    controlvm   k8-slv02 poweroff 
+# VBoxManage    startvm     k8-slv02
+#
+
+__all__ = []
+
+from socket import error as SOCKETError
+
+from paramiko import (
+    AutoAddPolicy,
+    SSHClient,
+    SSHException,
+)
+
+from provisioningserver.logger import get_maas_logger
+maaslog = get_maas_logger("drivers.virtualbox")
+
+import pprint
+
+class VirtualBoxVMState:
+    OFF = "poweroff"
+    ON = "running"
+    PAUSED = "paused"
+
+
+VB_STATE_TO_POWER_STATE = {
+    VirtualBoxVMState.OFF: "off",
+    VirtualBoxVMState.ON: "on",
+    VirtualBoxVMState.PAUSED: "off"
+    }
+
+
+class VirtualBoxError(Exception):
+        """Failure communicating to virtualbox. """
+        
+
+def run_vboxmanage_command(command, power_address=None, power_user=None,power_pass=None):
+        """Run a VBoxManage via SSH and return output."""
+        try:
+            ssh_client = SSHClient()
+            ssh_client.set_missing_host_key_policy(AutoAddPolicy())
+            ssh_client.connect(
+                power_address, username=power_user, password=power_pass)
+            _, stdout, stderr = ssh_client.exec_command("VBoxManage %s" % command)
+            output = stdout.read().decode('utf-8').split()
+            error = stderr.read().decode('utf-8').split()
+        except (SSHException, EOFError, SOCKETError) as e:
+            raise VirtualBoxError(
+                "Could not make SSH connection do run VBoxManage for "
+                "%s on %s - %s" % (power_user, power_address, e))
+        finally:
+            ssh_client.close()
+        return {'stdout':output,'stderr':error}
+
+    
+
+def power_control_virtualbox(poweraddr, machine, power_change, password=None):
+    """Powers controls a VM using virtualbox."""
+
+    # Force password to None if blank, as the power control
+    # script will send a blank password if one is not set.
+    if password == '':
+        password = None
+
+    powerinfos=poweraddr.split("@")
+    if len(powerinfos) == 1 :
+        powerhost=poweraddr
+        poweruser=None
+    else:
+        poweruser=powerinfos[0]
+        powerhost=powerinfos[1]
+    
+    state = power_state_virtualbox(poweraddr, machine, password)
+
+    print('current state ( %s ) '%machine) 
+    pprint.pprint(state) 
+    
+    if state == 'off':
+        if power_change == 'on':
+            status = run_vboxmanage_command ("startvm %s" % machine ,
+                                             power_address=powerhost, power_user=poweruser,power_pass=password) 
+            if not status :
+                raise VirtualBoxError('%s: Failed to power on VM' % machine)
+    elif state == 'on':
+        if power_change == 'off':
+            status = run_vboxmanage_command ("controlvm %s poweroff" % machine ,
+                                             power_address=powerhost, power_user=poweruser,power_pass=password) 
+            if not status :
+                raise VirtualBoxError('%s: Failed to power off VM' % machine)
+
+
+def power_state_virtualbox(poweraddr, machine, password=None):
+    """Return the power state for the VM using virtualbox."""
+
+    # Force password to None if blank, as the power control
+    # script will send a blank password if one is not set.
+    if password == '':
+        password = None
+
+    powerinfos=poweraddr.split("@")
+    if len(powerinfos) == 1 :
+        powerhost=poweraddr
+        poweruser=None
+    else:
+        poweruser=powerinfos[0]
+        powerhost=powerinfos[1]
+    
+    status = run_vboxmanage_command ("showvminfo  --machinereadable %s" % machine ,
+                                    power_address=powerhost, power_user=poweruser,power_pass=password) 
+    if status is None:
+        raise VirtualBoxError('Failed to get domain: %s' % machine)
+
+    state=None
+    for v in status['stdout'] :
+        if v[0:9] == 'VMState="' :
+            state=v[9:v.find('"',10)]
+
+    try:
+        return VB_STATE_TO_POWER_STATE[state]
+    except KeyError:
+        raise VirtualBoxError('Unknown state: "%s" "%s" ( was VBoxManage   showvminfo  --machinereadable "%s"  ) ' % (state,status,machine) )
--- /usr/lib/python3/dist-packages/provisioningserver/drivers/power/virtualbox.py.old	1969-12-31 19:00:00.000000000 -0500
+++ /usr/lib/python3/dist-packages/provisioningserver/drivers/power/virtualbox.py	2017-04-19 14:12:24.060429569 -0400
@@ -0,0 +1,49 @@
+# 
+# 
+
+"""VirtualBox Power Driver."""
+
+__all__ = []
+
+from provisioningserver.drivers.hardware.virtualbox import (
+    power_control_virtualbox,
+    power_state_virtualbox,
+)
+from provisioningserver.drivers.power import PowerDriver
+from provisioningserver.utils import shell
+
+
+def extract_virtualbox_parameters(context):
+    poweraddr = context.get('power_address')
+    machine = context.get('power_id')
+    password = context.get('power_pass')
+    return poweraddr, machine, password
+
+
+class VirtualBoxPowerDriver(PowerDriver):
+
+    name = 'virtualbox'
+    description = "VirtualBox Power Driver."
+    settings = []
+
+    def detect_missing_packages(self):
+        return list(set())
+
+    def power_on(self, system_id, context):
+        """Power on VirtualBox node."""
+        power_change = 'on'
+        poweraddr, machine, password = extract_virtualbox_parameters(context)
+        power_control_virtualbox (
+            poweraddr, machine, power_change, password)
+
+    def power_off(self, system_id, context):
+        """Power off VirtualBox node."""
+        power_change = 'off'
+        poweraddr, machine, password = extract_virtualbox_parameters(context)
+        power_control_virtualbox (
+            poweraddr, machine, power_change, password)
+
+    def power_query(self, system_id, context):
+        """Power query VirtualBox node."""
+        poweraddr, machine, password = extract_virtualbox_parameters(context)
+        return power_state_virtualbox (poweraddr, machine, password)
-------------- next part --------------
--- usr/lib/python3/dist-packages/provisioningserver/drivers/hardware/virtualbox.py.old	1969-12-31 19:00:00.000000000 -0500
+++ usr/lib/python3/dist-packages/provisioningserver/drivers/hardware/virtualbox.py	2017-04-17 17:18:30.000000000 -0400
@@ -0,0 +1,123 @@
+# 
+# VBoxManage   showvminfo  --machinereadable pxe-maas | grep -i ^VMState=
+# VBoxManage    controlvm   k8-slv02 poweroff 
+# VBoxManage    startvm     k8-slv02
+#
+
+__all__ = []
+
+from socket import error as SOCKETError
+
+from paramiko import (
+    AutoAddPolicy,
+    SSHClient,
+    SSHException,
+)
+
+from provisioningserver.logger import get_maas_logger
+maaslog = get_maas_logger("drivers.virtualbox")
+
+import pprint
+
+class VirtualBoxVMState:
+    OFF = "poweroff"
+    ON = "running"
+    PAUSED = "paused"
+
+
+VB_STATE_TO_POWER_STATE = {
+    VirtualBoxVMState.OFF: "off",
+    VirtualBoxVMState.ON: "on",
+    VirtualBoxVMState.PAUSED: "off"
+    }
+
+
+class VirtualBoxError(Exception):
+        """Failure communicating to virtualbox. """
+        
+
+def run_vboxmanage_command(command, power_address=None, power_user=None,power_pass=None):
+        """Run a VBoxManage via SSH and return output."""
+        try:
+            ssh_client = SSHClient()
+            ssh_client.set_missing_host_key_policy(AutoAddPolicy())
+            ssh_client.connect(
+                power_address, username=power_user, password=power_pass)
+            _, stdout, stderr = ssh_client.exec_command("VBoxManage %s" % command)
+            output = stdout.read().decode('utf-8').split()
+            error = stderr.read().decode('utf-8').split()
+        except (SSHException, EOFError, SOCKETError) as e:
+            raise VirtualBoxError(
+                "Could not make SSH connection do run VBoxManage for "
+                "%s on %s - %s" % (power_user, power_address, e))
+        finally:
+            ssh_client.close()
+        return {'stdout':output,'stderr':error}
+
+    
+
+def power_control_virtualbox(poweraddr, machine, power_change, password=None):
+    """Powers controls a VM using virtualbox."""
+
+    # Force password to None if blank, as the power control
+    # script will send a blank password if one is not set.
+    if password == '':
+        password = None
+
+    powerinfos=poweraddr.split("@")
+    if len(powerinfos) == 1 :
+        powerhost=poweraddr
+        poweruser=None
+    else:
+        poweruser=powerinfos[0]
+        powerhost=powerinfos[1]
+    
+    state = power_state_virtualbox(poweraddr, machine, password)
+
+    print('current state ( %s ) '%machine) 
+    pprint.pprint(state) 
+    
+    if state == 'off':
+        if power_change == 'on':
+            status = run_vboxmanage_command ("startvm %s" % machine ,
+                                             power_address=powerhost, power_user=poweruser,power_pass=password) 
+            if not status :
+                raise VirtualBoxError('%s: Failed to power on VM' % machine)
+    elif state == 'on':
+        if power_change == 'off':
+            status = run_vboxmanage_command ("controlvm %s poweroff" % machine ,
+                                             power_address=powerhost, power_user=poweruser,power_pass=password) 
+            if not status :
+                raise VirtualBoxError('%s: Failed to power off VM' % machine)
+
+
+def power_state_virtualbox(poweraddr, machine, password=None):
+    """Return the power state for the VM using virtualbox."""
+
+    # Force password to None if blank, as the power control
+    # script will send a blank password if one is not set.
+    if password == '':
+        password = None
+
+    powerinfos=poweraddr.split("@")
+    if len(powerinfos) == 1 :
+        powerhost=poweraddr
+        poweruser=None
+    else:
+        poweruser=powerinfos[0]
+        powerhost=powerinfos[1]
+    
+    status = run_vboxmanage_command ("showvminfo  --machinereadable %s" % machine ,
+                                    power_address=powerhost, power_user=poweruser,power_pass=password) 
+    if status is None:
+        raise VirtualBoxError('Failed to get domain: %s' % machine)
+
+    state=None
+    for v in status['stdout'] :
+        if v[0:9] == 'VMState="' :
+            state=v[9:v.find('"',10)]
+
+    try:
+        return VB_STATE_TO_POWER_STATE[state]
+    except KeyError:
+        raise VirtualBoxError('Unknown state: "%s" "%s" ( was VBoxManage   showvminfo  --machinereadable "%s"  ) ' % (state,status,machine) )
--- usr/lib/python3/dist-packages/provisioningserver/drivers/power/registry.py.old	2017-04-14 17:51:41.000000000 -0400
+++ usr/lib/python3/dist-packages/provisioningserver/drivers/power/registry.py	2017-04-17 17:17:33.000000000 -0400
@@ -24,6 +24,7 @@
 from provisioningserver.drivers.power.seamicro import SeaMicroPowerDriver
 from provisioningserver.drivers.power.ucsm import UCSMPowerDriver
 from provisioningserver.drivers.power.virsh import VirshPowerDriver
+from provisioningserver.drivers.power.virtualbox import VirtualBoxPowerDriver
 from provisioningserver.drivers.power.vmware import VMwarePowerDriver
 from provisioningserver.drivers.power.wedge import WedgePowerDriver
 from provisioningserver.utils.registry import Registry
@@ -62,6 +63,7 @@
     SeaMicroPowerDriver(),
     UCSMPowerDriver(),
     VirshPowerDriver(),
+    VirtualBoxPowerDriver(),    
     VMwarePowerDriver(),
     WedgePowerDriver(),
 ]
--- usr/lib/python3/dist-packages/provisioningserver/drivers/power/virtualbox.py.old	1969-12-31 19:00:00.000000000 -0500
+++ usr/lib/python3/dist-packages/provisioningserver/drivers/power/virtualbox.py	2017-04-17 17:18:58.000000000 -0400
@@ -0,0 +1,64 @@
+# 
+# 
+
+"""VirtualBox Power Driver."""
+
+__all__ = []
+
+from provisioningserver.drivers import (
+    IP_EXTRACTOR_PATTERNS,
+    make_ip_extractor,
+    make_setting_field,
+    SETTING_SCOPE,
+)
+from provisioningserver.drivers.hardware.virtualbox import (
+    power_control_virtualbox,
+    power_state_virtualbox,
+)
+from provisioningserver.drivers.power import PowerDriver
+from provisioningserver.utils import shell
+
+
+def extract_virtualbox_parameters(context):
+    poweraddr = context.get('power_address')
+    machine = context.get('power_id')
+    password = context.get('power_pass')
+    return poweraddr, machine, password
+
+
+class VirtualBoxPowerDriver(PowerDriver):
+
+    name = 'virtualbox'
+    description = "VirtualBox Power Driver."
+    settings = [
+        make_setting_field('power_address', "Power address", required=True),
+        make_setting_field(
+            'power_id', "Power ID", scope=SETTING_SCOPE.NODE,
+            required=True),
+        make_setting_field(
+            'power_pass', "Power password (optional)",
+            required=False, field_type='password'),
+    ]
+    ip_extractor = None
+
+    def detect_missing_packages(self):
+        return list(set())
+
+    def power_on(self, system_id, context):
+        """Power on VirtualBox node."""
+        power_change = 'on'
+        poweraddr, machine, password = extract_virtualbox_parameters(context)
+        power_control_virtualbox (
+            poweraddr, machine, power_change, password)
+
+    def power_off(self, system_id, context):
+        """Power off VirtualBox node."""
+        power_change = 'off'
+        poweraddr, machine, password = extract_virtualbox_parameters(context)
+        power_control_virtualbox (
+            poweraddr, machine, power_change, password)
+
+    def power_query(self, system_id, context):
+        """Power query VirtualBox node."""
+        poweraddr, machine, password = extract_virtualbox_parameters(context)
+        return power_state_virtualbox (poweraddr, machine, password)
--- usr/share/maas/web/static/js/angular/controllers/add_hardware.js.old	2017-04-14 17:51:41.000000000 -0400
+++ usr/share/maas/web/static/js/angular/controllers/add_hardware.js	2017-04-17 17:17:13.000000000 -0400
@@ -176,6 +176,11 @@
                 fields: virshFields
             },
             {
+		name: 'virtualbox',
+		description: 'VirtualBox (virtual systems)',
+		fields: virshFields
+	    },
+	    {
                 name: 'vmware',
                 description: 'VMware',
                 fields: [


More information about the Maas-devel mailing list