File: api.md

package info (click to toggle)
bundlewrap 4.24.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,216 kB
  • sloc: python: 20,299; makefile: 2
file content (328 lines) | stat: -rw-r--r-- 8,854 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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# API

While most users will interact with BundleWrap through the `bw` command line utility, you can also use it from your own code to extract data or further automate config management tasks.

Even within BundleWrap itself (e.g. templates, libs, and hooks) you are often given repo and/or node objects to work with. Their methods and attributes are documented below.

Some general notes on using BundleWrap's API:

* There can be an arbitrary amount of `bundlewrap.repo.Repository` objects per process.
* Repositories are read as needed and not re-read when something changes. Modifying files in a repo during the lifetime of the matching Repository object may result in undefined behavior.

<br>

## Example

Here's a short example of how to use BundleWrap to get the uptime for a node.

	from bundlewrap.repo import Repository

	repo = Repository("/path/to/my/repo")
	node = repo.get_node("mynode")
	uptime = node.run("uptime")
	print(uptime.stdout)

<br>

## Reference


### bundlewrap.repo.Repository(path)

The starting point of any interaction with BundleWrap. An object of this class represents the repository at the given path. `path` can be a subpath of your repository (e.g., `bundles/nginx/`) and will internally be resolved to the root path of said repository.

<br>

**`.branch`**

The current git branch of this repo. `None` if not in a git repo.

<br>

**`.clean`**

Boolean indicating if there are uncommitted changes in git. `None` if not in a git repo.

<br>

**`.groups`**

A list of all groups in the repo (instances of `bundlewrap.group.Group`)


<br>

**`.nodes`**

A list of all nodes in the repo (instances of `bundlewrap.node.Node`)

<br>

**`.revision`**

The current git, hg or bzr revision of this repo. `None` if no SCM was detected.

<br>

**`.get_group(group_name)`**

Returns the Group object for the given name.

<br>

**`.get_node(node_name)`**

Returns the Node object with the given name.

<br>

**`.nodes_in_all_groups(group_names)`**

Returns a list of Node objects where every node is a member of every group name given.

<br>

**`.nodes_in_any_group(group_names)`**

Returns all Node objects that are a member of at least one of the given group names.

<br>

**`.nodes_in_group(group_name)`**

Returns a list of Node objects in the named group.

<br>

**`.nodes_not_in_group(group_name)`**

Returns a list of Node objects not in the named group.

<br>

**`.nodes_with_bundle(bundle_name)`**

Returns a list of Node objects that do have the named bundle.

<br>

**`.nodes_without_bundle(bundle_name)`**

Returns a list of Node objects that do not have the named bundle.

<br>

**`.nodes_matching_lambda(self, lambda_str, lambda_workers=None)`**

Returns a list of Node objects matching the lambda.

Example:
```python
nodes = repo.nodes_matching_lambda("node.metadata.get('foo/magic', 47) < 3")
```

- `lambda_str` is evaluated as python code with `node` being one of the nodes and expected to return a boolean.
- `lambda_workers` is number of parallel workers used to check lambda condition on every node

<br>

**`.nodes_matching(self, target_strings, lambda_workers=None)`**

Returns a list of nodes matching any of the given target-strings. This is the same API that is used by
all the bw commandlines, i.e. `bw items` or `bw apply` to select which nodes to operate on.

Example:
```python
nodes = repo.nodes_matching("lambda:node.metadata.get('foo/magic', 47) < 3")
nodes = repo.nodes_matching("loc.routers")
nodes = repo.nodes_matching("loc.router-1")
```

- `target_strings` is a list of expression to select target nodes
- `lambda_workers` is number of parallel workers used to check lambda condition on every node

