Generate functor to read a property

Given the dbus member class for an interface and property hosted by
inventory manager, generate the getProperty functor for retrieving that
property from inventory. This ability is only supported when using the
`PropertyIs` functor to compare a property within inventory to a given
value before triggering an action.

Example yaml:
filters:
    - name: propertyIs
      service: xyz.openbmc_project.Inventory.Manager
      path: /system/chassis
      interface: xyz.openbmc_project.Inventory.Decorator.CoolingType
      property: WaterCooled
      value:
          value: true
          type: boolean
      dbusMember: "sdbusplus::xyz::openbmc_project::Inventory::\
                Decorator::server::CoolingType"

Generates:
make_filter(functor::propertyIs(
    "/system/chassis",
    "xyz.openbmc_project.Inventory.Decorator.CoolingType",
    "WaterCooled",
    true,
    "xyz.openbmc_project.Inventory.Manager",
    make_get_property<>
    (
        functor::getProperty<sdbusplus::xyz::openbmc_project::
            Inventory::Decorator::server::CoolingType>
        (
            "/system/chassis",
            "xyz.openbmc_project.Inventory.Decorator.CoolingType",
            &sdbusplus::xyz::openbmc_project::Inventory::
                Decorator::server::CoolingType::getPropertyByName,
            "WaterCooled"
        )
    )
))

Tested:
    Code generated as expected with service name given
    Code generated as expected without service name given

Resolves: openbmc/phosphor-inventory-manager#1

Change-Id: I5600854a1b0b9ce3e1511d3bcd9a452d6b1107ba
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 94600cc..27885d3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -43,11 +43,12 @@
 
 generated.cpp:
 	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/pimgen.py $(PIMGEN_ARGS) -d \
-		$(base_yamldir) -o $(builddir) generate-cpp
+		$(base_yamldir) -o $(builddir) -b $(BUSNAME) generate-cpp
 
 gen_serialization.hpp:
 	$(AM_V_GEN)$(PYTHON) $(top_srcdir)/pimgen.py $(PIMGEN_ARGS) -d \
-		$(base_yamldir) -o $(builddir) generate-serialization
+		$(base_yamldir) -o $(builddir) -b $(BUSNAME) \
+		generate-serialization
 
 SUBDIRS = . test
 
diff --git a/example/events.d/match1.yaml b/example/events.d/match1.yaml
index 3763093..6246dbd 100644
--- a/example/events.d/match1.yaml
+++ b/example/events.d/match1.yaml
@@ -78,8 +78,19 @@
 
     - name: startup event example
       description: >
-          Create /createme3 at startup.
+          Create /createme3 at startup if ExampleProperty1 on /changeme
+          equals changed.
       type: startup
+      filters:
+          - name: propertyIs
+            path: /changeme
+            interface: xyz.openbmc_project.Example.Iface1
+            property: ExampleProperty1
+            value:
+                value: changed
+                type: string
+            dbusMember: "sdbusplus::xyz::openbmc_project::Example::\
+                server::Iface1"
       actions:
           - name: createObjects
             objs:
diff --git a/pimgen.py b/pimgen.py
index de5419d..7c9ee53 100755
--- a/pimgen.py
+++ b/pimgen.py
@@ -26,6 +26,10 @@
 from sdbusplus.renderer import Renderer
 
 
+# Global busname for use within classes where necessary
+busname = "xyz.openbmc_project.Inventory.Manager"
+
+
 def cppTypeName(yaml_type):
     ''' Convert yaml types to cpp types.'''
     return sdbusplus.property.Property(type=yaml_type).cppTypeName
@@ -269,6 +273,14 @@
         super(PathCondition, self).__init__(**kw)
 
 
+class GetProperty(MethodCall):
+    '''Convenience type for getting inventory properties'''
+
+    def __init__(self, **kw):
+        kw['name'] = 'make_get_property'
+        super(GetProperty, self).__init__(**kw)
+
+
 class CreateObjects(MethodCall):
     '''Assemble a createObjects functor.'''
 
@@ -388,8 +400,10 @@
             path = TrivialArgument(value=path, type='string')
 
         args.append(path)
-        args.append(TrivialArgument(value=kw.pop('interface'), type='string'))
-        args.append(TrivialArgument(value=kw.pop('property'), type='string'))
+        iface = TrivialArgument(value=kw.pop('interface'), type='string')
+        args.append(iface)
+        prop = TrivialArgument(value=kw.pop('property'), type='string')
+        args.append(prop)
         args.append(TrivialArgument(
             decorators=[
                 Literal(kw['value'].get('type', None))], **kw.pop('value')))
@@ -398,6 +412,34 @@
         if service:
             args.append(TrivialArgument(value=service, type='string'))
 
+        dbusMember = kw.pop('dbusMember', None)
+        if dbusMember:
+            # Inventory manager's service name is required
+            if not service or service != busname:
+                args.append(TrivialArgument(value=busname, type='string'))
+
+            gpArgs = []
+            gpArgs.append(path)
+            gpArgs.append(iface)
+            # Prepend '&' and append 'getPropertyByName' function on dbusMember
+            gpArgs.append(TrivialArgument(
+                value='&'+dbusMember+'::getPropertyByName'))
+            gpArgs.append(prop)
+            fArg = MethodCall(
+                name='getProperty',
+                namespace=['functor'],
+                templates=[Template(
+                    name=dbusMember,
+                    namespace=[])],
+                args=gpArgs)
+
+            # Append getProperty functor
+            args.append(GetProperty(
+                templates=[Template(
+                    name=dbusMember+'::PropertiesVariant',
+                    namespace=[])],
+                    args=[fArg]))
+
         kw['args'] = args
         kw['namespace'] = ['functor']
         super(PropertyIs, self).__init__(**kw)
@@ -505,6 +547,8 @@
                 os.path.join(args.inputdir, 'extra_interfaces.d'))
         interface_composite.update(extra_interface_composite)
         interface_composite = InterfaceComposite(interface_composite)
+        # Update busname if configured differenly than the default
+        busname = args.busname
 
         return Everything(
             *events,
@@ -613,6 +657,10 @@
         default=os.path.join(script_dir, 'example'),
         help='Location of files to process.')
     parser.add_argument(
+        '-b', '--bus-name', dest='busname',
+        default='xyz.openbmc_project.Inventory.Manager',
+        help='Inventory manager busname.')
+    parser.add_argument(
         'command', metavar='COMMAND', type=str,
         choices=valid_commands.keys(),
         help='%s.' % " | ".join(valid_commands.keys()))