[PATCH] config_tools: pretty-print JSON schema violations


Junjie Mao
 

The default error messages provided by the ajv validation library read like
this:

* must have required property MEMORY.STACK_SIZE

* must match pattern "<a regular expression>"

Such messages may look confusing as users are not supposed to understand
the internal naming of the config items or the regular expressions used to
validate strings.

This patch enables the XML schema to include 'acrn:errormsg' annotations
which is a dictionary from error types to customized error messages. This
mechanism is used to show more user-friendly messages upon common errors
such as missing or invalid data in required config item.

Tracked-On: #6691
Signed-off-by: Junjie Mao <junjie.mao@...>
---
.../scenario_config/jsonschema/converter.py | 19 +++++++++++++++++--
misc/config_tools/schema/VMtypes.xsd | 15 ++++++++++-----
misc/config_tools/schema/config.xsd | 15 +++++++++------
misc/config_tools/schema/types.xsd | 6 ++++--
4 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/misc/config_tools/scenario_config/jsonschema/converter.py b/misc/config_tools/scenario_config/jsonschema/converter.py
index 086aed148..8274c1bf0 100644
--- a/misc/config_tools/scenario_config/jsonschema/converter.py
+++ b/misc/config_tools/scenario_config/jsonschema/converter.py
@@ -151,6 +151,17 @@ class XS2JS:
if '@acrn:widget-options' in annotation:
js_ele['ui:options'] = eval(f"{{{annotation['@acrn:widget-options']}}}")

+ def convert_errormsg_config(self, annotation, js_ele):
+ if '@acrn:errormsg' in annotation:
+ opts = eval(f"{{{annotation['@acrn:errormsg']}}}")
+
+ # An items of an error schema requires an "err:" prefix.
+ keys = list(opts.keys())
+ for key in keys:
+ opts[f"err:{key}"] = opts.pop(key)
+
+ js_ele.update(opts)
+
def xst2jst(self, type_name) -> str:
"""convert xml schema type name to json schema type name"""
if type_name in self.xst2jst_mapping:
@@ -195,9 +206,10 @@ class XS2JS:
enum_names.append(enum_name)
js_st["enumNames"] = enum_names

- # widget and its options
+ # widget configs and error messages
if 'xs:annotation' in obj:
self.convert_widget_config(obj['xs:annotation'], js_st)
+ self.convert_errormsg_config(obj['xs:annotation'], js_st)

js_st.update(self.xsa2jsa(restriction))
return js_st
@@ -298,7 +310,7 @@ class XS2JS:

if '@maxOccurs' in element:
# ui:options seen at this moment are copied from the annotation of the type.
- possible_keys = ['type', '$ref', 'oneOf', 'ui:options']
+ possible_keys = ['type', '$ref', 'oneOf', 'ui:options', 'err:required', 'err:pattern']
convert_to_items_success = False
js_ele['items'] = {}
for possible_key in possible_keys:
@@ -353,6 +365,9 @@ class XS2JS:
# widget and its options
self.convert_widget_config(element['xs:annotation'], js_ele)

+ # Error messages
+ self.convert_errormsg_config(element['xs:annotation'], js_ele)
+
properties[name] = js_ele

