text: ## @file invt.py
  ## @brief Inventory functions
  """
  Inventory functions
  
  Detailed description (for [e]pydoc goes here)
  """
  
  from pyVmomi import vmodl, vim
  from . import connect
  from itertools import chain
  
  
  def GetEnv(dataCenter=None, si=None):
      """Get the default environment for this host"""
      cr = GetHostFolder(dataCenter, si).GetChildEntity()[0]
      return cr.GetEnvironmentBrowser()
  
  
  def GetResourcePool(dataCenter=None, si=None):
      """Get the default resource pool"""
      computeResources = GetHostFolder(dataCenter, si).GetChildEntity()
      if len(computeResources) == 0:
          return None
  
      cr = computeResources[0]
      return cr.GetResourcePool()
  
  
  def GetRootFolder(si=None):
      """Gets the root folder of the inventory"""
      if not si:
          si = connect.GetSi()
      return si.RetrieveContent().GetRootFolder()
  
  
  def GetDatacenter(name=None, si=None):
      """
      Gets the datacenter with the given name. If the name is None, returns the first
      (and in the case of hostd, only) datacenter.
      """
      if not si:
          si = connect.GetSi()
      content = si.RetrieveContent()
  
      if name is None:
          if len(content.GetRootFolder().GetChildEntity()) == 0:
              return None
          else:
              for child in content.GetRootFolder().GetChildEntity():
                  # Not all children of the root folder are Datacenters, so we need
                  # to scan the list looking for one.
                  if isinstance(child, vim.Datacenter):
                      return child
  
              return None
      else:
          return FindChild(content.GetRootFolder(), name)
  
  
  def GetDatacenterAll(si=None):
      """ Returns an iterator object that walks the inventory tree and yields all
      Datacenter instances.
      """
  
      children = GetRootFolder(si).childEntity
      while children:
          entities, children = children, []
          for entity in entities:
              if isinstance(entity, vim.Datacenter):
                  yield entity
              elif isinstance(entity, vim.Folder):
                  children = chain(children, entity.childEntity)
              else:
                  raise NotImplementedError
  
  
  def GetVmFolder(dataCenter=None, si=None):
      """Retrieve the folder where virtual machines are stored on this host."""
      return GetDatacenter(dataCenter, si).GetVmFolder()
  
  
  def GetVmFolderAll(datacenter=None, si=None):
      """ Returns an iterator object that walks the inventory tree and yields
      all vm Folder instances for each Datacenter object that satisfies
      the datacenter() predicate, if specified.
      """
      return (dc.GetVmFolder() for dc in GetDatacenterAll(si) \
                 if not datacenter or datacenter(dc))
  
  
  def GetHostFolder(dataCenter=None, si=None):
      """Retrieve the host folder for the given datacenter."""
      return GetDatacenter(dataCenter, si).GetHostFolder()
  
  
  def GetHostFolderAll(datacenter=None, si=None):
      """ Returns an iterator object that walks the inventory tree and yields
      all host Folder instances for each Datacenter object that satisfies
      the datacenter() predicate, if specified.
      """
      return (dc.GetHostFolder() for dc in GetDatacenterAll(si) \
                 if not datacenter or datacenter(dc))
  
  
  def GetNetworkFolder(dataCenter=None, si=None):
      """Retrieve the network folder for the given datacenter."""
      return GetDatacenter(dataCenter, si).GetNetworkFolder()
  
  
  def GetNetworkFolderAll(datacenter=None, si=None):
      """ Returns an iterator object that walks the inventory tree and yields
      all network Folder instances for each Datacenter object that satisfies
      the datacenter() predicate, if specified.
      """
      return (dc.GetNetworkFolder() for dc in GetDatacenterAll(si) \
                 if not datacenter or datacenter(dc))
  
  
  def GetDatastoreFolder(dataCenter=None, si=None):
      """Retrieve the datastore folder for the given datacenter."""
      return GetDatacenter(dataCenter, si).GetDatastoreFolder()
  
  
  def GetDatastoreFolderAll(datacenter=None, si=None):
      """ Returns an iterator object that walks the inventory tree and yields
      all datastore Folder instances for each Datacenter object that satisfies
      the datacenter() predicate, if specified.
      """
      return (dc.GetDatastoreFolder() for dc in GetDatacenterAll(si) \
                 if not datacenter or datacenter(dc))
  
  
  def GetCluster(dataCenter=None, clusterName=None, si=None):
      """Retrieve the specified cluster object"""
      if si is None:
          si = connect.GetSi()
      content = si.RetrieveContent()
  
      return FindChild(GetHostFolder(dataCenter, si), clusterName)
  
  
  def FindChild(parent, name):
      """
      Find the managed entity with the given name in the list of children for this
      entity. If the input name is None, returns the first child. Returns None if
      the child with the given name is not found
      """
  
      if name is None:
          return parent.GetChildEntity()[0]
  
      for child in parent.GetChildEntity():
          try:
              if child.GetName() == name:
                  return child
          except vmodl.fault.ManagedObjectNotFound:
              # By the time GetName is called, child from parent.GetChildEntity
              # may have been destroyed, see PR 2012619
              pass
      return None
  
  
  def GetLLPM():
      """Retrieve the internal LowLevelProvisioningManager object."""
      si = connect.GetSi()
      internalContent = si.RetrieveInternalContent()
  
      return internalContent.GetLlProvisioningManager()
  
  
  def matchName(obj, pathToMatch):
      currentName = obj.GetName()
      currentName = currentName.replace("/", "%2f")
      if pathToMatch == "" or pathToMatch == "*":
          return [True, pathToMatch, True]
  
      pathSplit = pathToMatch.split("/", 1)
      if pathSplit[0] == "*":
          if len(pathSplit) == 1:
              return [True, "*", True]
          else:
              return [True, pathSplit[1], True]
      else:
          if pathSplit[0] == currentName:
              if len(pathSplit) == 1:
                  return [True, "", False]
              else:
                  return [True, pathSplit[1], True]
          else:
              return [False]
  
  
  def checkfilter(flt, obj):
      # Does this item match the filter?
      # Simple "and" grammar and python regular expression test
      # Syntax for filter = "name=foo&&powerstate=bar"
      # XXX: Not implemented
      # XXX: Doesnt work with =, &
      if flt is not None and flt != "":
          if obj.GetName() != flt:
              return False
  
      return True
  
  
  def recurseFolder(filter,
                    children,
                    recurseList,
                    entityType,
                    pathToMatch,
                    parentPath,
                    recurseFurther=True):
      """ Recurse folders and find certain types of children """
      results = []
      if type(children) != list:
          for recurseTypes in recurseList:
              if isinstance(children, recurseTypes[0]):
                  if isinstance(children, entityType) and checkfilter(
                          filter, children):
                      results.append([children, parentPath])
                  if not recurseFurther:
                      return results
                  res = matchName(children, pathToMatch)
                  if res[0] == False:
                      return results
                  newPathToMatch = res[1]
                  fnslist = recurseTypes[1]
                  for fnname in fnslist:
                      fn = getattr(children, fnname)
                      babies = fn()
                      results = results + recurseFolder(
                          filter, babies, recurseList, entityType, res[1],
                          parentPath + "/" + children.GetName(), res[2])
                  return results
      for child in children:
          res = matchName(child, pathToMatch)
          if res[0] == False:
              continue
          if isinstance(child, entityType) and checkfilter(filter, child):
              results.append([child, parentPath])
          if recurseFurther:
              for recurseTypes in recurseList:
                  if isinstance(child, recurseTypes[0]):
                      fnslist = recurseTypes[1]
                      for fnname in fnslist:
                          fn = getattr(child, fnname)
                          babies = fn()
                          results = results + recurseFolder(
                              filter, babies, recurseList, entityType, res[1],
                              parentPath + "/" + child.GetName(), res[2])
      return results
  
  
  def startFind(list, objType, parent, filter, si=None):
      if not si:
          si = connect.GetSi()
      sc = si.RetrieveContent()
      root = sc.GetRootFolder()
      children = root.GetChildEntity()
      # The first / in the path is always useless, get rid of it
      if len(parent) > 0 and parent[0] == "/":
          parent = parent[1:]
      results = recurseFolder(filter, children, list, objType, parent, "")
      return results
  
  
  def findVms(parent, filter, si=None):
      return startFind(
          [[vim.Folder, ["GetChildEntity"]], [vim.Datacenter, ["GetVmFolder"]]],
          vim.VirtualMachine, parent, filter, si)
  
  
  def findDatacenters(parent, filter, si=None):
      return startFind([[vim.Folder, ["GetChildEntity"]]], vim.Datacenter,
                       parent, filter, si)
  
  
  def findFolders(parent, filter, si=None):
      return startFind([[vim.Folder, ["GetChildEntity"]],
                        [vim.Datacenter, ["GetVmFolder", "GetHostFolder"]]],
                       vim.Folder, parent, filter, si)
  
  
  def findComputeResource(parent, filter, si=None):
      return startFind([[vim.Folder, ["GetChildEntity"]],
                        [vim.Datacenter, ["GetHostFolder"]]],
                       vim.ComputeResource, parent, filter, si)
  
  
  def findHost(parent, filter, si=None):
      return startFind(
          [[vim.Folder, ["GetChildEntity"]], [vim.Datacenter, ["GetHostFolder"]],
           [vim.ComputeResource, ["GetHost"]]], vim.HostSystem, parent, filter,
          si)
  
  
  def findResourcePools(parent, filter, si=None):
      return startFind(
          [[vim.Folder, ["GetChildEntity"]], [vim.Datacenter, ["GetHostFolder"]],
           [vim.ComputeResource, ["GetResourcePool"]],
           [vim.ResourcePool, ["GetResourcePool"]]], vim.ResourcePool, parent,
          filter, si)
  
  
  def getInventoryPath(entity, si=None):
      if si is None:
          si = connect.GetSi().RetrieveContent()
      else:
          si = si.content
      pc = si.GetPropertyCollector()
      PC = vmodl.query.PropertyCollector
      # Retrieve the "name" and "parent" properties for all the managed entities
      # reachable by following "parent" properties starting from managedEntity.
      objectSet = pc.RetrieveContents([
          PC.FilterSpec(
              propSet=[
                  PC.PropertySpec(type=vim.ManagedEntity,
                                  pathSet=["name", "parent"])
              ],
              objectSet=[
                  PC.ObjectSpec(
                      obj=entity,
                      selectSet=[
                          PC.TraversalSpec(
                              name="ParentTraversalSpec",
                              type=vim.ManagedEntity,
                              path="parent",
                              selectSet=[
                                  PC.SelectionSpec(name="ParentTraversalSpec")
                              ])
                      ])
              ])
      ])
  
      # Create map containing an entry for each manaaged entity retrieved by
      # the RetrieveContents call above.  Each entry maps the MOID of the managed entity
      # to a dict with 'name' and 'parent' keys.
      entityMap = dict()
      for oc in objectSet:
          if len(oc.missingSet) != 0:
              raise RuntimeError('Missing properties: %s' %
                                 [mp.path for mp in oc.missingSet])
  
          entityMap[oc.obj._moId] = dict([(p.name, p.val) for p in oc.propSet])
          entityMap[oc.obj._moId].setdefault('parent', None)
  
      # Use entityMap to walk the "parent" properties of the managed entities,
      # starting with entity, computing the inventory path (from end to
      # beginning) along the way.
      result = []
      current = entityMap[entity._moId]
      while current:
          parent = current['parent']
          if parent:
              result.insert(0, current['name'])
              current = entityMap[parent._moId]
          else:
              current = None
      return '/'.join(result)
  
  
  def getContainingDatacenter(entity, si=None):
      if si is None:
          si = connect.GetSi().RetrieveContent()
      else:
          si = si.content
      pc = si.GetPropertyCollector()
      PC = vmodl.query.PropertyCollector
      # Retrieve the moRef of the datacenter reachable by following ResourcePool
      # "owner" and ManagedEntity "parent" properties starting from entity.
      #
      # Note: This does not (currently) work for virtual machines inside a VApp.
      objectSet = pc.RetrieveContents([
          PC.FilterSpec(
              propSet=[PC.PropertySpec(type=vim.Datacenter)],
              objectSet=[
                  PC.ObjectSpec(
                      obj=entity,
                      selectSet=[
                          PC.TraversalSpec(
                              type=vim.HostSystem,
                              path="parent",
                              selectSet=[
                                  PC.TraversalSpec(
                                      type=vim.ComputeResource,
                                      path="parent",
                                      selectSet=[
                                          PC.SelectionSpec(
                                              name='FolderParentTraversalSpec')
                                      ]),
                              ]),
                          PC.TraversalSpec(
                              type=vim.ResourcePool,
                              path="owner",
                              selectSet=[
                                  PC.TraversalSpec(
                                      type=vim.ComputeResource,
                                      path="parent",
                                      selectSet=[
                                          PC.SelectionSpec(
                                              name='FolderParentTraversalSpec')
                                      ]),
                              ]),
                          PC.TraversalSpec(
                              type=vim.ManagedEntity,
                              path="parent",
                              selectSet=[
                                  PC.TraversalSpec(
                                      name='FolderParentTraversalSpec',
                                      type=vim.Folder,
                                      path="parent",
                                      selectSet=[
                                          PC.SelectionSpec(
                                              name='FolderParentTraversalSpec')
                                      ]),
                              ])
                      ])
              ])
      ])
      if len(objectSet) == 0:
          return None
      elif len(objectSet) == 1:
          return objectSet[0].obj
      else:
          raise RuntimeError('multiple objects returned from property collector')
  