This example describes a way to discover the path to a PCI device in the managed server by using the portgroup connections. This information is useful to system administrators who want to troubleshoot device problems or upgrade the hardware in a managed server.

The PCI Device profile specification allows flexibility in how the profile is implemented. Designers can apply one of three approaches to modeling PCI device connections, or they can combine these approaches for a more complete implementation. Device connections can be modeled with a combination of the following approaches.

  • DeviceConnection associations
  • PCIPortGroup instances that express relationships between PCI ports
  • Primary and secondary bus numbers that relate PCI devices to bridges and switches

The VMware implementation supports the first two modeling approaches.

For convenience, the VMware implementation also provides a fourth way to model device connections: ParentDeviceID. For an example that uses the ParentDeviceID property, see Tracing the Path to a PCI Device By Using PortGroups. The ParentDeviceID property is specific to VMware classes, so it cannot be used in vendor-independent object traversal algorithms.

This example shows how you can trace the path to a PCI device by using the PCIPortGroup associations. This way of relating PCI devices depends only on the properties defined in the CIM schema, so it is vendor-independent. Tracing the Path to a PCI Device By Using PortGroups shows the relationships of the CIM objects involved.

Given a PCI device identified by bus, device, and function numbers (<bus>:<device>:<function>), this example identifies and displays all ports, bridges, and switches between the chosen device and the CPU. The PCI Device profile specifies how to model associations between devices and their ports, and between ports and the logical port groups that represent all ports on the same PCI bus.

In Tracing the Path to a PCI Device By Using PortGroups, the SystemDevice association to the managed server is included for reference, but is not used in this example.

Figure 1. Tracing the Path to a PCI Device By Using PortGroups
Diagram shows path to locate a specific PCI device by portgroup linkage.

This pseudocode depends on the pseudocode in Make a Connection to the CIMOM.

To trace the path to a PCI device

