[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