# build result
diff --git a/misc/config_tools/schema/VMtypes.xsd b/misc/config_tools/schema/VMtypes.xsd
index ea612e144..850db0d91 100644
--- a/misc/config_tools/schema/VMtypes.xsd
+++ b/misc/config_tools/schema/VMtypes.xsd
@@ -88,12 +88,14 @@ CLOSID 0 and the second is mapped to virtual CLOSID 1, etc.</xs:documentation>
<xs:complexType name="EPCSection">
<xs:sequence>
<xs:element name="base" type="HexFormat" default="0">
- <xs:annotation acrn:title="EPC section base" acrn:applicable-vms="pre-launched">
+ <xs:annotation acrn:title="EPC section base" acrn:applicable-vms="pre-launched"
+ acrn:errormsg="'required': 'EPC section base is required.'">
<xs:documentation>Specify the enclave page cache (EPC) section base for Intel Software Guard Extensions (SGX). Must be page aligned.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="size" type="HexFormat" default="0">
- <xs:annotation acrn:title="EPC section size (bytes)" acrn:applicable-vms="pre-launched">
+ <xs:annotation acrn:title="EPC section size (bytes)" acrn:applicable-vms="pre-launched"
+ acrn:errormsg="'required': 'EPC section size is required.'">
<xs:documentation>Specify the enclave page cache (EPC) section size in bytes for Intel Software Guard Extensions (SGX). Must be page aligned.</xs:documentation>
</xs:annotation>
</xs:element>
@@ -103,12 +105,14 @@ CLOSID 0 and the second is mapped to virtual CLOSID 1, etc.</xs:documentation>
<xs:complexType name="HPARegionType">
<xs:sequence>
<xs:element name="start_hpa" type="HexFormat">
- <xs:annotation acrn:title="Start physical address">
+ <xs:annotation acrn:title="Start physical address"
+ acrn:errormsg="'required': 'Physical memory base address is required.'">
<xs:documentation>Specify the starting address for non-contiguous allocation.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="size_hpa" type="xs:integer">
- <xs:annotation acrn:title="Size (MB)">
+ <xs:annotation acrn:title="Size (MB)"
+ acrn:errormsg="'required': 'Physical memory size is required.'">
<xs:documentation>Specify the physical memory size for non-contiguous allocation in megabytes.
The size is a subset of the VM's total memory size specified on the Basic tab.</xs:documentation>
</xs:annotation>
@@ -290,7 +294,8 @@ The size is a subset of the VM's total memory size specified on the Basic tab.</
<xs:sequence>
<xs:element name="usb_dev" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:annotation acrn:title="USB device assignment"
- acrn:options="//usb_device/@description" acrn:options-sorted-by="lambda s: s">
+ acrn:options="//usb_device/@description" acrn:options-sorted-by="lambda s: s"
+ acrn:errormsg="'required': 'USB device required. If no USB device is available, click the X at the top right corner of this entry to remove.'">
<xs:documentation>Select the USB devices you want to assign to this virtual machine.</xs:documentation>
</xs:annotation>
</xs:element>
diff --git a/misc/config_tools/schema/config.xsd b/misc/config_tools/schema/config.xsd
index b99198982..d5e6ba3df 100644
--- a/misc/config_tools/schema/config.xsd
+++ b/misc/config_tools/schema/config.xsd
@@ -134,7 +134,8 @@ These settings can only be changed at build time.</xs:documentation>
<xs:complexType name="MemoryOptionsType">
<xs:all>
<xs:element name="STACK_SIZE" type="HexFormat" default="0x2000">
- <xs:annotation acrn:title="CPU memory stack size (bytes)" acrn:views="advanced">
+ <xs:annotation acrn:title="CPU memory stack size (bytes)" acrn:views="advanced"
+ acrn:errormsg="'required': 'Stack size is required.'">
<xs:documentation>Specify the size of the memory stack in bytes for each physical CPU. For example, if you specify 8 kilobytes, each CPU will get its own 8-kilobyte stack.</xs:documentation>
</xs:annotation>
</xs:element>
@@ -302,9 +303,10 @@ Refer to :ref:`vuart_config` for detailed vUART settings.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name="name" type="VMNameType">
- <xs:annotation acrn:title="VM name" acrn:views="basic">
- <xs:documentation>Specify the name used to identify this VM. The VM name will be shown in the hypervisor console vm_list command.</xs:documentation>
- </xs:annotation>
+ <xs:annotation acrn:title="VM name" acrn:views="basic"
+ acrn:errormsg="'required': 'VM name is required.'">
+ <xs:documentation>Specify the name used to identify this VM. The VM name will be shown in the hypervisor console vm_list command.</xs:documentation>
+ </xs:annotation>
</xs:element>
<xs:element name="vm_type" type="VMType" minOccurs="0">
<xs:annotation acrn:title="VM type" acrn:views="basic">
@@ -495,8 +497,9 @@ mouse, and tablet. It sends Linux input layer events over virtio.</xs:documenta
<xs:documentation>The virtio-blk device presents a block device to the VM. Each virtio-blk device appears as a disk inside the VM.</xs:documentation>
</xs:annotation>
<xs:simpleType>
- <xs:annotation acrn:widget-options="'placeholder': '/home/user/path/to/disk.image'" />
- <xs:restriction base="xs:string" />
+ <xs:annotation acrn:widget-options="'placeholder': '/home/user/path/to/disk.image'"
+ acrn:errormsg="'required': 'Path to a disk image required.'"/>
+ <xs:restriction base="xs:string" />
</xs:simpleType>
</xs:element>
</xs:all>
diff --git a/misc/config_tools/schema/types.xsd b/misc/config_tools/schema/types.xsd
index a637654dc..8450fffae 100644
--- a/misc/config_tools/schema/types.xsd
+++ b/misc/config_tools/schema/types.xsd
@@ -24,7 +24,8 @@
</xs:simpleType>

<xs:simpleType name="HexFormat">
- <xs:annotation acrn:widget-options="'placeholder': 'A hexadecimal number with a leading 0x, e.g. 0x1000.'">
+ <xs:annotation acrn:widget-options="'placeholder': 'A hexadecimal number with a leading 0x, e.g. 0x1000.'"
+ acrn:errormsg="'pattern': 'Must be a hexadecimal integer (case-insensitive).'">
<xs:documentation>An Integer value in hexadecimal format (with a leading ``0x``).</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
@@ -177,7 +178,8 @@ Read more about the available scheduling options in :ref:`cpu_sharing`.</xs:docu
</xs:simpleType>

<xs:simpleType name="VMNameType">
- <xs:annotation acrn:widget-options="'placeholder': 'A string with at most 15 non-space characters, e.g. Linux-VM-1.'">
+ <xs:annotation acrn:widget-options="'placeholder': 'A string with at most 15 non-space characters, e.g. Linux-VM-1.'"
+ acrn:errormsg="'pattern': 'Must NOT be longer than 15 characters or contain characters other than letters, digits, \'_\' and \'-\'.'">
<xs:documentation>A string of up to 15 letters, digits, ``_``, or ``-``.</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
--
2.30.2

Join acrn-dev@lists.projectacrn.org to automatically receive all group messages.