2016-10-25 15 views
0

Я использую python, чтобы попасть в API-интерфейс мастера, чтобы собрать некоторые факты обо всех узлах, о которых знает бригадир. К сожалению, нет get-all-hosts-фактов (или что-то подобное) в API-интерфейсе v1, поэтому мне нужно пройти через все хосты и получить информацию. Это привело меня к раздражающей проблеме. Каждый вызов данного хоста вернуть объект JSON выглядеть примерно так:Запись нескольких объектов JSON как одного объекта в один файл с помощью python

{ 
    "host1.com": { 
    "apt_update_last_success": "1452187711", 
    "architecture": "amd64", 
    "augeasversion": "1.2.0", 
    "bios_release_date": "06/03/2015", 
    "bios_vendor": "Dell Inc." 
    } 
} 

Это совершенно нормально, проблема возникает, когда я добавляю информацию следующего хозяина. Затем я получаю JSON-файл, который выглядит примерно так:

{ 
    "host1.com": { 
    "apt_update_last_success": "1452187711", 
    "architecture": "amd64", 
    "augeasversion": "1.2.0", 
    "bios_release_date": "06/03/2015", 
    "bios_vendor": "Dell Inc." 
} 
}{ 
"host2.com": { 
    "apt_update_last_success": "1452703454", 
    "architecture": "amd64", 
    "augeasversion": "1.2.0", 
    "bios_release_date": "06/03/2015", 
    "bios_vendor": "Dell Inc." 
    } 
} 

Вот код, который делает это:

for i in hosts_data: 
    log.info("Gathering host facts for host: {}".format(i['host']['name'])) 
    try: 
     facts = requests.get(foreman_host+api+"hosts/{}/facts".format(i['host']['id']), auth=(username, password)) 
     if hosts.status_code != 200: 
      log.error("Unable to connect to Foreman! Got retcode '{}' and error message '{}'" 
      .format(hosts.status_code, hosts.text)) 
      sys.exit(1) 
    except requests.exceptions.RequestException as e: 
     log.error(e) 
    facts_data = json.loads(facts.text) 
    log.debug(facts_data) 
    with open(results_file, 'a') as f: 
     f.write(json.dumps(facts_data, sort_keys=True, indent=4)) 

Вот что мне нужно файл выглядеть следующим образом:

{ 
"host1.com": { 
    "apt_update_last_success": "1452187711", 
    "architecture": "amd64", 
    "augeasversion": "1.2.0", 
    "bios_release_date": "06/03/2015", 
    "bios_vendor": "Dell Inc." 
}, 
"host2.com": { 
    "apt_update_last_success": "1452703454", 
    "architecture": "amd64", 
    "augeasversion": "1.2.0", 
    "bios_release_date": "06/03/2015", 
    "bios_vendor": "Dell Inc." 
    } 
} 

ответ

4

Было бы лучше собрать все ваши данные в один диктофон, а затем написать все это один раз, а не каждый раз в цикле.

d = {} 
for i in hosts_data: 
    log.info("Gathering host facts for host: {}".format(i['host']['name'])) 
    try: 
     facts = requests.get(foreman_host+api+"hosts/{}/facts".format(i['host']['id']), auth=(username, password)) 
     if hosts.status_code != 200: 
      log.error("Unable to connect to Foreman! Got retcode '{}' and error message '{}'" 
      .format(hosts.status_code, hosts.text)) 
      sys.exit(1) 
    except requests.exceptions.RequestException as e: 
     log.error(e) 
    facts_data = json.loads(facts.text) 
    log.debug(facts_data) 
    d.update(facts_data) #add to dict 
# write everything at the end 
with open(results_file, 'a') as f: 
    f.write(json.dumps(d, sort_keys=True, indent=4)) 
+0

Спасибо! Я сейчас дам это. –

+0

Работал как шарм! Спасибо @pault –

+1

Np. Единственное, что нужно знать, это то, что 'dict.update()' заменяет содержимое, если ключ уже существует (например, в вашем случае, если 'hosts_data' содержит дубликаты). – pault

1

Вместо того, чтобы писать json внутри цикла, вставьте данные в dict с правильной структурой. Затем напишите, что dict для json, когда цикл закончен.

Это предполагает, что ваш набор данных вписывается в память.

0

Для обеспечения безопасности/согласованности необходимо загружать старые данные, мутировать их, а затем записывать обратно.

Изменение тока with и write к:

# If file guaranteed to exist, can use r+ and avoid initial seek 
with open(results_file, 'a+') as f: 
    f.seek(0) 
    combined_facts = json.load(f) 
    combined_facts.update(facts_data) 
    f.seek(0) 
    json.dump(combined_facts, f, sort_keys=True, indent=4) 
    f.truncate() # In case new JSON encoding smaller, e.g. due to replaced key 

Примечание: Если возможно, вы хотите использовать pault's answer, чтобы свести к минимуму ненужные I/O, это просто, как вы могли бы сделать это, если поиск данных должен выполняться по частям, с немедленными обновлениями для каждого элемента по мере его появления.

FYI, небезопасный способ состоит в том, чтобы в основном найти конечную фигурную скобку, удалить ее, а затем записать запятую, за которой следует новый JSON (удаление ведущей фигурной скобки из ее представления JSON). Это намного меньше интенсивность ввода-вывода, но она также менее безопасна, не очищает дубликаты, не сортирует хосты, не проверяет входной файл вообще и т. Д. Поэтому не делайте этого.

+0

Спасибо @ShadowRanger! Хорошая информация о проблемах безопасности и ввода-вывода. Очень признателен! –