Commits

Anonymous committed 9006c1b

Multithreading (needs more testing)
Several fixes (names, exceptions, etc)
Changed some methods behavior
Added unit tests (current coverage 54%)

git-svn-id: https://pysphere.googlecode.com/svn/trunk@5593908643-0a7e-a946-f69b-c8c46af5c9d1

Comments (0)

Files changed (14)

 - Added method get_datastores in VIServer
 - Fix issue 3, 4
 - Added method migrate in VIVirtualMachine
-- Added VIMor class
+- Added VIMor class
+- Multithread support

pysphere/ZSI/client.py

 #
 # Copyright (c) 2001 Zolera Systems.  All rights reserved.
 
+import threading
+
 from pysphere.ZSI import _copyright, _seqtypes, ParsedSoap, SoapWriter, TC, ZSI_SCHEMA_URI,\
     EvaluateException, FaultFromFaultMessage, _child_elements, _attrs, _find_arraytype,\
     _find_type, _get_idstr, _get_postvalue_from_absoluteURI, FaultException, WSActionException,\
             sig_handler -- XML Signature handler, must sign and verify.
             endPointReference -- optional Endpoint Reference.
         '''
-        self.data = None
-        self.ps = None
+        #self.data = None
+        #self.ps = None
         self.user_headers = []
         self.nsdict = nsdict or {}
         self.transport = transport
         self.endPointReference = kw.get('endPointReference', None)
         self.cookies = Cookie.SimpleCookie()
         self.http_callbacks = {}
+        
+        #thread local data
+        self.local = threading.local()
 
         if kw.has_key('auth'):
             self.SetAuth(*kw['auth'])
         return self
 
     def __addcookies(self):
-        '''Add cookies from self.cookies to request in self.h
+        '''Add cookies from self.cookies to request in self.local.h
         '''
         for cname, morsel in self.cookies.items():
             attrs = []
             value = morsel.get('domain')
             if value:
                 attrs.append('$Domain=%s' % value)
-            self.h.putheader('Cookie', "; ".join(attrs))
+            self.local.h.putheader('Cookie', "; ".join(attrs))
 
     def RPC(self, url, opname, obj, replytype=None, **kw):
         '''Send a request, return the reply.  See Send() and Recieve()
             sw.serialize(obj, requesttypecode)
 
         for i in soapheaders:
-           sw.serialize_header(i)
+            sw.serialize_header(i)
 
         #
         # Determine the SOAP auth element.  SOAP:Header element
             raise TypeError, 'transport must be a HTTPConnection'
 
         soapdata = str(sw)
-        self.h = transport(netloc, None, **self.transdict)
-        self.h.connect()
-        self.boundary = sw.getMIMEBoundary()
-        self.startCID = sw.getStartCID()
+        self.local.h = transport(netloc, None, **self.transdict)
+        self.local.h.connect()
+        self.local.boundary = sw.getMIMEBoundary()
+        self.local.startCID = sw.getStartCID()
         self.SendSOAPData(soapdata, url, soapaction, **kw)
 
     def SendSOAPData(self, soapdata, url, soapaction, headers={}, **kw):
 
         url = url or self.url
         request_uri = _get_postvalue_from_absoluteURI(url)
-        self.h.putrequest("POST", request_uri)
-        self.h.putheader("Content-Length", "%d" % len(soapdata))
-        if len(self.boundary) == 0:
+        self.local.h.putrequest("POST", request_uri)
+        self.local.h.putheader("Content-Length", "%d" % len(soapdata))
+        if len(self.local.boundary) == 0:
             #no attachment
-            self.h.putheader("Content-Type", 'text/xml; charset="%s"' %UNICODE_ENCODING)
+            self.local.h.putheader("Content-Type", 'text/xml; charset="%s"' %UNICODE_ENCODING)
         else:
             #we have attachment
             contentType =  "multipart/related; "
-            self.h.putheader("Content-Type" , "multipart/related; boundary=\"" + self.boundary + "\"; start=\"" + self.startCID + '\"; type="text/xml"')
+            self.local.h.putheader("Content-Type" , "multipart/related; boundary=\"" + self.local.boundary + "\"; start=\"" + self.local.startCID + '\"; type="text/xml"')
         self.__addcookies()
 
         for header,value in headers.items():
-            self.h.putheader(header, value)
+            self.local.h.putheader(header, value)
 
         SOAPActionValue = '"%s"' % (soapaction or self.soapaction)
-        self.h.putheader("SOAPAction", SOAPActionValue)
+        self.local.h.putheader("SOAPAction", SOAPActionValue)
         if self.auth_style & AUTH.httpbasic:
             val = _b64_encode(self.auth_user + ':' + self.auth_pass) \
                         .replace("\012", "")
-            self.h.putheader('Authorization', 'Basic ' + val)
+            self.local.h.putheader('Authorization', 'Basic ' + val)
         elif self.auth_style == AUTH.httpdigest and not headers.has_key('Authorization') \
             and not headers.has_key('Expect'):
             def digest_auth_cb(response):
             self.http_callbacks[401] = digest_auth_cb
 
         for header,value in self.user_headers:
-            self.h.putheader(header, value)
-        self.h.endheaders()
-        self.h.send(soapdata)
+            self.local.h.putheader(header, value)
+        self.local.h.endheaders()
+        self.local.h.send(soapdata)
 
         # Clear prior receive state.
-        self.data, self.ps = None, None
+        self.local.data, self.local.ps = None, None
 
     def SendSOAPDataHTTPDigestAuth(self, response, soapdata, url, request_uri, soapaction, **kw):
         '''Resend the initial request w/http digest authorization headers.
     def ReceiveRaw(self, **kw):
         '''Read a server reply, unconverted to any format and return it.
         '''
-        if self.data: return self.data
+        if self.local.data: return self.local.data
         trace = self.trace
         while 1:
-            response = self.h.getresponse()
-            self.reply_code, self.reply_msg, self.reply_headers, self.data = \
+            response = self.local.h.getresponse()
+            reply_code, reply_msg, self.local.reply_headers, self.local.data = \
                 response.status, response.reason, response.msg, response.read()
             if trace:
                 print >>trace, "_" * 33, time.ctime(time.time()), "RESPONSE:"
-                for i in (self.reply_code, self.reply_msg,):
+                for i in (reply_code, reply_msg,):
                     print >>trace, str(i)
                 print >>trace, "-------"
-                print >>trace, str(self.reply_headers)
-                print >>trace, self.data
+                print >>trace, str(self.local.reply_headers)
+                print >>trace, self.local.data
             saved = None
             for d in response.msg.getallmatchingheaders('set-cookie'):
                 if d[0] in [ ' ', '\t' ]:
 
             # The httplib doesn't understand the HTTP continuation header.
             # Horrible internals hack to patch things up.
-            self.h._HTTPConnection__state = httplib._CS_REQ_SENT
-            self.h._HTTPConnection__response = None
-        return self.data
+            self.local.h._HTTPConnection__state = httplib._CS_REQ_SENT
+            self.local.h._HTTPConnection__response = None
+        return self.local.data
 
     def IsSOAP(self):
