Я пытаюсь получить все элементы конфигурации из нашей CMDB для конкретного запроса, используя API REST Service Now и библиотеку запросов Python. Когда я использую веб-интерфейс, общее количество элементов соответствует тому, что REST API возвращает для заголовка 'X-Total-Count
. Однако, когда я разбиваю результаты на страницы, используя al oop и их REST API, мое общее количество возвращаемых элементов меньше, чем это.
Я написал тестовый скрипт (ниже), чтобы попробовать и отладить это. Скрипт распечатает количество элементов, возвращаемых каждым запросом, и я заметил, что некоторые страницы возвращают число в соответствии с установленным пределом.
Например, страница 2 вернет 4730 результатов вместо 5000, затем несколько других страниц вернут 5000 результатов, а затем другая случайная страница вернет некоторое число ниже предела. Когда я снова запускаю запрос, одна и та же страница каждый раз возвращает одинаковое количество результатов (или отсутствие результатов).
Что здесь происходит? Почему бы не вернуть максимальное количество результатов, если оно далеко от последней страницы? Это услуга сейчас? У кого-нибудь есть опыт с этим? Я довольно много пытался поиграть со своим запросом и размером лимита, и до сих пор сталкиваюсь с той же проблемой. У меня не было этой проблемы на прошлой неделе, и теперь я не могу исправить это для моей жизни.
import json
import requests
def get_ci() -> list:
"""
Returns a list of all configuration items in Service Now CMDB.
:return: configuration item list
"""
config_items = [] # The configuration items
limit = 5000 # REST API call limit
# Build the url for the API call
url = 'https://company.com/'
url += 'api/now/table/cmdb_ci?' # REST API call to cmdb_ci table (configuration items table).
url += 'sysparm_query=' # Query parameters begin, parameters are on the following lines.
url += 'ip_addressISNOTEMPTY' # IP Address can't be empty
url += '^install_statusIN7,9,10013,10015,10016' # State is either Deployed, Disposed, Build, Live, or Retired
url += '^u_business_segmentNOT INEnterprise,Federal' # Business Segment is neither Enterprise or Federal
url += '^ORu_business_segment=' # OR Business Segment is None
total_count = __get_total_count(url) # Get the total number of results to our query (# of ci's)
url += '&sysparm_limit=' + str(limit) # Convert the limit to string and use it.
print("Collecting items from REST API.")
print("This may take quite a while ( > 30 minutes...)")
print("")
# Debug counter
counter = 0
# Enter a loop to gather paginated results from the Service Now API
while True:
# Do the HTTP request
response = requests.get(url, auth=(username, password),
headers={"Content-Type": "application/json", "Accept": "application/json"})
# Check for HTTP codes other than 200
if response.status_code != 200:
print('Status:', response.status_code, 'Headers:', response.headers, 'Error Response:',
response.content)
exit(1)
print("\nAdding " + str(len(json.loads(response.content)['result'])) + " items to list.")
print(url)
config_items.extend(json.loads(response.content)['result'])
counter += 1
percentage = ((limit*counter)/total_count)*100
print("Total Done: " + str(percentage) + "%")
# If there's another page of results, continue.
if 'next' in response.links:
url = response.links['next']['url']
# Exit the loop if there's no more pages to go through.
else:
break
print("Loops done: " + str(counter))
print("List length: " + str(len(config_items)))
# Build a new list that only contains the columns we're looking for.
# Function returns the list containing IP, OS Patch Owner, Tech Group Owner, and Status.
return config_items
def __get_total_count(url) -> int:
"""
Returns the total number of items for a given query
:param url: query url
:return: total number of items
"""
print("Counting the number of items for your query...")
url += '&sysparm_limit=1' # Set a limit of one so it's a faster return.
# Do the HTTP request
response = requests.get(url, auth=(user, password),
headers={"Content-Type": "application/json", "Accept": "application/json"})
# Check for HTTP codes other than 200
if response.status_code != 200:
print('Status:', response.status_code, 'Headers:', response.headers, 'Error Response:',
response.content)
exit(1)
total = int(response.headers['X-Total-Count'])
print("Total count: " + str(total))
return total