Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update_cluster_revisions.py: Add possibility to bump cluster revisions to the latest in spec #36456

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
77 changes: 77 additions & 0 deletions src/tools/zap_cluster_rev/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
orphan: true
---

# ZAP Cluster Revision Bump Tool

This tool parses ZAP files and updates outdated cluster revisions according to
the specification.

**WARNING**: This tool only updates the revision number. Please ensure any new
attributes, events or commands are implemented accordingly.

# Prerequisites

This tool uses the python environment, which can be built and activated using:

```
# Build
./scripts/build_python.sh -i out/python_env

# Activate
source out/python_env/bin/activate
```

# How to run

Usage:

```
usage: zap_cluster_rev.py [-h] (--print-only | --update) filenames [filenames ...]
```

Example #1: Check cluster revisions and update a ZAP file:

```
python src/tools/zap_cluster_rev/zap_cluster_rev.py --update examples/network-manager-app/network-manager-common/network-manager-app.zap
```

Expected output if outdated clusters are found:

```
python src/tools/zap_cluster_rev/zap_cluster_rev.py --update examples/network-manager-app/network-manager-common/network-manager-app.zap
Checking for outdated cluster revisions on: examples/network-manager-app/network-manager-common/network-manager-app.zap
3 found!
Endpoint: 0 cluster_code: 40 cluster_revision: 3 cluster_spec_revision: 4 name: Basic Information
Endpoint: 0 cluster_code: 48 cluster_revision: 1 cluster_spec_revision: 2 name: General Commissioning
Endpoint: 0 cluster_code: 53 cluster_revision: 2 cluster_spec_revision: 3 name: Thread Network Diagnostics
Cluster revisions updated successfully!
```

Expected output if no outdated clusters are found:

```
Checking for outdated cluster revisions on: examples/network-manager-app/network-manager-common/network-manager-app.zap
0 found!
```

Example #2: Check the cluster revisions and print only (do not modify the ZAP
file):

```
python src/tools/zap_cluster_rev/zap_cluster_rev.py --print-only examples/network-manager-app/network-manager-common/network-manage
r-app.zap
```

Expected output:

```
Checking for outdated cluster revisions on: examples/network-manager-app/network-manager-common/network-manager-app.zap
3 found!
Endpoint: 0 cluster_code: 40 cluster_revision: 3 cluster_spec_revision: 4 name: Basic Information
Endpoint: 0 cluster_code: 48 cluster_revision: 1 cluster_spec_revision: 2 name: General Commissioning
Endpoint: 0 cluster_code: 53 cluster_revision: 2 cluster_spec_revision: 3 name: Thread Network Diagnostics
```

The option `--print-only` is useful for testing and ensuring the tool is
identifying the right outdated clusters before updating.
98 changes: 98 additions & 0 deletions src/tools/zap_cluster_rev/zap_cluster_rev.py
soares-sergio marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/env python

import argparse
import json
from dataclasses import dataclass

from chip.testing.spec_parsing import build_xml_clusters


@dataclass
class ClusterInfo():
endpoint_id: int
cluster_code: int
cluster_spec_revision: int
cluster_name: str
json_attribute: object

def cluster_revision(self):
return int(self.json_attribute["defaultValue"])

def __str__(self):
return ('Endpoint: %d cluster_code: %d cluster_revision: %d cluster_spec_revision: %d name: %s' % (self.endpoint_id, self.cluster_code, self.cluster_revision(), self.cluster_spec_revision, self.cluster_name))

def update_cluster_revision(self):
self.json_attribute["defaultValue"] = self.cluster_spec_revision


def load_zap(filename: str):
with open(filename, "rt") as infile:
return json.load(infile)


def save_zap(body: object, filename: str):
with open(filename, "wt+") as outfile:
return json.dump(body, outfile, indent=2)


def get_outdated_clusters(body: object, xml_clusters: dict) -> list[ClusterInfo]:
result = []
for endpoint in body.get("endpointTypes", []):
endpoint_id = endpoint.get("id") - 1
for cluster in endpoint.get("clusters", []):
for attribute in cluster.get("attributes", []):
if attribute.get("name") != "ClusterRevision" or attribute.get("storageOption") != "RAM":
continue
cluster_revision = int(attribute.get("defaultValue"))
spec_revision = xml_clusters[cluster.get("code")].revision
# Filter in outdated clusters only
if (cluster_revision == spec_revision):
break
cluster_info = ClusterInfo(endpoint_id=endpoint_id, cluster_code=cluster.get("code"),
cluster_spec_revision=spec_revision, cluster_name=cluster.get("name"), json_attribute=attribute)
result.append(cluster_info)
return result


def main():
parser = argparse.ArgumentParser(
description="Process ZAP Files and update outdated cluster revisions according to the spec")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--print-only", action="store_true", help="Print outdated cluster information.")
group.add_argument("--update", action="store_true", help="Update outdated cluster revisions on ZAP file.")
parser.add_argument("filenames", nargs="+", help="A sequence of ZAP filenames.")
args = parser.parse_args()

print("**WARNING**: This tool only updates the revision number. Please ensure any new attributes, events or commands are implemented accordingly.")
outdated_count = 0
for zap_filename in args.filenames:
print("Checking for outdated cluster revisions on: %s" % zap_filename)
body = load_zap(zap_filename)
spec_xml_clusters, problems = build_xml_clusters()

outdated_clusters = get_outdated_clusters(body, spec_xml_clusters)
print("%d found!" % len(outdated_clusters))
outdated_count += len(outdated_clusters)
print(*outdated_clusters, sep='\n')

if (args.print_only):
soares-sergio marked this conversation as resolved.
Show resolved Hide resolved
continue

# Update outdated cluster revisions according to the spec
for cluster in outdated_clusters:
cluster.update_cluster_revision()
if (outdated_clusters):
print('Cluster revisions updated successfully!\n')

# Check there's no longer any outdated cluster
assert (not get_outdated_clusters(body, spec_xml_clusters))

save_zap(body, zap_filename)

# If it's printing only, return the number of outdated clusters, so it can be used as a test
if (args.print_only):
soares-sergio marked this conversation as resolved.
Show resolved Hide resolved
return outdated_count


if __name__ == "__main__":
main()
Loading