-        if self.ps: return 1
+        if self.local.ps: return 1
         self.ReceiveRaw()
-        mimetype = self.reply_headers.type
+        mimetype = self.local.reply_headers.type
         return mimetype == 'text/xml'
 
     def ReceiveSOAP(self, readerclass=None, **kw):
         '''Get back a SOAP message.
         '''
-        if self.ps: return self.ps
+        if self.local.ps: return self.local.ps
         if not self.IsSOAP():
             raise TypeError(
-                'Response is "%s", not "text/xml"' % self.reply_headers.type)
-        if len(self.data) == 0:
+                'Response is "%s", not "text/xml"' % self.local.reply_headers.type)
+        if len(self.local.data) == 0:
             raise TypeError('Received empty response')
 
-        self.ps = ParsedSoap(self.data,
+        self.local.ps = ParsedSoap(self.local.data,
                         readerclass=readerclass or self.readerclass,
                         encodingStyle=kw.get('encodingStyle'))
 
         if self.sig_handler is not None:
-            self.sig_handler.verify(self.ps)
+            self.sig_handler.verify(self.local.ps)
 
-        return self.ps
+        return self.local.ps
 
     def IsAFault(self):
         '''Get a SOAP message, see if it has a fault.
         '''
         self.ReceiveSOAP()
-        return self.ps.IsAFault()
+        return self.local.ps.IsAFault()
 
     def ReceiveFault(self, **kw):
         '''Parse incoming message as a fault. Raise TypeError if no
         fault found.
         '''
         self.ReceiveSOAP(**kw)
-        if not self.ps.IsAFault():
+        if not self.local.ps.IsAFault():
             raise TypeError("Expected SOAP Fault not found")
-        return FaultFromFaultMessage(self.ps)
+        return FaultFromFaultMessage(self.local.ps)
 
     def Receive(self, replytype, **kw):
         '''Parse message, create Python object.
                 receive.
         '''
         self.ReceiveSOAP(**kw)
-        if self.ps.IsAFault():
-            msg = FaultFromFaultMessage(self.ps)
+        if self.local.ps.IsAFault():
+            msg = FaultFromFaultMessage(self.local.ps)
             raise FaultException(msg)
 
         tc = replytype
         if hasattr(replytype, 'typecode'):
             tc = replytype.typecode
 
-        reply = self.ps.Parse(tc)
+        reply = self.local.ps.Parse(tc)
         if self.address is not None:
-            self.address.checkResponse(self.ps, kw.get('wsaction'))
+            self.address.checkResponse(self.local.ps, kw.get('wsaction'))
         return reply
 
     def __repr__(self):
             self.logger.debug('didnt find typecode for "%s" in typesmodule: %s',
                 node.localName, self.typesmodule)
             tc = TC.Any(aslist=1)
-            return tc.parse(node, self.ps)
+            return tc.parse(node, self.local.ps)
 
         self.logger.debug('parse child with typecode : %s', tc)
         try:
-            return tc.parse(node, self.ps)
+            return tc.parse(node, self.local.ps)
         except Exception:
             self.logger.debug('parse failed try Any : %s', tc)
 
         tc = TC.Any(aslist=1)
-        return tc.parse(node, self.ps)
+        return tc.parse(node, self.local.ps)
 
     def Receive(self, replytype, **kw):
         '''Parse message, create Python object.
                 receive.
         '''
         self.ReceiveSOAP(**kw)
-        ps = self.ps
+        ps = self.local.ps
         tp = _find_type(ps.body_root)
         isarray = ((type(tp) in (tuple,list) and tp[1] == 'Array') or _find_arraytype(ps.body_root))
         if self.typesmodule is None or isarray:

pysphere/__init__.py

 # accompanying credits file.
 #
 #--
-__all__ = ['VIServer', 'VIException', 'VIApiException', 'VITask',
+__all__ = ['VIServer', 'VIException', 'VIApiException', 'VITask', 'FaultTypes',
             'VIMor', 'MORTypes', 'VMPowerState', 'ToolsStatus', 'VIProperty']
 
 from vi_server import VIServer
 from vi_property import VIProperty
 from vi_task import VITask
 from vi_mor import VIMor, MORTypes
-from resources.vi_exception import VIException, VIApiException
+from resources.vi_exception import VIException, VIApiException, FaultTypes
 #from version import version as __version__

pysphere/resources/vi_exception.py

 
 class FaultTypes:
     PARAMETER_ERROR    = 'Parameter Error'
-    VM_NOT_FOUND_ERROR = 'VM Not Found'
-    FOLDER_NOT_FOUND   = 'Folder Not Found'
+    OBJECT_NOT_FOUND   = 'Object Not Found'
     NOT_CONNECTED      = 'Not Connected'
     TIME_OUT           = 'Operation Timed Out'
-    SNAPSHOT_NOT_FOUND = 'Snapshot Not Found'
     TASK_ERROR         = 'Task Error'
     NOT_SUPPORTED      = 'Operation Not Supported'
     INVALID_OPERATION  = 'Invalid Operation'

pysphere/vi_mor.py

     
     def set_attribute_type(self, mor_type):
         self._mor_type = mor_type
-        
+    
     @staticmethod
     def is_mor(obj):
         return hasattr(obj, "get_attribute_type")

pysphere/vi_performance_manager.py

             performance counter ID and an instance name.
         counter_obj [list]: An array consisting of performance
             counter information for the specified counterIds.
-        counter_name [list of strings]: The wanted counter name.
         """
-        if not isinstance(counter_ids, list):
-            counter_name = [counter_name]
         metric_list = []
         for metric in metrics:
             if metric.CounterId in counter_ids:

pysphere/vi_property.py

         #If this is a MOR we need to recurse
         if self._type == 'ManagedObjectReference':
             oc = self._server._get_object_properties(self._obj, get_all=True)
-            ps = oc[0].get_element_propSet()
+            ps = oc.get_element_propSet()
             self._values = dict([(i.Name, i.Val) for i in ps])
         #Just inspect the attributes
         else:

pysphere/vi_server.py

 from resources.vi_exception import *
 from vi_virtual_machine import VIVirtualMachine
 from vi_performance_manager import PerformanceManager
-from vi_mor import VIMor
+from vi_mor import VIMor, MORTypes
 
 class VIServer:
 
     def __init__(self):
         self.__logged = False
-        self.__datacenters = None
-        self.__datastores = None
-        self.__clusters = None
-        self.__hosts =  None
-        self.__resource_pools = None
         self.__server_type = None
         self.__api_version = None
         self.__session = None
             #get service content from service instance
             request = VI.RetrieveServiceContentRequestMsg()
             mor_service_instance = request.new__this('ServiceInstance')
-            mor_service_instance.set_attribute_type('ServiceInstance')
+            mor_service_instance.set_attribute_type(MORTypes.ServiceInstance)
             request.set_element__this(mor_service_instance)
             self._do_service_content = self._proxy.RetrieveServiceContent(
                                                              request)._returnval
             request = VI.LoginRequestMsg()
             mor_session_manager = request.new__this(
                                         self._do_service_content.SessionManager)
-            mor_session_manager.set_attribute_type('SessionManager')
+            mor_session_manager.set_attribute_type(MORTypes.SessionManager)
             request.set_element__this(mor_session_manager)
             request.set_element_userName(user)
             request.set_element_password(password)
                             FaultTypes.NOT_CONNECTED)
         request = VI.CurrentTimeRequestMsg()
         mor_service_instance = request.new__this("ServiceInstance")
-        mor_service_instance.set_attribute_type("ServiceInstance")
+        mor_service_instance.set_attribute_type(MORTypes.ServiceInstance)
         request.set_element__this(mor_service_instance)
         try:
             self._proxy.CurrentTime(request)
                 request = VI.LogoutRequestMsg()
                 mor_session_manager = request.new__this(
                                         self._do_service_content.SessionManager)
-                mor_session_manager.set_attribute_type('SessionManager')
+                mor_session_manager.set_attribute_type(MORTypes.SessionManager)
                 request.set_element__this(mor_session_manager)
                 self._proxy.Logout(request)
             except (VI.ZSI.FaultException), e:
         """Returns a Performance Manager entity"""
         return PerformanceManager(self, self._do_service_content.PerfManager)
 
-    def get_hosts(self, from_cache=True):
-        """Returns a dictionary of the existing hosts keys are their names
-        and values their ManagedObjectReference object."""
-        return self.__get_managed_objects_dict(self.__hosts,
-                                              'HostSystem',
-                                              from_cache)
+    def get_hosts(self, from_mor=None):
+        """
+        Returns a dictionary of the existing hosts keys are their names
+        and values their ManagedObjectReference object.
+        @from_mor: if given, retrieves the hosts contained within the specified 
+            managed entity.
+        """
+        return self.__get_managed_objects_dict(MORTypes.HostSystem, from_mor)
+    
 
-    def get_datastores(self, from_cache=True):
-        """Returns a dictionary of the existing datastores. Keys are
-        ManagedObjectReference and values datastore names"""
-        
-        return self.__get_managed_objects_dict(self.__datastores,
-                                               'Datastore',
-                                               from_cache)
-        
-    def get_clusters(self, from_cache=True):
-        """Returns a dictionary of the existing clusters. Keys are their 
-        ManagedObjectReference objects and values their names."""
+    def get_datastores(self, from_mor=None):
+        """
+        Returns a dictionary of the existing datastores. Keys are
+        ManagedObjectReference and values datastore names.
+        @from_mor: if given, retrieves the datastores contained within the 
+            specified managed entity.
+        """
+        return self.__get_managed_objects_dict(MORTypes.Datastore, from_mor)
         
-        return self.__get_managed_objects_dict(self.__clusters,
-                                               'ClusterComputeResource',
-                                               from_cache)
+    def get_clusters(self, from_mor=None):
+        """
+        Returns a dictionary of the existing clusters. Keys are their 
+        ManagedObjectReference objects and values their names.
+        @from_mor: if given, retrieves the clusters contained within the 
+            specified managed entity.
+        """
+        return self.__get_managed_objects_dict(MORTypes.ClusterComputeResource,
+                                               from_mor)
 
-    def get_datacenters(self, from_cache=True):
-        """Returns a dictionary of the existing datacenters. keys are their
-        ManagedObjectReference objects and values their names."""
-        
-        return self.__get_managed_objects_dict(self.__datacenters,
-                                               'Datacenter',
-                                               from_cache)           
+    def get_datacenters(self, from_mor=None):
+        """
+        Returns a dictionary of the existing datacenters. keys are their
+        ManagedObjectReference objects and values their names.
+        @from_mor: if given, retrieves the datacenters contained within the 
+            specified managed entity.
+        """
+        return self.__get_managed_objects_dict(MORTypes.Datacenter, from_mor)           
 
-    def get_resource_pools(self, from_cache=True, from_mor=None):
-        """Returns a dictionary of the existing ResourcePools. keys are their
-        ManagedObjectReference objects and values their full path names."""
-        
-        if self.__resource_pools and from_cache:
-            return self.__resource_pools
+    def get_resource_pools(self, from_mor=None):
+        """
+        Returns a dictionary of the existing ResourcePools. keys are their
+        ManagedObjectReference objects and values their full path names.
+        @from_mor: if given, retrieves the resource pools contained within the
+            specified managed entity.
+        """
 
         def get_path(nodes, node):
             if 'parent' in node:
         prop = self._retrieve_properties_traversal(
                                       property_names=["name", "resourcePool"],
                                       from_node=from_mor,
-                                      obj_type="ResourcePool")
+                                      obj_type=MORTypes.ResourcePool)
         for oc in prop:
             this_rp = {}
             this_rp["children"] = []
         ret = {}
         for rp in rps.values():
             ret[rp["mor"]] = get_path(rps, rp) 
-        self.__resource_pools = ret
-
-        return self.__resource_pools
+        
+        return ret
 
     def get_vm_by_path(self, path, datacenter=None):
         """Returns an instance of VIVirtualMachine. Where its path matches
             raise VIException("Must call 'connect' before invoking this method",
                             FaultTypes.NOT_CONNECTED)
         try:
-            dc = self.get_datacenters()
             dc_list = []
             if datacenter and VIMor.is_mor(datacenter):
                 dc_list.append(datacenter)
-            elif datacenter:
-                dc_list = [k for k,v in dc.items() if v==datacenter]
             else:
-                dc_list = dc.keys()
+                dc = self.get_datacenters()
+                if datacenter:
+                    dc_list = [k for k,v in dc.items() if v==datacenter]
+                else:
+                    dc_list = dc.keys()
 
             for mor_dc in dc_list:
                 request = VI.FindByDatastorePathRequestMsg()
                 mor_search_index = request.new__this(
                                            self._do_service_content.SearchIndex)
-                mor_search_index.set_attribute_type('SearchIndex')
+                mor_search_index.set_attribute_type(MORTypes.SearchIndex)
                 request.set_element__this(mor_search_index)
                 mor_datacenter = request.new_datacenter(mor_dc)
-                mor_datacenter.set_attribute_type('Datacenter')
+                mor_datacenter.set_attribute_type(MORTypes.Datacenter)
                 request.set_element_datacenter(mor_datacenter)
                 request.set_element_path(path)
                 try:
         except (VI.ZSI.FaultException), e:
             raise VIApiException(e)
 
-        raise VIException("Could not find a VM with path '%s'"
-                                          % path, FaultTypes.VM_NOT_FOUND_ERROR)
+        raise VIException("Could not find a VM with path '%s'" % path, 
+                          FaultTypes.OBJECT_NOT_FOUND)
 
     def get_vm_by_name(self, name, datacenter=None):
-        """Returns an instance of VIVirtualMachine. Where its name matches
-        @name. The VM is searched througout all the datacenters, unless the
-        name of the datacenter the VM belongs to is provided."""
+        """
+        Returns an instance of VIVirtualMachine. Where its name matches @name.
+        The VM is searched throughout all the datacenters, unless the name or 
+        MOR of the datacenter the VM belongs to is provided. The first instance
+        matching @name is returned.
+        NOTE: As names might be duplicated is recommended to use get_vm_by_path
+        instead.
+        """
         if not self.__logged:
             raise VIException("Must call 'connect' before invoking this method",
                               FaultTypes.NOT_CONNECTED)
-
         try:
-            node = self._do_service_content.RootFolder
-            if datacenter is not None:
-                dc = self._get_datacenters()
-                node = dc[datacenter]
-            do_object_content = self._retrieve_properties_traversal(
-                                            property_names=['name'],
-                                            from_node=node,
-                                            obj_type='VirtualMachine')
-            if do_object_content is not None:
-                for oc in do_object_content:
-                    mor = oc.get_element_obj()
-                    properties = oc.PropSet
-                    if properties is not None:
-                        for prop in properties:
-                            if prop.Name == 'name' and prop.Val == name:
-                                return VIVirtualMachine(self, mor)
+            nodes = [None]
+            if datacenter and VIMor.is_mor(datacenter):
+                nodes = [datacenter]
+            elif datacenter:
+                dc = self.get_datacenters()
+                nodes = [k for k,v in dc.items() if v==datacenter]
+                
+            for node in nodes:
+                vms = self.__get_managed_objects_dict(MORTypes.VirtualMachine,
+                                                      from_mor=node)
+                for k,v in vms.items():
+                    if v == name:
+                        return VIVirtualMachine(self, k)
         except (VI.ZSI.FaultException), e:
             raise VIApiException(e)
 
-        raise VIException("Could not find a VM named '%s'"
-                                          % name, FaultTypes.VM_NOT_FOUND_ERROR)
+        raise VIException("Could not find a VM named '%s'" % name, 
+                          FaultTypes.OBJECT_NOT_FOUND)
 
     def get_server_type(self):
         """Returns a string containing a the server type name: E.g:
         """Returns a string containing a the vSphere API version: E.g: '4.1' """
         return self.__api_version
 
-    def get_registered_vms(self, datacenter=None,
-                                cluster=None,
-                                resource_pool=None,
-                                status= None):
+    def get_registered_vms(self, datacenter=None, cluster=None, 
+                           resource_pool=None, status=None):
         """Returns a list of VM Paths. You might also filter by datacenter,
         cluster, or resource pool by providing their name or MORs. 
         if  cluster is set, datacenter is ignored, and if resource pool is set
             if status:
                 property_filter.append('runtime.powerState')
             ret = []
-            node = self._do_service_content.RootFolder
+            nodes = [None]
             if resource_pool and VIMor.is_mor(resource_pool):
-                node = resource_pool
+                nodes = [resource_pool]
             elif resource_pool:
-                node = [k for k,v in self.get_resource_pools().items()
-                        if v==resource_pool][0]
+                nodes = [k for k,v in self.get_resource_pools().items()
+                         if v==resource_pool]
             elif cluster and VIMor.is_mor(cluster):
-                node = cluster
+                nodes = [cluster]
             elif cluster:
-                node = [k for k,v in self.get_clusters().items() 
-                        if v==cluster][0]
+                nodes = [k for k,v in self.get_clusters().items() 
+                        if v==cluster]
             elif datacenter and VIMor.is_mor(datacenter):
-                node = datacenter
+                nodes = [datacenter]
             elif datacenter:
-                node = [k for k,v in self.get_datacenters().items()
-                        if v==datacenter][0]
+                nodes = [k for k,v in self.get_datacenters().items()
+                        if v==datacenter]
 
-            do_object_content = self._retrieve_properties_traversal(
+            for node in nodes:
+                obj_content = self._retrieve_properties_traversal(
                                             property_names=property_filter,
                                             from_node=node,
-                                            obj_type='VirtualMachine')
-            if not do_object_content:
-                return ret
-            
-            for oc in do_object_content:
-                try:
-                    prop_set = oc.PropSet
-                except AttributeError:
+                                            obj_type=MORTypes.VirtualMachine)
+                if not obj_content:
                     continue
-                if prop_set is not None and len(prop_set)>0:
-                    if status:
-                        if prop_set[1].Val == status:
-                            ret.append(prop_set[0].Val)
-                    else:
-                        ret.append(prop_set[0].Val)
+                for obj in obj_content:
+                    try:
+                        prop_set = obj.PropSet
+                    except AttributeError:
+                        continue
+                    ppath = None
+                    pstatus = None
+                    for item in prop_set:
+                        if item.Name == 'config.files.vmPathName':
+                            ppath = item.Val
+                        elif item.Name == 'runtime.powerState':
+                            pstatus = item.Val
+                    if (status and status==pstatus) or not status:
+                        ret.append(ppath)
             return ret
 
         except (VI.ZSI.FaultException), e:
             raise VIApiException(e)
 
-    def get_objects_tree(self, from_mor=None):
-        """Returns a nested dictionary of all the VC/ESX Tree from RootFolder
-        up to the VM level (datacenters, datastores, hosts, folders,
-        resource pools, VMs). The return dictionary has this format:
-        {'key'      : mor id string,
-         'mor'      : Managed object reference,
-         'type'     : Managed object Type,
-         'name'     : Managed object name
-         'children' : [list of dictionaries of this same structure]
-        }"""
-
-        def build_node(object_content, object_list, done_list):
-            try:
-                mor = object_content.Obj
-                properties = object_content.PropSet
-                this = done_list.get(str(mor), None)
-                if this is None:
-                    node = {}
-                    node['key'] = str(mor)
-                    node['mor'] = mor
-                    node['type'] = str(mor.get_attribute_type())
-                    node['children'] = []
-                    node['name'] = None
-                    parent = None
-
-                    if properties is not None:
-                        for prop in properties:
-                            if prop is not None and prop.Name == 'name':
-                                node['name'] = prop.Val
-                            if prop is not None and prop.Name == 'parent':
-                                parent = prop.Val
-                    if parent is not None:
-                        #recursive call
-                        parent_node = build_node(object_list[parent],
-                                                 object_list, done_list)
-                        parent_node['children'].append(node)
-                    else:
-                        done_list['main'] = node
-                    done_list[str(mor)] = node
-
-                    return node
-                else:
-                    return this
-            except (VI.ZSI.FaultException), e:
-                raise VIApiException(e)
-
-        if not self.__logged:
-            raise VIException("Must call 'connect' before invoking this method",
-                              FaultTypes.NOT_CONNECTED)
-
-        do_object_content = self._retrieve_properties_traversal(
-                                              property_names=['name', 'parent'],
-                                              from_node=from_mor)
-        tree = {}
-        try:
-            if do_object_content is not None:
-                done_list = {}
-                object_list = {}
-                for oc in do_object_content:
-                    mor = oc.Obj
-                    object_list[str(mor)] = oc
-
-                for oc in do_object_content:
-                    build_node(oc, object_list, done_list)
-                tree = done_list['main']
-        except (VI.ZSI.FaultException), e:
-            raise VIApiException(e)
-        return tree
-
     def _get_object_properties(self, mor, property_names=[], get_all=False):
         """Returns the properties defined in property_names (or all if get_all
         is set to True) of the managed object reference given in @mor.
 
             request, request_call = self._retrieve_property_request()
 
-            mor_property_collector = request.new__this(
+            _this = request.new__this(
                                      self._do_service_content.PropertyCollector)
-            mor_property_collector.set_attribute_type('PropertyCollector')
-            request.set_element__this(mor_property_collector)
+            _this.set_attribute_type(MORTypes.PropertyCollector)
+            request.set_element__this(_this)
 
             do_PropertyFilterSpec_specSet = request.new_specSet()
 
             do_PropertyFilterSpec_specSet.set_element_objectSet(objects_set)
             request.set_element_specSet([do_PropertyFilterSpec_specSet])
 
-            return request_call(request)
+            ret = request_call(request)
+            if ret and isinstance(ret, list):
+                return ret[0]
 
         except (VI.ZSI.FaultException), e:
             raise VIApiException(e)
         @from_node (RootFolder by default). Returns the corresponding
         objectContent data object."""
         try:
-            if from_node is None:
+            if not from_node:
                 from_node = self._do_service_content.RootFolder
-
-
+            
+            elif isinstance(from_node, tuple) and len(from_node) == 2:
+                from_node = VIMor(from_node[0], from_node[1])
+            elif not VIMor.is_mor(from_node):
+                raise VIException("from_node must be a MOR object or a "
+                                  "(<str> mor_id, <str> mor_type) tuple",
+                                  FaultTypes.PARAMETER_ERROR)
+            
             request, request_call = self._retrieve_property_request()
 
 
-            mor_property_collector = request.new__this(
+            _this = request.new__this(
                                      self._do_service_content.PropertyCollector)
-            mor_property_collector.set_attribute_type('PropertyCollector')
+            _this.set_attribute_type(MORTypes.PropertyCollector)
 
-            request.set_element__this(mor_property_collector)
+            request.set_element__this(_this)
             do_PropertyFilterSpec_specSet = request.new_specSet()
 
             props_set = []
             #Recurse through all ResourcePools
             rp_to_rp = VI.ns0.TraversalSpec_Def('rpToRp').pyclass()
             rp_to_rp.set_element_name('rpToRp')
-            rp_to_rp.set_element_type('ResourcePool')
+            rp_to_rp.set_element_type(MORTypes.ResourcePool)
             rp_to_rp.set_element_path('resourcePool')
             rp_to_rp.set_element_skip(False)
             rp_to_vm= VI.ns0.TraversalSpec_Def('rpToVm').pyclass()
             rp_to_vm.set_element_name('rpToVm')
-            rp_to_vm.set_element_type('ResourcePool')
+            rp_to_vm.set_element_type(MORTypes.ResourcePool)
             rp_to_vm.set_element_path('vm')
             rp_to_vm.set_element_skip(False)
 
             #Traversal through resource pool branch
             cr_to_rp = VI.ns0.TraversalSpec_Def('crToRp').pyclass()
             cr_to_rp.set_element_name('crToRp')
-            cr_to_rp.set_element_type('ComputeResource')
+            cr_to_rp.set_element_type(MORTypes.ComputeResource)
             cr_to_rp.set_element_path('resourcePool')
             cr_to_rp.set_element_skip(False)
             spec_array_computer_resource =[do_ObjectSpec_objSet.new_selectSet(),
             #Traversal through host branch
             cr_to_h = VI.ns0.TraversalSpec_Def('crToH').pyclass()
             cr_to_h.set_element_name('crToH')
-            cr_to_h.set_element_type('ComputeResource')
+            cr_to_h.set_element_type(MORTypes.ComputeResource)
             cr_to_h.set_element_path('host')
             cr_to_h.set_element_skip(False)
 
             #Traversal through hostFolder branch
             dc_to_hf = VI.ns0.TraversalSpec_Def('dcToHf').pyclass()
             dc_to_hf.set_element_name('dcToHf')
-            dc_to_hf.set_element_type('Datacenter')
+            dc_to_hf.set_element_type(MORTypes.Datacenter)
             dc_to_hf.set_element_path('hostFolder')
             dc_to_hf.set_element_skip(False)
             spec_array_datacenter_host = [do_ObjectSpec_objSet.new_selectSet()]
             #Traversal through vmFolder branch
             dc_to_vmf = VI.ns0.TraversalSpec_Def('dcToVmf').pyclass()
             dc_to_vmf.set_element_name('dcToVmf')
-            dc_to_vmf.set_element_type('Datacenter')
+            dc_to_vmf.set_element_type(MORTypes.Datacenter)
             dc_to_vmf.set_element_path('vmFolder')
             dc_to_vmf.set_element_skip(False)
             spec_array_datacenter_vm = [do_ObjectSpec_objSet.new_selectSet()]
             #Traversal through datastore branch
             dc_to_ds = VI.ns0.TraversalSpec_Def('dcToDs').pyclass()
             dc_to_ds.set_element_name('dcToDs')
-            dc_to_ds.set_element_type('Datacenter')
+            dc_to_ds.set_element_type(MORTypes.Datacenter)
             dc_to_ds.set_element_path('datastore')
             dc_to_ds.set_element_skip(False)
             spec_array_datacenter_ds = [do_ObjectSpec_objSet.new_selectSet()]
             #Recurse through all hosts
             h_to_vm = VI.ns0.TraversalSpec_Def('hToVm').pyclass()
             h_to_vm.set_element_name('hToVm')
-            h_to_vm.set_element_type('HostSystem')
+            h_to_vm.set_element_type(MORTypes.HostSystem)
             h_to_vm.set_element_path('vm')
             h_to_vm.set_element_skip(False)
             spec_array_host_vm = [do_ObjectSpec_objSet.new_selectSet()]
             #Recurse through the folders
             visit_folders = VI.ns0.TraversalSpec_Def('visitFolders').pyclass()
             visit_folders.set_element_name('visitFolders')
-            visit_folders.set_element_type('Folder')
+            visit_folders.set_element_type(MORTypes.Folder)
             visit_folders.set_element_path('childEntity')
             visit_folders.set_element_skip(False)
             spec_array_visit_folders = [do_ObjectSpec_objSet.new_selectSet(),
                 token = retval.Token
                 request = VI.ContinueRetrievePropertiesExRequestMsg()
 
-                mor_prop_collector = request.new__this(
-                                 self._do_service_content.PropertyCollector)
-                mor_prop_collector.set_attribute_type('PropertyCollector')
-
-                request.set_element__this(mor_prop_collector)
+                _this = request.new__this(
+                                     self._do_service_content.PropertyCollector)
+                _this.set_attribute_type(MORTypes.PropertyCollector)
+                request.set_element__this(_this)
                 request.set_element_token(token)
                 retval = self._proxy.ContinueRetrievePropertiesEx(
                                                   request)._returnval
         else:
             self._proxy.binding.ResetHeaders()
             
-    def __get_managed_objects_dict(self, original, mo_type, from_cache):
+    def __get_managed_objects_dict(self, mo_type, from_mor):
         """Returns a dictionary of managed objects and their names"""
-        if original and from_cache:
-            return original
         
-        obj_content = self._retrieve_properties_traversal(
-                      property_names=['name'], obj_type=mo_type)
+        content = self._retrieve_properties_traversal(property_names=['name'],
+                                                      from_node=from_mor,
+                                                      obj_type=mo_type)
+        if not content: return {}
         try:
-            original = dict([(o.Obj, o.PropSet[0].Val)
-                            for o in obj_content])
+            return dict([(o.Obj, o.PropSet[0].Val) for o in content])
         except VI.ZSI.FaultException, e:
             raise VIApiException(e)
-        return original
-    
     
     #---- DEPRECATED METHODS ----#    
             
                       "'get_clusters' instead",
                       DeprecationWarning)
         
-        ret = self.get_clusters(from_cache=from_cache)
+        ret = self.get_clusters()
         return dict([(v,k) for k,v in ret.items()])
     
     def _get_datacenters(self, from_cache=True):
                       "'get_datacenters' instead",
                       DeprecationWarning)
 
-        ret = self.get_datacenters(from_cache=from_cache)
+        ret = self.get_datacenters()
         return dict([(v,k) for k,v in ret.items()])
     
     def _get_resource_pools(self, from_cache=True):
                       "'get_resource_pools' instead",
                       DeprecationWarning)
 
-        ret = self.get_resource_pools(from_cache=from_cache)
+        ret = self.get_resource_pools()
         return dict([(v,k) for k,v in ret.items()])     

pysphere/vi_task.py

     def __poll_task_info(self, retries=3, interval=2):
         for i in range(retries):
             try:
-                do_object_content = self._server._get_object_properties(
+                object_content = self._server._get_object_properties(
                                                         self._mor, get_all=True)
-                if do_object_content is None:
-                    raise Exception("do_object_content is None")
+                if not object_content:
+                    raise Exception("Couldn't retrieve object properties")
 
-                for oc in do_object_content:
-                    properties = oc.PropSet
-                    for prop in properties:
-                        if prop.Name == 'info':
-                            self._task_info = prop.Val
-                            return True
+                properties = object_content.PropSet
+                for prop in properties:
+                    if prop.Name == 'info':
+                        self._task_info = prop.Val
+                        return True
             except Exception, e:
                 if i == retries -1:
                     raise e

pysphere/vi_virtual_machine.py

                            'poweredOff': VMPowerState.POWERED_OFF,
                            'suspended': VMPowerState.SUSPENDED}
 
-        vm_has_question = False
         power_state = None
 
         oc_vm_status_msg = self._server._get_object_properties(
                       self._mor,
                       property_names=['runtime.question', 'runtime.powerState']
                       )
-        oc_vm_status_msg = oc_vm_status_msg[0]
         properties = oc_vm_status_msg.PropSet
         for prop in properties:
             if prop.Name == 'runtime.powerState':
                       self._mor_vm_task_collector,
                       property_names=['latestPage']
                       )
-        oc_task_history = oc_task_history[0]
         properties = oc_task_history.PropSet
         if len(properties) == 0:
             return vi_power_states.get(power_state, VMPowerState.UNKNOWN)
             #get the folder to create the VM
             folders = self._server._retrieve_properties_traversal(
                                          property_names=['name', 'childEntity'],
-                                         obj_type="Folder")
+                                         obj_type=MORTypes.Folder)
             folder_mor = None
             for f in folders:
                 fname = ""
                     break
             if not folder_mor and folder:
                 raise VIException("Couldn't find folder %s" % folder,
-                                  FaultTypes.FOLDER_NOT_FOUND)
+                                  FaultTypes.OBJECT_NOT_FOUND)
             elif not folder_mor:
                 raise VIException("Error locating current VM folder",
-                                  FaultTypes.FOLDER_NOT_FOUND)
+                                  FaultTypes.OBJECT_NOT_FOUND)
     
             request = VI.CloneVM_TaskRequestMsg()
             _this = request.new__this(self._mor)
                 break
         if not mor:
             raise VIException("Could not find snapshot '%s'" % name,
-                              FaultTypes.SNAPSHOT_NOT_FOUND)
+                              FaultTypes.OBJECT_NOT_FOUND)
 
         try:
             request = VI.RevertToSnapshot_TaskRequestMsg()
                 break
         if not mor:
             raise VIException("Couldn't find snapshot with path '%s' (index %d)"
-                              % (path, index), FaultTypes.SNAPSHOT_NOT_FOUND)
+                              % (path, index), FaultTypes.OBJECT_NOT_FOUND)
 
         try:
             request = VI.RevertToSnapshot_TaskRequestMsg()
         self.refresh_snapshot_list()
         if not self.__current_snapshot:
             raise VIException("There is no current snapshot",
-                              FaultTypes.SNAPSHOT_NOT_FOUND)
+                              FaultTypes.OBJECT_NOT_FOUND)
 
         return self.__delete_snapshot(self.__current_snapshot, remove_children,
                                                                        sync_run)
                 break
         if mor is None:
             raise VIException("Could not find snapshot '%s'" % name,
-                              FaultTypes.SNAPSHOT_NOT_FOUND)
+                              FaultTypes.OBJECT_NOT_FOUND)
 
         return self.__delete_snapshot(mor, remove_children, sync_run)
 
                 break
         if not mor:
             raise VIException("Couldn't find snapshot with path '%s' (index %d)"
-                              % (path, index), FaultTypes.SNAPSHOT_NOT_FOUND)
+                              % (path, index), FaultTypes.OBJECT_NOT_FOUND)
 
         self.__delete_snapshot(mor, remove_children, sync_run)
 
                     'toolsOk':ToolsStatus.RUNNING,
                     'toolsOld':ToolsStatus.RUNNING_OLD}
 
