Imported from huaweicloud/huaweicloud-skills (
skills/devtools/kafka/huawei-cloud-kafka-replica-reassignment/SKILL.md). Install upstream withnpx skills add huaweicloud/huaweicloud-skills --skill huawei-cloud-kafka-replica-reassignment. Copyright stays with the author.
Kafka Replica Expansion & Partition Reassignment
Expand Kafka cluster partitions from their current replication factor to a target factor and
produce partition reassignment JSON that can be passed directly to
kafka-reassign-partitions.sh --execute.
Configurable Parameters
| Parameter | Default | Description |
|---|---|---|
replica_count |
2 | Target replication factor. Accepts 2, 3, N. The scripts add replica_count - current_replicas new replicas per partition. |
disk_threshold |
70 | Disk usage ceiling (%). After allocation, no mount point's projected usage may exceed this value. |
json_strategy |
size_10g |
Strategy for splitting the output JSON, described below. |
json_strategy_param |
10 | Strategy parameter (topic-count threshold or size threshold, depending on strategy). |
JSON Split Strategies
| Strategy | Meaning | Parameter Usage |
|---|---|---|
size_10g |
Topics with any single partition > 10 GB get their own JSON file; the rest are merged into a common file. | json_strategy_param = size threshold (GB), default 10 |
topic_count |
Merge N topics into one JSON file. | json_strategy_param = topic count, default 10 |
single_file |
Merge all partitions into one JSON file. | none |
per_topic |
One JSON file per topic. | none |
custom |
Combined strategy. | json_strategy_param = JSON string, e.g. {"big_size": 10, "small_batch": 10}: topics larger than big_size get one file each, the remaining topics are packed small_batch per file |
Users can also define combined strategies in the UI, e.g. "one file per topic larger than 10 GB,
then 10 topics per file for the rest" — this combination maps to the custom strategy.
The Three Stages (sequential, each gated by checks)
Stage 1: Allocate new replicas -> check -> PASS? -> Stage 2
Stage 2: Merge master table -> check -> PASS? -> Stage 3
Stage 3: Emit JSON -> check -> PASS? -> Deliver
Gate rule: after each stage its corresponding validation runs (see
references/validation-rules.md). Only a PASS moves to the next stage; a FAIL reports the exact
assertion and offending rows, so fix the data and re-run that stage. The gate lives inside the
scripts themselves (validate.py); the Web UI and the CLI share the same checks. If the
validation module is unavailable, the stage is treated as FAIL — never silently pass. Later
stages depend on the previous stage's output files.
Stage 1: Allocate new replicas -> read references/stage1-allocate.md
- Input:
topic.xlsx(current partitions),broker.xlsx(disk usage) - Outputs:
topic.xlsx(original replica table),topic_new.xlsx(new replicas),broker_updated.xlsx(refreshed disk table) - Naming: output names are fixed (
topic.xlsx/topic_new.xlsx/topic_all.xlsx/broker_updated.xlsx) and contain no cluster name. Because the input table is also calledtopic.xlsx,--output-dirmust differ from the input directory; otherwise the script blocks withOUTPUT_OVERWRITES_INPUT(rc=1, no artifacts written). Legacydolce_main*.csvintermediate files from older versions can still be read. - Core: process topics in order and bring every partition up to the target replication factor. Placement is decided by projected usage (current value + the capacity being placed now). Only distinct brokers (L0) are allowed; new replicas are spread across brokers to avoid piling onto one or two, and broker watermarks are refreshed after every allocation.
- Checks: no threshold breach caused by this run (pre-existing over-threshold disks only warn and
are listed in
pre_existing_over_threshold), replica count complete, no same-broker conflicts, new-replica distribution balanced. - When allocation is impossible, block:
status=FAIL, exit code 2, no artifacts written,blockersprovides quantified gaps — never "place it anyway" to fake a complete plan:NO_OTHER_BROKER: only same-broker different-mount candidates are within threshold. Kafka reassignment JSON forbids duplicate broker IDs in one partition'sreplicas(log_dirscan only name directories for existing replicas), so such a plan cannot be expressed and would be rejected on submission — block instead of degrading.L3_BLOCKED: no mount point is within threshold at all (insufficient capacity).
Stage 2: Merge master table -> read references/stage2-merge.md
- Inputs:
topic.xlsx(original replicas),topic_new.xlsx(new replicas) - Output:
topic_all.xlsx(merged master table) - Core: sort by topic total capacity ascending; same topic grouped together, partitions ascending.
- Checks: row count matches, sort order correct, column structure complete.
Stage 3: Emit JSON -> read references/stage3-json.md
- Input:
topic_all.xlsx - Outputs: the reassignment JSON file set under
/json/ - Core: split files by strategy, then validate the generated JSON. The Web UI parameter panel can
select "split by topic count" or "split by topic partition size" and fill in the parameter; the
CLI expresses the same choice with
--strategy+--strategy-param. - Checks: JSON schema valid, file split matches the strategy,
replicas/log_dirsfields complete, no duplicate broker IDs per partition (the old L2 exception has been removed — such JSON would be rejected by Kafka). - Security boundary: topic names come from user-uploaded Excel files (untrusted input), and
per_topic/size_10g/customembed topic names into file names. Before writing, topic names are validated against a whitelist ([A-Za-z0-9._-], no./.., max length 249) plus a realpath containment check; invalid names fail the stage withINVALID_TOPIC_NAMEand no file is written.
Command Line Usage (headless / CI / agent)
The three stages are standalone CLI scripts that run without the Web UI; chain them in headless environments as follows:
# Stage 1: allocate new replicas (--replica-count target factor, --disk-threshold usage %)
python scripts/stage1_allocate.py --topic topic.xlsx --broker broker.xlsx \
--output-dir out --replica-count 2 --disk-threshold 70
# Stage 2: merge master table
python scripts/stage2_merge.py --main out/topic.xlsx --new out/topic_new.xlsx \
--output-dir out --replica-count 2
# Stage 3: emit JSON (artifacts land in out/json/)
python scripts/stage3_json.py --input out/topic_all.xlsx --output-dir out \
--replica-count 2 --strategy size_10g --strategy-param 10
--strategy values and their parameters: size_10g (--strategy-param = per-partition capacity
threshold in GB, default 10), topic_count (= topics per file), single_file, per_topic,
custom (--strategy-param is a JSON string such as
'{"big_size": 10, "small_batch": 10}': topics larger than big_size get one file each, the rest
are packed small_batch per file).
Output contract (identical across the three scripts):
stdoutcarries exactly one JSON object (stage result +stats+validation); callers dojson.loads(stdout)directly.stderrcarries a human-readable four-line stage report (stage / inputs / outputs / validation).- Exit codes:
0= PASS,1= stage validation has FAIL,2= blocked (NO_OTHER_BROKERno distinct broker /L3_BLOCKEDinsufficient capacity; no artifacts written in either case).
Gating: every script runs its own stage validation (validate.py) before exiting and only
returns 0 on PASS — there is no "bypass the validation" path even in headless execution. If
validation fails, do not feed that stage's artifacts downstream; fix the data and re-run the stage.
Working directory: Web UI uploads and artifacts land by default under the system temp
directory kafka-replica-reassignment-work/, overridable with the KAFKA_REASSIGN_WORK_DIR
environment variable; the skill package itself is never written to (the package may be read-only).
Regression Self-Check (run after any script change)
python evals/run_evals.py # exit code = number of failing assertions, 0 = all pass
Fixtures are fixed (evals/fixtures/, regeneratable with evals/build_fixtures.py), artifacts go
to temp directories, and every case verifies that input file shas are unchanged — reruns always
produce the same numbers. Cases cover the golden path, NO_OTHER_BROKER blocking, L3 blocking,
read-only behavior, pre-existing over-threshold disks, replication factor 3, the Stage-3 duplicate-
broker hard check, and topic-name path-traversal interception (eight cases).
Web UI
Start the UI: python scripts/app.py (default port 5000)
UI features:
- Upload area: drag-and-drop
topic.xlsxandbroker.xlsx - Template download: download blank templates (
topic_template.xlsx,broker_template.xlsx) - Parameter panel: adjust replication factor, disk threshold, JSON strategy and parameters
- Run button: run the three-stage pipeline in one click, showing each stage's status live
- Report view: show the review report after the pipeline finishes
Output Format
Every stage reports a fixed four-line format (stage / inputs / outputs / validation) in the runtime language. This version keeps Chinese runtime labels for those four lines.
The review report (report.html) contains: parameter summary, per-stage validation results,
degradation/block list, disk usage distribution, JSON file inventory.
When to Stop and Ask
Read references/execution-safety.md. At minimum, stop when:
- a change to the user's original input files is needed
- projected disk usage approaches or exceeds the threshold after allocation
- allocation is blocked (
NO_OTHER_BROKER/L3_BLOCKED), or the merged table / JSON fails validation - validation fails and the cause is unclear
File Structure
huawei-cloud-kafka-replica-reassignment/
├── SKILL.md
├── README.md # quick start, parameters, FAQ
├── evals/
│ ├── evals.json # regression cases (golden path / NO_OTHER_BROKER / L3 block / read-only / over-threshold / RF 3 / duplicate-broker hard check / topic-name sanitization)
│ ├── run_evals.py # regression entry: python evals/run_evals.py (exit code = failure count)
│ ├── build_fixtures.py # regenerate fixtures (deterministic)
│ └── fixtures/ # input tables for NO_OTHER_BROKER / L3 block / over-threshold
├── references/
│ ├── stage1-allocate.md # Stage 1 detailed rules
│ ├── stage2-merge.md # Stage 2 merge rules
│ ├── stage3-json.md # Stage 3 JSON conversion + strategies
│ ├── validation-rules.md # per-stage validation checklist
│ └── execution-safety.md # safety rules
├── scripts/
│ ├── app.py # Flask Web UI
│ ├── stage1_allocate.py # Stage 1 script
│ ├── stage2_merge.py # Stage 2 script
│ ├── stage3_json.py # Stage 3 script
│ ├── tabio.py # intermediate artifact I/O (xlsx/TSV compatible, new names preferred, legacy fallback)
│ ├── validate.py # validation module
│ ├── report.py # report generation
│ ├── generate_templates.py # template generation
│ └── requirements.txt # dependencies
├── assets/
│ ├── topic_template.xlsx # topic template
│ └── broker_template.xlsx # broker template
└── templates/
└── index.html # UI page
