besapi 3.7.8__tar.gz → 3.8.1__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-3.7.8/src/besapi.egg-info → besapi-3.8.1}/PKG-INFO +1 -1
- {besapi-3.7.8 → besapi-3.8.1}/src/besapi/besapi.py +93 -7
- {besapi-3.7.8 → besapi-3.8.1}/src/besapi/plugin_utilities.py +1 -1
- {besapi-3.7.8 → besapi-3.8.1/src/besapi.egg-info}/PKG-INFO +1 -1
- {besapi-3.7.8 → besapi-3.8.1}/tests/tests.py +35 -2
- {besapi-3.7.8 → besapi-3.8.1}/LICENSE.txt +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/MANIFEST.in +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/README.md +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/pyproject.toml +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/setup.cfg +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/setup.py +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/src/besapi/__init__.py +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/src/besapi/__main__.py +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/src/besapi/schemas/BES.xsd +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/src/besapi/schemas/BESAPI.xsd +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/src/besapi/schemas/BESActionSettings.xsd +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/src/besapi.egg-info/SOURCES.txt +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/src/besapi.egg-info/dependency_links.txt +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/src/besapi.egg-info/requires.txt +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/src/besapi.egg-info/top_level.txt +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/src/bescli/__init__.py +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/src/bescli/__main__.py +0 -0
- {besapi-3.7.8 → besapi-3.8.1}/src/bescli/bescli.py +0 -0
|
@@ -12,13 +12,13 @@ Library for communicating with the BES (BigFix) REST API.
|
|
|
12
12
|
import configparser
|
|
13
13
|
import datetime
|
|
14
14
|
import hashlib
|
|
15
|
-
import io
|
|
16
15
|
import json
|
|
17
16
|
import logging
|
|
18
17
|
import os
|
|
19
18
|
import random
|
|
20
19
|
import site
|
|
21
20
|
import string
|
|
21
|
+
import typing
|
|
22
22
|
|
|
23
23
|
try:
|
|
24
24
|
from urllib import parse
|
|
@@ -29,7 +29,7 @@ import requests
|
|
|
29
29
|
from lxml import etree, objectify
|
|
30
30
|
from pkg_resources import resource_filename
|
|
31
31
|
|
|
32
|
-
__version__ = "3.
|
|
32
|
+
__version__ = "3.8.1"
|
|
33
33
|
|
|
34
34
|
besapi_logger = logging.getLogger("besapi")
|
|
35
35
|
|
|
@@ -94,7 +94,7 @@ def elem2dict(node):
|
|
|
94
94
|
def replace_text_between(
|
|
95
95
|
original_text, first_delimiter, second_delimiter, replacement_text
|
|
96
96
|
):
|
|
97
|
-
"""Replace text between
|
|
97
|
+
"""Replace text between delimiters. Each delimiter should only appear once."""
|
|
98
98
|
leading_text = original_text.split(first_delimiter)[0]
|
|
99
99
|
trailing_text = original_text.split(second_delimiter)[1]
|
|
100
100
|
|
|
@@ -120,7 +120,7 @@ def parse_bes_modtime(string_datetime):
|
|
|
120
120
|
# """custom HTTPAdapter for requests to override blocksize
|
|
121
121
|
# for Uploading or Downloading large files"""
|
|
122
122
|
|
|
123
|
-
# # override
|
|
123
|
+
# # override init_poolmanager from regular HTTPAdapter
|
|
124
124
|
# # https://stackoverflow.com/questions/22915295/python-requests-post-and-big-content/22915488#comment125583017_22915488
|
|
125
125
|
# def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs):
|
|
126
126
|
# """Initializes a urllib3 PoolManager.
|
|
@@ -152,6 +152,92 @@ def parse_bes_modtime(string_datetime):
|
|
|
152
152
|
# )
|
|
153
153
|
|
|
154
154
|
|
|
155
|
+
def get_action_combined_relevance(relevances: typing.List[str]):
|
|
156
|
+
"""take array of ordered relevance clauses and return relevance string for action"""
|
|
157
|
+
|
|
158
|
+
relevance_combined = ""
|
|
159
|
+
|
|
160
|
+
if not relevances:
|
|
161
|
+
return "False"
|
|
162
|
+
if len(relevances) == 0:
|
|
163
|
+
return "False"
|
|
164
|
+
if len(relevances) == 1:
|
|
165
|
+
return relevances[0]
|
|
166
|
+
if len(relevances) > 0:
|
|
167
|
+
for clause in relevances:
|
|
168
|
+
if len(relevance_combined) == 0:
|
|
169
|
+
relevance_combined = clause
|
|
170
|
+
else:
|
|
171
|
+
relevance_combined = (
|
|
172
|
+
"( " + relevance_combined + " ) AND ( " + clause + " )"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
return relevance_combined
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def get_target_xml(targets=None):
|
|
179
|
+
"""get target xml based upon input
|
|
180
|
+
|
|
181
|
+
Input can be a single string:
|
|
182
|
+
- starts with "<AllComputers>" if all computers should be targeted
|
|
183
|
+
- Otherwise will be interpreted as custom relevance
|
|
184
|
+
|
|
185
|
+
Input can be a single int:
|
|
186
|
+
- Single Computer ID Target
|
|
187
|
+
|
|
188
|
+
Input can be an array:
|
|
189
|
+
- Array of Strings: ComputerName
|
|
190
|
+
- Array of Integers: ComputerID
|
|
191
|
+
"""
|
|
192
|
+
if targets is None or not targets:
|
|
193
|
+
logging.warning("No valid targeting found, will target no computers.")
|
|
194
|
+
# default if invalid:
|
|
195
|
+
return "<CustomRelevance>False</CustomRelevance>"
|
|
196
|
+
|
|
197
|
+
# if targets is int:
|
|
198
|
+
if isinstance(targets, int):
|
|
199
|
+
if targets == 0:
|
|
200
|
+
raise ValueError(
|
|
201
|
+
"Int 0 is not valid Computer ID, set targets to an array of strings of computer names or an array of ints of computer ids or custom relevance string or <AllComputers>"
|
|
202
|
+
)
|
|
203
|
+
return f"<ComputerID>{targets}</ComputerID>"
|
|
204
|
+
|
|
205
|
+
# if targets is str:
|
|
206
|
+
if isinstance(targets, str):
|
|
207
|
+
# if targets string starts with "<AllComputers>":
|
|
208
|
+
if targets.startswith("<AllComputers>"):
|
|
209
|
+
if "false" in targets.lower():
|
|
210
|
+
# In my testing, <AllComputers>false</AllComputers> does not work correctly
|
|
211
|
+
return "<CustomRelevance>False</CustomRelevance>"
|
|
212
|
+
# return "<AllComputers>false</AllComputers>"
|
|
213
|
+
return "<AllComputers>true</AllComputers>"
|
|
214
|
+
# treat as custom relevance:
|
|
215
|
+
return f"<CustomRelevance><![CDATA[{targets}]]></CustomRelevance>"
|
|
216
|
+
|
|
217
|
+
# if targets is array:
|
|
218
|
+
if isinstance(targets, list):
|
|
219
|
+
element_type = type(targets[0])
|
|
220
|
+
if element_type is int:
|
|
221
|
+
# array of computer ids
|
|
222
|
+
return (
|
|
223
|
+
"<ComputerID>"
|
|
224
|
+
+ "</ComputerID><ComputerID>".join(map(str, targets))
|
|
225
|
+
+ "</ComputerID>"
|
|
226
|
+
)
|
|
227
|
+
if element_type is str:
|
|
228
|
+
# array of computer names
|
|
229
|
+
return (
|
|
230
|
+
"<ComputerName>"
|
|
231
|
+
+ "</ComputerName><ComputerName>".join(targets)
|
|
232
|
+
+ "</ComputerName>"
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
logging.warning("No valid targeting found, will target no computers.")
|
|
236
|
+
|
|
237
|
+
# default if invalid:
|
|
238
|
+
return "<CustomRelevance>False</CustomRelevance>"
|
|
239
|
+
|
|
240
|
+
|
|
155
241
|
def validate_xsd(doc):
|
|
156
242
|
"""validate results using XML XSDs"""
|
|
157
243
|
try:
|
|
@@ -257,7 +343,7 @@ class BESConnection:
|
|
|
257
343
|
def __repr__(self):
|
|
258
344
|
"""object representation"""
|
|
259
345
|
# https://stackoverflow.com/a/2626364/861745
|
|
260
|
-
return f"Object: besapi.
|
|
346
|
+
return f"Object: besapi.BESConnection( username={self.username}, rootserver={self.rootserver} )"
|
|
261
347
|
|
|
262
348
|
def __eq__(self, other):
|
|
263
349
|
if (
|
|
@@ -669,7 +755,7 @@ class BESConnection:
|
|
|
669
755
|
file_url = str(result_upload.besobj.FileUpload.URL)
|
|
670
756
|
if use_https:
|
|
671
757
|
file_url = file_url.replace("http://", "https://")
|
|
672
|
-
# there are 3 different
|
|
758
|
+
# there are 3 different possibilities for the server FQDN
|
|
673
759
|
# localhost
|
|
674
760
|
# self.rootserver (without port number)
|
|
675
761
|
# the returned value from the upload result
|
|
@@ -927,7 +1013,7 @@ class RESTResult:
|
|
|
927
1013
|
f"\n - HTTP Response Status Code: `403` Forbidden\n - ERROR: `{self.text}`\n - URL: `{self.request.url}`"
|
|
928
1014
|
)
|
|
929
1015
|
|
|
930
|
-
besapi_logger.
|
|
1016
|
+
besapi_logger.debug(
|
|
931
1017
|
"HTTP Request Status Code `%d` from URL `%s`",
|
|
932
1018
|
self.request.status_code,
|
|
933
1019
|
self.request.url,
|
|
@@ -58,7 +58,7 @@ def get_invoke_file_name(verbose=0):
|
|
|
58
58
|
def setup_plugin_argparse(plugin_args_required=False):
|
|
59
59
|
"""setup argparse for plugin use"""
|
|
60
60
|
arg_parser = argparse.ArgumentParser(
|
|
61
|
-
description="
|
|
61
|
+
description="Provide command line arguments for REST URL, username, and password"
|
|
62
62
|
)
|
|
63
63
|
arg_parser.add_argument(
|
|
64
64
|
"-v",
|
|
@@ -91,6 +91,37 @@ assert b"<BES>Example</BES>" in rest_result.xmlparse_text("<BES>Example</BES>")
|
|
|
91
91
|
|
|
92
92
|
assert rest_result.text == "this is just a test"
|
|
93
93
|
|
|
94
|
+
# test date parsing function:
|
|
95
|
+
assert 2017 == besapi.besapi.parse_bes_modtime("Tue, 05 Sep 2017 23:31:48 +0000").year
|
|
96
|
+
|
|
97
|
+
# test action combined relevance
|
|
98
|
+
assert (
|
|
99
|
+
"( ( True ) AND ( windows of operating system ) ) AND ( False )"
|
|
100
|
+
== besapi.besapi.get_action_combined_relevance(
|
|
101
|
+
["True", "windows of operating system", "False"]
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# test target xml
|
|
106
|
+
assert "<CustomRelevance>False</CustomRelevance>" == besapi.besapi.get_target_xml()
|
|
107
|
+
assert "<AllComputers>true</AllComputers>" == besapi.besapi.get_target_xml(
|
|
108
|
+
"<AllComputers>"
|
|
109
|
+
)
|
|
110
|
+
assert "<ComputerID>1</ComputerID>" == besapi.besapi.get_target_xml(1)
|
|
111
|
+
assert (
|
|
112
|
+
"<CustomRelevance><![CDATA[not windows of operating system]]></CustomRelevance>"
|
|
113
|
+
== besapi.besapi.get_target_xml("not windows of operating system")
|
|
114
|
+
)
|
|
115
|
+
assert (
|
|
116
|
+
"<ComputerID>1</ComputerID><ComputerID>2</ComputerID>"
|
|
117
|
+
== besapi.besapi.get_target_xml([1, 2])
|
|
118
|
+
)
|
|
119
|
+
assert (
|
|
120
|
+
"<ComputerName>Computer 1</ComputerName><ComputerName>Another Computer</ComputerName>"
|
|
121
|
+
== besapi.besapi.get_target_xml(["Computer 1", "Another Computer"])
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# test bescli:
|
|
94
125
|
import bescli
|
|
95
126
|
|
|
96
127
|
bigfix_cli = bescli.bescli.BESCLInterface()
|
|
@@ -151,8 +182,10 @@ if bigfix_cli.bes_conn:
|
|
|
151
182
|
'CMD /C python -m besapi ls clear ls conf "query number of bes computers" version error_count exit',
|
|
152
183
|
check=True,
|
|
153
184
|
)
|
|
154
|
-
|
|
155
|
-
|
|
185
|
+
|
|
186
|
+
bes_conn = besapi.besapi.get_bes_conn_using_config_file()
|
|
187
|
+
print("login succeeded:", bes_conn.login())
|
|
188
|
+
assert bes_conn.login()
|
|
156
189
|
|
|
157
190
|
# test plugin_utilities:
|
|
158
191
|
print(besapi.plugin_utilities.get_invoke_folder())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|