-        do_object_content = self._server._get_object_properties(self._mor,
+        oc = self._server._get_object_properties(self._mor,
                                            property_names=['guest.toolsStatus'])
-        oc = do_object_content[0]
         if not hasattr(oc, 'PropSet'):
             return ToolsStatus.UNKNOWN
         prop_set = oc.PropSet
             vm.set_attribute_type(self._mor.get_attribute_type())
             request.set_element_vm(vm)
             request.set_element_auth(self._auth_obj)
-            vars = self._server._proxy.ReadEnvironmentVariableInGuest(request
+            envvars = self._server._proxy.ReadEnvironmentVariableInGuest(request
                                                                     )._returnval
-            return dict([v.split("=", 1) for v in vars])
+            return dict([v.split("=", 1) for v in envvars])
         except (VI.ZSI.FaultException), e:
             raise VIApiException(e)
 
         """Returns the name of the resource pool where this VM belongs to. Or
         None if there isn't any or it can't be retrieved"""
         if self._resource_pool:
-            do_object_content = self._server._get_object_properties(
+            oc = self._server._get_object_properties(
                                    self._resource_pool, property_names=['name'])
-            oc = do_object_content[0]
             if not hasattr(oc, 'PropSet'):
                 return None
             prop_set = oc.PropSet
                                    })
         
         def update_files(files):
-            for file in files:
-                self._files[file.key] = {
-                                        'key': file.key,
-                                        'name': file.name,
-                                        'size': file.size,
-                                        'type': file.type
+            for file_info in files:
+                self._files[file_info.key] = {
+                                        'key': file_info.key,
+                                        'name': file_info.name,
+                                        'size': file_info.size,
+                                        'type': file_info.type
                                         }
                 
         

tests/__init__.py

Empty file added.

tests/config.ini.dist

+[READ_ONLY_ENV]
+
+host = hostname
+user = username
+password = secret
+server_type = VMware vCenter Server
+api_version = 4.1
+
+[SANDBOX_ENV]
+
+host = hostname
+user = root
+password = secret2
+server_type = ESXi
+api_version = 5.0

tests/test_vi_property.py

+import os
+import ConfigParser
+from unittest import TestCase
+
+from pysphere import VIServer, VIProperty, VIMor
+
+class VIPropertyTest(TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        config_path = os.path.join(os.path.dirname(__file__), "config.ini")
+        cls.config = ConfigParser.ConfigParser()
+        cls.config.read(config_path)
+        
+        host = cls.config.get("READ_ONLY_ENV", "host")
+        user = cls.config.get("READ_ONLY_ENV", "user")
+        pswd = cls.config.get("READ_ONLY_ENV", "password")
+        
+        cls.server = VIServer()
+        cls.server.connect(host, user, pswd)
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.server.disconnect()
+
+    def test_property_types(self):
+        hosts = self.server.get_hosts()
+        for hmor, hname in hosts.items():
+            p = VIProperty(self.server, hmor)
+            assert p.name == hname
+            #string
+            assert isinstance(p.name, basestring)
+            #property mor
+            assert VIMor.is_mor(p._obj)
+            #nested properties
+            assert isinstance(p.configManager, VIProperty)
+            #bool
+            assert isinstance(p.runtime.inMaintenanceMode, bool)
+            #list
+            assert isinstance(p.vm, list)
+            #mor without traversing
+            assert VIMor.is_mor(p.vm[0]._obj)
+            #traversing
+            assert isinstance(p.vm[0].name, basestring)
+            #enum
+            assert p.runtime.powerState in ['poweredOff', 'poweredOn', 'standBy', 'unknown']
+            #int
+            assert isinstance(p.summary.config.port, int)
+            #long
+            assert isinstance(p.hardware.memorySize, long)
+            #short as int
+            assert isinstance(p.hardware.cpuInfo.numCpuCores, int)
+            #date as tuple
+            assert isinstance(p.runtime.bootTime, tuple)
+            #check property cache
+            assert "runtime" in p.__dict__.keys()
+            assert "memorySize" in p.hardware.__dict__.keys()
+            assert "numCpuCores" in p.hardware.cpuInfo.__dict__.keys()
+            assert "name" in p.vm[0].__dict__.keys()
+            #check cache flush
+            p._flush_cache()
+            assert "runtime" not in p.__dict__.keys()
+            assert "memorySize" not in p.hardware.__dict__.keys()
+            assert "numCpuCores" not in p.hardware.cpuInfo.__dict__.keys()
+            assert "name" not in p.vm[0].__dict__.keys()
+            #check unexistent property
+            try:
+                p.hoochiemama
+            except AttributeError:
+                pass
+            except:
+                raise AssertionError("Attribute Error expected")
+            else:
+                raise AssertionError("Attribute Error expected")

tests/test_vi_server.py

+import os
+import random
+import ConfigParser
+from unittest import TestCase
+
+from pysphere import VIServer, VIProperty, MORTypes, VIException, FaultTypes, \
+                     VMPowerState
+
+class VIServerTest(TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        config_path = os.path.join(os.path.dirname(__file__), "config.ini")
+        cls.config = ConfigParser.ConfigParser()
+        cls.config.read(config_path)
+        
+        host = cls.config.get("READ_ONLY_ENV", "host")
+        user = cls.config.get("READ_ONLY_ENV", "user")
+        pswd = cls.config.get("READ_ONLY_ENV", "password")
+        
+        cls.server = VIServer()
+        cls.server.connect(host, user, pswd)
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.server.disconnect()
+
+    def test_get_hosts_by_datacenter(self):
+        all_hosts = self.server.get_hosts()
+        datacenters = self.server.get_datacenters()
+        hosts_by_datacenter = [self.server.get_hosts(from_mor=dc)
+                            for dc in datacenters.keys()]
+        all_hosts2 = {}
+        [all_hosts2.update(group) for group in hosts_by_datacenter]
+        
+        assert all_hosts == all_hosts2
+
+    def test_get_hosts_by_cluster(self):
+        clusters = self.server.get_clusters()
+        count_hosts1 = 0
+        count_hosts2 = 0
+        for cl in clusters.keys():
+            count_hosts1 += len(self.server.get_hosts(from_mor=cl))
+            prop = VIProperty(self.server, cl)
+            count_hosts2 += len(prop.host)
+        assert count_hosts1 == count_hosts2
+        
+    def test_get_datastores_by_datacenter(self):
+        all_datastores = self.server.get_datastores()
+        datacenters = self.server.get_datacenters()
+        datastores_by_datacenter = [self.server.get_datastores(from_mor=dc)
+                            for dc in datacenters.keys()]
+        all_datastores2 = {}
+        [all_datastores2.update(group) for group in datastores_by_datacenter]
+        
+        assert all_datastores == all_datastores2
+        
+    def test_get_resource_pools(self):
+        all_rp = self.server.get_resource_pools()
+        rp_by_root_rp = []
+        for rp_key, rp_path in all_rp.items():
+            if rp_path.count('/') == 1:
+                rp_by_root_rp.extend(self.server.get_resource_pools(
+                                                        from_mor=rp_key).keys())
+        print len(rp_by_root_rp), len(all_rp)
+        assert sorted(rp_by_root_rp) == sorted(all_rp.keys())
+        
+    def test_get_registered_vms(self):
+        all_vms = self.server.get_registered_vms()
+        datacenters = self.server.get_datacenters()
+        vms_by_datacenter = []
+        resource_pools = self.server.get_resource_pools()
+        vms_by_root_rp = []
+        for dc in datacenters.keys():
+            vms_by_datacenter.extend(self.server.get_registered_vms(
+                                                                 datacenter=dc))
+        for rp_key, rp_path in resource_pools.items():
+            if rp_path.count('/') == 1:
+                vms_by_root_rp.extend(self.server.get_registered_vms(
+                                                          resource_pool=rp_key))
+        oc = self.server._retrieve_properties_traversal(
+                                             property_names=['config.template',
+                                                     'config.files.vmPathName'],
+                                             obj_type=MORTypes.VirtualMachine)
+        templates = []
+        for o in oc:
+            path = None
+            template = False
+            if not hasattr(o, "PropSet"):
+                continue
+            for p in o.PropSet:
+                if p.Name=="config.template" and p.Val:
+                    template = True
+                if p.Name=="config.files.vmPathName":
+                    path = p.Val
+            if template:
+                templates.append(path)
+                
+        vms_by_root_rp.extend(templates)
+        assert sorted(all_vms) == sorted(vms_by_datacenter) == sorted(
+                                                                 vms_by_root_rp)
+
+    def test_get_registered_vms_by_datacenter(self):
+        for dc_key, dc_name in self.server.get_datacenters().items():
+            vms1 = self.server.get_registered_vms(datacenter=dc_key)
+            vms2 = self.server.get_registered_vms(datacenter=dc_name)
+            assert vms1 == vms2
+
+    def test_get_registered_vms_by_cluster(self):
+        for cl_key, cl_name in self.server.get_clusters().items():
+            vms1 = self.server.get_registered_vms(cluster=cl_key)
+            vms2 = self.server.get_registered_vms(cluster=cl_name)
+            assert vms1 == vms2
+
+    def test_get_registerd_vms_by_status(self):
+        #powered on
+        vms = self.server.get_registered_vms(status='poweredOn')
+        if vms:
+            vm = self.server.get_vm_by_path(random.choice(vms))
+            assert vm.get_status(basic_status=True) == VMPowerState.POWERED_ON
+        #powered off
+        vms = self.server.get_registered_vms(status='poweredOff')
+        if vms:
+            vm = self.server.get_vm_by_path(random.choice(vms))
+            assert vm.get_status(basic_status=True) == VMPowerState.POWERED_OFF
+        #suspended
+        vms = self.server.get_registered_vms(status='suspended')
+        if vms:
+            vm = self.server.get_vm_by_path(random.choice(vms))
+            assert vm.get_status(basic_status=True) == VMPowerState.SUSPENDED
+
+    def test_is_connected(self):
+        assert self.server.is_connected()
+
+    def test_keep_session_alive(self):
+        assert self.server.keep_session_alive()
+        
+    def test_api_version_and_server(self):
+        assert self.server.get_api_version()
+        assert self.server.get_server_type()
+        
+    def test_existent_vm_path(self):
+        vms = self.server.get_registered_vms()
+        path = random.choice(vms)
+        vm = self.server.get_vm_by_path(path)
+        assert path == vm.properties.config.files.vmPathName
+        
+    def test_unexistent_vm_path(self):
+        try:
+            self.server.get_vm_by_path("zaraza")
+        except VIException, e:
+            assert e.fault == FaultTypes.OBJECT_NOT_FOUND
+        else:
+            assert False
+
+    def test_existent_vm_path_by_datacenter(self):
+        datacenters = self.server.get_datacenters()
+        for k, v in datacenters.items():
+            vms = self.server.get_registered_vms(datacenter=k)
+            path = random.choice(vms)
+            vm1 = self.server.get_vm_by_path(path, datacenter=k)
+            vm2 = self.server.get_vm_by_path(path, datacenter=v)
+            assert vm1._mor == vm2._mor
+            
+    def test_existent_vm_path_in_other_datacenter(self):
+        datacenters = self.server.get_datacenters()
+        if len(datacenters) >= 2:
+            dc1 = datacenters.keys()[0]
+            dc2 = datacenters.keys()[1]
+            vms = self.server.get_registered_vms(datacenter=dc1)
+            path = random.choice(vms)
+            try:
+                self.server.get_vm_by_path(path, datacenter=dc2)
+            except VIException, e:
+                assert e.fault == FaultTypes.OBJECT_NOT_FOUND
+            else:
+                raise AssertionError("VM shouldn't be found")
+                
+    def test_get_object_properties(self):
+        hosts = self.server.get_hosts()
+        props = ['name', 'summary.config.vmotionEnabled']
+        for mor, name in hosts.items():
+            prop = self.server._get_object_properties(mor, 
+                                                          property_names=props)
+            assert prop.Obj == mor
+            assert len(prop.PropSet) == 2
+            pname = None
+            pvmotion = None
+            for p in prop.PropSet:
+                if p.Name == 'name':
+                    pname = p.Val
+                elif p.Name == "summary.config.vmotionEnabled":
+                    pvmotion = p.Val
+                else:
+                    raise AssertionError("Got not expected property")
+            assert pname == name
+            assert pvmotion in (True, False)
+            
+    def test_get_object_properties_get_all(self):
+        datastores = self.server.get_datastores()
+        props = ['name', 'info.url']
+        for mor, name in datastores.items():
+            prop = self.server._get_object_properties(mor, 
+                                                        property_names=props,
+                                                        get_all=True)
+            assert prop.Obj == mor
+            assert len(prop.PropSet) > 2
+            
+            for p in prop.PropSet:
+                if p.Name == 'name':
+                    assert p.Val == name
+                    
+    def test_get_object_properties_bulk(self):
+        
+        hosts = self.server.get_hosts()
+        datastores = self.server.get_datastores()
+        oc = self.server._retrieve_properties_traversal(
+                                                        property_names=['name'],
+                                               obj_type=MORTypes.VirtualMachine)
+        random.shuffle(oc)
+        vms = dict([(o.Obj, o.PropSet[0].Val) for o in oc[:10]])
+        
+        all_mors = hosts.keys() + datastores.keys() + vms.keys()
+        found = []
+        random.shuffle(all_mors)
+        
+        properties = {MORTypes.VirtualMachine:['name', 'config.files.vmPathName'],
+                      MORTypes.HostSystem:['name', 'summary.config.vmotionEnabled'],
+                      MORTypes.Datastore:[], #get all
+                      MORTypes.Folder:['name']
+                      }
+        
+        res = self.server._get_object_properties_bulk(all_mors, properties)
+        
+        for o in res:
+            mor = o.Obj
+            found.append(mor)
+            assert mor in all_mors
+            if mor.get_attribute_type() == MORTypes.VirtualMachine:
+                assert len(o.PropSet) == 2
+                path = None
+                name = None
+                for p in o.PropSet:
+                    if p.Name == "name": name = p.Val
+                    elif p.Name == "config.files.vmPathName": path = p.Val
+                    else:
+                        raise AssertionError("Unexpected property for VM: %s"
+                                             % p.Name)
+                assert name == vms[mor]
+                assert path
+            elif mor.get_attribute_type() == MORTypes.Datastore:
+                assert len(o.PropSet) > 5
+                name = None
+                for p in o.PropSet:
+                    if p.Name == "name": name = p.Val
+                assert name == datastores[mor]
+            elif mor.get_attribute_type() == MORTypes.HostSystem:
+                assert len(o.PropSet) == 2
+                name = None
+                vmotion = None
+                for p in o.PropSet:
+                    if p.Name == "name": name = p.Val
+                    elif p.Name == "summary.config.vmotionEnabled": vmotion = p.Val
+                    else:
+                        raise AssertionError("Unexpected property for Host: %s"
+                                             % p.Name)
+                assert name == hosts[mor]
+                assert vmotion in [True, False]
+            else:
+                raise AssertionError("Unexpected mor type %s" 
+                                     % mor.get_attribute_type())
+        assert sorted(all_mors) == sorted(found)
+                
+