diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000..6b556e8
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,26 @@
+FROM ghcr.io/converged-computing/metric-ior:latest
+
+LABEL maintainer="Vanessasaurus <@vsoch>"
+
+# Match the default user id for a single system so we aren't root
+ARG USERNAME=vscode
+ARG USER_UID=1000
+ARG USER_GID=1000
+ENV USERNAME=${USERNAME}
+ENV USER_UID=${USER_UID}
+ENV USER_GID=${USER_GID}
+USER root
+
+# extra interactive utilities
+RUN apt-get update \
+ && apt-get -qq install -y --no-install-recommends \
+ vim less sudo python3-pip
+
+# Ensure regular python is visible
+RUN ln -s /usr/bin/python3 /usr/bin/python
+
+# Add the group and user that match our ids
+RUN groupadd -g ${USER_GID} ${USERNAME} && \
+ adduser --disabled-password --uid ${USER_UID} --gid ${USER_GID} --gecos "" ${USERNAME} && \
+ echo "${USERNAME} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers
+USER $USERNAME
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..49813f2
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,17 @@
+{
+ "name": "Compspec IOR Development Environment",
+ "dockerFile": "Dockerfile",
+ "context": "../",
+
+ "customizations": {
+ "vscode": {
+ "settings": {
+ "terminal.integrated.defaultProfile.linux": "bash"
+ },
+ "extensions": [
+ "ms-vscode.cmake-tools"
+ ]
+ }
+ },
+ "postStartCommand": "git config --global --add safe.directory /workspaces/compspec-ior"
+}
diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml
new file mode 100644
index 0000000..2477c06
--- /dev/null
+++ b/.github/workflows/main.yaml
@@ -0,0 +1,54 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+ pull_request: []
+
+jobs:
+ test:
+ name: Test IOR
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+ - name: Install compspec
+ run: pip install compspec
+ - name: Install compspec-ior
+ run: pip install .
+ - name: Test with loading data
+ run: compspec extract ior --ior-load ./examples/test/ior-data.json
+ - name: Test Python
+ run: python ./examples/singleton-run.py
+
+ validate-schema:
+ name: Validate schema
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+ - name: Validate Schema
+ uses: compspec/actions/validate-schema@main
+ with:
+ schema: ./compspec_ior/schema.json
+
+ formatting:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup black linter
+ run: conda create --quiet --name black pyflakes
+
+ - name: Check Spelling
+ uses: crate-ci/typos@7ad296c72fa8265059cc03d1eda562fbdfcd6df2 # v1.9.0
+ with:
+ files: ./README.md
+
+ - name: Lint and format Python code
+ run: |
+ export PATH="/usr/share/miniconda/bin:$PATH"
+ source activate black
+ pip install -r .github/dev-requirements.txt
+ pre-commit run --all-files
diff --git a/README.md b/README.md
index bcaa156..53f72b8 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,8 @@
+[![PyPI version](https://badge.fury.io/py/compspec-ior.svg)](https://badge.fury.io/py/compspec-ior)
+
A compspec (Composition spec) is a specification and model for comparing things. Compspec IOR is
a plugin for extraction of [IOR](https://github.com/hpc/ior) metadata from applications, and packaging in compatibility specification
artifacts. This means that we also maintain the compatibility schema here. To learn more:
@@ -23,21 +25,205 @@ pip install compspec-ior
```
Then run an extraction with IOR. You can use defaults, or add any parameters to IOR after the plugin name "ior"
+Here is how to print to the terminal:
+
+```bash
+compspec extract ior
+```
+
+
+
+IOR output
+
+```console
+{
+ "version": "0.0.0",
+ "kind": "CompatibilitySpec",
+ "metadata": {
+ "name": "compat-experiment",
+ "schemas": {
+ "io.compspec.ior": "https://raw.githubusercontent.com/compspec/compspec-ior/main/compspec_ior/schema.json"
+ }
+ },
+ "compatibilities": [
+ {
+ "name": "io.compspec.ior",
+ "version": "0.0.0",
+ "attributes": {
+ "version": "4.0.0rc1",
+ "began": "Thu Feb 22 00:36:12 2024",
+ "machine": "Linux 2b3ee0c4c948",
+ "finished": "Thu Feb 22 00:36:12 2024",
+ "command_line": "ior -O summaryFormat=JSON",
+ "summary.write.operation": "write",
+ "summary.write.API": "POSIX",
+ "summary.write.TestID": 0,
+ "summary.write.ReferenceNumber": 0,
+ "summary.write.segmentCount": 1,
+ "summary.write.blockSize": 1048576,
+ "summary.write.transferSize": 262144,
+ "summary.write.numTasks": 1,
+ "summary.write.tasksPerNode": 1,
+ "summary.write.repetitions": 1,
+ "summary.write.filePerProc": 0,
+ "summary.write.reorderTasks": 0,
+ "summary.write.taskPerNodeOffset": 1,
+ "summary.write.reorderTasksRandom": 0,
+ "summary.write.reorderTasksRandomSeed": 0,
+ "summary.write.bwMaxMIB": 904.92,
+ "summary.write.bwMinMIB": 904.92,
+ "summary.write.bwMeanMIB": 904.92,
+ "summary.write.bwStdMIB": 0.0,
+ "summary.write.OPsMax": 3619.6798,
+ "summary.write.OPsMin": 3619.6798,
+ "summary.write.OPsMean": 3619.6798,
+ "summary.write.OPsSD": 0.0,
+ "summary.write.MeanTime": 0.0011,
+ "summary.write.xsizeMiB": 1.0,
+ "summary.read.operation": "read",
+ "summary.read.API": "POSIX",
+ "summary.read.TestID": 0,
+ "summary.read.ReferenceNumber": 0,
+ "summary.read.segmentCount": 1,
+ "summary.read.blockSize": 1048576,
+ "summary.read.transferSize": 262144,
+ "summary.read.numTasks": 1,
+ "summary.read.tasksPerNode": 1,
+ "summary.read.repetitions": 1,
+ "summary.read.filePerProc": 0,
+ "summary.read.reorderTasks": 0,
+ "summary.read.taskPerNodeOffset": 1,
+ "summary.read.reorderTasksRandom": 0,
+ "summary.read.reorderTasksRandomSeed": 0,
+ "summary.read.bwMaxMIB": 6615.6215,
+ "summary.read.bwMinMIB": 6615.6215,
+ "summary.read.bwMeanMIB": 6615.6215,
+ "summary.read.bwStdMIB": 0.0,
+ "summary.read.OPsMax": 26462.4858,
+ "summary.read.OPsMin": 26462.4858,
+ "summary.read.OPsMean": 26462.4858,
+ "summary.read.OPsSD": 0.0,
+ "summary.read.MeanTime": 0.0002,
+ "summary.read.xsizeMiB": 1.0,
+ "test.0.starttime": "Thu Feb 22 00:36:12 2024",
+ "test.0.capacity": "1.8 TiB",
+ "test.0.used_capacity": "20.2%",
+ "test.0.inodes": "116.4 Mi",
+ "test.0.used_inodes": "5.3%",
+ "test.0.parameters.testID": 0,
+ "test.0.parameters.refnum": 0,
+ "test.0.parameters.api": "POSIX",
+ "test.0.parameters.platform": "2b3ee0c4c(Linux)",
+ "test.0.parameters.testFileName": "testFile",
+ "test.0.parameters.deadlineForStonewall": 0,
+ "test.0.parameters.stoneWallingWearOut": 0,
+ "test.0.parameters.maxTimeDuration": 0,
+ "test.0.parameters.outlierThreshold": 0,
+ "test.0.parameters.options": "(null)",
+ "test.0.parameters.dryRun": 0,
+ "test.0.parameters.nodes": 1,
+ "test.0.parameters.memoryPerTask": 0,
+ "test.0.parameters.memoryPerNode": 0,
+ "test.0.parameters.tasksPerNode": 1,
+ "test.0.parameters.repetitions": 1,
+ "test.0.parameters.multiFile": 0,
+ "test.0.parameters.interTestDelay": 0,
+ "test.0.parameters.fsync": 0,
+ "test.0.parameters.fsyncperwrite": 0,
+ "test.0.parameters.useExistingTestFile": 0,
+ "test.0.parameters.uniqueDir": 0,
+ "test.0.parameters.singleXferAttempt": 0,
+ "test.0.parameters.readFile": 1,
+ "test.0.parameters.writeFile": 1,
+ "test.0.parameters.filePerProc": 0,
+ "test.0.parameters.reorderTasks": 0,
+ "test.0.parameters.reorderTasksRandom": 0,
+ "test.0.parameters.reorderTasksRandomSeed": 0,
+ "test.0.parameters.randomOffset": 0,
+ "test.0.parameters.checkWrite": 0,
+ "test.0.parameters.checkRead": 0,
+ "test.0.parameters.dataPacketType": 0,
+ "test.0.parameters.keepFile": 0,
+ "test.0.parameters.keepFileWithError": 0,
+ "test.0.parameters.warningAsErrors": 0,
+ "test.0.parameters.verbose": 0,
+ "test.0.parameters.data packet type": "g",
+ "test.0.parameters.setTimeStampSignature/incompressibleSeed": 0,
+ "test.0.parameters.collective": 0,
+ "test.0.parameters.segmentCount": 1,
+ "test.0.parameters.transferSize": 262144,
+ "test.0.parameters.blockSize": 1048576,
+ "test.0.options.api": "POSIX",
+ "test.0.options.apiVersion": "",
+ "test.0.options.test filename": "testFile",
+ "test.0.options.access": "single-shared-file",
+ "test.0.options.type": "independent",
+ "test.0.options.segments": 1,
+ "test.0.options.ordering in a file": "sequential",
+ "test.0.options.ordering inter file": "no tasks offsets",
+ "test.0.options.nodes": 1,
+ "test.0.options.tasks": 1,
+ "test.0.options.clients per node": 1,
+ "test.0.options.repetitions": 1,
+ "test.0.options.xfersize": "262144 bytes",
+ "test.0.options.blocksize": "1 MiB",
+ "test.0.options.aggregate filesize": "1 MiB",
+ "test.0.results.0.access": "write",
+ "test.0.results.0.bwMiB": 904.92,
+ "test.0.results.0.blockKiB": 1024.0,
+ "test.0.results.0.xferKiB": 256.0,
+ "test.0.results.0.iops": 3842.6972,
+ "test.0.results.0.latency": 0.0003,
+ "test.0.results.0.openTime": 0.0001,
+ "test.0.results.0.wrRdTime": 0.001,
+ "test.0.results.0.closeTime": 0.0,
+ "test.0.results.0.totalTime": 0.0011,
+ "test.0.results.1.access": "read",
+ "test.0.results.1.bwMiB": 6615.6215,
+ "test.0.results.1.blockKiB": 1024.0,
+ "test.0.results.1.xferKiB": 256.0,
+ "test.0.results.1.iops": 27962.0267,
+ "test.0.results.1.latency": 0.0,
+ "test.0.results.1.openTime": 0.0,
+ "test.0.results.1.wrRdTime": 0.0001,
+ "test.0.results.1.closeTime": 0.0,
+ "test.0.results.1.totalTime": 0.0002
+ }
+ }
+ ]
+}
+```
+
+
+
+And how to save to file
+
+```bash
+compspec extract --outfile ior-test.json ior
+```
+
+And run the example to see how to use compspec-ior directly in Python to generate the same
+artifact.
+
+```bash
+python ./examples/singleton-run.py
+```
+
+
+### Development
+
+If you open the [Development container](.devcontainer) in VSCode, you'll find ior on the path:
```bash
-compspec extract ior ...
+$ which ior
+/usr/bin/ior
```
-More coming soon!
+This allows us to easily develop and test the compatibility plugin. You can
## TODO
-- Developer environment with IOR installed (for others and me too)
-- testing, etc with pre-commit and spell checking
-- implement run functionality
- - use reasonable defaults for when nothing provided
- - outputs should map to new schema.json attributes
- - main library compspec should have support for oras push, etc.
+- how to handle adding lists (with indices) to schema?
## License
diff --git a/compspec_ior/__init__.py b/compspec_ior/__init__.py
index 6a49c28..3def8be 100644
--- a/compspec_ior/__init__.py
+++ b/compspec_ior/__init__.py
@@ -1,2 +1,2 @@
-from .plugin import ExtractorPlugin
+from .plugin import Plugin
from .version import __version__
diff --git a/compspec_ior/defaults.py b/compspec_ior/defaults.py
new file mode 100644
index 0000000..ee66130
--- /dev/null
+++ b/compspec_ior/defaults.py
@@ -0,0 +1,4 @@
+# These are required to be given to compspec
+spec_version = "0.0.0"
+schema_url = "https://raw.githubusercontent.com/compspec/compspec-ior/main/compspec_ior/schema.json"
+namespace = "io.compspec.ior"
diff --git a/compspec_ior/plugin.py b/compspec_ior/plugin.py
index d938094..969ea83 100644
--- a/compspec_ior/plugin.py
+++ b/compspec_ior/plugin.py
@@ -1,14 +1,27 @@
import argparse
+import json
+import logging
+import os
+import shlex
-from compspec.plugin import Plugin
+import compspec.utils as utils
+from compspec.plugin import PluginBase
+import compspec_ior.defaults as defaults
-class ExtractorPlugin(Plugin):
+logger = logging.getLogger("compspec-ior")
+
+
+class Plugin(PluginBase):
"""
The IOR extractor plugin
"""
+ # These metadata fields are required (and checked for)
description = "IOR parallel I/O benchmarks"
+ namespace = defaults.namespace
+ version = defaults.spec_version
+ schema = defaults.schema_url
def add_arguments(self, subparser):
"""
@@ -19,19 +32,116 @@ def add_arguments(self, subparser):
formatter_class=argparse.RawTextHelpFormatter,
description=self.description,
)
+ # Ensure these are namespaced to your plugin
ior.add_argument(
- "args",
+ "ior_args",
help="Arguments for IOR (defaults to reasonable set if not defined)",
- nargs="?",
+ nargs="*",
+ )
+ ior.add_argument(
+ "--ior-load",
+ dest="ior_load",
+ help="Load metadata from this file instead of extraction from system directly.",
)
- def run(self, args):
+ def run_ior(self, command):
+ """
+ Run IOR to generate json instead of loading from file.
+ """
+ if not isinstance(command, list):
+ command = shlex.split(command)
+
+ if "ior" not in command:
+ command = ["ior"] + command
+
+ # We must output to json
+ if "summaryFormat" not in command:
+ command += ["-O", "summaryFormat=JSON"]
+ logger.debug(" ".join(command))
+
+ result = utils.run_command(command)
+ if result["return_code"] != 0:
+ msg = " ".join(command)
+ raise ValueError(f"Issue with running {msg}: {result['message']}")
+
+ # Load the result
+ return json.loads(result["message"])
+
+ def extract(self, args, extra):
"""
Run IOR and map metadata into compspec schema.
+
+ Note that "extract" simply needs to return key value
+ pairs of extraction metadata and values. Ideally, you can
+ maintain things in lowercase, and if you have flattened
+ groups represent them as .. Do not worry
+ about adding a top level namespace for the plugin, this
+ is handled by compspec.
"""
- # TODO
- print(args)
- print("RUN IOR HERE - choose defaults if")
- import IPython
+ if args.ior_load:
+ data = utils.read_json(args.ior_load)
+ else:
+ data = self.run_ior(args.ior_args)
+
+ # Prepare metadata, this is handled by another function for extended use
+ return self.load_metadata(data)
+
+ def load_metadata(self, data):
+ """
+ Load IOR metadata into a dictionary that can be given to compspec.
+ """
+ # Be forgiving if they provide a filepath
+ if isinstance(data, str) and os.path.exists(data):
+ data = utils.read_json(data)
+
+ meta = {}
+
+ # High level metadata
+ for key in ["Version", "Began", "Machine", "Finished", "Command line"]:
+ value = data.get(key)
+
+ # This handles a single entry, lowercase and removes spaces
+ key = utils.normalize_key(key)
+
+ # Key needs to be lowercase and
+ # Do not add empty values
+ if value is not None:
+ meta[key] = value
+
+ # Add in summary - the operations (I think) should be unique
+ for entry in data["summary"]:
+ key = f"summary.{entry['operation']}"
+ for k, v in entry.items():
+ # This is more of the namespace
+ if k == "operation":
+ continue
+ meta[f"{key}.{k}"] = v
+
+ # Now add in tests (note it's not clear yet which of these we should keep)
+ for test in data["tests"]:
+ key = f"test.{test['TestID']}"
+ for a in [
+ "StartTime",
+ "Capacity",
+ "Used Capacity",
+ "Inodes",
+ "Used Inodes",
+ ]:
+ subkey = utils.normalize_key(a)
+ meta[f"{key}.{subkey}"] = test[a]
+
+ # Add in Parameters for tests
+ # These are in camel case, let's keep them as such
+ for k, v in (test.get("Parameters") or {}).items():
+ meta[f"{key}.parameters.{k}"] = v
+
+ # Now add in options
+ for k, v in (test.get("Options") or {}).items():
+ subkey = utils.normalize_key(k)
+ meta[f"{key}.options.{k}"] = v
- IPython.embed()
+ # Add results from list
+ for i, result in enumerate(test.get("Results") or []):
+ for k, v in result.items():
+ meta[f"{key}.results.{i}.{k}"] = v
+ return meta
diff --git a/compspec_ior/schema.json b/compspec_ior/schema.json
index 60642e6..a8bd491 100644
--- a/compspec_ior/schema.json
+++ b/compspec_ior/schema.json
@@ -3,6 +3,10 @@
"id": "hpc.ior",
"type": "compspec",
"label": "compatibilities",
+ "metadata": {
+ "version": "0.0.0",
+ "source": "https://github.com/supercontainers/compspec"
+ },
"nodes": {
"modules": {
"label": "IOR modules"
@@ -138,9 +142,176 @@
},
"options.random_offset_seed": {
"label": "The seed for -z"
+ },
+ "version": {
+ "label": "The version of IOR"
+ },
+ "began": {
+ "label": "When the analysis began (human readable time)"
+ },
+ "finished": {
+ "label": "When the analysis finished (human readable time)"
+ },
+ "machine": {
+ "label": "IORs representation of the machine it ran on"
+ },
+ "command_line": {
+ "label": "Command line provided to run IOR"
+ },
+ "summary.write.operation": {
+ "label": "A summary of a write operation"
+ },
+ "summary.write.API": {
+ "label": "Module API used"
+ },
+ "summary.write.TestID": {
+ "label": "ID of the test"
+ },
+ "summary.write.ReferenceNumber": {
+ "label": "Reference number of write"
+ },
+ "summary.write.segmentCount": {
+ "label": "Segments counts of write"
+ },
+ "summary.write.blockSize": {
+ "label": "Block size of write"
+ },
+ "summary.write.transferSize": {
+ "label": "Transfer size of write"
+ },
+ "summary.write.numTasks": {
+ "label": "Number of tasks"
+ },
+ "summary.write.tasksPerNode": {
+ "label": "Taks per node"
+ },
+ "summary.write.repetitions": {
+ "label": "Repetitions of write"
+ },
+ "summary.write.filePerProc": {
+ "label": "Files per process of write"
+ },
+ "summary.write.reorderTasks": {
+ "label": "Reorder of tasks for write"
+ },
+ "summary.write.taskPerNodeOffset": {
+ "label": "Task per node offset of write"
+ },
+ "summary.write.reorderTasksRandom": {
+ "label": "Reorder tasks random for write"
+ },
+ "summary.write.reorderTasksRandomSeed": {
+ "label": "Reorder task random seed of write"
+ },
+ "summary.write.bwMaxMIB": {
+ "label": "Bandwidth Max MIB of write"
+ },
+ "summary.write.bwMinMIB": {
+ "label": "Bandwidth Min MIB of write"
+ },
+ "summary.write.bwMeanMIB": {
+ "label": "Bandwidth mean MIB of write"
+ },
+ "summary.write.bwStdMIB": {
+ "label": "Bandwidth standard deviation MID of write"
+ },
+ "summary.write.OPsMax": {
+ "label": "Operations max of write"
+ },
+ "summary.write.OPsMin": {
+ "label": "Operations min of write"
+ },
+ "summary.write.OPsMean": {
+ "label": "Operations mean of write"
+ },
+ "summary.write.OPsSD": {
+ "label": "Operations standard deviation of write"
+ },
+ "summary.write.MeanTime": {
+ "label": "Mean time of write"
+ },
+ "summary.write.xsizeMiB": {
+ "label": "Size MIB of write"
+ },
+ "summary.read.operation": {
+ "label": "A summary of a read operation"
+ },
+ "summary.read.API": {
+ "label": "Module API used"
+ },
+ "summary.read.TestID": {
+ "label": "ID of the test"
+ },
+ "summary.read.ReferenceNumber": {
+ "label": "Reference number of read"
+ },
+ "summary.read.segmentCount": {
+ "label": "Segments counts of read"
+ },
+ "summary.read.blockSize": {
+ "label": "Block size of read"
+ },
+ "summary.read.transferSize": {
+ "label": "Transfer size of read"
+ },
+ "summary.read.numTasks": {
+ "label": "Number of tasks"
+ },
+ "summary.read.tasksPerNode": {
+ "label": "Taks per node"
+ },
+ "summary.read.repetitions": {
+ "label": "Repetitions of read"
+ },
+ "summary.read.filePerProc": {
+ "label": "Files per process of read"
+ },
+ "summary.read.reorderTasks": {
+ "label": "Reorder of tasks for read"
+ },
+ "summary.read.taskPerNodeOffset": {
+ "label": "Task per node offset of read"
+ },
+ "summary.read.reorderTasksRandom": {
+ "label": "Reorder tasks random for read"
+ },
+ "summary.read.reorderTasksRandomSeed": {
+ "label": "Reorder task random seed of read"
+ },
+ "summary.read.bwMaxMIB": {
+ "label": "Bandwidth Max MIB of read"
+ },
+ "summary.read.bwMinMIB": {
+ "label": "Bandwidth Min MIB of read"
+ },
+ "summary.read.bwMeanMIB": {
+ "label": "Bandwidth mean MIB of read"
+ },
+ "summary.read.bwStdMIB": {
+ "label": "Bandwidth standard deviation MID of read"
+ },
+ "summary.read.OPsMax": {
+ "label": "Operations max of read"
+ },
+ "summary.read.OPsMin": {
+ "label": "Operations min of read"
+ },
+ "summary.read.OPsMean": {
+ "label": "Operations mean of read"
+ },
+ "summary.read.OPsSD": {
+ "label": "Operations standard deviation of read"
+ },
+ "summary.read.MeanTime": {
+ "label": "Mean time of read"
+ },
+ "summary.read.xsizeMiB": {
+ "label": "Size MIB of read"
+ },
+ "test": {
+ "label": "test list"
}
},
-
"edges": [
{
"source": "modules",
@@ -356,11 +527,267 @@
"source": "options",
"target": "options.random_offset_seed",
"relation": "has-option"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.operation",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.API",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.TestID",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.ReferenceNumber",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.SegmentCount",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.blockSize",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.transferSize",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.numTasks",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.tasksPerNode",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.repetitions",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.filePerProc",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.reorderTasks",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.taskPerNodeOffset",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.reorderTasksRandom",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.reorderTasksRandomSeed",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.bwMaxMIB",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.bwMinMIB",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.bwMeanMIB",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.bwStdMIB",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.OPsMax",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.OPsMin",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.OPsMean",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.OPsSD",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.MeanTime",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.write.xsizeMiB",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.operation",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.API",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.TestID",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.ReferenceNumber",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.SegmentCount",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.blockSize",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.transferSize",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.numTasks",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.tasksPerNode",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.repetitions",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.filePerProc",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.reorderTasks",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.taskPerNodeOffset",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.reorderTasksRandom",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.reorderTasksRandomSeed",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.bwMaxMIB",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.bwMinMIB",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.bwMeanMIB",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.bwStdMIB",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.OPsMax",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.OPsMin",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.OPsMean",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.OPsSD",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.MeanTime",
+ "relation": "contains"
+ },
+ {
+ "source": "summary",
+ "target": "summary.read.xsizeMiB",
+ "relation": "contains"
}
- ],
- "metadata": {
- "version": "0.0.0",
- "source": "https://github.com/supercontainers/compspec"
- }
+ ]
}
}
diff --git a/examples/singleton-run.py b/examples/singleton-run.py
new file mode 100644
index 0000000..6f782fe
--- /dev/null
+++ b/examples/singleton-run.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+# This is a singleton example of running the plugin without compspec,
+# primarily to test, etc. There is no validation of the schema or other.
+
+import os
+
+import compspec.artifact
+
+from compspec_ior.plugin import Plugin
+
+here = os.path.dirname(os.path.abspath(__file__))
+test_file = os.path.join(here, "test", "ior-data.json")
+
+
+def main():
+ # Load data we've generated with IOR
+ plugin = Plugin("ior")
+ attributes = plugin.load_metadata(test_file)
+ artifact = compspec.artifact.generate(plugin, "ior-compat-example", attributes)
+ print(artifact.render())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/test/ior-data.json b/examples/test/ior-data.json
new file mode 100644
index 0000000..c115236
--- /dev/null
+++ b/examples/test/ior-data.json
@@ -0,0 +1,165 @@
+{
+ "Version": "4.0.0rc1",
+ "Began": "Wed Feb 21 15:39:09 2024",
+ "Command line": "ior -O summaryFormat=JSON",
+ "Machine": "Linux 2b3ee0c4c948",
+ "tests": [
+ {
+ "TestID": 0,
+ "StartTime": "Wed Feb 21 15:39:09 2024" , "Path": "testFile","Capacity": "1.8 TiB", "Used Capacity": "20.0%","Inodes": "116.4 Mi", "Used Inodes" : "5.2%"
+,
+ "Parameters": {
+ "testID": 0,
+ "refnum": 0,
+ "api": "POSIX",
+ "platform": "2b3ee0c4c(Linux)",
+ "testFileName": "testFile",
+ "deadlineForStonewall": 0,
+ "stoneWallingWearOut": 0,
+ "maxTimeDuration": 0,
+ "outlierThreshold": 0,
+ "options": "(null)",
+ "dryRun": 0,
+ "nodes": 1,
+ "memoryPerTask": 0,
+ "memoryPerNode": 0,
+ "tasksPerNode": 1,
+ "repetitions": 1,
+ "multiFile": 0,
+ "interTestDelay": 0,
+ "fsync": 0,
+ "fsyncperwrite": 0,
+ "useExistingTestFile": 0,
+ "uniqueDir": 0,
+ "singleXferAttempt": 0,
+ "readFile": 1,
+ "writeFile": 1,
+ "filePerProc": 0,
+ "reorderTasks": 0,
+ "reorderTasksRandom": 0,
+ "reorderTasksRandomSeed": 0,
+ "randomOffset": 0,
+ "checkWrite": 0,
+ "checkRead": 0,
+ "dataPacketType": 0,
+ "keepFile": 0,
+ "keepFileWithError": 0,
+ "warningAsErrors": 0,
+ "verbose": 0,
+ "data packet type": "g",
+ "setTimeStampSignature/incompressibleSeed": 0,
+ "collective": 0,
+ "segmentCount": 1,
+ "transferSize": 262144,
+ "blockSize": 1048576
+ }
+,
+ "Options": {
+ "api": "POSIX",
+ "apiVersion": "",
+ "test filename": "testFile",
+ "access": "single-shared-file",
+ "type": "independent",
+ "segments": 1,
+ "ordering in a file": "sequential",
+ "ordering inter file": "no tasks offsets",
+ "nodes": 1,
+ "tasks": 1,
+ "clients per node": 1,
+ "repetitions": 1,
+ "xfersize": "262144 bytes",
+ "blocksize": "1 MiB",
+ "aggregate filesize": "1 MiB"
+ }
+,
+ "Results": [
+ {
+ "access": "write",
+ "bwMiB": 1377.4397,
+ "blockKiB": 1024.0000,
+ "xferKiB": 256.0000,
+ "iops": 5714.3106,
+ "latency": 0.0002,
+ "openTime": 0.0000,
+ "wrRdTime": 0.0007,
+ "closeTime": 0.0000,
+ "totalTime": 0.0007
+ }
+,
+ {
+ "access": "read",
+ "bwMiB": 14122.2357,
+ "blockKiB": 1024.0000,
+ "xferKiB": 256.0000,
+ "iops": 58867.4246,
+ "latency": 0.0000,
+ "openTime": 0.0000,
+ "wrRdTime": 0.0001,
+ "closeTime": 0.0000,
+ "totalTime": 0.0001
+ }
+]
+
+ }
+]
+,
+"summary": [
+ {
+ "operation": "write",
+ "API": "POSIX",
+ "TestID": 0,
+ "ReferenceNumber": 0,
+ "segmentCount": 1,
+ "blockSize": 1048576,
+ "transferSize": 262144,
+ "numTasks": 1,
+ "tasksPerNode": 1,
+ "repetitions": 1,
+ "filePerProc": 0,
+ "reorderTasks": 0,
+ "taskPerNodeOffset": 1,
+ "reorderTasksRandom": 0,
+ "reorderTasksRandomSeed": 0,
+ "bwMaxMIB": 1377.4397,
+ "bwMinMIB": 1377.4397,
+ "bwMeanMIB": 1377.4397,
+ "bwStdMIB": 0.0000,
+ "OPsMax": 5509.7589,
+ "OPsMin": 5509.7589,
+ "OPsMean": 5509.7589,
+ "OPsSD": 0.0000,
+ "MeanTime": 0.0007,
+ "xsizeMiB": 1.0000
+ }
+,
+ {
+ "operation": "read",
+ "API": "POSIX",
+ "TestID": 0,
+ "ReferenceNumber": 0,
+ "segmentCount": 1,
+ "blockSize": 1048576,
+ "transferSize": 262144,
+ "numTasks": 1,
+ "tasksPerNode": 1,
+ "repetitions": 1,
+ "filePerProc": 0,
+ "reorderTasks": 0,
+ "taskPerNodeOffset": 1,
+ "reorderTasksRandom": 0,
+ "reorderTasksRandomSeed": 0,
+ "bwMaxMIB": 14122.2357,
+ "bwMinMIB": 14122.2357,
+ "bwMeanMIB": 14122.2357,
+ "bwStdMIB": 0.0000,
+ "OPsMax": 56488.9428,
+ "OPsMin": 56488.9428,
+ "OPsMean": 56488.9428,
+ "OPsSD": 0.0000,
+ "MeanTime": 0.0001,
+ "xsizeMiB": 1.0000
+ }
+]
+,
+"Finished": "Wed Feb 21 15:39:09 2024"
+}