# Copyright (c) 2007 - ALBAR (Toulouse, FRANCE). # mailto:barthe@albar.fr # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Use epydoc (http://epydoc.sourceforge.net) to produce the documentation. # $Id: VMPython.py 9 2008-08-14 15:39:42Z alain $ """This module is a translation from perl to python of the U{VMWare API} for B{VMWare server}, available U{here}. Quick start =========== >>> import VMPython >>> s = VMPython.Server() # s is a Server object >>> c = VMPython.ConnectParams() # c is a ConnectParams object >>> s.connect(c) # connect s using c True # success >>> vm = VMPython.VM() # vm is a VM object >>> vmid = s.registered_vm_names()[0] # the first registered vm on server s >>> vm.connect(c, vmid) # connect vm using c True # success >>> vm.get_pid() # PID of the guest process '27502' # I get this number as a string Description =========== The VMWare scripting API is fully mapped to python. To each perl class corresponds a python class with the same name, all perl methods are available from python on created objects. Each object created in python has its counterpart in a parallel perl process. There is differences in the behaviour of the APIs: - All data are returned as string in python, some data are numeric in perl (e.g. C{get_pid()}) - Constants belong to the VMPython module namespace, in perl they are exported in the main namespace. Example: - in python:: vm_is_alive = vm.get_execution_state() == VMPython.VM_EXECUTION_STATE_ON - in perl:: $vm_is_alive = $vm->get_execution_state() == VM_EXECUTION_STATE_ON - If the module flag B{USE_EXCEPTIONS} is True, the B{VMAPIError} exception is raised when the API function I{get_last_error()} returns an error after the execution of any other command. Using this feature changes the programing style: >>> vm = VMPython.VM() >>> VMPython.USE_EXCEPTIONS False >>> vm.is_connected() False >>> VMPython.USE_EXCEPTIONS = True >>> vm.is_connected() Traceback (most recent call last): File "", line 1, in ? File "VMPython.py", line 200, in get_boolean def get_boolean(self, *args): File "VMPython.py", line 212, in execute def execute(self, command_spec, *args): File "VMPython.py", line 158, in run (out, err) = PERLSERVER.run(command) File "VMPython.py", line 219, in check_error out = run('printlist(%s->get_last_error())' % self.PerlId) VMPython.VMAPIError: Not connected Using this feature may help because the VMPerl API simply returns an empty value is case of error, translated in python by an empty string. In some cases, this may be confusing. When this module is imported, a L{perlprocess} object is created. The B{ScriptInitialisationError} exception may be raised if the VMWare perl API is not found. @requires: perlprocess module @requires: VMWare perl API @version: 0.1 @status: beta @author: A. Barthe @copyright: 2007 - ALBAR (Toulouse, FRANCE) @contact: mailto:barthe@albar.fr @group Exceptions: UnknownCommand, VMAPIError, WrongUseError """ import sys, re import perlprocess EPYDOC_RUNNING = 'epydoc' in sys.modules DEBUG = False #: If True, executed perl commands are printed USE_EXCEPTIONS = True #: If True, the B{VMAPIError} exception is raised when a command fails class UnknownCommand(Exception): "API command not found." class WrongUseError(Exception): "Should never occur." class VMAPIError(Exception): "VMWare scripting API error, detected using I{get_last_error()} command." PERL_USE = """ use VMware::VmPerl; use VMware::VmPerl::VM; use VMware::VmPerl::Server; use VMware::VmPerl::ConnectParams; use VMware::VmPerl::Question; """ #: Use directives of the VMWare API VM_COMMANDS = { 'ConnectParams' :{ 'scalar' : ['get_hostname', 'get_port', 'get_username', 'get_password'], }, 'Server' : { 'boolean' : ['connect', 'is_connected', 'register_vm', 'unregister_vm'], 'list' : ['registered_vm_names', 'get_last_error'], }, 'VM' : { 'boolean' : ['connect', 'is_connected', 'start', 'stop', 'reset', 'suspend', 'device_is_connected', 'connect_device', 'disconnect_device', 'set_guest_info', 'set_config', 'answer_question'], 'list' : ['get_connected_users', 'get_last_error'], 'scalar' : ['get_execution_state', 'get_guest_info', 'get_config_file_name', 'get_config', 'get_product_info', 'get_heartbeat', 'get_tools_last_active', 'get_id', 'get_pid', 'get_uptime'] }, 'Question' : { 'list' : ['get_choices'], 'scalar' : ['get_text', 'get_id'], } } #: All API methods classified by classes and type of return value VM_CONSTANTS = [ 'VM_EXECUTION_STATE_ON', 'VM_EXECUTION_STATE_OFF', 'VM_EXECUTION_STATE_SUSPENDED', 'VM_EXECUTION_STATE_STUCK', 'VM_EXECUTION_STATE_UNKNOWN', 'VM_POWEROP_MODE_SOFT', 'VM_POWEROP_MODE_HARD', 'VM_POWEROP_MODE_TRYSOFT', 'VM_PRODINFO_PRODUCT', 'VM_PRODINFO_PLATFORM', 'VM_PRODINFO_BUILD', 'VM_PRODINFO_VERSION_MAJOR', 'VM_PRODINFO_VERSION_MINOR', 'VM_PRODINFO_VERSION_REVISION', 'VM_PRODUCT_WS', 'VM_PRODUCT_GSX', 'VM_PRODUCT_ESX', 'VM_PRODUCT_UNKNOWN', 'VM_PLATFORM_WINDOWS', 'VM_PLATFORM_LINUX', 'VM_PLATFORM_VMNIX', 'VM_PLATFORM_UNKNOWN', ] #: API constants __all__ = ['ConnectParams', 'Server', 'VM', 'Question', 'UnknownCommand', 'WrongUseError', 'VMAPIError'] + VM_CONSTANTS def run(command): """Run the command, raise the L{perlprocess.PerlRuntimeError} exception if a perl error occur.""" return PERLSERVER.run(command) def init_process(): "Creates the I{perlprocess} object and initialises constants" global PERLSERVER PERLSERVER = perlprocess.perlprocess(PERL_USE, DEBUG) # Assign VM constants. for const in VM_CONSTANTS: value = run(const) sys.modules['VMPython'].__dict__[const] = value if not EPYDOC_RUNNING: init_process() VAR_PREFIX = 'var' #: Prefix for perl variables. class VMPythonObject(object): "Base class." def __init__(self, *args): """Each object have a I{PerlId} attribut. It's the name of the corresponding perl variable in the parallel process. It is created here.""" self.PerlId = "$%s%s" % (VAR_PREFIX, str(id(self))[1:]) try: self.VM_COMMANDS = VM_COMMANDS[self.__class__.__name__] except KeyError: raise WrongUseError('%s: unknown class' % self.__class__.__name__) self.init_perl(args) def init_perl(self, *args): "Initialises the I{PerlId} perl variable." run('%s = VMware::VmPerl::%s::new%s;' % (self.PerlId, self.__class__.__name__, args)) def __getattr__(self, name): "A different method is called, depending the type of return value (boolean, list or scalar)." for key in self.VM_COMMANDS.keys(): if name in self.VM_COMMANDS[key]: break else: raise UnknownCommand('%s: Unknown API command' % name) self.current_command = name if key == 'boolean': return self.get_boolean if key == 'list' : return self.get_list if key == 'scalar' : return self.get_scalar raise WrongUseError('%s: unknown return type' % key) def __repr__(self): "Returns the attribute I{PerlId}." return self.PerlId def get_boolean(self, *args): "A boolean value is expected" return self.execute('%s->%s%s;', args) == '1' def get_list(self, *args): "A list value is expected" return self.execute('printlist(%s->%s%s)', args) def get_scalar(self, *args): "A scalar value is expected" return self.execute('%s->%s%s', args) def execute(self, command_spec, *args): "Runs the command.,and call eventually the I{check_error} method." out = run(command_spec % (self.PerlId, self.current_command, args)) if USE_EXCEPTIONS: self.check_error() return out def check_error(self): """Checks the status of the I{get_last_error()} command and raise an exception if False""" out = run('printlist(%s->get_last_error())' % self.PerlId) if out[0] != '0': raise VMAPIError(out[1]) def __del__(self): "Undefines the corresponding perl object" run('undef(%s)' % self.PerlId) class ConnectParams(VMPythonObject): "See VMWare API documentation" def check_error(self): "Unapplicable on this object" class Server(VMPythonObject): "See VMWare API documentation" class VM(VMPythonObject): "See VMWare API documentation" def get_pending_question(self): """This method is particular because it creates and returns a I{Question} object.""" question = Question() out = run('%s = %s->get_pending_question(); defined(%s)' % \ (question.PerlId, self.PerlId, question.PerlId)) return out == '1' and question or None class Question(VMPythonObject): "See VMWare API documentation" def init_perl(self, *args): "Questions are not created the normal way." def check_error(self): "Unapplicable on this object"