besapi 3.7.7__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.7/src/besapi.egg-info → besapi-3.8.1}/PKG-INFO +1 -1
- {besapi-3.7.7 → besapi-3.8.1}/src/besapi/besapi.py +93 -7
- {besapi-3.7.7 → besapi-3.8.1}/src/besapi/plugin_utilities.py +1 -1
- {besapi-3.7.7 → besapi-3.8.1/src/besapi.egg-info}/PKG-INFO +1 -1
- {besapi-3.7.7 → besapi-3.8.1}/src/bescli/bescli.py +99 -39
- {besapi-3.7.7 → besapi-3.8.1}/tests/tests.py +35 -2
- {besapi-3.7.7 → besapi-3.8.1}/LICENSE.txt +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/MANIFEST.in +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/README.md +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/pyproject.toml +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/setup.cfg +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/setup.py +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/src/besapi/__init__.py +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/src/besapi/__main__.py +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/src/besapi/schemas/BES.xsd +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/src/besapi/schemas/BESAPI.xsd +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/src/besapi/schemas/BESActionSettings.xsd +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/src/besapi.egg-info/SOURCES.txt +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/src/besapi.egg-info/dependency_links.txt +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/src/besapi.egg-info/requires.txt +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/src/besapi.egg-info/top_level.txt +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/src/bescli/__init__.py +0 -0
- {besapi-3.7.7 → besapi-3.8.1}/src/bescli/__main__.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",
|
|
@@ -10,6 +10,7 @@ Simple command line interface for the BES (BigFix) REST API.
|
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
import getpass
|
|
13
|
+
import json
|
|
13
14
|
import logging
|
|
14
15
|
import os
|
|
15
16
|
import site
|
|
@@ -56,8 +57,50 @@ class BESCLInterface(Cmd):
|
|
|
56
57
|
# set default config file path
|
|
57
58
|
self.conf_path = os.path.expanduser("~/.besapi.conf")
|
|
58
59
|
self.CONFPARSER = SafeConfigParser()
|
|
60
|
+
# for completion:
|
|
61
|
+
self.api_resources = []
|
|
59
62
|
self.do_conf()
|
|
60
63
|
|
|
64
|
+
def parse_help_resources(self):
|
|
65
|
+
"""get api resources from help"""
|
|
66
|
+
if self.bes_conn:
|
|
67
|
+
help_result = self.bes_conn.get("help")
|
|
68
|
+
help_result = help_result.text.split("\n")
|
|
69
|
+
# print(help_result)
|
|
70
|
+
help_resources = []
|
|
71
|
+
for item in help_result:
|
|
72
|
+
if "/api/" in item:
|
|
73
|
+
_, _, res = item.partition("/api/")
|
|
74
|
+
# strip whitespace just in case:
|
|
75
|
+
help_resources.append(res.strip())
|
|
76
|
+
|
|
77
|
+
return help_resources
|
|
78
|
+
else:
|
|
79
|
+
return [
|
|
80
|
+
"actions",
|
|
81
|
+
"clientqueryresults",
|
|
82
|
+
"dashboardvariables",
|
|
83
|
+
"help",
|
|
84
|
+
"login",
|
|
85
|
+
"query",
|
|
86
|
+
"relaysites",
|
|
87
|
+
"serverinfo",
|
|
88
|
+
"sites",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
def complete_api_resources(self, text, line, begidx, endidx):
|
|
92
|
+
"""define completion for apis"""
|
|
93
|
+
|
|
94
|
+
# only initialize once
|
|
95
|
+
if not self.api_resources:
|
|
96
|
+
self.api_resources = self.parse_help_resources()
|
|
97
|
+
|
|
98
|
+
# TODO: make this work to complete only the first word after get/post/delete
|
|
99
|
+
# return the matching subset:
|
|
100
|
+
return [name for name in self.api_resources if name.startswith(text)]
|
|
101
|
+
|
|
102
|
+
complete_get = complete_api_resources
|
|
103
|
+
|
|
61
104
|
def do_get(self, line):
|
|
62
105
|
"""Perform get request to BigFix server using provided api endpoint argument"""
|
|
63
106
|
|
|
@@ -80,16 +123,18 @@ class BESCLInterface(Cmd):
|
|
|
80
123
|
b = self.bes_conn.get(robjs[0])
|
|
81
124
|
# print objectify.ObjectPath(robjs[1:])
|
|
82
125
|
if b:
|
|
83
|
-
|
|
126
|
+
self.poutput(eval("b()." + ".".join(robjs[1:])))
|
|
84
127
|
else:
|
|
85
128
|
output_item = self.bes_conn.get(line)
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
#
|
|
89
|
-
#
|
|
129
|
+
# self.poutput(type(output_item))
|
|
130
|
+
self.poutput(output_item)
|
|
131
|
+
# self.poutput(output_item.besdict)
|
|
132
|
+
# self.poutput(output_item.besjson)
|
|
90
133
|
else:
|
|
91
134
|
self.pfeedback("Not currently logged in. Type 'login'.")
|
|
92
135
|
|
|
136
|
+
complete_delete = complete_api_resources
|
|
137
|
+
|
|
93
138
|
def do_delete(self, line):
|
|
94
139
|
"""Perform delete request to BigFix server using provided api endpoint argument"""
|
|
95
140
|
|
|
@@ -104,16 +149,18 @@ class BESCLInterface(Cmd):
|
|
|
104
149
|
if self.bes_conn:
|
|
105
150
|
output_item = self.bes_conn.delete(line)
|
|
106
151
|
|
|
107
|
-
|
|
108
|
-
#
|
|
109
|
-
#
|
|
152
|
+
self.poutput(output_item)
|
|
153
|
+
# self.poutput(output_item.besdict)
|
|
154
|
+
# self.poutput(output_item.besjson)
|
|
110
155
|
else:
|
|
111
156
|
self.pfeedback("Not currently logged in. Type 'login'.")
|
|
112
157
|
|
|
158
|
+
complete_post = complete_api_resources
|
|
159
|
+
|
|
113
160
|
def do_post(self, statement):
|
|
114
161
|
"""post file as data to path"""
|
|
115
|
-
|
|
116
|
-
|
|
162
|
+
self.poutput(statement)
|
|
163
|
+
self.poutput("not yet implemented")
|
|
117
164
|
|
|
118
165
|
def do_config(self, conf_file=None):
|
|
119
166
|
"""Attempt to load config info from file and login"""
|
|
@@ -244,7 +291,7 @@ class BESCLInterface(Cmd):
|
|
|
244
291
|
else:
|
|
245
292
|
self.perror("Login Error!")
|
|
246
293
|
|
|
247
|
-
def do_logout(self,
|
|
294
|
+
def do_logout(self, _=None):
|
|
248
295
|
"""Logout and clear session"""
|
|
249
296
|
if self.bes_conn:
|
|
250
297
|
self.bes_conn.logout()
|
|
@@ -254,7 +301,7 @@ class BESCLInterface(Cmd):
|
|
|
254
301
|
|
|
255
302
|
def do_debug(self, setting):
|
|
256
303
|
"""Enable or Disable Debug Mode"""
|
|
257
|
-
|
|
304
|
+
self.poutput(bool(setting))
|
|
258
305
|
self.debug = bool(setting)
|
|
259
306
|
self.echo = bool(setting)
|
|
260
307
|
self.quiet = bool(setting)
|
|
@@ -288,12 +335,12 @@ class BESCLInterface(Cmd):
|
|
|
288
335
|
"""save current config to file"""
|
|
289
336
|
self.do_saveconf(arg)
|
|
290
337
|
|
|
291
|
-
def do_saveconf(self,
|
|
338
|
+
def do_saveconf(self, _=None):
|
|
292
339
|
"""save current config to file"""
|
|
293
340
|
if not self.bes_conn:
|
|
294
341
|
self.do_login()
|
|
295
342
|
if not self.bes_conn:
|
|
296
|
-
|
|
343
|
+
self.poutput("Can't save config without working login")
|
|
297
344
|
else:
|
|
298
345
|
conf_file_path = self.conf_path
|
|
299
346
|
self.pfeedback(f"Saving Config File to: {conf_file_path}")
|
|
@@ -304,29 +351,31 @@ class BESCLInterface(Cmd):
|
|
|
304
351
|
"""List the current settings and connection status"""
|
|
305
352
|
self.do_ls(arg)
|
|
306
353
|
|
|
307
|
-
def do_ls(self,
|
|
354
|
+
def do_ls(self, _=None):
|
|
308
355
|
"""List the current settings and connection status"""
|
|
309
|
-
|
|
310
|
-
|
|
356
|
+
self.poutput(" Connected: " + str(bool(self.bes_conn)))
|
|
357
|
+
self.poutput(
|
|
311
358
|
" BES_ROOT_SERVER: "
|
|
312
359
|
+ (self.BES_ROOT_SERVER if self.BES_ROOT_SERVER else "")
|
|
313
360
|
)
|
|
314
|
-
|
|
361
|
+
self.poutput(
|
|
315
362
|
" BES_USER_NAME: " + (self.BES_USER_NAME if self.BES_USER_NAME else "")
|
|
316
363
|
)
|
|
317
|
-
|
|
364
|
+
self.poutput(
|
|
318
365
|
" Password Length: "
|
|
319
366
|
+ str(len(self.BES_PASSWORD if self.BES_PASSWORD else ""))
|
|
320
367
|
)
|
|
321
|
-
|
|
368
|
+
self.poutput(" Config File Path: " + self.conf_path)
|
|
322
369
|
if self.bes_conn:
|
|
323
|
-
|
|
370
|
+
self.poutput(
|
|
371
|
+
"Current Site Path: " + self.bes_conn.get_current_site_path(None)
|
|
372
|
+
)
|
|
324
373
|
|
|
325
|
-
def do_error_count(self,
|
|
374
|
+
def do_error_count(self, _=None):
|
|
326
375
|
"""Output the number of errors"""
|
|
327
376
|
self.poutput(f"Error Count: {self.num_errors}")
|
|
328
377
|
|
|
329
|
-
def do_exit(self,
|
|
378
|
+
def do_exit(self, _=None):
|
|
330
379
|
"""Exit this application"""
|
|
331
380
|
self.exit_code = self.num_errors
|
|
332
381
|
# no matter what I try I can't get anything but exit code 0 on windows
|
|
@@ -347,7 +396,7 @@ class BESCLInterface(Cmd):
|
|
|
347
396
|
self.pfeedback("A: ")
|
|
348
397
|
self.poutput(rel_result)
|
|
349
398
|
|
|
350
|
-
def do_version(self,
|
|
399
|
+
def do_version(self, _=None):
|
|
351
400
|
"""output version of besapi"""
|
|
352
401
|
self.poutput(f"besapi version: {__version__}")
|
|
353
402
|
|
|
@@ -361,7 +410,7 @@ class BESCLInterface(Cmd):
|
|
|
361
410
|
result_op = self.bes_conn.get_user(statement)
|
|
362
411
|
self.poutput(result_op)
|
|
363
412
|
|
|
364
|
-
def do_get_current_site(self,
|
|
413
|
+
def do_get_current_site(self, _=None):
|
|
365
414
|
"""output current site path context"""
|
|
366
415
|
self.poutput(
|
|
367
416
|
f"Current Site Path: `{ self.bes_conn.get_current_site_path(None) }`"
|
|
@@ -375,11 +424,11 @@ class BESCLInterface(Cmd):
|
|
|
375
424
|
|
|
376
425
|
def do_get_content(self, resource_url):
|
|
377
426
|
"""get a specific item by resource url"""
|
|
378
|
-
|
|
427
|
+
self.poutput(self.bes_conn.get_content_by_resource(resource_url))
|
|
379
428
|
|
|
380
429
|
def do_export_item_by_resource(self, statement):
|
|
381
430
|
"""export content itemb to current folder"""
|
|
382
|
-
|
|
431
|
+
self.poutput(self.bes_conn.export_item_by_resource(statement))
|
|
383
432
|
|
|
384
433
|
def do_export_site(self, site_path):
|
|
385
434
|
"""export site contents to current folder"""
|
|
@@ -387,7 +436,7 @@ class BESCLInterface(Cmd):
|
|
|
387
436
|
site_path, verbose=True, include_site_folder=False, include_item_ids=False
|
|
388
437
|
)
|
|
389
438
|
|
|
390
|
-
def do_export_all_sites(self,
|
|
439
|
+
def do_export_all_sites(self, _=None):
|
|
391
440
|
"""export site contents to current folder"""
|
|
392
441
|
self.bes_conn.export_all_sites(verbose=False)
|
|
393
442
|
|
|
@@ -409,47 +458,58 @@ class BESCLInterface(Cmd):
|
|
|
409
458
|
def do_upload(self, file_path):
|
|
410
459
|
"""upload file to root server"""
|
|
411
460
|
if not os.access(file_path, os.R_OK):
|
|
412
|
-
|
|
461
|
+
self.poutput(file_path, "is not a readable file")
|
|
413
462
|
else:
|
|
414
463
|
upload_result = self.bes_conn.upload(file_path)
|
|
415
|
-
|
|
416
|
-
|
|
464
|
+
self.poutput(upload_result)
|
|
465
|
+
self.poutput(self.bes_conn.parse_upload_result_to_prefetch(upload_result))
|
|
417
466
|
|
|
418
467
|
complete_create_group = Cmd.path_complete
|
|
419
468
|
|
|
420
469
|
def do_create_group(self, file_path):
|
|
421
470
|
"""create bigfix group from bes file"""
|
|
422
471
|
if not os.access(file_path, os.R_OK):
|
|
423
|
-
|
|
472
|
+
self.poutput(file_path, "is not a readable file")
|
|
424
473
|
else:
|
|
425
|
-
|
|
474
|
+
self.poutput(self.bes_conn.create_group_from_file(file_path))
|
|
426
475
|
|
|
427
476
|
complete_create_user = Cmd.path_complete
|
|
428
477
|
|
|
429
478
|
def do_create_user(self, file_path):
|
|
430
479
|
"""create bigfix user from bes file"""
|
|
431
480
|
if not os.access(file_path, os.R_OK):
|
|
432
|
-
|
|
481
|
+
self.poutput(file_path, "is not a readable file")
|
|
433
482
|
else:
|
|
434
|
-
|
|
483
|
+
self.poutput(self.bes_conn.create_user_from_file(file_path))
|
|
435
484
|
|
|
436
485
|
complete_create_site = Cmd.path_complete
|
|
437
486
|
|
|
438
487
|
def do_create_site(self, file_path):
|
|
439
488
|
"""create bigfix site from bes file"""
|
|
440
489
|
if not os.access(file_path, os.R_OK):
|
|
441
|
-
|
|
490
|
+
self.poutput(file_path, "is not a readable file")
|
|
442
491
|
else:
|
|
443
|
-
|
|
492
|
+
self.poutput(self.bes_conn.create_site_from_file(file_path))
|
|
444
493
|
|
|
445
494
|
complete_update_item = Cmd.path_complete
|
|
446
495
|
|
|
447
496
|
def do_update_item(self, file_path):
|
|
448
497
|
"""update bigfix content item from bes file"""
|
|
449
498
|
if not os.access(file_path, os.R_OK):
|
|
450
|
-
|
|
499
|
+
self.poutput(file_path, "is not a readable file")
|
|
451
500
|
else:
|
|
452
|
-
|
|
501
|
+
self.poutput(self.bes_conn.update_item_from_file(file_path))
|
|
502
|
+
|
|
503
|
+
def do_serverinfo(self, _=None):
|
|
504
|
+
"""get server info and return formatted"""
|
|
505
|
+
|
|
506
|
+
# not sure what the minimum version for this is:
|
|
507
|
+
result = self.bes_conn.get("serverinfo")
|
|
508
|
+
|
|
509
|
+
result_json = json.loads(result.text)
|
|
510
|
+
|
|
511
|
+
self.poutput(f"\nServer Info for {self.BES_ROOT_SERVER}")
|
|
512
|
+
self.poutput(json.dumps(result_json, indent=2))
|
|
453
513
|
|
|
454
514
|
|
|
455
515
|
def main():
|
|
@@ -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
|