Procedure

  1. Connect to the server URL.

    Specify the Implementation namespace, supplied as a parameter, for the connection.

    The actual namespace you will use depends on your implementation.

    use wbemlib
    use sys
    use connection renamed cnx
    connection = Null
    
    params = cnx.get_params()
    if params is Null
       sys.exit(-1)
    connection = cnx.connect_to_host( params )
    if connection is Null
       print 'Failed to connect to: ' + params['host'] + ' as user: ' + params['user']
       sys.exit(-1)
  2. Enumerate the names of all CIM_PCIDevice instances and save each instance name in an array.
    dev_instance_names = connection.EnumerateInstances( ’CIM_PCIDevice’ )
    if len( dev_instance_names ) is 0
       print ’Error: No CIM_PCIDevice instances were found.’
       sys.exit(-1)
  3. Search the array of PCI devices for one that matches the bus number, device number, and function number selected by the command-line parameters.
    param_bus, param_device, param_function = params[’extra_params’][0].split( ’:’ )
    chosen_name = Null
    for dev_name in dev_instance_names
       dev = connection.GetInstance( dev_name )
       if (dev[’BusNumber’], dev[’DeviceNumber’], dev[’FunctionNumber’]) == \
          (param_bus, param_device, param_function)
          chosen_name = dev_name
          break
    if chosen_name is Null
       print ’Error: Chosen device (%s:%s:%s) not found on the managed system.’ % \
             (param_bus, param_device, param_function)
       exit(-1)
  4. Print the DeviceID, the BusNumber, DeviceNumber, and FunctionNumber, the PhysicalSlot, and the ElementName properties of the chosen device.
    print ’Chosen device:’
    print_dev( dev )
    
    function print_dev( dev )
       print ’ID=%s B/D/F=%s/%s/%s Slot=%s Type=%s (%s)’ % \
             (dev[’DeviceID’], dev[’BusNumber’], dev[’DeviceNumber’], dev[’FunctionNumber’], \
             dev[’PhysicalSlot’], dev[ ’ElementName’ ])
  5. Traverse the CIM_ControlledBy association to get instance names of the class CIM_PCIPort, selecting the instance that has the same BusNumber as the chosen instance of CIM_PCIDevice.

    Print the PortType property of the CIM_PCIPort instance. This example maps the PortType property to the corresponding string value in its Values qualifier.

    port_name = connected_port_on_bus( dev_name, dev[ ’BusNumber’ ]
    if port_name is Null
       print ’No upstream port found.’
       break
    port = connection.GetInstance( port_name )
    print_port( port )
    
    function connected_port_on_bus( dev_name, bus_number )
       port_instance_names = connection.AssociatorNames( dev_name, \
                                                         AssocClass = ’CIM_ControlledBy’, \
                                                         ResultClass = ’CIM_PCIPort’ )
       for port_instance_name in port_instance_names
          port = connection.GetInstance( port_instance_name, \
                                         PropertyList = [’BusNumber’, ’PortType’] )
          if port[ ’BusNumber’ ] == bus_number
             return port_instance_name
       return Null
    
    use value_mapper renamed mapper
    function print_port( port )
       port_type = mapper.map_property_value_to_string( port, ’PortType’ )
       print ’ (%s port on bus %s)’ % (port_type, port[ ’BusNumber’ ])
  6. Traverse the CIM_MemberOfCollection association to the class CIM_PCIPortGroup.

    A port can only belong to one portgroup, so the result is a list with one member. Print the ElementName property of the portgroup. If this portgroup has BusNumber 0, stop looping because bus 0 connects to the CPU.

    portgroup = portgroup_of_port( port_name )
    print_portgroup( portgroup )
    if (portgroup[ ’BusNumber’ ] == 0
       break
    
    function portgroup_of_port( port_name )
       portgroup_instance_names = connection.AssociatorNames( \
                                             port_name, \
                                             AssocClass = ’CIM_MemberOfCollection’, \
                                             ResultClass = ’CIM_PCIPortGroup’ )
       portgroup_instance_name = portgroup_instance_names[ 0 ]
       return connection.GetInstance( portgroup_instance_name, \
                                      PropertyList = [’BusNumber’, ’ElementName’]
    
    function print_portgroup( portgroup )
       print ’  ’, portgroup[ ’ElementName’ ]
  7. Enumerate instances of the CIM_PCIBridge and find one that has the same SecondaryBusNumber as the BusNumber of the instance of CIM_PCIPortGroup.

    If no instance of CIM_PCIBridge is found, search for an instance of CIM_PCIeSwitch that has a SecondaryBusNumbers property containing the same BusNumber as the instance of CIM_PCIPortGroup.

    dev_name = upstream_bridge_or_switch( portgroup[ ’BusNumber’ ], ’CIM_PCIBridge’ )
    if dev_name is Null
       dev_name = upstream_bridge_or_switch( portgroup[ ’BusNumber’ ], ’CIM_PCIeSwitch’ )
       if dev_name is Null
          print ’No upstream PCI device found.’
          break
    
    function upstream_bridge_or_switch( bus_number, class_name )
       names = connection.EnumerateInstanceNames( class_name )
       for name in names
          instance = connection.GetInstance( name )
          if class_name == ’CIM_PCIBridge’ and instance[ ’SecondaryBusNumber’ ] == bus_number \
          or class_name == ’CIM_PCIeSwitch’ and bus_number in instance[ ’SecondaryBusNumbers’ ]
             return name
       return Null
  8. Working backwards from the bridge or switch, traverse the CIM_ControlledBy association to the class CIM_PCIPort, selecting the instance that has the same BusNumber as the portgroup.
    port_name = connected_port_on_bus( dev_name, portgroup[ ’BusNumber’ ] )
    if port_name is Null
       print ’Error: Missing port on downstream side of upstream device.’
       sys.exit(-1)
  9. Print the PortType property of the CIM_PCIPort.
    port = connection.GetInstance( port_name )
    print_port( port )
  10. Print the DeviceID, the BusNumber, DeviceNumber, and FunctionNumber, the PhysicalSlot, and the ElementName properties of the upstream bridge or switch.
    dev = connection.GetInstance( dev_name )
    print_dev( dev )
  11. Repeat from step 4.

    A sample of the output looks like the following:

    Chosen device:
     ID=PCI 0:4:1:0 B/D/F=4/1/0 Slot=0 (Mercuricity Generic USB OHCI Hub)
      (PCI-X port on bus 4)
        PCI port group for bus number 4
      (PCI-X port on bus 4)
     ID=PCI 0:3:3:0 B/D/F=3/3/0 Slot=2 (Plutonic Devices PD-631 PCI-X Bridge)
      (PCI-X port on bus 3)
        PCI port group for bus number 3
      (PCI-X port on bus 3)
     ID=PCI 0:0:1:0 B/D/F=0/1/0 Slot=0 (Plutonic Devices PD-631 PCI-X Bridge)
      (PCI port on bus 0)