def nodes(self, unreported=2, with_status=False, **kwargs):
        nodes = self._query('nodes', **kwargs)
        # If we happen to only get one node back it
        # won't be inside a list so iterating over it
        # goes boom. Therefor we wrap a list around it.
        if type(nodes) == dict:
            nodes = [nodes, ]

        if with_status:
            latest_events = self.event_counts(
                query='["=", "latest_report?", true]',
                summarize_by='certname'
            )

        for node in nodes:
            node['unreported_time'] = None
            node['status'] = None
            node['events'] = None
            latest_report_hash = None

            if with_status:
                status = [s for s in latest_events
                          if s['subject']['title'] == node['certname']]

                try:
                    node['status'] = node['latest_report_status']
                    latest_report_hash = node['latest_report_hash']

                    if status:
                        node['events'] = status = status[0]
                        if status['noops'] > 0:
                            node['status'] = 'noop'
                except KeyError:
                    if status:
                        node['events'] = status = status[0]
                        if status['successes'] > 0:
                            node['status'] = 'changed'
                        if status['noops'] > 0:
                            node['status'] = 'noop'
                        if status['failures'] > 0:
                            node['status'] = 'failed'
                    else:
                        node['status'] = 'unchanged'

                # node report age
                if node['report_timestamp'] is not None:
                    try:
                        last_report = json_to_datetime(
                            node['report_timestamp'])
                        last_report = last_report.replace(tzinfo=None)
                        now = datetime.utcnow()
                        unreported_border = now - timedelta(hours=unreported)
                        if last_report < unreported_border:
                            delta = (datetime.utcnow() - last_report)
                            node['status'] = 'unreported'
                            node['unreported_time'] = '{0}d {1}h {2}m'.format(
                                delta.days,
                                int(delta.seconds / 3600),
                                int((delta.seconds % 3600) / 60)
                            )
                    except AttributeError:
                        node['status'] = 'unreported'

                if not node['report_timestamp']:
                    node['status'] = 'unreported'

            yield Node(self,
                       name=node['certname'],
                       deactivated=node['deactivated'],
                       expired=node['expired'],
                       report_timestamp=node['report_timestamp'],
                       catalog_timestamp=node['catalog_timestamp'],
                       facts_timestamp=node['facts_timestamp'],
                       status=node['status'],
                       events=node['events'],
                       unreported_time=node['unreported_time'],
                       report_environment=node['report_environment'],
                       catalog_environment=node['catalog_environment'],
                       facts_environment=node['facts_environment'],
                       latest_report_hash=latest_report_hash
                       )
Query for nodes by either name or query. If both aren't
dataset/ETHPy150Open voxpupuli/pypuppetdb/pypuppetdb/api/__init__.py/BaseAPI.nodes