besapi 4.1.4__tar.gz → 4.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {besapi-4.1.4/src/besapi.egg-info → besapi-4.2.0}/PKG-INFO +86 -50
- besapi-4.2.0/README.md +187 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/besapi/besapi.py +32 -2
- {besapi-4.1.4 → besapi-4.2.0}/src/besapi/plugin_utilities.py +86 -11
- besapi-4.2.0/src/besapi/plugin_utilities_linux.py +266 -0
- {besapi-4.1.4 → besapi-4.2.0/src/besapi.egg-info}/PKG-INFO +86 -50
- {besapi-4.1.4 → besapi-4.2.0}/src/besapi.egg-info/SOURCES.txt +2 -0
- besapi-4.2.0/tests/test_plugin_utilities.py +125 -0
- besapi-4.2.0/tests/test_plugin_utilities_linux.py +268 -0
- besapi-4.1.4/README.md +0 -151
- besapi-4.1.4/tests/test_plugin_utilities.py +0 -60
- {besapi-4.1.4 → besapi-4.2.0}/LICENSE.txt +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/MANIFEST.in +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/pyproject.toml +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/setup.cfg +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/setup.py +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/besapi/__init__.py +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/besapi/__main__.py +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/besapi/plugin_utilities_win.py +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/besapi/schemas/BES.xsd +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/besapi/schemas/BESAPI.xsd +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/besapi/schemas/BESActionSettings.xsd +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/besapi.egg-info/dependency_links.txt +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/besapi.egg-info/requires.txt +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/besapi.egg-info/top_level.txt +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/bescli/__init__.py +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/bescli/__main__.py +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/src/bescli/bescli.py +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/tests/test_besapi.py +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/tests/test_plugin_utilities_logging.py +0 -0
- {besapi-4.1.4 → besapi-4.2.0}/tests/tests.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: besapi
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: Library for working with the BigFix REST API
|
|
5
5
|
Home-page: https://github.com/jgstew/besapi
|
|
6
6
|
Author: Matt Hansen, James Stewart
|
|
@@ -30,25 +30,38 @@ Dynamic: license-file
|
|
|
30
30
|
|
|
31
31
|
# besapi
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
[](https://pypi.org/project/besapi/)
|
|
34
|
+
[](LICENSE.txt)
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
**besapi** is a Python library that makes it easy to work with the [BigFix REST API](https://developer.bigfix.com/rest-api/api/). It handles authentication, requests, and XML parsing for you, so you can focus on automating your BigFix environment instead of wrangling raw HTTP and XML.
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
It also includes **bescli**, an interactive command-line interface for exploring the REST API — great for trying things out before writing any code.
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
## Installation
|
|
40
41
|
|
|
42
|
+
```bash
|
|
43
|
+
pip install besapi
|
|
41
44
|
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
Connect to your BigFix root server and start making requests:
|
|
49
|
+
|
|
50
|
+
```python
|
|
42
51
|
import besapi
|
|
43
|
-
b = besapi.besapi.BESConnection('my_username', 'my_password', 'https://rootserver.domain.org:52311')
|
|
44
|
-
rr = b.get('sites')
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
b = besapi.besapi.BESConnection(
|
|
54
|
+
"my_username", "my_password", "https://rootserver.domain.org:52311"
|
|
55
|
+
)
|
|
56
|
+
rr = b.get("sites")
|
|
57
|
+
|
|
58
|
+
# Every request returns a RESTResult with several handy views of the response:
|
|
59
|
+
# rr.request - the original requests object
|
|
60
|
+
# rr.text - the raw text returned by the server
|
|
61
|
+
# rr.besxml - the response as an XML string
|
|
62
|
+
# rr.besobj - the response as an lxml.objectify.ObjectifiedElement
|
|
50
63
|
|
|
51
|
-
|
|
64
|
+
print(rr)
|
|
52
65
|
```
|
|
53
66
|
|
|
54
67
|
```xml
|
|
@@ -78,48 +91,60 @@ rr = b.get('sites')
|
|
|
78
91
|
</BESAPI>
|
|
79
92
|
```
|
|
80
93
|
|
|
81
|
-
|
|
82
|
-
|
|
94
|
+
### Working with results
|
|
95
|
+
|
|
96
|
+
The `besobj` attribute lets you navigate the XML response like a regular Python object:
|
|
97
|
+
|
|
98
|
+
```pycon
|
|
99
|
+
>>> rr.besobj.attrib
|
|
83
100
|
{'{http://www.w3.org/2001/XMLSchema-instance}noNamespaceSchemaLocation': 'BESAPI.xsd'}
|
|
84
101
|
|
|
85
|
-
>>>rr.besobj.ActionSite.attrib
|
|
102
|
+
>>> rr.besobj.ActionSite.attrib
|
|
86
103
|
{'Resource': 'http://rootserver.domain.org:52311/api/site/master'}
|
|
87
104
|
|
|
88
|
-
>>>rr.besobj.ActionSite.attrib[
|
|
105
|
+
>>> rr.besobj.ActionSite.attrib["Resource"]
|
|
89
106
|
'http://rootserver.domain.org:52311/api/site/master'
|
|
90
107
|
|
|
91
|
-
>>>rr.besobj.ActionSite.Name
|
|
108
|
+
>>> rr.besobj.ActionSite.Name
|
|
92
109
|
'ActionSite'
|
|
93
110
|
|
|
94
|
-
>>>rr.besobj.OperatorSite.Name
|
|
111
|
+
>>> rr.besobj.OperatorSite.Name
|
|
95
112
|
'mah60'
|
|
96
113
|
|
|
97
|
-
>>>for cSite in rr.besobj.CustomSite:
|
|
98
|
-
... print
|
|
114
|
+
>>> for cSite in rr.besobj.CustomSite:
|
|
115
|
+
... print(cSite.Name)
|
|
116
|
+
...
|
|
99
117
|
Org
|
|
100
118
|
Org/Mac
|
|
101
119
|
Org/Windows
|
|
102
120
|
ContentDev
|
|
103
|
-
|
|
121
|
+
```
|
|
104
122
|
|
|
105
|
-
|
|
106
|
-
>>>with open('/Users/Shared/Test.bes", "wb") as file:
|
|
107
|
-
... file.write(rr.text)
|
|
123
|
+
### Downloading, uploading, and deleting content
|
|
108
124
|
|
|
109
|
-
|
|
125
|
+
```python
|
|
126
|
+
# save a task to a file:
|
|
127
|
+
rr = b.get("task/operator/mah60/823975")
|
|
128
|
+
with open("/Users/Shared/Test.bes", "wb") as bes_file:
|
|
129
|
+
bes_file.write(rr.text.encode("utf-8"))
|
|
110
130
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
131
|
+
# delete a task:
|
|
132
|
+
b.delete("task/operator/mah60/823975")
|
|
133
|
+
|
|
134
|
+
# create or update a task from a file:
|
|
135
|
+
with open("/Users/Shared/Test.bes") as bes_file:
|
|
136
|
+
b.post("tasks/operator/mah60", bes_file)
|
|
137
|
+
b.put("task/operator/mah60/823975", bes_file)
|
|
114
138
|
```
|
|
115
139
|
|
|
116
|
-
|
|
140
|
+
Looking for more? The [examples folder](https://github.com/jgstew/besapi/tree/master/examples) has ready-to-run scripts for many common tasks — exporting sites, importing content, taking actions, running session relevance, and more.
|
|
141
|
+
|
|
142
|
+
## Command-Line Interface
|
|
143
|
+
|
|
144
|
+
The included `bescli` interactive shell is the fastest way to poke around the REST API:
|
|
117
145
|
|
|
118
146
|
```
|
|
119
|
-
$ python bescli
|
|
120
|
-
OR
|
|
121
|
-
>>> import bescli
|
|
122
|
-
>>> bescli.main()
|
|
147
|
+
$ python -m bescli
|
|
123
148
|
|
|
124
149
|
BigFix> login
|
|
125
150
|
User [mah60]: mah60
|
|
@@ -141,20 +166,17 @@ BigFix> get fixlets/operator/mah60
|
|
|
141
166
|
...
|
|
142
167
|
```
|
|
143
168
|
|
|
144
|
-
|
|
169
|
+
## Requirements
|
|
145
170
|
|
|
146
|
-
-
|
|
147
|
-
-
|
|
148
|
-
|
|
149
|
-
|
|
171
|
+
- Python 3.9 or later
|
|
172
|
+
- besapi 1.1.3 was the last version with partial Python 2 support
|
|
173
|
+
- [lxml](https://pypi.org/project/lxml/)
|
|
174
|
+
- [requests](https://pypi.org/project/requests/)
|
|
175
|
+
- [cmd2](https://pypi.org/project/cmd2/) (for the CLI)
|
|
150
176
|
|
|
151
|
-
|
|
152
|
-
- version 1.1.3 of besapi was the last to have partial python2 support
|
|
153
|
-
- lxml
|
|
154
|
-
- requests
|
|
155
|
-
- cmd2
|
|
177
|
+
All dependencies are installed automatically by pip.
|
|
156
178
|
|
|
157
|
-
|
|
179
|
+
## More Examples Using besapi
|
|
158
180
|
|
|
159
181
|
- https://github.com/jgstew/besapi/tree/master/examples
|
|
160
182
|
- https://github.com/jgstew/generate_bes_from_template/blob/master/examples/generate_uninstallers.py
|
|
@@ -162,12 +184,22 @@ BigFix> get fixlets/operator/mah60
|
|
|
162
184
|
- https://github.com/jgstew/jgstew-recipes/blob/main/SharedProcessors/BigFixActioner.py
|
|
163
185
|
- https://github.com/jgstew/jgstew-recipes/blob/main/SharedProcessors/BigFixSessionRelevance.py
|
|
164
186
|
|
|
165
|
-
|
|
187
|
+
## Building a Standalone Binary
|
|
188
|
+
|
|
189
|
+
You can package the CLI into a single executable with [PyInstaller](https://pyinstaller.org/):
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
pyinstaller --clean --collect-all besapi --onefile .\src\bescli\bescli.py
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Note: using UPX to compress the binary only saves about 2MB out of 16MB on Windows.
|
|
196
|
+
|
|
197
|
+
## BigFix REST API Documentation
|
|
166
198
|
|
|
167
|
-
-
|
|
168
|
-
-
|
|
199
|
+
- https://developer.bigfix.com/rest-api/
|
|
200
|
+
- http://bigfix.me/restapi
|
|
169
201
|
|
|
170
|
-
|
|
202
|
+
## Related Items
|
|
171
203
|
|
|
172
204
|
- https://forum.bigfix.com/t/rest-api-python-module/2170
|
|
173
205
|
- https://gist.github.com/hansen-m/58667f370047af92f634
|
|
@@ -176,6 +208,10 @@ BigFix> get fixlets/operator/mah60
|
|
|
176
208
|
- https://forum.bigfix.com/t/query-for-finding-who-deleted-tasks-fixlets/13668/6
|
|
177
209
|
- https://forum.bigfix.com/t/rest-api-java-wrapper/12693
|
|
178
210
|
|
|
179
|
-
|
|
211
|
+
## Contributing
|
|
212
|
+
|
|
213
|
+
Issues and pull requests are welcome! If you have a script built on besapi that others might find useful, consider adding it to the [examples](https://github.com/jgstew/besapi/tree/master/examples).
|
|
214
|
+
|
|
215
|
+
## License
|
|
180
216
|
|
|
181
|
-
|
|
217
|
+
[MIT License](LICENSE.txt)
|
besapi-4.2.0/README.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# besapi
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/besapi/)
|
|
4
|
+
[](LICENSE.txt)
|
|
5
|
+
|
|
6
|
+
**besapi** is a Python library that makes it easy to work with the [BigFix REST API](https://developer.bigfix.com/rest-api/api/). It handles authentication, requests, and XML parsing for you, so you can focus on automating your BigFix environment instead of wrangling raw HTTP and XML.
|
|
7
|
+
|
|
8
|
+
It also includes **bescli**, an interactive command-line interface for exploring the REST API — great for trying things out before writing any code.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install besapi
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
Connect to your BigFix root server and start making requests:
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import besapi
|
|
22
|
+
|
|
23
|
+
b = besapi.besapi.BESConnection(
|
|
24
|
+
"my_username", "my_password", "https://rootserver.domain.org:52311"
|
|
25
|
+
)
|
|
26
|
+
rr = b.get("sites")
|
|
27
|
+
|
|
28
|
+
# Every request returns a RESTResult with several handy views of the response:
|
|
29
|
+
# rr.request - the original requests object
|
|
30
|
+
# rr.text - the raw text returned by the server
|
|
31
|
+
# rr.besxml - the response as an XML string
|
|
32
|
+
# rr.besobj - the response as an lxml.objectify.ObjectifiedElement
|
|
33
|
+
|
|
34
|
+
print(rr)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```xml
|
|
38
|
+
<BESAPI xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="BESAPI.xsd">
|
|
39
|
+
<ExternalSite Resource="http://rootserver.domain.org:52311/api/site/external/BES%20Support">
|
|
40
|
+
<Name>BES Support</Name>
|
|
41
|
+
</ExternalSite>
|
|
42
|
+
<!---...--->
|
|
43
|
+
<CustomSite Resource="http://rootserver.domain.org:52311/api/site/custom/Org">
|
|
44
|
+
<Name>Org</Name>
|
|
45
|
+
</CustomSite>
|
|
46
|
+
<CustomSite Resource="http://rootserver.domain.org:52311/api/site/custom/Org%2fMac">
|
|
47
|
+
<Name>Org/Mac</Name>
|
|
48
|
+
</CustomSite>
|
|
49
|
+
<CustomSite Resource="http://rootserver.domain.org:52311/api/site/custom/Org%2fWindows">
|
|
50
|
+
<Name>Org/Windows</Name>
|
|
51
|
+
</CustomSite>
|
|
52
|
+
<CustomSite Resource="http://rootserver.domain.org:52311/api/site/custom/ContentDev">
|
|
53
|
+
<Name>ContentDev</Name>
|
|
54
|
+
</CustomSite>
|
|
55
|
+
<OperatorSite Resource="http://rootserver.domain.org:52311/api/site/operator/mah60">
|
|
56
|
+
<Name>mah60</Name>
|
|
57
|
+
</OperatorSite>
|
|
58
|
+
<ActionSite Resource="http://rootserver.domain.org:52311/api/site/master">
|
|
59
|
+
<Name>ActionSite</Name>
|
|
60
|
+
</ActionSite>
|
|
61
|
+
</BESAPI>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Working with results
|
|
65
|
+
|
|
66
|
+
The `besobj` attribute lets you navigate the XML response like a regular Python object:
|
|
67
|
+
|
|
68
|
+
```pycon
|
|
69
|
+
>>> rr.besobj.attrib
|
|
70
|
+
{'{http://www.w3.org/2001/XMLSchema-instance}noNamespaceSchemaLocation': 'BESAPI.xsd'}
|
|
71
|
+
|
|
72
|
+
>>> rr.besobj.ActionSite.attrib
|
|
73
|
+
{'Resource': 'http://rootserver.domain.org:52311/api/site/master'}
|
|
74
|
+
|
|
75
|
+
>>> rr.besobj.ActionSite.attrib["Resource"]
|
|
76
|
+
'http://rootserver.domain.org:52311/api/site/master'
|
|
77
|
+
|
|
78
|
+
>>> rr.besobj.ActionSite.Name
|
|
79
|
+
'ActionSite'
|
|
80
|
+
|
|
81
|
+
>>> rr.besobj.OperatorSite.Name
|
|
82
|
+
'mah60'
|
|
83
|
+
|
|
84
|
+
>>> for cSite in rr.besobj.CustomSite:
|
|
85
|
+
... print(cSite.Name)
|
|
86
|
+
...
|
|
87
|
+
Org
|
|
88
|
+
Org/Mac
|
|
89
|
+
Org/Windows
|
|
90
|
+
ContentDev
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Downloading, uploading, and deleting content
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
# save a task to a file:
|
|
97
|
+
rr = b.get("task/operator/mah60/823975")
|
|
98
|
+
with open("/Users/Shared/Test.bes", "wb") as bes_file:
|
|
99
|
+
bes_file.write(rr.text.encode("utf-8"))
|
|
100
|
+
|
|
101
|
+
# delete a task:
|
|
102
|
+
b.delete("task/operator/mah60/823975")
|
|
103
|
+
|
|
104
|
+
# create or update a task from a file:
|
|
105
|
+
with open("/Users/Shared/Test.bes") as bes_file:
|
|
106
|
+
b.post("tasks/operator/mah60", bes_file)
|
|
107
|
+
b.put("task/operator/mah60/823975", bes_file)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Looking for more? The [examples folder](https://github.com/jgstew/besapi/tree/master/examples) has ready-to-run scripts for many common tasks — exporting sites, importing content, taking actions, running session relevance, and more.
|
|
111
|
+
|
|
112
|
+
## Command-Line Interface
|
|
113
|
+
|
|
114
|
+
The included `bescli` interactive shell is the fastest way to poke around the REST API:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
$ python -m bescli
|
|
118
|
+
|
|
119
|
+
BigFix> login
|
|
120
|
+
User [mah60]: mah60
|
|
121
|
+
Root Server (ex. https://server.institution.edu:52311): https://my.company.org:52311
|
|
122
|
+
Password:
|
|
123
|
+
Login Successful!
|
|
124
|
+
BigFix> get help
|
|
125
|
+
...
|
|
126
|
+
BigFix> get sites
|
|
127
|
+
...
|
|
128
|
+
BigFix> get sites.OperatorSite.Name
|
|
129
|
+
mah60
|
|
130
|
+
BigFix> get help/fixlets
|
|
131
|
+
GET:
|
|
132
|
+
/api/fixlets/{site}
|
|
133
|
+
POST:
|
|
134
|
+
/api/fixlets/{site}
|
|
135
|
+
BigFix> get fixlets/operator/mah60
|
|
136
|
+
...
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Requirements
|
|
140
|
+
|
|
141
|
+
- Python 3.9 or later
|
|
142
|
+
- besapi 1.1.3 was the last version with partial Python 2 support
|
|
143
|
+
- [lxml](https://pypi.org/project/lxml/)
|
|
144
|
+
- [requests](https://pypi.org/project/requests/)
|
|
145
|
+
- [cmd2](https://pypi.org/project/cmd2/) (for the CLI)
|
|
146
|
+
|
|
147
|
+
All dependencies are installed automatically by pip.
|
|
148
|
+
|
|
149
|
+
## More Examples Using besapi
|
|
150
|
+
|
|
151
|
+
- https://github.com/jgstew/besapi/tree/master/examples
|
|
152
|
+
- https://github.com/jgstew/generate_bes_from_template/blob/master/examples/generate_uninstallers.py
|
|
153
|
+
- https://github.com/jgstew/jgstew-recipes/blob/main/SharedProcessors/BESImport.py
|
|
154
|
+
- https://github.com/jgstew/jgstew-recipes/blob/main/SharedProcessors/BigFixActioner.py
|
|
155
|
+
- https://github.com/jgstew/jgstew-recipes/blob/main/SharedProcessors/BigFixSessionRelevance.py
|
|
156
|
+
|
|
157
|
+
## Building a Standalone Binary
|
|
158
|
+
|
|
159
|
+
You can package the CLI into a single executable with [PyInstaller](https://pyinstaller.org/):
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
pyinstaller --clean --collect-all besapi --onefile .\src\bescli\bescli.py
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Note: using UPX to compress the binary only saves about 2MB out of 16MB on Windows.
|
|
166
|
+
|
|
167
|
+
## BigFix REST API Documentation
|
|
168
|
+
|
|
169
|
+
- https://developer.bigfix.com/rest-api/
|
|
170
|
+
- http://bigfix.me/restapi
|
|
171
|
+
|
|
172
|
+
## Related Items
|
|
173
|
+
|
|
174
|
+
- https://forum.bigfix.com/t/rest-api-python-module/2170
|
|
175
|
+
- https://gist.github.com/hansen-m/58667f370047af92f634
|
|
176
|
+
- https://docs.google.com/presentation/d/1pME28wdjkzj9378py9QjFyMOyOHcamB6bk4k8z-c-r0/edit#slide=id.g69e753e75_039
|
|
177
|
+
- https://forum.bigfix.com/t/bigfix-documentation-resources/12540
|
|
178
|
+
- https://forum.bigfix.com/t/query-for-finding-who-deleted-tasks-fixlets/13668/6
|
|
179
|
+
- https://forum.bigfix.com/t/rest-api-java-wrapper/12693
|
|
180
|
+
|
|
181
|
+
## Contributing
|
|
182
|
+
|
|
183
|
+
Issues and pull requests are welcome! If you have a script built on besapi that others might find useful, consider adding it to the [examples](https://github.com/jgstew/besapi/tree/master/examples).
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
[MIT License](LICENSE.txt)
|
|
@@ -29,7 +29,7 @@ import lxml.objectify
|
|
|
29
29
|
import requests
|
|
30
30
|
import urllib3.poolmanager
|
|
31
31
|
|
|
32
|
-
__version__ = "4.
|
|
32
|
+
__version__ = "4.2.0"
|
|
33
33
|
|
|
34
34
|
besapi_logger = logging.getLogger("besapi")
|
|
35
35
|
|
|
@@ -249,6 +249,21 @@ def validate_xml_bes_file(file_path):
|
|
|
249
249
|
return validate_xsd(file_data)
|
|
250
250
|
|
|
251
251
|
|
|
252
|
+
def xml_elem_strip_namespace(elem):
|
|
253
|
+
"""Strip namespace."""
|
|
254
|
+
clean_elem = lxml.etree.Element(elem.tag, attrib=elem.attrib)
|
|
255
|
+
|
|
256
|
+
# Move the inner text and children over to the fresh element
|
|
257
|
+
clean_elem.text = elem.text
|
|
258
|
+
|
|
259
|
+
for child in elem:
|
|
260
|
+
# Create a true, independent copy of the child element
|
|
261
|
+
child_copy = xml_elem_strip_namespace(child)
|
|
262
|
+
clean_elem.append(child_copy)
|
|
263
|
+
|
|
264
|
+
return clean_elem
|
|
265
|
+
|
|
266
|
+
|
|
252
267
|
def action_xml_from_bes_file(file_path, targets="<AllComputers>"):
|
|
253
268
|
"""Create action XML from bes file with fixlet or task or singleaction."""
|
|
254
269
|
# default to empty string:
|
|
@@ -318,6 +333,21 @@ def action_xml_from_bes_file(file_path, targets="<AllComputers>"):
|
|
|
318
333
|
|
|
319
334
|
logging.debug("Relevance Combined: %s", relevance_clauses_combined)
|
|
320
335
|
|
|
336
|
+
# get settings if found
|
|
337
|
+
settings_xml_string = ""
|
|
338
|
+
try:
|
|
339
|
+
if bes_type != "SingleAction":
|
|
340
|
+
settings_xml = tree.xpath(f"//BES/{bes_type}/DefaultAction/Settings")[0]
|
|
341
|
+
else:
|
|
342
|
+
settings_xml = tree.xpath(f"//BES/{bes_type}/Settings")[0]
|
|
343
|
+
|
|
344
|
+
# get_settings_xml = xml_elem_strip_namespace(get_settings_xml)
|
|
345
|
+
settings_xml_string = lxml.etree.tostring(
|
|
346
|
+
settings_xml, encoding="utf-8"
|
|
347
|
+
).decode("utf-8")
|
|
348
|
+
except IndexError:
|
|
349
|
+
pass
|
|
350
|
+
|
|
321
351
|
action_xml = f"""<?xml version="1.0" encoding="UTF-8"?>
|
|
322
352
|
<BES xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="BES.xsd">
|
|
323
353
|
<SingleAction>
|
|
@@ -326,7 +356,7 @@ def action_xml_from_bes_file(file_path, targets="<AllComputers>"):
|
|
|
326
356
|
<ActionScript MIMEType="application/x-Fixlet-Windows-Shell"><![CDATA[// Start:
|
|
327
357
|
{actionscript}
|
|
328
358
|
// End]]></ActionScript>
|
|
329
|
-
<SuccessCriteria Option="{success_criteria}">{custom_relevance_xml}</SuccessCriteria>
|
|
359
|
+
<SuccessCriteria Option="{success_criteria}">{custom_relevance_xml}</SuccessCriteria>{settings_xml_string}
|
|
330
360
|
<Target>
|
|
331
361
|
{get_target_xml(targets)}
|
|
332
362
|
</Target>
|
|
@@ -14,8 +14,24 @@ from typing import Union
|
|
|
14
14
|
|
|
15
15
|
import besapi
|
|
16
16
|
|
|
17
|
-
if
|
|
18
|
-
|
|
17
|
+
# the platform specific root server utilities module, or None if unavailable.
|
|
18
|
+
# NOTE: these are conveniences for plugins running on a root server,
|
|
19
|
+
# so a failure to import must never prevent the other connection methods:
|
|
20
|
+
PLATFORM_UTILITIES = None
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
if os.name == "nt":
|
|
24
|
+
import besapi.plugin_utilities_win
|
|
25
|
+
|
|
26
|
+
PLATFORM_UTILITIES = besapi.plugin_utilities_win
|
|
27
|
+
# NOTE: linux only, not all posix. macOS is never a root server,
|
|
28
|
+
# so none of these files will be in place there:
|
|
29
|
+
elif sys.platform.startswith("linux"):
|
|
30
|
+
import besapi.plugin_utilities_linux
|
|
31
|
+
|
|
32
|
+
PLATFORM_UTILITIES = besapi.plugin_utilities_linux
|
|
33
|
+
except BaseException as import_error: # pylint: disable=broad-exception-caught
|
|
34
|
+
logging.debug("platform specific plugin utilities unavailable: %s", import_error)
|
|
19
35
|
|
|
20
36
|
|
|
21
37
|
# NOTE: This does not work as expected when run from plugin_utilities
|
|
@@ -157,6 +173,64 @@ def get_besapi_connection_env_then_config():
|
|
|
157
173
|
return bes_conn
|
|
158
174
|
|
|
159
175
|
|
|
176
|
+
def _try_platform_utility(function_name: str):
|
|
177
|
+
"""Call a function from the platform specific utilities module, if possible.
|
|
178
|
+
|
|
179
|
+
These are best effort conveniences for the case where the plugin happens to
|
|
180
|
+
be running on a root server. If anything at all goes wrong, this is simply
|
|
181
|
+
not a usable root server, so the caller falls back to the other methods.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
function_name: The name of the function to call, with no arguments.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
Whatever the function returned, or None if it could not be used.
|
|
188
|
+
"""
|
|
189
|
+
if not PLATFORM_UTILITIES:
|
|
190
|
+
logging.debug("no platform specific plugin utilities available.")
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
platform_function = getattr(PLATFORM_UTILITIES, function_name, None)
|
|
194
|
+
|
|
195
|
+
if not platform_function:
|
|
196
|
+
logging.debug(
|
|
197
|
+
"`%s` not found in %s", function_name, PLATFORM_UTILITIES.__name__
|
|
198
|
+
)
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
return platform_function()
|
|
203
|
+
except BaseException as err: # pylint: disable=broad-exception-caught
|
|
204
|
+
# NOTE: intentionally broad, this must never prevent the other methods:
|
|
205
|
+
logging.debug("`%s` failed, ignoring: %s", function_name, err)
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def get_root_server_rest_pass() -> Union[str, None]:
|
|
210
|
+
"""Get the REST API password from the local root server, if this is one.
|
|
211
|
+
|
|
212
|
+
On Windows this reads the registry, otherwise it reads the
|
|
213
|
+
MasterOperatorCredentials file. Returns None if this is not a root server,
|
|
214
|
+
or if the attempt failed for any reason.
|
|
215
|
+
"""
|
|
216
|
+
if os.name == "nt":
|
|
217
|
+
return _try_platform_utility("get_win_registry_rest_pass")
|
|
218
|
+
|
|
219
|
+
return _try_platform_utility("get_linux_credentials_rest_pass")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def get_besconn_root_server() -> Union[besapi.besapi.BESConnection, None]:
|
|
223
|
+
"""Get a connection using local root server credentials, if this is one.
|
|
224
|
+
|
|
225
|
+
Returns None if this is not a root server, or if the attempt failed for
|
|
226
|
+
any reason.
|
|
227
|
+
"""
|
|
228
|
+
if os.name == "nt":
|
|
229
|
+
return _try_platform_utility("get_besconn_root_windows_registry")
|
|
230
|
+
|
|
231
|
+
return _try_platform_utility("get_besconn_root_linux")
|
|
232
|
+
|
|
233
|
+
|
|
160
234
|
def get_besapi_connection_args(
|
|
161
235
|
args: argparse.Namespace,
|
|
162
236
|
) -> Union[besapi.besapi.BESConnection, None]:
|
|
@@ -169,10 +243,9 @@ def get_besapi_connection_args(
|
|
|
169
243
|
|
|
170
244
|
# if user was provided as arg but password was not:
|
|
171
245
|
if args.user and not password:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
password = besapi.plugin_utilities_win.get_win_registry_rest_pass()
|
|
246
|
+
# attempt to get password from the local root server:
|
|
247
|
+
# this is specifically for the case where user is provided for a plugin
|
|
248
|
+
password = get_root_server_rest_pass()
|
|
176
249
|
|
|
177
250
|
# if user was provided as arg but password was not:
|
|
178
251
|
if args.user and not password:
|
|
@@ -242,6 +315,8 @@ def get_besapi_connection(
|
|
|
242
315
|
"""Get connection to besapi.
|
|
243
316
|
|
|
244
317
|
If on Windows, will attempt to get connection from Windows Registry first.
|
|
318
|
+
If not on Windows, will attempt to get connection from the root server
|
|
319
|
+
MasterOperatorCredentials file first.
|
|
245
320
|
If args provided, will attempt to get connection using provided args.
|
|
246
321
|
If no args provided, will attempt to get connection from env vars.
|
|
247
322
|
If no env vars, will attempt to get connection from config file.
|
|
@@ -251,11 +326,11 @@ def get_besapi_connection(
|
|
|
251
326
|
Returns:
|
|
252
327
|
A BESConnection object if successful, otherwise None.
|
|
253
328
|
"""
|
|
254
|
-
# if
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
329
|
+
# if this is a root server, try its local credentials first:
|
|
330
|
+
# (windows registry, or the linux MasterOperatorCredentials file)
|
|
331
|
+
bes_conn = get_besconn_root_server()
|
|
332
|
+
if bes_conn:
|
|
333
|
+
return bes_conn
|
|
259
334
|
|
|
260
335
|
# if no args provided, try to get connection from env then config file:
|
|
261
336
|
if not args:
|