Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial swag at VirtualBox support #5

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

pbarry-r7
Copy link

Using the pyvbox library, we can enumerate all VMs on an install of VirtualBox, check their powered state, and change their powered state (up or down). Offering this PR now to see if this looks like a reasonable direction (and I can go back and add more functionality if this to 'too minimum' for an MVP of a PR).

Also includes some minor verbiage tweaks to the README.md file (e.g. consistent company/product names, removal of trailing spaces, minor stuff...).

This commit supports:

* enumerating existing VirtualBox VMs
* checking the powered state of a VirtualBox VM
* powering a VirtualBox VM up or down
setup.py Outdated
@@ -1,7 +1,7 @@
from setuptools import setup

setup(name='vm_automation',
version='0.1.2',
version='0.1.3',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No bump here 0.1.2 is the currently in development version.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, wasn't sure since I was adding a dependency and keyword. Will un-bump.

self.vmSession.console.power_down()
self.vmSession.unlock_machine()
self.vmSession = None
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you expand this out to have all methods implemented from workstationVm for Server and VM classes?

Throwing some sort of not implemented or returning false for not yet supported items seems a good start to make these classes interchangeable.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

NOTE: I went with the NotImplmentedError exception, which appears to be
acceptable for this use case based on random googling and the [Python3
docs](https://docs.python.org/3/library/exceptions.html#NotImplementedError).
Happy to change if a different one would be preferable.
@pbarry-r7
Copy link
Author

Updated...!

This requires the [VirtualBox Guest Additions](https://www.virtualbox.org/manual/ch04.html) to be properly installed+running in the guest VM to work.
Copy link
Contributor

@jmartin-tech jmartin-tech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issues in testing, I can address if need be.

return None

def enumerateVms(self, negFilter = None):
for vm in self.vm.machines:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks to fail when no vms already exist...

>>> myserver.enumerateVms()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "vm_automation/virtualboxVm.py", line 20, in enumerateVms
    self.vmList.append(virtualboxVm(self, vm))
  File "vm_automation/virtualboxVm.py", line 56, in __init__
    self.vmName = str(self.vmObject.name)
  File "/home/msfadmin/.pyenv/versions/2.7.13/lib/python2.7/site-packages/virtualbox/library.py", line 9619, in name
    ret = self._get_attr("name")
  File "/home/msfadmin/.pyenv/versions/2.7.13/lib/python2.7/site-packages/virtualbox/library_base.py", line 165, in _get_attr
    attr = self._search_attr(name, prefix='get')
  File "/home/msfadmin/.pyenv/versions/2.7.13/lib/python2.7/site-packages/virtualbox/library_base.py", line 152, in _search_attr
    attr = getattr(self._i, attr_name, self)
  File "/usr/lib/virtualbox/sdk/bindings/xpcom/python/xpcom/client/__init__.py", line 381, in __getattr__
    return getattr(interface, attr)
  File "/usr/lib/virtualbox/sdk/bindings/xpcom/python/xpcom/client/__init__.py", line 467, in __getattr__
    return XPTC_InvokeByIndex(self._comobj_, method_index, args)
xpcom.Exception: 0x80070005 (The object functionality is limited)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hah, welp. Good catch! I can fix...

return None

def enumerateVms(self, negFilter = None):
for vm in self.vm.machines:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enumerating the Vms available does not clear the list on each pass, when a vm is renamed by another task the list "expands" instead of changes.

>>> myserver.enumerateVms()
True
>>> myserver.vmList
[]
>>> myserver.enumerateVms()
True
>>> myserver.vmList
[<vm_automation.virtualboxVm.virtualboxVm instance at 0x7fecc9505680>]
>>> for vm in myserver.vmList:
...     print(vm.vmName)
...
vm
>>> myserver.enumerateVms()
True
>>> for vm in myserver.vmList:
...     print(vm.vmName)
...
vm
Nexpose
>>> myserver.vmList
[<vm_automation.virtualboxVm.virtualboxVm instance at 0x7fecc9505680>, <vm_automation.virtualboxVm.virtualboxVm instance at 0x7fecc95057e8>]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, will fix. Thx!

@pbarry-r7
Copy link
Author

PR updated to clear the list of VMs prior to enumeration.

Upon closer inspection, the stacktrace (above) when no VMs are present is interesting, as that loop should only be entered when there is at least one VirtualBox VM present (i.e. self.vm.machines is not an empty list). Each "machine" object should have a name attribute. I'm wondering if maybe the machines list contains an entry for a VM that was removed (allowing the loop body to execute but fail to get the name) or if the machines list is corrupted in some way.

I have a LOT of VirtualBox VMs on my system (it's my primary VM host), so I'd prefer to not remove them for replicating this issue. :) FWIW, here's what a machine object should "look" like, I'd be curious if you might check this on your setup, @jmartin-r7, to maybe see what's up with the machines list. Thx!

>>> import vm_automation
>>> myserver = vm_automation.virtualboxServer()
>>> myserver.vm.machines
[<virtualbox.library_ext.machine.IMachine object at 0x111020ad0>, <virtualbox.library_ext.machine.IMachine object at 0x111020b10>, <virtualbox.library_ext.machine.IMachine object at 0x111020b50>, <virtualbox.library_ext.machine.IMachine object at 0x111020b90>, <virtualbox.library_ext.machine.IMachine object at 0x111020bd0>, <virtualbox.library_ext.machine.IMachine object at 0x111020c10>, <virtualbox.library_ext.machine.IMachine object at 0x111020c50>, <virtualbox.library_ext.machine.IMachine object at 0x111020c90>, <virtualbox.library_ext.machine.IMachine object at 0x111020cd0>, <virtualbox.library_ext.machine.IMachine object at 0x111020d10>, <virtualbox.library_ext.machine.IMachine object at 0x111020d50>, <virtualbox.library_ext.machine.IMachine object at 0x111020d90>, <virtualbox.library_ext.machine.IMachine object at 0x111020dd0>, <virtualbox.library_ext.machine.IMachine object at 0x111020e10>, <virtualbox.library_ext.machine.IMachine object at 0x111020e50>, <virtualbox.library_ext.machine.IMachine object at 0x111020e90>, <virtualbox.library_ext.machine.IMachine object at 0x111020ed0>, <virtualbox.library_ext.machine.IMachine object at 0x111020f10>]
>>> from pprint import pprint
>>> pprint(myserver.vm.machines[0])
<virtualbox.library_ext.machine.IMachine object at 0x1117fb2d0>
>>> pprint(dir(myserver.vm.machines[0]))
['__class__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__getattribute__',
 '__hash__',
 '__init__',
 '__module__',
 '__new__',
 '__nonzero__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__uuid__',
 '__weakref__',
 '__wsmap__',
 '_call',
 '_call_method',
 '_cast_to_valuetype',
 '_get_attr',
 '_i',
 '_search_attr',
 '_set_attr',
 'accelerate2_d_video_enabled',
 'accelerate3_d_enabled',
 'access_error',
 'accessible',
 'add_storage_controller',
 'add_usb_controller',
 'adopt_saved_state',
 'allow_tracing_to_access_vm',
 'apply_defaults',
 'attach_device',
 'attach_device_without_medium',
 'attach_host_pci_device',
 'audio_adapter',
 'autostart_delay',
 'autostart_enabled',
 'autostop_type',
 'bandwidth_control',
 'bios_settings',
 'can_show_console_window',
 'chipset_type',
 'clipboard_mode',
 'clone',
 'clone_to',
 'cpu_count',
 'cpu_execution_cap',
 'cpu_hot_plug_enabled',
 'cpu_profile',
 'cpuid_portability_level',
 'create_session',
 'create_shared_folder',
 'current_snapshot',
 'current_state_modified',
 'default_frontend',
 'delete_config',
 'delete_guest_property',
 'delete_snapshot',
 'delete_snapshot_and_all_children',
 'delete_snapshot_range',
 'description',
 'detach_device',
 'detach_host_pci_device',
 'discard_saved_state',
 'discard_settings',
 'dn_d_mode',
 'emulated_usb_card_reader_enabled',
 'enumerate_guest_properties',
 'export_to',
 'fault_tolerance_address',
 'fault_tolerance_password',
 'fault_tolerance_port',
 'fault_tolerance_state',
 'fault_tolerance_sync_interval',
 'find_snapshot',
 'firmware_type',
 'get_boot_order',
 'get_cpu_property',
 'get_cpu_status',
 'get_cpuid_leaf',
 'get_effective_paravirt_provider',
 'get_extra_data',
 'get_extra_data_keys',
 'get_guest_property',
 'get_guest_property_timestamp',
 'get_guest_property_value',
 'get_hw_virt_ex_property',
 'get_medium',
 'get_medium_attachment',
 'get_medium_attachments_of_controller',
 'get_network_adapter',
 'get_parallel_port',
 'get_serial_port',
 'get_storage_controller_by_instance',
 'get_storage_controller_by_name',
 'get_usb_controller_by_name',
 'get_usb_controller_count_by_type',
 'graphics_controller_type',
 'groups',
 'hardware_uuid',
 'hardware_version',
 'hot_plug_cpu',
 'hot_unplug_cpu',
 'hpet_enabled',
 'icon',
 'id_p',
 'io_cache_enabled',
 'io_cache_size',
 'keyboard_hid_type',
 'last_state_change',
 'launch_vm_process',
 'lock_machine',
 'log_folder',
 'medium_attachments',
 'memory_balloon_size',
 'memory_size',
 'monitor_count',
 'mount_medium',
 'name',
 'non_rotational_device',
 'os_type_id',
 'page_fusion_enabled',
 'paravirt_debug',
 'paravirt_provider',
 'parent',
 'passthrough_device',
 'pci_device_assignments',
 'pointing_hid_type',
 'query_log_filename',
 'query_saved_guest_screen_info',
 'query_saved_screenshot_info',
 'read_log',
 'read_saved_screenshot_to_array',
 'read_saved_thumbnail_to_array',
 'remove',
 'remove_all_cpuid_leaves',
 'remove_cpuid_leaf',
 'remove_shared_folder',
 'remove_storage_controller',
 'remove_usb_controller',
 'restore_snapshot',
 'rtc_use_utc',
 'save_settings',
 'save_state',
 'session_name',
 'session_pid',
 'session_state',
 'set_auto_discard_for_device',
 'set_bandwidth_group_for_device',
 'set_boot_order',
 'set_cpu_property',
 'set_cpuid_leaf',
 'set_extra_data',
 'set_guest_property',
 'set_guest_property_value',
 'set_hot_pluggable_for_device',
 'set_hw_virt_ex_property',
 'set_no_bandwidth_group_for_device',
 'set_settings_file_path',
 'set_storage_controller_bootable',
 'settings_aux_file_path',
 'settings_file_path',
 'settings_modified',
 'shared_folders',
 'show_console_window',
 'snapshot_count',
 'snapshot_folder',
 'state',
 'state_file_path',
 'storage_controllers',
 'take_snapshot',
 'teleporter_address',
 'teleporter_enabled',
 'teleporter_password',
 'teleporter_port',
 'temporary_eject_device',
 'tracing_config',
 'tracing_enabled',
 'unmount_medium',
 'unregister',
 'usb_controllers',
 'usb_device_filters',
 'usb_proxy_available',
 'video_capture_enabled',
 'video_capture_file',
 'video_capture_fps',
 'video_capture_height',
 'video_capture_max_file_size',
 'video_capture_max_time',
 'video_capture_options',
 'video_capture_rate',
 'video_capture_screens',
 'video_capture_width',
 'vm_process_priority',
 'vram_size',
 'vrde_server']

@jmartin-tech
Copy link
Contributor

jmartin-tech commented Oct 20, 2017

The fail is not really due to an empty return more likely due to accessing a value that is not there when the virtualbox env had a vm removed directly from disk without using virtualbox commands.

Will send details separately for a machine you can test on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants