[英]Python 3 XML xml.etree parsing stdout bash cli commands
Greetings good Python people. 问候好Python的人。
I am trying to build a python script that is doing two things. 我正在尝试构建一个做两件事的python脚本。 It will take an input.xml file with some commands (that the users can edit later and add more) and then create another output.xml file with the results of the commands and then start some other .py program that inserts the output via API to a web application. 它将使用带有一些命令的input.xml文件(用户可以稍后编辑并添加更多命令),然后使用命令的结果创建另一个output.xml文件,然后启动其他一些.py程序,以通过API插入输出到Web应用程序。 These commands are all BASH commands, like dmidecode, cat, hpconfg and similar. 这些命令都是BASH命令,例如dmidecode,cat,hpconfg等。 The Input.xml looks like this: Input.xml看起来像这样:
<?xml version="1.0"?>
<platform>
<vendor name="HP">
<tests type = "Hardware" name="bios_versions">
<command>dmidecode --type bios | grep -e Version -e "Release Date"</command>
</tests>
<tests type = "Hardware" name="iLo">
<command>hponcfg -f /tmp/Get_ILO_Firmware_Version.xml | awk -F"\"" '/LICENSE_TYPE/ {print $2}'</command>
</tests>
<tests type = "Hardware" name="hard_disks">
<command>hpssacli ctrl slot=0 ld all show detail | egrep "Status|Size"</command>
<command>hpssacli ctrl slot=1 ld all show detail | egrep "Status|Size"</command>
<command>hpssacli ctrl slot=2 ld all show detail | egrep "Status|Size"</command>
<command>hpssacli ctrl slot=3 ld all show detail | egrep "Status|Size"</command>
</tests>
</vendor>
</platform>
I would need to iterate trough this input.xml (that will be later maybe bigger with more commands) and i will use the Paramiko ssh library to connect remotely to the servers and collect the stdout from the commands and parse the stdout to an output.xml file. 我将需要遍历这个input.xml(以后可能会通过更多命令变得更大),并且我将使用Paramiko ssh库远程连接到服务器,并从命令中收集stdout并将stdout解析为输出。 xml文件。
This is my older script to collect and print the stdout of the commands but I think I will need a subprocess module for this more complex task. 这是我收集和打印命令标准输出的较早脚本,但是我认为我将需要一个子流程模块来完成此更复杂的任务。
import atexit
import paramiko
import sys
if len(sys.argv) < 2:
print("args missing")
sys.exit(1)
hostname = sys.argv[1]
password = sys.argv[2]
#user = sys.argv[3]
username = "root"
#Check Vendor
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname, port = 22, username=username, password=password)
stdin, stdout, stderr = client.exec_command("dmidecode | egrep Vendor | awk '{print $2}'")
vendor = stdout.read().decode('utf-8')
vendor = vendor.strip('\n')
client.close()
#finish check vendor
class myssh:
def __init__(self, hostname, username, password, port = 22):
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname, port=port, username=username, password=password)
atexit.register(client.close)
self.client = client
def __call__(self, command):
stdin,stdout,stderr = self.client.exec_command(command)
sshdata = stdout.readlines()
for line in sshdata:
#end = '' - to loose space after the loops new lines
print(line,end = '')
remote = myssh(hostname,username,password)
#CHECK HARDWARE COMMANDS
if vendor == "Dell":
print("1. HARDWARE VENDOR:"+vendor)
print("2. CPU INFO")
remote("cat /proc/cpuinfo | grep -e 'model name' -e 'physical id' | cut -c 1- | sort | uniq -c")
remote("cat /etc/release | grep PLATFORM")
print("3. PHYSICAL DISK SMART PREDICABLE STATUS: NO is Ok!")
remote("omreport storage pdisk controller=0 | egrep '^ID|Status|State|Failure|Capacity' | grep -v Power | grep -v 'Status' | grep 'Failure Predicted'")
print("4. BATERRY STATUS:")
remote("omreport storage battery | grep H740 -A 4 | grep -oh 'Ok'")
remote("omreport storage battery | grep H740 -A 4 | grep -oh 'Ready'")
print("5. Bios Version:")
remote("omreport chassis bios | grep 'Version' | awk '{print $3}'")
elif vendor == "HP":
print("1. HARDWARE VENDOR:"+vendor)
print("2. CPU INFO")
remote("cat /proc/cpuinfo | grep -e 'model name' -e 'physical id' | cut -c 1- | sort | uniq -c")
remote("cat /etc/release | grep PLATFORM")
print("3. PHYSICAL DISK STATUS")
remote("hpssacli ctrl all show config | egrep -i '(ok|failed|error|offline|rebuild|ignoring|degraded|skipping|nok)'")
print("4. BATERRY STATUS")
remote("hpssacli ctrl all show status | grep 'OK'")
print("5. Bios Version:")
remote("dmidecode --type bios | grep -e Version -e 'Release Date'")
else:
print("1. HARDWARE VENDOR:"+vendor)
print(vendor+" is this")
Any help and hints would be appreciated. 任何帮助和提示,将不胜感激。
!!! ! UPDATE !!! 更新!!!
I decided to use CSV to create output.csv file with some ID which I can use later to call upon external api.py file to insert data with API to a Web Application. 我决定使用CSV创建具有某些ID的output.csv文件,以后可以使用它调用外部api.py文件,以将具有API的数据插入Web应用程序。
The code looks like this, but still now finished and needs more work. 代码看起来像这样,但是现在仍然完成并且需要更多的工作。 Any help would be appreciated: 任何帮助,将不胜感激:
class myssh:
def __init__(self, hostname, username, password, port=22):
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname, port=port, username=username, password=password)
atexit.register(client.close)
self.client = client
def __call__(self, command):
stdin, stdout, stderr = self.client.exec_command(command)
sshdata = stdout.readlines()
for line in sshdata:
# end = '' - to loose space after the loops new lines
print(line, end='')
remote = myssh(hostname, username, password)
if vendor == "Dell":
f = open('dellcsv.csv', 'r')
csv_f = csv.reader(f, delimiter=';')
for row in csv_f:
remote(row[4])
# print(row[4])
f.close()
elif vendor == "HP":
f = open('dellcsv.csv', 'r')
csv_f = csv.reader(f, delimiter=';')
for row in csv_f:
remote(row[4])
# print(row[4])
f.close()
else:
print("1. HARDWARE VENDOR:" + vendor)
print(vendor + " is this")
As per comments, you can append every line
to an empty list, open the csv file in desired format in w
(write) mode and write the list to the csv. 根据注释,您可以将每line
追加到一个空列表中,以w
(写入)模式以所需格式打开csv文件,然后将该列表写入csv。 This will create the csv file with the data in sshdata
, if that's what you are looking for: 如果您要查找的是,这将使用sshdata
的数据创建csv文件:
import csv
filename = 'ssh_output.csv'
def __call__():
#your code here#
hostdata_list = []
hardware_data = []
for line in sshdata:
if 'HARDWARE' in line:
row = 'some_data'
hardware_data.append(row)
hostdata_list.append(line)
with open('input.csv', 'r') as f:
reader = csv.reader(f)
header = next(reader)
with open(filename, 'a+') as f:
writer = csv.writer(f)
writer.writerow(header)
if hardware_data is not None:
writer.writerow(hardware_data)
writer.writerow(hostdata_list)
print('sshdata written to {} for host {}'.format(filename, hostname))
A proposed XML solution : 提出的XML解决方案 :
The following solution provides a workflow that accepts hp_input.xml
for vendor HP. 以下解决方案提供了接受供应商HP的hp_input.xml
的工作流。 Of course, you may have dell_input.xml
if the vendor is Dell. 当然,如果供应商是Dell,则可能会有dell_input.xml
。
I've used the xmltodict
and dicttoxml
libraries to convert the input to a dict, add an output
key for each command, update the value for this key based on output for the command, then convert the output dict to xml again and write to an hp_output.xml
or dell_output.xml
file. 我已经使用xmltodict
和dicttoxml
库将输入转换为dict,为每个命令添加一个output
键,根据命令的output
更新此键的值,然后再次将输出dict转换为xml并写入hp_output.xml
或dell_output.xml
文件。 To write the xml in pretty print, I used lxml
. 为了漂亮地打印xml,我使用了lxml
。 Make sure you install the required libraries with pip
before proceeding. 在继续之前,请确保使用pip
安装所需的库。
First, return the stdout
from __call__()
in the ssh
class: 首先,从ssh
类的__call__()
返回stdout
:
class ssh:
#your code#
def __call__():
stdout_outputs = []
#your code#
stdout_outputs.append(stdout.read())
return stdout_outputs
Next, the following function will accept the input xml and convert it to dict: 接下来,以下函数将接受输入的xml并将其转换为dict:
import xmltodict
import lxml.etree as etree
import dicttoxml
import json
def xml_to_dict(xml_doc):
with open(xml_doc) as f:
data = xmltodict.parse(f.read())
# print(json.dumps(data, indent=4)) #check dict
return data
The following function gets the command value from the above dict and sends it for execution to remote
, receives the output and updates the dict received from above. 以下函数从上面的字典中获取命令值,并将其发送给remote
,以接收输出并更新从上面接收的字典。
def get_output_dict():
if vendor == 'HP':
data = xml_to_dict(xml_doc='hp_input.xml')
elif vendor == 'Dell':
data = xml_to_dict(xml_doc='dell_input.xml')
for test in data['platform']['vendor']['tests']:
command = test.get('command',[]) #continue if command is not present
if isinstance(command, list):
for i in command:
output = list(dict(i=remote(i)))
output = list(dict(command=remote(command)))
test['output'] = output
return json.loads(json.dumps(data))
Now that we have the final dict from the above function, the following function will convert it to xml again and write it to an output xml file: 现在我们有了上述函数的最终决定,下面的函数将再次将其转换为xml并将其写入输出xml文件:
def get_output_xml(output_dict):
output_xml = dicttoxml.dicttoxml(output_dict,custom_root='output', attr_type=False, root=False)
if vendor == 'HP':
filename = 'hp_output.xml'
elif vendor == 'Dell':
filename = 'dell_output.xml'
tree = etree.fromstring(output_xml)
output_xml_string = etree.tostring(tree, pretty_print=True)
with open(filename, 'wb') as f:
f.write(output_xml_string)
print('Output for hostname server: {} written to: {}'.format(hostname, filename))
return output_xml
You may put all the functions into one class and add exception handling as you like. 您可以将所有函数放在一个类中,并根据需要添加异常处理。 We can call the functions like this: 我们可以这样调用函数:
data = get_output_dict()
xml = get_output_xml(data)
The code was tested with input xml provided and a dummy value for output. 使用提供的输入xml和用于输出的虚拟值对代码进行了测试。 Although, I haven't tested with paramiko
, this code should give you a basic workflow to get your desired output xml and if any errors crop up, they can be debugged. 尽管我尚未使用paramiko
进行测试, paramiko
此代码应为您提供基本的工作流程,以获取所需的输出xml,并且如果出现任何错误,可以对其进行调试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.