The following expressions can be used:
- `my_node` to select a single node
- `my_group` all nodes in this group
- `bundle:my_bundle` all nodes with this bundle
- `!bundle:my_bundle` all nodes without this bundle
` `!group:my_group` all nodes not in this group
- `"lambda:node.metadata_get('foo/magic', 47) < 3"` all nodes whose `metadata["foo"]["magic"]` is less than three

<br>

### bundlewrap.node.Node()

A system managed by BundleWrap.

<br>

**`.bundles`**

A list of all bundles associated with this node (instances of `bundlewrap.bundle.Bundle`)

<br>

**`.groups`**

A list of `bundlewrap.group.Group` objects this node belongs to

<br>

**`.hostname`**

The DNS name BundleWrap uses to connect to this node

<br>

**`.items`**

A list of items on this node (instances of subclasses of `bundlewrap.items.Item`)

<br>

**`.magic_number`**

A large number derived from the node's name. This number is very likely to be unique for your entire repository. You can, for example, use this number to easily "jitter" cronjobs:

    '{} {} * * * root /my/script'.format(
        node.magic_number % 60,
        node.magic_number % 2 + 4,
    )

<br>

**`.metadata`**

A dictionary of custom metadata, merged from information in [nodes.py](../repo/nodes.py.md) and [groups.py](../repo/groups.py.md)

<br>

**`.name`**

The internal identifier for this node

<br>

**`.download(remote_path, local_path)`**

Downloads a file from the node.

-   `remote_path` Which file to get from the node
-   `local_path` Where to put the file

<br>

**`.get_item(item_id)`**

Get the Item object with the given ID (e.g. "file:/etc/motd").

<br>

**`.has_bundle(bundle_name)`**

`True` if the node has a bundle with the given name.

<br>

**`.has_any_bundle(bundle_names)`**

`True` if the node has a bundle with any of the given names.

<br>

**`.in_group(group_name)`**

`True` if the node is in a group with the given name.

<br>

**`.in_any_group(group_names)`**

`True` if the node is in a group with any of the given names.

<br>

**`.run(command, may_fail=False)`**

Runs a command on the node. Returns an instance of `bundlewrap.operations.RunResult`.

-   `command` What should be executed on the node
-   `may_fail` If `False`, `bundlewrap.exceptions.RemoteException` will be raised if the command does not return 0.

`bundlewrap.exceptions.TransportException` will be raised if there was a transport error while running the command, e.g. the SSH process died unexpectedly.


<br>

**`.run_ipmitool(command)`**

Runs a command on an ipmi interface. Returns an instance of
`bundlewrap.operations.RunResult`. A command is a string of tokens which
gets passed to ipmitool. The command will be split at whitespace
characters and added to the commandline:

    # runs `ipmitool -H hostname -U username -P password power status`
    node.run_ipmitool('power status')

<br>

**`.upload(local_path, remote_path, mode=None, owner="", group="")`**

Uploads a file to the node.

-   `local_path` Which file to upload
-   `remote_path` Where to put the file on the target node
-   `mode` File mode, e.g. "0644"
-   `owner` Username of the file owner
-   `group` Group name of the file group

<br>

### bundlewrap.group.Group

A user-defined group of nodes.

<br>

**`.name`**

The name of this group

<br>

**`.nodes`**

A list of all nodes in this group (instances of `bundlewrap.node.Node`, includes subgroup members)

<br>

### bundlewrap.utils.Fault

A Fault acts as a lazy stand-in object for the result of a given callback function. These objects are returned from the "vault" attached to `Repository` objects:

	>>> repo.vault.password_for("demo")
	<bundlewrap.utils.Fault object at 0x10782b208>

Only when the `value` property of a Fault is accessed or when the Fault is converted to a string, the callback function is executed. In the example above, this means that the password is only generated when it is really required (e.g. when used in a template). This is particularly useful when used in metadata in connection with [secrets](secrets.md). Users will be able to generate metadata with Faults in it, even if they lack the required keys for the decryption operation represented by the Fault. The key will only be required for files etc. that actually use it. If a Fault cannot be resolved (e.g. for lack of the required key), BundleWrap can just skip the item using the Fault, while still allowing other items on the same node to be applied.

Faults also support some rudimentary string operations such as appending a string or another Fault, as well as some string methods:

	>>> f = repo.vault.password_for("1") + ":" + repo.vault.password_for("2")
	>>> f
	<bundlewrap.utils.Fault object at 0x10782b208>
	>>> f.value
	'VOd5PC:JUgYUb'
	>>> f += " "
	>>> f.value
	'VOd5PC:JUgYUb '
	>>> f.strip().value
	'VOd5PC:JUgYUb'
	>>> repo.vault.password_for("1").format_into("Password: {}").value
	'Password: VOd5PC'
	>>> repo.vault.password_for("1").b64encode().value
	'Vk9kNVA='
	>>> repo.vault.password_for("1").as_htpasswd_entry("username").value
	'username:$apr1$8be694c7…'

These string methods are supported on Faults: `format`, `lower`, `lstrip`, `replace`, `rstrip`, `strip`, `upper`, `zfill`