Skip to main content

Synthetic RACF IRRDBU00 unload generator

Project description

racf-generator

A synthetic RACF IRRDBU00 unload generator.

racf_generator builds a fully synthetic, internally consistent enterprise (users, groups, datasets, general resources, started tasks, OMVS/TSO segments, ACLs) and serializes it into a RACF IRRDBU00 unload file — the same flat-file format produced by running IRRDBU00 on z/OS and downloading it via FTP/ASCII transfer.

No real RACF data is ever used. Everything is generated from a seed, so the same seed always produces byte-identical output.

The generator is schema-driven: it reads the record layout (offsets.json) from the mfpandas package at runtime instead of hardcoding field positions, so it stays compatible as that schema evolves. Output produced by this generator is verified (in this project's own test suite) to parse cleanly through the real mfpandas.IRRDBU00 parser.

Why

Useful anywhere you need a RACF unload but can't (or shouldn't) use a real one:

  • unit/integration tests for tools that consume IRRDBU00 unloads
  • CI pipelines
  • documentation and screenshots
  • demos and sales environments
  • training environments
  • benchmarking parsers against different data volumes

Install

pip install racf-generator

Requires Python 3.10+. Runtime dependencies are mfpandas (schema source) and Faker (realistic human names/emails/phone numbers) — both installed automatically.

Development install

If you've cloned this repository instead (to run the tests, use scripts/try_it.py, or work on the generator itself):

pip install -e ".[dev]"
# or: make install

Quick start

from racf_generator import IRRDBU00Generator

generator = IRRDBU00Generator(seed=42, profile="medium")
generator.write("dummy.unload")

That's it — dummy.unload is now a plain-text IRRDBU00 file you can hand to any RACF unload parser, including mfpandas.IRRDBU00:

from mfpandas import IRRDBU00

r = IRRDBU00(irrdbu00="dummy.unload")
r.parse_t()
print(r.errors)        # []
print(len(r._users))   # 500
print(len(r._groups))  # 60

Reusing a generator

.build() builds the in-memory enterprise model once and caches it — calling .write() multiple times (or .build() directly to inspect the model) never regenerates different data:

generator = IRRDBU00Generator(seed=42, profile="small")

model = generator.build()          # build once
print(len(model.users))            # 50
print(len(model.groups))           # 10

generator.write("first.unload")    # uses the cached model
generator.write("second.unload")   # same model, same bytes as first.unload

Determinism

The same seed always produces the same enterprise, down to the byte:

IRRDBU00Generator(seed=42, profile="small").write("a.unload")
IRRDBU00Generator(seed=42, profile="small").write("b.unload")
# a.unload and b.unload are byte-identical

IRRDBU00Generator(seed=7, profile="small").write("c.unload")
# c.unload differs — different seed, different (but still internally
# consistent) enterprise

Try it from a clone, no typing required

scripts/try_it.py is a convenience script for this repository — it's not part of the published PyPI package, so it only works if you've cloned the repo (see Development install above):

./scripts/try_it.py                                            # medium profile, verified
./scripts/try_it.py --profile small
./scripts/try_it.py --profile large --out /tmp/big.unload
./scripts/try_it.py --profile enterprise --users 200 --groups 25
./scripts/try_it.py --seed 7 --no-verify                       # skip the mfpandas round-trip

It writes the file, prints a summary (user/group/dataset/resource counts, ...), and by default parses the result back with the real mfpandas.IRRDBU00 to confirm it's valid.

Pointing at a different schema

By default the generator loads offsets.json from whatever version of mfpandas is installed. If you need to generate against a different/newer schema file without upgrading mfpandas, pass schema_path explicitly:

IRRDBU00Generator(seed=42, profile="small", schema_path="/path/to/offsets.json").write("dummy.unload")

Profiles

A profile just controls how big the generated enterprise is — how many users and groups get created. Everything else (naming conventions, privilege rarity, dataset/resource realism, referential integrity) is identical across profiles; only the scale changes.

Profile Users Groups Typical use
small 50 10 fast unit tests, quick local checks
medium 500 60 demos, docs, "realistic-looking" screenshots
large 5000 400 load testing, parser benchmarking
enterprise you choose you choose anything the three presets don't cover

small, medium, and large are fixed presets (see racf_generator/profiles.py) — you just name them:

IRRDBU00Generator(seed=42, profile="small").write("small.unload")
IRRDBU00Generator(seed=42, profile="medium").write("medium.unload")
IRRDBU00Generator(seed=42, profile="large").write("large.unload")

enterprise is the escape hatch: instead of picking a preset, you supply user_count and group_count yourself. Use it when you need a size the presets don't offer — say, 150 users for a specific test fixture, or 50,000 users to stress-test a downstream parser well beyond what large covers:

IRRDBU00Generator(
    seed=42,
    profile="enterprise",
    user_count=150,
    group_count=20,
).write("custom.unload")

profile="enterprise" requires both user_count and group_count — it raises ValueError if either is missing, since there's no sensible default size for "enterprise" the way there is for the three presets.

One constraint applies to every profile including enterprise: group_count must be at least 9. That's because 9 groups (SYS1, SECURITY, OPERATIONS, PAYROLL, HR, FINANCE, DBA, NETWORK, AUDIT) are a fixed catalog that always gets created first — they're what everything else (dataset ownership, resource access, started tasks) hangs off of. Any additional groups beyond those 9 are generated as subgroups underneath them (e.g. PAYROLL2, FINANCE3, ...).

Similarly, user_count must be large enough to fit the fixed catalog of service IDs (CICSA, DB2MSTR, MQM, TCPIP, WASUSER, OMVSKERN, JES2) and admin IDs (SECADM01, SYSADM1, AUDITOR1) — 10 users at minimum. Every user above that count is a Faker-generated human.

What gets generated

  • Groups — a fixed department hierarchy (SYS1SECURITY, OPERATIONS, PAYROLL, HR, FINANCE, DBA, NETWORK, AUDIT), plus extra subgroups if group_count exceeds 9.
  • Users — a mix of:
    • human users, with Faker-generated full names and RACF-style userids derived from them (Maria JohnsonMJOHNSON), plus DEPT/COSTCTR/EMAIL/ PHONE installation data (also Faker-driven)
    • service IDs (CICSA, DB2MSTR, MQM, TCPIP, WASUSER, OMVSKERN, JES2)
    • admin IDs (SECADM01, SYSADM1, AUDITOR1)
    • realistic privilege rarity: SPECIAL is rare, OPERATIONS is uncommon, AUDITOR is very rare, and SPECIAL/AUDITOR users belong to SECURITY
    • internally consistent lifecycle state: active, revoked (locked out, no recent logon), or expired (stale password, still logs on)
  • Group connects — every user connected to their default group, plus some human users connected to a second group
  • OMVS segments — unique UIDs, home paths, and shell programs (always present for service/admin users, probabilistic for human users)
  • TSO segments — probabilistic, human users only
  • Datasets — under SYS1.**, SYS2.**, PAYROLL.**, HR.**, FINANCE.**, USER.<userid>.**, DB2.<subsystem>.**, CICS.<region>.**, with realistic access patterns (e.g. PAYROLL group gets ALTER on payroll datasets, HR group gets UPDATE on HR datasets)
  • General resourcesFACILITY, OPERCMDS, XFACILIT, SURROGAT, PROGRAM, SERVER, EJBROLE profiles with group-based access
  • Started tasksCICSA, DB2MSTR, MQM, TCPIP, OMVS, JES2, each mapped to a real backing user

Every reference resolves — no dangling groups, users, datasets, or resources — and it's enforced by the test suite (tests/test_generator_integration.py).

Testing

pip install -e ".[dev]"
pytest tests/ -v
# or: make test

The suite includes a real round-trip: it writes a file with this generator and parses it back with the actual installed mfpandas.IRRDBU00, asserting zero parse errors.

Releasing

make check       # build sdist + wheel, validate with twine
make upload-test # dry run against TestPyPI
make upload      # publish to PyPI

upload/upload-test need PyPI credentials — either a ~/.pypirc, or TWINE_USERNAME=__token__ TWINE_PASSWORD=<pypi-api-token> in the environment. Run make help for the full target list.

Architecture

Two layers:

  • racf_generator.model / racf_generator.builder — the enterprise domain model and the generators that populate it. No knowledge of the IRRDBU00 file format at all.
  • racf_generator.writer — schema-driven serialization only. Loads offsets.json from mfpandas, maps domain objects to field dicts, and renders fixed-width lines. No business logic — if a record type is missing from the schema, it's skipped with a warning instead of crashing.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

racf_generator-0.1.0.tar.gz (26.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

racf_generator-0.1.0-py3-none-any.whl (21.4 kB view details)

Uploaded Python 3

File details

Details for the file racf_generator-0.1.0.tar.gz.

File metadata

  • Download URL: racf_generator-0.1.0.tar.gz
  • Upload date:
  • Size: 26.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for racf_generator-0.1.0.tar.gz
Algorithm Hash digest
SHA256 bab8f06d04f35d1598ff03d3e2e89c65e1c4df1e3741cb96fd735c2468b8758e
MD5 6e60e3ea5286a6498e147ea85952b008
BLAKE2b-256 b025692dd8df4c2a7ab7c09903da51f537ad629d148b60e3f4931dd90e03a45c

See more details on using hashes here.

File details

Details for the file racf_generator-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: racf_generator-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 21.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for racf_generator-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e1b02eb2825589c6171f3fea5fe3445205d64f1ebe5615484f80fd64a0bf21e5
MD5 f1bcd34bd8010bef6f377a38b7917429
BLAKE2b-256 612471d83a5f8849d81540e9cfbfcf129025f7c7936f16c57ebffbcf462444b9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page