diff --git a/103221-1/TS_103_221_01.xsd b/103221-1/TS_103_221_01.xsd
index c3ca89a06ef81b0671a57ac1465f637ef38c7dbf..894785a2117afa75aba931c47f8c1b44236b6f05 100644
--- a/103221-1/TS_103_221_01.xsd
+++ b/103221-1/TS_103_221_01.xsd
@@ -156,6 +156,7 @@
+
@@ -251,6 +252,7 @@
+
@@ -319,6 +321,14 @@
+
+
+
+
+
+
+
+
diff --git a/103221-1/TS_103_221_01_TrafficPolicy.xsd b/103221-1/TS_103_221_01_TrafficPolicy.xsd
new file mode 100644
index 0000000000000000000000000000000000000000..2e59aa91533e0ac94096483a97c1468fc50f385c
--- /dev/null
+++ b/103221-1/TS_103_221_01_TrafficPolicy.xsd
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/103221-1/examples/CreateTrafficPolicy_example.xml b/103221-1/examples/CreateTrafficPolicy_example.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2edc79858298c25d81b5c616a4bb181fbfabe622
--- /dev/null
+++ b/103221-1/examples/CreateTrafficPolicy_example.xml
@@ -0,0 +1,64 @@
+
+
+
+ admfID
+ neID
+ 2023-02-14T18:46:21.247432Z
+ v1.13.1
+ 2654a277-bafb-4240-9143-806ee41a3751
+
+ 6c67de93-926a-488b-9435-b0a30c20f676
+
+
+
+
+ 203.0.113.0/24
+
+
+
+
+
+ ETSI
+ PolicyAction
+ PDSR
+
+
+
+
+ admfID
+ neID
+ 2023-02-14T18:46:21.247432Z
+ v1.13.1
+ 2654a277-bafb-4240-9143-806ee41a3751
+
+ b04057af-362f-4e8d-9337-416f43bfd7ec
+ Template Rule 1
+
+
+ 1
+ 6c67de93-926a-488b-9435-b0a30c20f676
+
+
+
+
+
+ admfID
+ neID
+ 2023-02-14T18:46:21.247432Z
+ v1.13.1
+ 211900a7-afb5-4187-b036-c85a92ab94aa
+
+ 2f5faff3-077d-4ec3-b66a-226e3a921eec
+
+
+ 447700900000
+
+
+ X2andX3
+
+
+ b04057af-362f-4e8d-9337-416f43bfd7ec
+
+
+
+
diff --git a/create_attachments.py b/create_attachments.py
new file mode 100644
index 0000000000000000000000000000000000000000..9f5f02642be950126d9675f5ca937f368c2f0c70
--- /dev/null
+++ b/create_attachments.py
@@ -0,0 +1,60 @@
+from io import BytesIO
+from pathlib import Path
+import logging
+import zipfile
+
+# For ETSI portal attachments, a single zip archive is created which
+# contains:
+# - All files in the top-level spec directory
+# - A zipfile for each subdirectory, which contains all the files in that subdirectory
+# This second step is recursive, so each zip file contains only files and other zipfiles, never subdirectories
+# For an explanation as to *why* this is the case, speak to your ETSI technical officer.
+#
+# Example: Assuming the files for the portal attachment are of the following form:
+#
+# (contents of directory)
+# - top_level_file.ext
+# - subdirectory_1
+# - subdirectory_1_file.ext
+# - subdirectory_2
+# - subdirectory_2_file.ext
+# - TS 102 232-1
+#
+# ...then the correct form for ETSI portal attachments is as follows
+#
+# ts_xxxxxx_vxxyyzzp0.zip
+# which contains:
+# - top_level_file.ext
+# - subdirectory_1.zip
+# (which contains)
+# - subdirectory_1_file.ext
+# - subdirectory_2.zip
+# (which contains)
+# - subdirectory_2_file.ext
+
+
+def recursively_zip_directory(directory: Path, zipname: str, recursion=0):
+ logging.info(
+ f"{'':{recursion * 4}}Packaging contents of {directory} into {zipname}"
+ )
+ buffer = BytesIO()
+ zip = zipfile.ZipFile(buffer, "a")
+ for f in directory.iterdir():
+ if f.is_file():
+ logging.info(f"{'':{recursion * 4}}Adding file: {f}")
+ zip.write(f, f.name)
+ elif f.is_dir():
+ zipname = f.with_suffix(".zip").name
+ logging.info(f"{'':{recursion * 4}}Adding archive: {f}")
+ recurse_buffer = recursively_zip_directory(f, zipname, recursion + 1)
+ zip.writestr(zipname, recurse_buffer.getvalue())
+ return buffer
+
+
+if __name__ == "__main__":
+ logging.info("Creating attachments...")
+ for directory in Path(".").glob("1*"):
+ zip_name = f"ts_{directory.name}vXXYYZZp0.zip"
+ zip_buffer = recursively_zip_directory(directory, zip_name)
+ with open(zip_name, "wb") as f:
+ f.write(zip_buffer.getvalue())
diff --git a/testing/xsd_compile_targets.json b/testing/xsd_compile_targets.json
index a1bde12e8cf2c0d58135e37cdd05317cc16e7a31..6110fd84da8b2311bad0ae356cbecc4ed821ecc4 100644
--- a/testing/xsd_compile_targets.json
+++ b/testing/xsd_compile_targets.json
@@ -15,8 +15,13 @@
"103221-1/TS_103_221_01.xsd",
"103221-1/TS_103_221_01_HashedID.xsd",
"103221-1/TS_103_221_01_DestinationSet.xsd",
+ "103221-1/TS_103_221_01_TrafficPolicy.xsd",
"103280/TS_103_280.xsd",
- "103221-1/examples/ExampleGenericObjects.xsd"
+ "103221-1/examples/ExampleGenericObjects.xsd",
+ "103120/schema/ts_103120_Common.xsd",
+ "103120/schema/ts_103120_Core.xsd",
+ "103120/schema/ts_103120_TrafficPolicy.xsd",
+ "testing/deps/xmldsig/xmldsig-core-schema.xsd"
],
"exampleFiles" : [
"103221-1/examples"