Add better exception handling for XML configs

This commit is contained in:
Joshua Boniface 2024-08-16 10:46:04 -04:00
parent 9aca8e215b
commit 942de9f15b
1 changed files with 77 additions and 17 deletions

View File

@ -155,10 +155,37 @@ def define_vm(
# Parse the XML data # Parse the XML data
try: try:
parsed_xml = lxml.objectify.fromstring(config_data) parsed_xml = lxml.objectify.fromstring(config_data)
except Exception: except Exception as e:
return False, "ERROR: Failed to parse XML data." return False, f"ERROR: Failed to parse XML data: {e}"
# Extract the required items from the XML document and error if not valid
next_field = 0
next_map = {
0: "uuid",
1: "name",
2: "memory",
3: "vcpu",
4: "networks",
5: "disks",
}
try:
dom_uuid = parsed_xml.uuid.text dom_uuid = parsed_xml.uuid.text
next_field += 1
dom_name = parsed_xml.name.text dom_name = parsed_xml.name.text
next_field += 1
parsed_memory = int(parsed_xml.memory.text)
next_field += 1
parsed_vcpu = int(parsed_xml.vcpu.text)
next_field += 1
dnetworks = common.getDomainNetworks(parsed_xml, {})
next_field += 1
ddisks = common.getDomainDisks(parsed_xml, {})
next_field += 1
except Exception as e:
return (
False,
f'ERROR: Failed to parse XML data: field data for "{next_map[next_field]}" is not valid: {e}',
)
# Ensure that the UUID and name are unique # Ensure that the UUID and name are unique
if searchClusterByUUID(zkhandler, dom_uuid) or searchClusterByName( if searchClusterByUUID(zkhandler, dom_uuid) or searchClusterByName(
@ -181,26 +208,25 @@ def define_vm(
# Validate the new RAM against the current active node # Validate the new RAM against the current active node
node_total_memory = int(zkhandler.read(("node.memory.total", target_node))) node_total_memory = int(zkhandler.read(("node.memory.total", target_node)))
if int(parsed_xml.memory.text) >= node_total_memory: if parsed_memory >= node_total_memory:
return ( return (
False, False,
'ERROR: VM configuration specifies more memory ({} MiB) than node "{}" has available ({} MiB).'.format( 'ERROR: VM configuration specifies more memory ({} MiB) than node "{}" has available ({} MiB).'.format(
parsed_xml.memory.text, target_node, node_total_memory parsed_memory, target_node, node_total_memory
), ),
) )
# Validate the number of vCPUs against the current active node # Validate the number of vCPUs against the current active node
node_total_cpus = int(zkhandler.read(("node.data.static", target_node)).split()[0]) node_total_cpus = int(zkhandler.read(("node.data.static", target_node)).split()[0])
if (node_total_cpus - 2) <= int(parsed_xml.vcpu.text): if parsed_vcpu >= (node_total_cpus - 2):
return ( return (
False, False,
'ERROR: VM configuration specifies more vCPUs ({}) than node "{}" has available ({} minus 2).'.format( 'ERROR: VM configuration specifies more vCPUs ({}) than node "{}" has available ({} minus 2).'.format(
parsed_xml.vcpu.text, target_node, node_total_cpus parsed_vcpu, target_node, node_total_cpus
), ),
) )
# If a SR-IOV network device is being added, set its used state # If a SR-IOV network device is being added, set its used state
dnetworks = common.getDomainNetworks(parsed_xml, {})
for network in dnetworks: for network in dnetworks:
if network["type"] in ["direct", "hostdev"]: if network["type"] in ["direct", "hostdev"]:
dom_node = zkhandler.read(("domain.node", dom_uuid)) dom_node = zkhandler.read(("domain.node", dom_uuid))
@ -239,7 +265,6 @@ def define_vm(
) )
# Obtain the RBD disk list using the common functions # Obtain the RBD disk list using the common functions
ddisks = common.getDomainDisks(parsed_xml, {})
rbd_list = [] rbd_list = []
for disk in ddisks: for disk in ddisks:
if disk["type"] == "rbd": if disk["type"] == "rbd":
@ -404,6 +429,35 @@ def modify_vm(zkhandler, domain, restart, new_vm_config):
except Exception: except Exception:
return False, "ERROR: Failed to parse new XML data." return False, "ERROR: Failed to parse new XML data."
# Extract the required items from the XML document and error if not valid
next_field = 0
next_map = {
0: "uuid",
1: "name",
2: "memory",
3: "vcpu",
4: "networks",
5: "disks",
}
try:
dom_uuid = parsed_xml.uuid.text
next_field += 1
dom_name = parsed_xml.name.text
next_field += 1
parsed_memory = int(parsed_xml.memory.text)
next_field += 1
parsed_vcpu = int(parsed_xml.vcpu.text)
next_field += 1
dnetworks = common.getDomainNetworks(parsed_xml, {})
next_field += 1
ddisks = common.getDomainDisks(parsed_xml, {})
next_field += 1
except Exception as e:
return (
False,
f'ERROR: Failed to parse XML data: field data for "{next_map[next_field]}" is not valid: {e}',
)
# Get our old network list for comparison purposes # Get our old network list for comparison purposes
old_vm_config = zkhandler.read(("domain.xml", dom_uuid)) old_vm_config = zkhandler.read(("domain.xml", dom_uuid))
old_parsed_xml = lxml.objectify.fromstring(old_vm_config) old_parsed_xml = lxml.objectify.fromstring(old_vm_config)
@ -412,26 +466,25 @@ def modify_vm(zkhandler, domain, restart, new_vm_config):
# Validate the new RAM against the current active node # Validate the new RAM against the current active node
node_name = zkhandler.read(("domain.node", dom_uuid)) node_name = zkhandler.read(("domain.node", dom_uuid))
node_total_memory = int(zkhandler.read(("node.memory.total", node_name))) node_total_memory = int(zkhandler.read(("node.memory.total", node_name)))
if int(parsed_xml.memory.text) >= node_total_memory: if parsed_memory >= node_total_memory:
return ( return (
False, False,
'ERROR: Updated VM configuration specifies more memory ({} MiB) than node "{}" has available ({} MiB).'.format( 'ERROR: Updated VM configuration specifies more memory ({} MiB) than node "{}" has available ({} MiB).'.format(
parsed_xml.memory.text, node_name, node_total_memory parsed_memory, node_name, node_total_memory
), ),
) )
# Validate the number of vCPUs against the current active node # Validate the number of vCPUs against the current active node
node_total_cpus = int(zkhandler.read(("node.data.static", node_name)).split()[0]) node_total_cpus = int(zkhandler.read(("node.data.static", node_name)).split()[0])
if (node_total_cpus - 2) <= int(parsed_xml.vcpu.text): if parsed_vcpu >= (node_total_cpus - 2):
return ( return (
False, False,
'ERROR: Updated VM configuration specifies more vCPUs ({}) than node "{}" has available ({} minus 2).'.format( 'ERROR: Updated VM configuration specifies more vCPUs ({}) than node "{}" has available ({} minus 2).'.format(
parsed_xml.vcpu.text, node_name, node_total_cpus parsed_vcpu, node_name, node_total_cpus
), ),
) )
# If a SR-IOV network device is being added, set its used state # If a SR-IOV network device is being added, set its used state
dnetworks = common.getDomainNetworks(parsed_xml, {})
for network in dnetworks: for network in dnetworks:
# Ignore networks that are already there # Ignore networks that are already there
if network["source"] in [net["source"] for net in old_dnetworks]: if network["source"] in [net["source"] for net in old_dnetworks]:
@ -482,7 +535,6 @@ def modify_vm(zkhandler, domain, restart, new_vm_config):
unset_sriov_vf_vm(zkhandler, dom_node, network["source"]) unset_sriov_vf_vm(zkhandler, dom_node, network["source"])
# Obtain the RBD disk list using the common functions # Obtain the RBD disk list using the common functions
ddisks = common.getDomainDisks(parsed_xml, {})
rbd_list = [] rbd_list = []
for disk in ddisks: for disk in ddisks:
if disk["type"] == "rbd": if disk["type"] == "rbd":
@ -754,7 +806,15 @@ def update_vm_sriov_nics(zkhandler, dom_uuid, source_node, target_node):
# Update all the SR-IOV device states on both nodes, used during migrations but called by the node-side # Update all the SR-IOV device states on both nodes, used during migrations but called by the node-side
vm_config = zkhandler.read(("domain.xml", dom_uuid)) vm_config = zkhandler.read(("domain.xml", dom_uuid))
parsed_xml = lxml.objectify.fromstring(vm_config) parsed_xml = lxml.objectify.fromstring(vm_config)
# Extract the required items from the XML document and error if not valid
try:
dnetworks = common.getDomainNetworks(parsed_xml, {}) dnetworks = common.getDomainNetworks(parsed_xml, {})
except Exception as e:
return (
False,
f'ERROR: Failed to parse XML data: field data for "networks" is not valid: {e}',
)
retcode = True retcode = True
retmsg = "" retmsg = ""
for network in dnetworks: for network in dnetworks: