besapi 3.8.2__tar.gz → 3.8.3__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.8.2/src/besapi.egg-info → besapi-3.8.3}/PKG-INFO +9 -13
- {besapi-3.8.2 → besapi-3.8.3}/README.md +1 -1
- besapi-3.8.3/setup.cfg +46 -0
- besapi-3.8.3/setup.py +7 -0
- {besapi-3.8.2 → besapi-3.8.3}/src/besapi/besapi.py +69 -34
- {besapi-3.8.2 → besapi-3.8.3/src/besapi.egg-info}/PKG-INFO +9 -13
- {besapi-3.8.2 → besapi-3.8.3}/src/besapi.egg-info/SOURCES.txt +1 -0
- {besapi-3.8.2 → besapi-3.8.3}/src/bescli/bescli.py +1 -1
- besapi-3.8.3/tests/test_besapi.py +284 -0
- {besapi-3.8.2 → besapi-3.8.3}/tests/tests.py +22 -3
- besapi-3.8.2/setup.cfg +0 -21
- besapi-3.8.2/setup.py +0 -21
- {besapi-3.8.2 → besapi-3.8.3}/LICENSE.txt +0 -0
- {besapi-3.8.2 → besapi-3.8.3}/MANIFEST.in +0 -0
- {besapi-3.8.2 → besapi-3.8.3}/pyproject.toml +0 -0
- {besapi-3.8.2 → besapi-3.8.3}/src/besapi/__init__.py +0 -0
- {besapi-3.8.2 → besapi-3.8.3}/src/besapi/__main__.py +0 -0
- {besapi-3.8.2 → besapi-3.8.3}/src/besapi/plugin_utilities.py +0 -0
- {besapi-3.8.2 → besapi-3.8.3}/src/besapi/schemas/BES.xsd +0 -0
- {besapi-3.8.2 → besapi-3.8.3}/src/besapi/schemas/BESAPI.xsd +0 -0
- {besapi-3.8.2 → besapi-3.8.3}/src/besapi/schemas/BESActionSettings.xsd +0 -0
- {besapi-3.8.2 → besapi-3.8.3}/src/besapi.egg-info/dependency_links.txt +0 -0
- {besapi-3.8.2 → besapi-3.8.3}/src/besapi.egg-info/requires.txt +2 -2
- {besapi-3.8.2 → besapi-3.8.3}/src/besapi.egg-info/top_level.txt +0 -0
- {besapi-3.8.2 → besapi-3.8.3}/src/bescli/__init__.py +0 -0
- {besapi-3.8.2 → besapi-3.8.3}/src/bescli/__main__.py +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: besapi
|
|
3
|
-
Version: 3.8.
|
|
3
|
+
Version: 3.8.3
|
|
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
|
|
7
7
|
Author-email: hansen.m@psu.edu, james@jgstew.com
|
|
8
|
+
Maintainer: James Stewart
|
|
9
|
+
Maintainer-email: james@jgstew.com
|
|
8
10
|
License: MIT
|
|
9
11
|
Keywords: bigfix iem tem rest api
|
|
10
12
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -15,20 +17,14 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
15
17
|
Classifier: Intended Audience :: Developers
|
|
16
18
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
19
|
Classifier: Topic :: System :: Systems Administration
|
|
18
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.9
|
|
19
21
|
Description-Content-Type: text/markdown
|
|
20
22
|
License-File: LICENSE.txt
|
|
21
|
-
Requires-Dist: requests
|
|
22
|
-
Requires-Dist: lxml
|
|
23
23
|
Requires-Dist: cmd2
|
|
24
|
+
Requires-Dist: lxml
|
|
25
|
+
Requires-Dist: requests
|
|
24
26
|
Requires-Dist: setuptools
|
|
25
|
-
Dynamic:
|
|
26
|
-
Dynamic: author-email
|
|
27
|
-
Dynamic: home-page
|
|
28
|
-
Dynamic: keywords
|
|
29
|
-
Dynamic: license
|
|
30
|
-
Dynamic: requires-dist
|
|
31
|
-
Dynamic: summary
|
|
27
|
+
Dynamic: license-file
|
|
32
28
|
|
|
33
29
|
# besapi
|
|
34
30
|
|
|
@@ -42,7 +38,7 @@ Usage:
|
|
|
42
38
|
|
|
43
39
|
```
|
|
44
40
|
import besapi
|
|
45
|
-
b = besapi.BESConnection('my_username', 'my_password', 'https://rootserver.domain.org:52311')
|
|
41
|
+
b = besapi.besapi.BESConnection('my_username', 'my_password', 'https://rootserver.domain.org:52311')
|
|
46
42
|
rr = b.get('sites')
|
|
47
43
|
|
|
48
44
|
# rr.request contains the original request object
|
|
@@ -10,7 +10,7 @@ Usage:
|
|
|
10
10
|
|
|
11
11
|
```
|
|
12
12
|
import besapi
|
|
13
|
-
b = besapi.BESConnection('my_username', 'my_password', 'https://rootserver.domain.org:52311')
|
|
13
|
+
b = besapi.besapi.BESConnection('my_username', 'my_password', 'https://rootserver.domain.org:52311')
|
|
14
14
|
rr = b.get('sites')
|
|
15
15
|
|
|
16
16
|
# rr.request contains the original request object
|
besapi-3.8.3/setup.cfg
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[metadata]
|
|
2
|
+
name = besapi
|
|
3
|
+
author = Matt Hansen, James Stewart
|
|
4
|
+
author_email = hansen.m@psu.edu, james@jgstew.com
|
|
5
|
+
maintainer = James Stewart
|
|
6
|
+
maintainer_email = james@jgstew.com
|
|
7
|
+
version = attr: besapi.besapi.__version__
|
|
8
|
+
description = Library for working with the BigFix REST API
|
|
9
|
+
keywords = bigfix iem tem rest api
|
|
10
|
+
long_description = file: README.md
|
|
11
|
+
long_description_content_type = text/markdown
|
|
12
|
+
url = https://github.com/jgstew/besapi
|
|
13
|
+
license = MIT
|
|
14
|
+
license_file = LICENSE.txt
|
|
15
|
+
classifiers =
|
|
16
|
+
Programming Language :: Python :: 3
|
|
17
|
+
Programming Language :: Python :: 3.9
|
|
18
|
+
Operating System :: OS Independent
|
|
19
|
+
License :: OSI Approved :: MIT License
|
|
20
|
+
Development Status :: 5 - Production/Stable
|
|
21
|
+
Intended Audience :: Developers
|
|
22
|
+
Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Topic :: System :: Systems Administration
|
|
24
|
+
package_dir =
|
|
25
|
+
= src
|
|
26
|
+
|
|
27
|
+
[options]
|
|
28
|
+
python_requires = >=3.9
|
|
29
|
+
include_package_data = True
|
|
30
|
+
packages = find:
|
|
31
|
+
install_requires =
|
|
32
|
+
cmd2
|
|
33
|
+
lxml
|
|
34
|
+
requests
|
|
35
|
+
setuptools
|
|
36
|
+
|
|
37
|
+
[options.package_data]
|
|
38
|
+
besapi = schemas/*.xsd
|
|
39
|
+
|
|
40
|
+
[options.packages.find]
|
|
41
|
+
where = src
|
|
42
|
+
|
|
43
|
+
[egg_info]
|
|
44
|
+
tag_build =
|
|
45
|
+
tag_date = 0
|
|
46
|
+
|
besapi-3.8.3/setup.py
ADDED
|
@@ -12,24 +12,20 @@ Library for communicating with the BES (BigFix) REST API.
|
|
|
12
12
|
import configparser
|
|
13
13
|
import datetime
|
|
14
14
|
import hashlib
|
|
15
|
+
import importlib.resources
|
|
15
16
|
import json
|
|
16
17
|
import logging
|
|
17
18
|
import os
|
|
18
19
|
import random
|
|
19
20
|
import site
|
|
20
21
|
import string
|
|
21
|
-
import
|
|
22
|
-
|
|
23
|
-
try:
|
|
24
|
-
from urllib import parse
|
|
25
|
-
except ImportError:
|
|
26
|
-
from urlparse import parse_qs as parse # type: ignore[no-redef,import-not-found]
|
|
22
|
+
import urllib.parse
|
|
27
23
|
|
|
24
|
+
import lxml.etree
|
|
25
|
+
import lxml.objectify
|
|
28
26
|
import requests
|
|
29
|
-
from lxml import etree, objectify
|
|
30
|
-
from pkg_resources import resource_filename
|
|
31
27
|
|
|
32
|
-
__version__ = "3.8.
|
|
28
|
+
__version__ = "3.8.3"
|
|
33
29
|
|
|
34
30
|
besapi_logger = logging.getLogger("besapi")
|
|
35
31
|
|
|
@@ -48,7 +44,7 @@ def rand_password(length=20):
|
|
|
48
44
|
|
|
49
45
|
def sanitize_txt(*args):
|
|
50
46
|
"""Clean arbitrary text for safe file system usage."""
|
|
51
|
-
valid_chars = "-_.()
|
|
47
|
+
valid_chars = f"-_.() {string.ascii_letters}{string.digits}"
|
|
52
48
|
|
|
53
49
|
sani_args = []
|
|
54
50
|
for arg in args:
|
|
@@ -158,7 +154,7 @@ def parse_bes_modtime(string_datetime):
|
|
|
158
154
|
# )
|
|
159
155
|
|
|
160
156
|
|
|
161
|
-
def get_action_combined_relevance(relevances:
|
|
157
|
+
def get_action_combined_relevance(relevances: list[str]):
|
|
162
158
|
"""Take array of ordered relevance clauses and return relevance string for
|
|
163
159
|
action.
|
|
164
160
|
"""
|
|
@@ -249,17 +245,19 @@ def get_target_xml(targets=None):
|
|
|
249
245
|
def validate_xsd(doc):
|
|
250
246
|
"""Validate results using XML XSDs."""
|
|
251
247
|
try:
|
|
252
|
-
xmldoc = etree.fromstring(doc)
|
|
248
|
+
xmldoc = lxml.etree.fromstring(doc)
|
|
253
249
|
except BaseException: # pylint: disable=broad-except
|
|
254
250
|
return False
|
|
255
251
|
|
|
256
252
|
for xsd in ["BES.xsd", "BESAPI.xsd", "BESActionSettings.xsd"]:
|
|
257
|
-
|
|
253
|
+
schema_path = importlib.resources.files(__package__) / f"schemas/{xsd}"
|
|
254
|
+
with schema_path.open("r") as xsd_file:
|
|
255
|
+
xmlschema_doc = lxml.etree.parse(xsd_file)
|
|
258
256
|
|
|
259
257
|
# one schema may throw an error while another will validate
|
|
260
258
|
try:
|
|
261
|
-
xmlschema = etree.XMLSchema(xmlschema_doc)
|
|
262
|
-
except etree.XMLSchemaParseError as err:
|
|
259
|
+
xmlschema = lxml.etree.XMLSchema(xmlschema_doc)
|
|
260
|
+
except lxml.etree.XMLSchemaParseError as err:
|
|
263
261
|
# this should only error if the XSD itself is malformed
|
|
264
262
|
besapi_logger.error("ERROR with `%s`: %s", xsd, err)
|
|
265
263
|
raise err
|
|
@@ -392,7 +390,7 @@ class BESConnection:
|
|
|
392
390
|
if path.startswith(self.rootserver):
|
|
393
391
|
url = path
|
|
394
392
|
else:
|
|
395
|
-
url = "
|
|
393
|
+
url = f"{self.rootserver}/api/{path}"
|
|
396
394
|
|
|
397
395
|
return url
|
|
398
396
|
|
|
@@ -414,9 +412,9 @@ class BESConnection:
|
|
|
414
412
|
if validate_xml:
|
|
415
413
|
logging.error(err_msg)
|
|
416
414
|
raise ValueError(err_msg)
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
415
|
+
|
|
416
|
+
# this is intended it validate_xml is None, but not used currently
|
|
417
|
+
logging.warning(err_msg)
|
|
420
418
|
|
|
421
419
|
self.last_connected = datetime.datetime.now()
|
|
422
420
|
return RESTResult(
|
|
@@ -435,9 +433,9 @@ class BESConnection:
|
|
|
435
433
|
if validate_xml:
|
|
436
434
|
logging.error(err_msg)
|
|
437
435
|
raise ValueError(err_msg)
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
436
|
+
|
|
437
|
+
# this is intended it validate_xml is None, but not used currently
|
|
438
|
+
logging.warning(err_msg)
|
|
441
439
|
|
|
442
440
|
return RESTResult(
|
|
443
441
|
self.session.put(self.url(path), data=data, verify=self.verify, **kwargs)
|
|
@@ -450,13 +448,48 @@ class BESConnection:
|
|
|
450
448
|
self.session.delete(self.url(path), verify=self.verify, **kwargs)
|
|
451
449
|
)
|
|
452
450
|
|
|
451
|
+
def session_relevance_json(self, relevance, **kwargs):
|
|
452
|
+
"""Get Session Relevance Results in JSON.
|
|
453
|
+
|
|
454
|
+
This will submit the relevance string as json instead of html form data.
|
|
455
|
+
"""
|
|
456
|
+
session_relevance = urllib.parse.quote(relevance, safe=":+")
|
|
457
|
+
rel_data = {"output": "json", "relevance": session_relevance}
|
|
458
|
+
self.last_connected = datetime.datetime.now()
|
|
459
|
+
result = RESTResult(
|
|
460
|
+
self.session.post(
|
|
461
|
+
self.url("query"),
|
|
462
|
+
data=rel_data,
|
|
463
|
+
verify=self.verify,
|
|
464
|
+
**kwargs,
|
|
465
|
+
)
|
|
466
|
+
)
|
|
467
|
+
return json.loads(result.text)
|
|
468
|
+
|
|
469
|
+
def session_relevance_json_array(self, relevance, **kwargs):
|
|
470
|
+
"""Get Session Relevance Results in an array from the json return.
|
|
471
|
+
|
|
472
|
+
This will submit the relevance string as json instead of html form data.
|
|
473
|
+
"""
|
|
474
|
+
result = self.session_relevance_json(relevance, **kwargs)
|
|
475
|
+
return result["result"]
|
|
476
|
+
|
|
477
|
+
def session_relevance_json_string(self, relevance, **kwargs):
|
|
478
|
+
"""Get Session Relevance Results in a string from the json return.
|
|
479
|
+
|
|
480
|
+
This will submit the relevance string as json instead of html form data.
|
|
481
|
+
"""
|
|
482
|
+
rel_result_array = self.session_relevance_json_array(relevance, **kwargs)
|
|
483
|
+
# Ensure each element is converted to a string
|
|
484
|
+
return "\n".join(map(str, rel_result_array))
|
|
485
|
+
|
|
453
486
|
def session_relevance_xml(self, relevance, **kwargs):
|
|
454
487
|
"""Get Session Relevance Results XML."""
|
|
455
488
|
self.last_connected = datetime.datetime.now()
|
|
456
489
|
return RESTResult(
|
|
457
490
|
self.session.post(
|
|
458
491
|
self.url("query"),
|
|
459
|
-
data=f"relevance={parse.quote(relevance, safe=':+')}",
|
|
492
|
+
data=f"relevance={urllib.parse.quote(relevance, safe=':+')}",
|
|
460
493
|
verify=self.verify,
|
|
461
494
|
**kwargs,
|
|
462
495
|
)
|
|
@@ -626,6 +659,8 @@ class BESConnection:
|
|
|
626
659
|
self.site_path = site_path
|
|
627
660
|
return self.site_path
|
|
628
661
|
|
|
662
|
+
return None
|
|
663
|
+
|
|
629
664
|
def import_bes_to_site(self, bes_file_path, site_path=None):
|
|
630
665
|
"""Import bes file to site."""
|
|
631
666
|
|
|
@@ -651,7 +686,7 @@ class BESConnection:
|
|
|
651
686
|
|
|
652
687
|
def create_site_from_file(self, bes_file_path, site_type="custom"):
|
|
653
688
|
"""Create new site."""
|
|
654
|
-
xml_parsed = etree.parse(bes_file_path)
|
|
689
|
+
xml_parsed = lxml.etree.parse(bes_file_path)
|
|
655
690
|
new_site_name = xml_parsed.xpath("/BES/CustomSite/Name/text()")[0]
|
|
656
691
|
|
|
657
692
|
result_site_path = self.validate_site_path(
|
|
@@ -662,7 +697,7 @@ class BESConnection:
|
|
|
662
697
|
besapi_logger.warning("Site `%s` already exists", result_site_path)
|
|
663
698
|
return None
|
|
664
699
|
|
|
665
|
-
result_site = self.post("sites", etree.tostring(xml_parsed))
|
|
700
|
+
result_site = self.post("sites", lxml.etree.tostring(xml_parsed))
|
|
666
701
|
|
|
667
702
|
return result_site
|
|
668
703
|
|
|
@@ -678,7 +713,7 @@ class BESConnection:
|
|
|
678
713
|
|
|
679
714
|
def create_user_from_file(self, bes_file_path):
|
|
680
715
|
"""Create user from xml."""
|
|
681
|
-
xml_parsed = etree.parse(bes_file_path)
|
|
716
|
+
xml_parsed = lxml.etree.parse(bes_file_path)
|
|
682
717
|
new_user_name = xml_parsed.xpath("/BESAPI/Operator/Name/text()")[0]
|
|
683
718
|
result_user = self.get_user(new_user_name)
|
|
684
719
|
|
|
@@ -686,7 +721,7 @@ class BESConnection:
|
|
|
686
721
|
besapi_logger.warning("User `%s` Already Exists!", new_user_name)
|
|
687
722
|
return result_user
|
|
688
723
|
besapi_logger.info("Creating User `%s`", new_user_name)
|
|
689
|
-
_ = self.post("operators", etree.tostring(xml_parsed))
|
|
724
|
+
_ = self.post("operators", lxml.etree.tostring(xml_parsed))
|
|
690
725
|
# print(user_result)
|
|
691
726
|
return self.get_user(new_user_name)
|
|
692
727
|
|
|
@@ -708,7 +743,7 @@ class BESConnection:
|
|
|
708
743
|
def create_group_from_file(self, bes_file_path, site_path=None):
|
|
709
744
|
"""Create a new group."""
|
|
710
745
|
site_path = self.get_current_site_path(site_path)
|
|
711
|
-
xml_parsed = etree.parse(bes_file_path)
|
|
746
|
+
xml_parsed = lxml.etree.parse(bes_file_path)
|
|
712
747
|
new_group_name = xml_parsed.xpath("/BES/ComputerGroup/Title/text()")[0]
|
|
713
748
|
|
|
714
749
|
existing_group = self.get_computergroup(new_group_name, site_path)
|
|
@@ -719,7 +754,7 @@ class BESConnection:
|
|
|
719
754
|
|
|
720
755
|
# print(lxml.etree.tostring(xml_parsed))
|
|
721
756
|
|
|
722
|
-
_ = self.post(f"computergroups/{site_path}", etree.tostring(xml_parsed))
|
|
757
|
+
_ = self.post(f"computergroups/{site_path}", lxml.etree.tostring(xml_parsed))
|
|
723
758
|
|
|
724
759
|
return self.get_computergroup(site_path, new_group_name)
|
|
725
760
|
|
|
@@ -844,7 +879,7 @@ class BESConnection:
|
|
|
844
879
|
def update_item_from_file(self, file_path, site_path=None):
|
|
845
880
|
"""Update an item by name and last modified."""
|
|
846
881
|
site_path = self.get_current_site_path(site_path)
|
|
847
|
-
bes_tree = etree.parse(file_path)
|
|
882
|
+
bes_tree = lxml.etree.parse(file_path)
|
|
848
883
|
|
|
849
884
|
with open(file_path, "rb") as f:
|
|
850
885
|
content = f.read()
|
|
@@ -1130,7 +1165,7 @@ class RESTResult:
|
|
|
1130
1165
|
"""Property for python dict representation."""
|
|
1131
1166
|
if self._besdict is None:
|
|
1132
1167
|
if self.valid:
|
|
1133
|
-
self._besdict = elem2dict(etree.fromstring(self.besxml))
|
|
1168
|
+
self._besdict = elem2dict(lxml.etree.fromstring(self.besxml))
|
|
1134
1169
|
else:
|
|
1135
1170
|
self._besdict = {"text": str(self)}
|
|
1136
1171
|
|
|
@@ -1154,11 +1189,11 @@ class RESTResult:
|
|
|
1154
1189
|
def xmlparse_text(self, text):
|
|
1155
1190
|
"""Parse response text as xml."""
|
|
1156
1191
|
if type(text) is str:
|
|
1157
|
-
root_xml = etree.fromstring(text.encode("utf-8"))
|
|
1192
|
+
root_xml = lxml.etree.fromstring(text.encode("utf-8"))
|
|
1158
1193
|
else:
|
|
1159
1194
|
root_xml = text
|
|
1160
1195
|
|
|
1161
|
-
return etree.tostring(root_xml, encoding="utf-8", xml_declaration=True)
|
|
1196
|
+
return lxml.etree.tostring(root_xml, encoding="utf-8", xml_declaration=True)
|
|
1162
1197
|
|
|
1163
1198
|
def objectify_text(self, text):
|
|
1164
1199
|
"""Parse response text as objectified xml."""
|
|
@@ -1167,7 +1202,7 @@ class RESTResult:
|
|
|
1167
1202
|
else:
|
|
1168
1203
|
root_xml = text
|
|
1169
1204
|
|
|
1170
|
-
return objectify.fromstring(root_xml)
|
|
1205
|
+
return lxml.objectify.fromstring(root_xml)
|
|
1171
1206
|
|
|
1172
1207
|
|
|
1173
1208
|
def main():
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: besapi
|
|
3
|
-
Version: 3.8.
|
|
3
|
+
Version: 3.8.3
|
|
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
|
|
7
7
|
Author-email: hansen.m@psu.edu, james@jgstew.com
|
|
8
|
+
Maintainer: James Stewart
|
|
9
|
+
Maintainer-email: james@jgstew.com
|
|
8
10
|
License: MIT
|
|
9
11
|
Keywords: bigfix iem tem rest api
|
|
10
12
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -15,20 +17,14 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
15
17
|
Classifier: Intended Audience :: Developers
|
|
16
18
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
19
|
Classifier: Topic :: System :: Systems Administration
|
|
18
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.9
|
|
19
21
|
Description-Content-Type: text/markdown
|
|
20
22
|
License-File: LICENSE.txt
|
|
21
|
-
Requires-Dist: requests
|
|
22
|
-
Requires-Dist: lxml
|
|
23
23
|
Requires-Dist: cmd2
|
|
24
|
+
Requires-Dist: lxml
|
|
25
|
+
Requires-Dist: requests
|
|
24
26
|
Requires-Dist: setuptools
|
|
25
|
-
Dynamic:
|
|
26
|
-
Dynamic: author-email
|
|
27
|
-
Dynamic: home-page
|
|
28
|
-
Dynamic: keywords
|
|
29
|
-
Dynamic: license
|
|
30
|
-
Dynamic: requires-dist
|
|
31
|
-
Dynamic: summary
|
|
27
|
+
Dynamic: license-file
|
|
32
28
|
|
|
33
29
|
# besapi
|
|
34
30
|
|
|
@@ -42,7 +38,7 @@ Usage:
|
|
|
42
38
|
|
|
43
39
|
```
|
|
44
40
|
import besapi
|
|
45
|
-
b = besapi.BESConnection('my_username', 'my_password', 'https://rootserver.domain.org:52311')
|
|
41
|
+
b = besapi.besapi.BESConnection('my_username', 'my_password', 'https://rootserver.domain.org:52311')
|
|
46
42
|
rr = b.get('sites')
|
|
47
43
|
|
|
48
44
|
# rr.request contains the original request object
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Test besapi with pytest.
|
|
3
|
+
|
|
4
|
+
This was converted from tests/tests.py which was used before this pytest was added.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import random
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
# mypy: disable-error-code="arg-type"
|
|
16
|
+
|
|
17
|
+
if not os.getenv("TEST_PIP"):
|
|
18
|
+
# add module folder to import paths for testing local src
|
|
19
|
+
sys.path.append(
|
|
20
|
+
os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "src")
|
|
21
|
+
)
|
|
22
|
+
# reverse the order so we make sure to get the local src module
|
|
23
|
+
sys.path.reverse()
|
|
24
|
+
|
|
25
|
+
import besapi
|
|
26
|
+
import besapi.plugin_utilities
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_besapi_version():
|
|
30
|
+
"""Test that the besapi version is not None."""
|
|
31
|
+
assert besapi.besapi.__version__ is not None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_rand_password():
|
|
35
|
+
"""Test that the generated random password has the correct length."""
|
|
36
|
+
assert 15 == len(besapi.besapi.rand_password(15))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_sanitize_txt():
|
|
40
|
+
"""Test that the sanitize_txt function works correctly."""
|
|
41
|
+
assert ("test--string", "test") == besapi.besapi.sanitize_txt(
|
|
42
|
+
r"test/\string", "test%"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_replace_text_between():
|
|
47
|
+
"""Test that the replace_text_between function works correctly."""
|
|
48
|
+
assert "http://localhost:52311/file.example" == besapi.besapi.replace_text_between(
|
|
49
|
+
"http://example:52311/file.example", "://", ":52311", "localhost"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_validate_site_path():
|
|
54
|
+
"""Test the validate_site_path function with various inputs."""
|
|
55
|
+
assert "master" in besapi.besapi.BESConnection.validate_site_path(
|
|
56
|
+
"", "master", False
|
|
57
|
+
)
|
|
58
|
+
assert "custom/" in besapi.besapi.BESConnection.validate_site_path(
|
|
59
|
+
"", "custom/Example", False
|
|
60
|
+
)
|
|
61
|
+
assert "operator/" in besapi.besapi.BESConnection.validate_site_path(
|
|
62
|
+
"", "operator/Example", False
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_validate_xml_bes_file():
|
|
67
|
+
"""Test the validate_xml_bes_file function with good and bad files."""
|
|
68
|
+
assert besapi.besapi.validate_xml_bes_file("tests/good/RelaySelectTask.bes") is True
|
|
69
|
+
assert (
|
|
70
|
+
besapi.besapi.validate_xml_bes_file("tests/good/ComputerGroupsExample.bes")
|
|
71
|
+
is True
|
|
72
|
+
)
|
|
73
|
+
assert (
|
|
74
|
+
besapi.besapi.validate_xml_bes_file("tests/bad/RelaySelectTask_BAD.bes")
|
|
75
|
+
is False
|
|
76
|
+
)
|
|
77
|
+
assert (
|
|
78
|
+
besapi.besapi.validate_xml_bes_file("tests/bad/ComputerGroups_BAD.bes") is False
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_failing_validate_site_path():
|
|
83
|
+
"""Test that validate_site_path raises ValueError for invalid inputs."""
|
|
84
|
+
|
|
85
|
+
with pytest.raises(ValueError):
|
|
86
|
+
besapi.besapi.BESConnection.validate_site_path("", "bad/Example", False)
|
|
87
|
+
|
|
88
|
+
with pytest.raises(ValueError):
|
|
89
|
+
besapi.besapi.BESConnection.validate_site_path("", "bad/master", False)
|
|
90
|
+
|
|
91
|
+
with pytest.raises(ValueError):
|
|
92
|
+
besapi.besapi.BESConnection.validate_site_path("", "", False, True)
|
|
93
|
+
|
|
94
|
+
with pytest.raises(ValueError):
|
|
95
|
+
besapi.besapi.BESConnection.validate_site_path("", None, False, True)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class RequestResult:
|
|
99
|
+
text = "this is just a test"
|
|
100
|
+
headers: list = []
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_rest_result():
|
|
104
|
+
"""Test the RESTResult class."""
|
|
105
|
+
request_result = RequestResult()
|
|
106
|
+
rest_result = besapi.besapi.RESTResult(request_result)
|
|
107
|
+
|
|
108
|
+
assert rest_result.besdict is not None
|
|
109
|
+
assert rest_result.besjson is not None
|
|
110
|
+
assert b"<BES>Example</BES>" in rest_result.xmlparse_text("<BES>Example</BES>")
|
|
111
|
+
assert rest_result.text == "this is just a test"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_parse_bes_modtime():
|
|
115
|
+
"""Test the parse_bes_modtime function."""
|
|
116
|
+
assert (
|
|
117
|
+
2017 == besapi.besapi.parse_bes_modtime("Tue, 05 Sep 2017 23:31:48 +0000").year
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_get_action_combined_relevance():
|
|
122
|
+
"""Test the get_action_combined_relevance function."""
|
|
123
|
+
assert (
|
|
124
|
+
"( ( True ) AND ( windows of operating system ) ) AND ( False )"
|
|
125
|
+
== besapi.besapi.get_action_combined_relevance(
|
|
126
|
+
["True", "windows of operating system", "False"]
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_get_target_xml():
|
|
132
|
+
"""Test the get_target_xml function with various inputs."""
|
|
133
|
+
assert "<CustomRelevance>False</CustomRelevance>" == besapi.besapi.get_target_xml()
|
|
134
|
+
assert "<AllComputers>true</AllComputers>" == besapi.besapi.get_target_xml(
|
|
135
|
+
"<AllComputers>"
|
|
136
|
+
)
|
|
137
|
+
assert "<ComputerID>1</ComputerID>" == besapi.besapi.get_target_xml(1)
|
|
138
|
+
assert (
|
|
139
|
+
"<CustomRelevance><![CDATA[not windows of operating system]]></CustomRelevance>"
|
|
140
|
+
== besapi.besapi.get_target_xml("not windows of operating system")
|
|
141
|
+
)
|
|
142
|
+
assert (
|
|
143
|
+
"<ComputerID>1</ComputerID><ComputerID>2</ComputerID>"
|
|
144
|
+
== besapi.besapi.get_target_xml([1, 2])
|
|
145
|
+
)
|
|
146
|
+
assert (
|
|
147
|
+
"<ComputerName>Computer 1</ComputerName><ComputerName>Another Computer</ComputerName>"
|
|
148
|
+
== besapi.besapi.get_target_xml(["Computer 1", "Another Computer"])
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def test_bescli():
|
|
153
|
+
"""Test the BESCLInterface class and its methods."""
|
|
154
|
+
import bescli
|
|
155
|
+
|
|
156
|
+
bigfix_cli = bescli.bescli.BESCLInterface()
|
|
157
|
+
|
|
158
|
+
# just make sure these don't throw errors:
|
|
159
|
+
bigfix_cli.do_ls()
|
|
160
|
+
bigfix_cli.do_clear()
|
|
161
|
+
bigfix_cli.do_ls()
|
|
162
|
+
bigfix_cli.do_logout()
|
|
163
|
+
bigfix_cli.do_error_count()
|
|
164
|
+
bigfix_cli.do_version()
|
|
165
|
+
bigfix_cli.do_conf()
|
|
166
|
+
|
|
167
|
+
# this should really only run if the config file is present:
|
|
168
|
+
if bigfix_cli.bes_conn:
|
|
169
|
+
# session relevance tests require functioning web reports server
|
|
170
|
+
assert (
|
|
171
|
+
int(bigfix_cli.bes_conn.session_relevance_string("number of bes computers"))
|
|
172
|
+
> 0
|
|
173
|
+
)
|
|
174
|
+
assert (
|
|
175
|
+
"test session relevance string result"
|
|
176
|
+
in bigfix_cli.bes_conn.session_relevance_string(
|
|
177
|
+
'"test session relevance string result"'
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
bigfix_cli.do_set_current_site("master")
|
|
181
|
+
|
|
182
|
+
# set working directory to folder this file is in:
|
|
183
|
+
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
|
184
|
+
|
|
185
|
+
# set working directory to src folder in parent folder
|
|
186
|
+
os.chdir("../src")
|
|
187
|
+
|
|
188
|
+
# Test file upload:
|
|
189
|
+
upload_result = bigfix_cli.bes_conn.upload(
|
|
190
|
+
"./besapi/__init__.py", "test_besapi_upload.txt"
|
|
191
|
+
)
|
|
192
|
+
# print(upload_result)
|
|
193
|
+
assert upload_result.besobj.FileUpload.Available == 1
|
|
194
|
+
assert upload_result.besdict["FileUpload"]["Available"] == "1"
|
|
195
|
+
assert upload_result.besjson is not None
|
|
196
|
+
upload_result_json = json.loads(upload_result.besjson)
|
|
197
|
+
assert upload_result_json["FileUpload"]["Available"] == "1"
|
|
198
|
+
|
|
199
|
+
assert "test_besapi_upload.txt</URL>" in str(upload_result)
|
|
200
|
+
upload_prefetch = bigfix_cli.bes_conn.parse_upload_result_to_prefetch(
|
|
201
|
+
upload_result
|
|
202
|
+
)
|
|
203
|
+
# print(upload_prefetch)
|
|
204
|
+
assert "prefetch test_besapi_upload.txt sha1:" in upload_prefetch
|
|
205
|
+
|
|
206
|
+
dashboard_name = "_PyBESAPI_tests.py"
|
|
207
|
+
var_name = "TestVarName"
|
|
208
|
+
var_value = "TestVarValue " + str(random.randint(0, 9999))
|
|
209
|
+
|
|
210
|
+
assert var_value in str(
|
|
211
|
+
bigfix_cli.bes_conn.set_dashboard_variable_value(
|
|
212
|
+
dashboard_name, var_name, var_value
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
assert var_value in str(
|
|
217
|
+
bigfix_cli.bes_conn.get_dashboard_variable_value(dashboard_name, var_name)
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
if os.name == "nt":
|
|
221
|
+
subprocess.run(
|
|
222
|
+
'CMD /C python -m besapi ls clear ls conf "query number of bes computers" version error_count exit',
|
|
223
|
+
check=True,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
bes_conn = besapi.besapi.get_bes_conn_using_config_file()
|
|
227
|
+
print("login succeeded:", bes_conn.login())
|
|
228
|
+
assert bes_conn.login()
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def test_bes_conn_json():
|
|
232
|
+
"""Test the BESConnection class with JSON output."""
|
|
233
|
+
bes_conn = besapi.besapi.get_bes_conn_using_config_file()
|
|
234
|
+
if bes_conn and bes_conn.login():
|
|
235
|
+
print("testing session_relevance_json")
|
|
236
|
+
result = bes_conn.session_relevance_json("number of bes computers")
|
|
237
|
+
assert result is not None
|
|
238
|
+
assert int(result["result"][0]) > 0
|
|
239
|
+
result = bes_conn.session_relevance_json(
|
|
240
|
+
"""("[%22" & it & "%22]") of concatenation "%22, %22" of names of bes computers"""
|
|
241
|
+
)
|
|
242
|
+
assert result is not None
|
|
243
|
+
string_first_result_json = result["result"][0]
|
|
244
|
+
print(string_first_result_json)
|
|
245
|
+
assert '", "' in string_first_result_json
|
|
246
|
+
assert '["' in string_first_result_json
|
|
247
|
+
# NOTE: the following check is specific to my env:
|
|
248
|
+
if "10.0." in bes_conn.rootserver:
|
|
249
|
+
print("doing check specific to my env")
|
|
250
|
+
assert '", "BIGFIX", "' in string_first_result_json
|
|
251
|
+
|
|
252
|
+
print("testing session_relevance_json_array")
|
|
253
|
+
result = bes_conn.session_relevance_json_array("number of bes computers")
|
|
254
|
+
print(result)
|
|
255
|
+
assert result is not None
|
|
256
|
+
assert int(result[0]) > 0
|
|
257
|
+
print("testing session_relevance_json_string")
|
|
258
|
+
result = bes_conn.session_relevance_json_string("number of bes computers")
|
|
259
|
+
print(result)
|
|
260
|
+
assert result is not None
|
|
261
|
+
assert int(result) > 0
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def test_plugin_utilities():
|
|
265
|
+
"""Test the plugin_utilities module."""
|
|
266
|
+
print(besapi.plugin_utilities.get_invoke_folder())
|
|
267
|
+
print(besapi.plugin_utilities.get_invoke_file_name())
|
|
268
|
+
|
|
269
|
+
parser = besapi.plugin_utilities.setup_plugin_argparse(plugin_args_required=False)
|
|
270
|
+
# allow unknown args to be parsed instead of throwing an error:
|
|
271
|
+
args, _unknown = parser.parse_known_args()
|
|
272
|
+
|
|
273
|
+
# test logging plugin_utilities:
|
|
274
|
+
import logging
|
|
275
|
+
|
|
276
|
+
logging_config = besapi.plugin_utilities.get_plugin_logging_config("./tests.log")
|
|
277
|
+
|
|
278
|
+
# this use of logging.basicConfig requires python >= 3.9
|
|
279
|
+
if sys.version_info >= (3, 9):
|
|
280
|
+
logging.basicConfig(**logging_config)
|
|
281
|
+
|
|
282
|
+
logging.warning("Just testing to see if logging is working!")
|
|
283
|
+
|
|
284
|
+
assert os.path.isfile("./tests.log")
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"""Test besapi."""
|
|
3
3
|
|
|
4
4
|
import argparse
|
|
5
|
+
import json
|
|
5
6
|
import os
|
|
6
7
|
import random
|
|
7
8
|
import subprocess
|
|
@@ -46,6 +47,13 @@ assert "operator/" in besapi.besapi.BESConnection.validate_site_path(
|
|
|
46
47
|
"", "operator/Example", False
|
|
47
48
|
)
|
|
48
49
|
|
|
50
|
+
assert besapi.besapi.validate_xml_bes_file("tests/good/RelaySelectTask.bes") is True
|
|
51
|
+
assert (
|
|
52
|
+
besapi.besapi.validate_xml_bes_file("tests/good/ComputerGroupsExample.bes") is True
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
assert besapi.besapi.validate_xml_bes_file("tests/bad/RelaySelectTask_BAD.bes") is False
|
|
56
|
+
|
|
49
57
|
# start failing tests:
|
|
50
58
|
raised_errors = 0
|
|
51
59
|
|
|
@@ -77,9 +85,9 @@ assert raised_errors == 4
|
|
|
77
85
|
# end failing tests
|
|
78
86
|
|
|
79
87
|
|
|
80
|
-
class RequestResult
|
|
88
|
+
class RequestResult:
|
|
81
89
|
text = "this is just a test"
|
|
82
|
-
headers = []
|
|
90
|
+
headers: list = []
|
|
83
91
|
|
|
84
92
|
|
|
85
93
|
request_result = RequestResult()
|
|
@@ -159,9 +167,20 @@ if bigfix_cli.bes_conn:
|
|
|
159
167
|
upload_result = bigfix_cli.bes_conn.upload(
|
|
160
168
|
"./besapi/__init__.py", "test_besapi_upload.txt"
|
|
161
169
|
)
|
|
170
|
+
|
|
162
171
|
# print(upload_result)
|
|
172
|
+
# print(upload_result.besobj.FileUpload.Available)
|
|
173
|
+
assert upload_result.besobj.FileUpload.Available == 1
|
|
163
174
|
assert "test_besapi_upload.txt</URL>" in str(upload_result)
|
|
164
|
-
|
|
175
|
+
upload_result_json = json.loads(upload_result.besjson)
|
|
176
|
+
# print(upload_result_json["FileUpload"]["Available"])
|
|
177
|
+
assert upload_result_json["FileUpload"]["Available"] == "1"
|
|
178
|
+
|
|
179
|
+
upload_prefetch = bigfix_cli.bes_conn.parse_upload_result_to_prefetch(upload_result)
|
|
180
|
+
# print(upload_prefetch)
|
|
181
|
+
assert "prefetch test_besapi_upload.txt sha1:" in upload_prefetch
|
|
182
|
+
assert "test_besapi_upload.txt" in str(upload_result)
|
|
183
|
+
# print(bigfix_cli.bes_conn.parse_upload_result_to_prefetch(upload_result))
|
|
165
184
|
|
|
166
185
|
dashboard_name = "_PyBESAPI_tests.py"
|
|
167
186
|
var_name = "TestVarName"
|
besapi-3.8.2/setup.cfg
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
[metadata]
|
|
2
|
-
version = attr: besapi.besapi.__version__
|
|
3
|
-
long_description = file: README.md
|
|
4
|
-
long_description_content_type = text/markdown
|
|
5
|
-
classifiers =
|
|
6
|
-
Programming Language :: Python :: 3
|
|
7
|
-
Programming Language :: Python :: 3.9
|
|
8
|
-
Operating System :: OS Independent
|
|
9
|
-
License :: OSI Approved :: MIT License
|
|
10
|
-
Development Status :: 5 - Production/Stable
|
|
11
|
-
Intended Audience :: Developers
|
|
12
|
-
Topic :: Software Development :: Libraries :: Python Modules
|
|
13
|
-
Topic :: System :: Systems Administration
|
|
14
|
-
|
|
15
|
-
[options]
|
|
16
|
-
python_requires = >=3.7
|
|
17
|
-
|
|
18
|
-
[egg_info]
|
|
19
|
-
tag_build =
|
|
20
|
-
tag_date = 0
|
|
21
|
-
|
besapi-3.8.2/setup.py
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
"""File to configure python build and packaging for pip."""
|
|
3
|
-
|
|
4
|
-
import setuptools
|
|
5
|
-
|
|
6
|
-
setuptools.setup(
|
|
7
|
-
name="besapi",
|
|
8
|
-
# version= moved to setup.cfg
|
|
9
|
-
author="Matt Hansen, James Stewart",
|
|
10
|
-
author_email="hansen.m@psu.edu, james@jgstew.com",
|
|
11
|
-
description="Library for working with the BigFix REST API",
|
|
12
|
-
license="MIT",
|
|
13
|
-
keywords="bigfix iem tem rest api",
|
|
14
|
-
url="https://github.com/jgstew/besapi",
|
|
15
|
-
# long_description= moved to setup.cfg
|
|
16
|
-
packages=["besapi", "bescli"],
|
|
17
|
-
package_data={"besapi": ["schemas/*.xsd"]},
|
|
18
|
-
install_requires=["requests", "lxml", "cmd2", "setuptools"],
|
|
19
|
-
include_package_data=True,
|
|
20
|
-
package_dir={"": "src"},
|
|
21
|
-
)
|
|
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
|