Add the sp_rand_init and sp_rand_cleanup tools.

These will be used on the worker node to set up a random volume name
prefix for the StorPool volumes and snapshots created during
the OpenStack CI run, and detach and remove them all later.

Change-Id: Ic558e2183db8068545f7454f956dc8bc740959c6
diff --git a/tools/sp-rand/src/sp_rand/init.py b/tools/sp-rand/src/sp_rand/init.py
new file mode 100644
index 0000000..60baa88
--- /dev/null
+++ b/tools/sp-rand/src/sp_rand/init.py
@@ -0,0 +1,100 @@
+"""Set up the random volume name prefix."""
+
+from __future__ import annotations
+
+import argparse
+import datetime
+import pathlib
+import random
+
+from typing import NamedTuple, Tuple  # noqa: H301
+
+from . import defs
+
+DEFAULT_TAG = "osci-1-date"
+
+
+class Config(NamedTuple):
+    """Runtime configuration for the sp_rand_init tool."""
+
+    confdir: pathlib.Path
+    noop: bool
+    prefix_var: str
+    tag: str
+
+    @staticmethod
+    def default() -> Config:
+        """Build a configuration object with the default values."""
+        return Config(
+            confdir=defs.DEFAULT_CONFDIR,
+            noop=False,
+            prefix_var=defs.PREFIX_VAR,
+            tag=DEFAULT_TAG,
+        )
+
+
+def generate_prefix(cfg: Config) -> str:
+    """Generate a random name prefix."""
+    now = datetime.datetime.now()
+    rval = random.randint(1000, 9999)
+    tdate = f"{now.year:04}{now.month:02}{now.day:02}"
+    ttime = f"{now.hour:02}{now.minute:02}"
+    return f"{cfg.tag}-{tdate}-{ttime}-{rval}-"
+
+
+def store_prefix(cfg: Config) -> Tuple[pathlib.Path, str]:
+    """Generate and store the random name prefix into a file."""
+    prefix = generate_prefix(cfg)
+    conffile = cfg.confdir / defs.FILENAME
+    if not cfg.noop:
+        conffile.write_text(f"{cfg.prefix_var}={prefix}\n", encoding="UTF-8")
+    return conffile, prefix
+
+
+def parse_args() -> Config:
+    """Parse the command-line arguments."""
+    parser = argparse.ArgumentParser(prog="sp_rand_init")
+    parser.add_argument(
+        "-d",
+        "--confdir",
+        type=pathlib.Path,
+        default=defs.DEFAULT_CONFDIR,
+        help="The directory to create the configuration file in",
+    )
+    parser.add_argument(
+        "-N",
+        "--noop",
+        action="store_true",
+        help="No-operation mode; display what would have been done",
+    )
+    parser.add_argument(
+        "-p",
+        "--prefix-var",
+        type=str,
+        default=defs.PREFIX_VAR,
+        help="The name of the variable to store into the configuration file",
+    )
+    parser.add_argument(
+        "-t",
+        "--tag",
+        type=str,
+        default=DEFAULT_TAG,
+        help="The tag that the prefix should start with",
+    )
+
+    args = parser.parse_args()
+
+    return Config(
+        confdir=args.confdir, noop=args.noop, prefix_var=args.prefix_var, tag=args.tag
+    )
+
+
+def main() -> None:
+    """Main program: parse options, generate a prefix, store it."""
+    cfg = parse_args()
+    conffile, prefix = store_prefix(cfg)
+    print(f"{prefix}\n{conffile}")
+
+
+if __name__ == "__main__":
+    main()