File: advanced_queries.rst

package info (click to toggle)
pypuppetdb 3.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 508 kB
  • sloc: python: 3,810; makefile: 127; sh: 9
file content (192 lines) | stat: -rw-r--r-- 5,845 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
.. _advanced_queries:

Advanced Queries
================

For more advanced use of pypuppetdb you will want to use queries in one of the
two available languages:

* **PQL** (Puppet Query Language) - newer, arguably more human-friendly. Available
  since PuppetDB v. 4.0.

* **AST** (Abstract Syntax Tree) - older, using the Reverse Polish Notation.


PQL query with rich types
-------------------------

Making a PQL query using ``pql()`` method without using projection for one of the supported types
(``nodes``, ``edges``, ``facts``, ``resources``, ``catalogs``, ``events``, ``reports``, ``inventory``)
will return the rich types.

For example the below code returns objects of the type ``Node``:

.. code-block:: python

   >>> nodes = db.pql("""
   >>>     nodes {
   >>>       facts {
   >>>         name = "operatingsystem"
   >>>         and value = "Debian"
   >>>       }
   >>>     }
   >>> """
   >>> for node in nodes:
   >>>     print(node.name)
   foo.example.com
   bar.example.com


PQL query returning raw dicts
-----------------------------

If you do use projections (fields/functions) in your PQL query or you query for a unsupported type,
you will get JSON like you would querying PuppetDB directly, just converted into a Python dicts
or list of dicts.

For example if you want to get just a list of certnames of nodes that have the fact "operatingsystem"
set to "Debian":

.. code-block:: python

   >>> nodes = db.pql("""
   >>>     nodes[certname] {
   >>>       facts {
   >>>         name = "operatingsystem"
   >>>         and value = "Debian"
   >>>       }
   >>>     }
   >>> """
   >>> for node in nodes:
   >>>     print(node)
   {'certname': 'foo.example.com'}
   {'certname': 'bar.example.com'}

Please see `the official PQL documentation <https://puppet.com/docs/puppetdb/7/api/query/v4/pql.html>`_
for more examples.


AST query
---------

Alternatively to use the AST query language, use one of these methods:

``nodes()``, ``edges()``,  ``facts()``, ``resources()``, ``catalogs()``, ``events()``,
``reports()``, ``inventory()``,
``event_counts()``, ``aggregate_event_counts()``,
``environments()``, ``factsets()``, ``fact_contents()``, ``fact_paths()``

...and pass the AST query in the ``query`` parameter.

An example of the same query as above but rewritten in AST:

.. code-block:: python

   >>> nodes = db.nodes(query="""
   >>>     ["subquery", "facts",
   >>>       ["and",
   >>>         ["=", "name", "operatingsystem"],
   >>>         ["=", "value", "Debian"]
   >>>       ]
   >>>     ]
   >>> """
   >>> for node in nodes:
   >>>     print(node.name)
   foo.example.com
   bar.example.com

Please see `the official AST documentation <https://puppet.com/docs/puppetdb/7/api/query/v4/ast.html>`_
for more examples .

AST QueryBuilder
^^^^^^^^^^^^^^^^

pypuppetdb laso comes shipped with a ``QueryBuilder`` component that, as the name suggests,
allows users to build PuppetDB AST queries in an Object-Oriented fashion.
In many cases adding additional clauses to fulfill new requirements is superior to constructing
long strings.

Above example rewritten using it:

.. code-block:: python

   >>> from pypuppetdb.QueryBuilder import *
   >>> op = InOperator('certname')
   >>> query = ExtractOperator()
   >>> query.add_field(str('certname'))
   >>> subquery = SubqueryOperator('facts')
   >>> _add = AndOperator()
   >>> _add.add(EqualsOperator('name', 'operatingsystem'))
   >>> _add.add(EqualsOperator('value', 'Debian'))
   >>> subquery.add_query(_add)
   >>> query.add_query(subquery)
   >>> op.add_query(query)
   >>> print(op)
   ["in", "certname", ["extract", ["certname"], ["select_facts", ["and", ["=", "name", "operatingsystem"], ["=", "value", "Debian"]]]]]
   >>> nodes = db.nodes(query=query)
   >>> for node in nodes:
   >>>     print(node.name)
   foo.example.com
   bar.example.com


The following code will build a query for the Nodes endpoint to find all
nodes belonging to the production environment.

.. code-block:: python

   >>> from pypuppetdb.QueryBuilder import *
   >>> op = AndOperator()
   >>> op.add(EqualsOperator('catalog_environment', 'production'))
   >>> op.add(EqualsOperator('facts_environment', 'production'))
   >>> print(op)
   ["and",["=", "catalog_environment", "production"],["=", "facts_environment", "production"]]


Subqueries are implemented using corresponding operators (like documented).

* SubqueryOperator
* InOperator
* ExtractOperator

.. code-block:: python

   >>> from pypuppetdb.QueryBuilder import *
   >>> op = InOperator('certname')
   >>> ex = ExtractOperator()
   >>> ex.add_field(str('certname'))
   >>> sub = SubqueryOperator('events')
   >>> sub.add_query(EqualsOperator('status', 'noop'))
   >>> ex.add_query(sub)
   >>> op.add_query(ex)
   >>> print(op)
   ["in","certname",["extract",["certname"],["select_events",["=", "status", "noop"]]]]

Or using in \<array\> querying:

.. code-block:: python

   >>> from pypuppetdb.QueryBuilder import *
   >>> op = InOperator('certname')
   >>> op.add_array(["prod1.server.net", "prod2.server.net"])
   >>> print(op)
   ["in","certname",["array", ['prod1.server.net', 'prod2.server.net']]]


You can also access different entities from a single query on the root
endpoint with the FromOperator:

.. code-block:: python

   >>> op = InOperator('certname')
   >>> ex = ExtractOperator()
   >>> ex.add_field('certname')
   >>> fr = FromOperator('fact_contents')
   >>> nd = AndOperator()
   >>> nd.add(EqualsOperator("path", ["networking", "eth0", "macaddresses", 0]))
   >>> nd.add(EqualsOperator("value", "aa:bb:cc:dd:ee:00"))
   >>> ex.add_query(nd)
   >>> fr.add_query(ex)
   >>> op.add_query(fr)
   >>> print(op)
   ["in","certname",["from","fact_contents",["extract",["certname"],["and",["=", "path", ['networking', 'eth0', 'macaddresses', 0]],["=", "value", "aa:bb:cc:dd:ee:00"]]]]]