
Alibabacloud Dlf Manage
- 62 installs
- 208 repo stars
- Updated August 4, 2026
- aliyun/alibabacloud-aiops-skills
alibabacloud-dlf-manage is a Claude skill that runs read-only queries of catalog, database, and table metadata in Alibaba Cloud Data Lake Formation via the DLF Python SDK.
About
This skill queries catalog, database, and table metadata in Alibaba Cloud Data Lake Formation (DLF). All operations are read-only and run through the DLF Python SDK via scripts/dlf_metadata_query.py. A developer uses it to list catalogs and databases, view table schemas, and fuzzy-search tables by name.
- Read-only queries of DLF catalogs, databases, tables, and schemas
- Runs through the alibabacloud-dlfnext20250310 Python SDK, not curl or a shell CLI
- Lightweight list vs heavier list-details/get actions for names vs full schema
Alibabacloud Dlf Manage by the numbers
- 62 all-time installs (skills.sh)
- +7 installs in the week ending Jun 23, 2026 (Skillselion tracking)
- Ranked #378 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
alibabacloud-dlf-manage capabilities & compatibility
Requires Alibaba Cloud credentials; Alibaba Cloud DLF usage may incur cloud charges
- Capabilities
- database · data analysis
- Works with
- aws
- Use cases
- database · data analysis
- Runs
- Runs locally
- Pricing
- Bring your own API key
What alibabacloud-dlf-manage says it does
This Skill only contains read-only operations — no create, modify, or delete operations.
Query Catalog, Database, and Table metadata resources in Alibaba Cloud Data Lake Formation (DLF).
npx skills add https://github.com/aliyun/alibabacloud-aiops-skills --skill alibabacloud-dlf-manageAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 62 |
|---|---|
| repo stars | ★ 208 |
| Last updated | August 4, 2026 |
| Repository | aliyun/alibabacloud-aiops-skills ↗ |
What it does
Query and inspect catalog, database, and table metadata in Alibaba Cloud Data Lake Formation (DLF).
Who is it for?
Inspecting data-lake schema and metadata in Alibaba Cloud DLF without modifying anything
Skip if: Creating, modifying, or deleting DLF resources (the skill is read-only)
When should I use this skill?
A developer needs to list DLF catalogs/databases or view a table schema in Alibaba Cloud
What you get
Returns catalog, database, and table metadata and schema definitions from DLF read-only.
- Catalog, database, and table metadata listings
- Table schema and property details
By the numbers
- Region defaults to cn-hangzhou
- pins alibabacloud-dlfnext20250310==3.0.0
Files
DLF Data Lake Metadata Query
Query Catalog, Database, and Table metadata resources in Alibaba Cloud Data Lake Formation (DLF).
CRITICAL: Use only the Python SDK script provided by this Skill.
All operations go through the DLF Python SDK (alibabacloud-dlfnext20250310) viascripts/dlf_metadata_query.py.
This Skill does not invoke any shell-based command-line client and does not require AI-Mode configuration.
>
- DO NOT attempt access via any shell-based command-line client — DLF is not exposed through one in this Skill
- DO NOT use curl, wget, or other HTTP clients to call the DLF API directly
- MUST use the scripts/dlf_metadata_query.py script provided by this Skill, which wraps the DLF Python SDK- All query operations are executed via python3 scripts/dlf_metadata_query.py <action> [options]Architecture
Catalog (Data Catalog)
└── Database
└── Table
├── Schema (column definitions)
├── PartitionKeys (partition keys)
├── PrimaryKeys (primary keys)
└── Options (table properties)Installation
pip install -r requirements.txtrequirements.txt pins the full transitive dependency closure (including alibabacloud-dlfnext20250310==3.0.0) for reproducible installs.
Pre-check: Python SDK dependency
>
```bash
python3 -c "from alibabacloud_dlfnext20250310.client import Client; print('SDK OK')"
```
If not installed, run pip install -r requirements.txt.Authentication
Pre-check: Alibaba Cloud Credentials Required
>
Use the default credential chain (CredentialClient) to obtain credentials automatically. Supported sources (in priority order):
1. Environment variables (ALIBABA_CLOUD_ACCESS_KEY_ID / ALIBABA_CLOUD_ACCESS_KEY_SECRET)
2. Configuration file (~/.alibabacloud/credentials)
3. ECS Instance RAM Role
4. OIDC Role ARN
>
Security Rules:
- NEVER read, echo, or print AK/SK values
- NEVER ask the user to input AK/SK directly in the conversation or command line
- NEVER explicitly handle or pass AK/SK in code — rely on the default credential chain
>
See https://help.aliyun.com/document_detail/378659.html for credential configuration details.
RAM Permissions
This Skill only involves read-only operations (List / Get). See references/ram-policies.md for the full permission list.
[MUST] Permission Failure Handling: When any command or API call fails due to permission errors at any point during execution, follow this process:
1. Read references/ram-policies.md to get the full list of permissions required by this SKILL2. Pause and wait until the user confirms that the required permissions have been granted
Parameter Confirmation
IMPORTANT: Parameter Confirmation — Before invoking the API,
the following user-specific parameters must be confirmed with the user; do not assume them.
Region defaults to cn-hangzhou; if the user does not specify one, use the default without asking.
| Parameter | Required | Description | Default |
|---|---|---|---|
region | No | Region ID | cn-hangzhou |
catalog_name | Conditional | Catalog name (--catalog, required for GetCatalog) | - |
catalog_id | Conditional | Catalog ID (--catalog-id, required when querying databases/tables, e.g. clg-paimon-xxxx) | - |
database | Conditional | Database name (--database) | - |
table | Conditional | Table name (--table) | - |
Core Workflow
The script automatically reads AK/SK from environment variables and reports a clear error if they are missing.
Region defaults to cn-hangzhou; use the default if the user does not specify one.
You MUST use scripts/dlf_metadata_query.py to query metadata. Do not use shell-based command-line clients or curl. Actions are in kebab-case.
*CRITICAL — list vs. list--details: pick the lightest action that satisfies the request.**
- For listing names / IDs (including fuzzy search): uselist-databases/list-tables. These call theListDatabases/ListTablesAPI.
- For full attributes / Schema / properties: uselist-database-details/list-table-details/get-database/get-table. These call the heavier*-details/Get*APIs.
- *Default to the lightweight `list- action** unless the user explicitly asks for full configuration, Schema, or properties. Calling list-*-details` when only names are needed is incorrect.Query Operations
# ---- Catalog ----
# 1. List all Catalogs (names + minimal info — preferred for listing/searching)
python3 scripts/dlf_metadata_query.py list-catalogs
# 2. Fuzzy-search Catalogs by name (uses ListCatalogs)
python3 scripts/dlf_metadata_query.py list-catalogs --pattern test
# 3. Get Catalog details (by name) — use only when full Catalog config is needed
python3 scripts/dlf_metadata_query.py get-catalog --catalog <catalog_name>
# 4. Get Catalog details (by ID) — use only when full Catalog config is needed
python3 scripts/dlf_metadata_query.py get-catalog-by-id --id <catalog_id>
# ---- Database ----
# 5. List databases (NAMES only — DEFAULT for "list / show / which databases", calls ListDatabases)
python3 scripts/dlf_metadata_query.py list-databases --catalog-id <catalog_id>
# 6. List database details (full attributes, calls ListDatabaseDetails) — use ONLY when the user asks for properties / configs / location / owner
python3 scripts/dlf_metadata_query.py list-database-details --catalog-id <catalog_id>
# 7. Get a single database's details (calls GetDatabase) — use when the user asks for ONE specific database's full info
python3 scripts/dlf_metadata_query.py get-database --catalog-id <catalog_id> --database <db_name>
# ---- Table ----
# 8. List tables (NAMES only — DEFAULT for "list / show / which tables", calls ListTables)
python3 scripts/dlf_metadata_query.py list-tables --catalog-id <catalog_id> --database <db_name>
# 9. Fuzzy-search tables by name (DEFAULT for "search / find tables matching ...", calls ListTables)
python3 scripts/dlf_metadata_query.py list-tables --catalog-id <catalog_id> --database <db_name> --pattern user%
# 10. List table details with Schema (calls ListTableDetails) — use ONLY when the user explicitly asks for Schema / columns / properties of all tables
python3 scripts/dlf_metadata_query.py list-table-details --catalog-id <catalog_id> --database <db_name>
# 11. Get a single table's details with Schema (calls GetTable) — use when the user asks for ONE specific table's Schema
python3 scripts/dlf_metadata_query.py get-table --catalog-id <catalog_id> --database <db_name> --table <table_name>Specify region (defaults to cn-hangzhou): add --region cn-shanghai
Typical Query Flow
1. list-catalogs → get catalog_name and catalog_id (names only)
2. list-databases → use catalog_id to view available database names
3. list-tables → use catalog_id + database to view available table names
4. get-table → use catalog_id + database + table to view ONE table's SchemaOnly step 4 (get-table) is a "details" call, because Schema is what the user actually asked for. Steps 1–3 stay on the lightweightlist-*actions.
Fuzzy Search
All list operations support the --pattern argument for fuzzy name matching, using % as the wildcard. *Use the lightweight `list-` action for pattern search unless the user explicitly asks for the full Schema / properties of every match.**
# Search Catalogs whose name contains "test"
python3 scripts/dlf_metadata_query.py list-catalogs --pattern %test%
# Search databases whose name starts with "prod_"
python3 scripts/dlf_metadata_query.py list-databases --catalog-id <catalog_id> --pattern prod_%
# Search tables whose name starts with "user" (DEFAULT — calls ListTables)
python3 scripts/dlf_metadata_query.py list-tables --catalog-id <catalog_id> --database <db_name> --pattern user%Anti-pattern: do not uselist-table-details --pattern ...to search by name. That callsListTableDetailsand is heavier than required. Reach forlist-table-detailsonly when the user has explicitly asked for the Schema / columns of every matching table.
Output Format
- List operations:
{"count": N, "items": [...]} - Get operations: a single JSON object
- Errors:
{"error": "...", "hint": "..."}
Verification
If list-catalogs returns the Catalog list, the connection and permissions are working:
python3 scripts/dlf_metadata_query.py list-catalogs --region cn-hangzhouSee references/verification-method.md for detailed verification steps.
Best Practices
1. *Prefer the lightweight `list- action over list--details` / `get-.** When the task only requires listing resource **names**, **IDs**, or **fuzzy matching**, you MUST use list-catalogs / list-databases / list-tables (which call ListCatalogs / ListDatabases / ListTables). Only use list--details` or `get- when the user explicitly asks for full configuration, Schema, columns, properties, owner, or location. Reaching for the heavier API when the lighter one suffices is incorrect. 2. **List before Get**: use list-catalogs to obtain catalog_id first, then use catalog_id to query databases and tables. 3. **Use fuzzy search with the lightweight action**: the --pattern argument supports fuzzy matching; use it on list-tables (not list-table-details) unless full Schema is also requested. 4. **Pagination**: use --max-results and --page-token for paginated queries when there is a lot of data. 5. **Catalog ID vs Name**: when querying Database/Table, use catalog_id` (e.g. clg-paimon-xxxx), not the catalog name.
References
| Reference | Description |
|---|---|
| references/related-apis.md | Full API list and parameter descriptions |
| references/ram-policies.md | RAM permission policy |
| references/acceptance-criteria.md | Acceptance criteria |
| references/verification-method.md | Verification method |
| DLF API overview | Official API documentation |
| DLF product documentation | Product documentation |
| Python SDK PyPI | SDK version info |
Acceptance Criteria: alibabacloud-dlf-manage
Scenario: Read-only metadata query for the DLF data lake Purpose: Skill test acceptance criteria
---
Correct Python SDK Code Patterns
1. Import Patterns
✅ CORRECT
from alibabacloud_tea_openapi.models import Config
from alibabacloud_dlfnext20250310.client import Client
from alibabacloud_dlfnext20250310 import models as dlf_models❌ INCORRECT
# Wrong: using the Common SDK instead of the dedicated SDK
from alibabacloud_tea_openapi.client import Client as OpenApiClient
# Wrong: importing a module that does not exist
from alibabacloud_dlf.client import Client2. Authentication — use the CredentialClient default credential chain
✅ CORRECT
from alibabacloud_credentials.client import Client as CredentialClient
credential = CredentialClient()
config = Config(
credential=credential,
endpoint='dlfnext.cn-hangzhou.aliyuncs.com',
region_id='cn-hangzhou'
)
client = Client(config)❌ INCORRECT
# Wrong: hard-coded credentials
config = Config(
access_key_id='LTAI5t...',
access_key_secret='abc123...',
)
# Wrong: explicitly reading env vars and passing AK/SK
config = Config(
access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),
access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
)3. Endpoint format
✅ CORRECT
config.endpoint = 'dlfnext.cn-hangzhou.aliyuncs.com'
config.endpoint = f'dlfnext.{region}.aliyuncs.com'❌ INCORRECT
# Wrong: using the legacy DLF endpoint
config.endpoint = 'dlf.cn-hangzhou.aliyuncs.com'
# Wrong: includes the https prefix
config.endpoint = 'https://dlfnext.cn-hangzhou.aliyuncs.com'4. API call patterns
✅ CORRECT — ListCatalogs
request = dlf_models.ListCatalogsRequest()
response = client.list_catalogs(request)
catalogs = response.body.catalogs✅ CORRECT — GetCatalog
response = client.get_catalog('my_catalog')✅ CORRECT — ListDatabases (requires catalog_id)
request = dlf_models.ListDatabasesRequest()
response = client.list_databases('clg-paimon-xxxx', request)
databases = response.body.databases✅ CORRECT — GetTable (requires catalog_id + database + table)
response = client.get_table('clg-paimon-xxxx', 'my_db', 'my_table')
table = response.body
schema = table.schema❌ INCORRECT
# Wrong: ListCatalogs does not take a catalog_id
response = client.list_catalogs('clg-paimon-xxxx', request)
# Wrong: using catalog name instead of catalog_id to query databases
response = client.list_databases('my_catalog_name', request) # should use catalog ID
# Wrong: omitting the request object
response = client.list_databases('clg-paimon-xxxx') # missing request argument5. Pagination
✅ CORRECT
page_token = None
all_items = []
while True:
request = dlf_models.ListTablesRequest(
max_results=100,
page_token=page_token
)
response = client.list_tables(catalog_id, database, request)
all_items.extend(response.body.tables or [])
page_token = response.body.next_page_token
if not page_token:
break❌ INCORRECT
# Wrong: no pagination — may lose data
request = dlf_models.ListTablesRequest()
response = client.list_tables(catalog_id, database, request)
# Directly using response.body.tables ignores the possibility of a next page6. Read-only constraint
✅ CORRECT — this Skill only uses query APIs
list-catalogs, get-catalog, get-catalog-by-id,
list-databases, list-database-details, get-database,
list-tables, list-table-details, get-table❌ INCORRECT — must not use write operations
# Wrong: this Skill does not contain write operations
client.create_database(...)
client.drop_table(...)
client.alter_catalog(...)RAM Permissions Required
Summary Table
| Product | RAM Action | Resource Scope | Description |
|---|---|---|---|
| DLF | dlfnext:ListCatalogs | * | List Catalogs |
| DLF | dlfnext:GetCatalog | * | Get Catalog details (by name) |
| DLF | dlfnext:GetCatalogById | * | Get Catalog details (by ID) |
| DLF | dlfnext:ListDatabases | * | List database names |
| DLF | dlfnext:ListDatabaseDetails | * | List database details |
| DLF | dlfnext:GetDatabase | * | Get a single database's details |
| DLF | dlfnext:ListTables | * | List table names |
| DLF | dlfnext:ListTableDetails | * | List table details |
| DLF | dlfnext:GetTable | * | Get a single table's details |
RAM Policy Document
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dlfnext:ListCatalogs",
"dlfnext:GetCatalog",
"dlfnext:GetCatalogById",
"dlfnext:ListDatabases",
"dlfnext:ListDatabaseDetails",
"dlfnext:GetDatabase",
"dlfnext:ListTables",
"dlfnext:ListTableDetails",
"dlfnext:GetTable"
],
"Resource": "*"
}
]
}DLF Data Permissions
In addition to RAM API permissions, the corresponding data permissions must be granted in the DLF console:
| Action | DLF Data Permission | Permission Level |
|---|---|---|
| ListCatalogs | LIST | Account-level |
| GetCatalog / GetCatalogById | DESCRIBE | CATALOG |
| ListDatabases / ListDatabaseDetails | LIST | CATALOG |
| GetDatabase | DESCRIBE | DATABASE |
| ListTables / ListTableDetails | LIST | DATABASE |
| GetTable | DESCRIBE | TABLE |
Note: DLF data permissions and RAM API permissions are two independent permission systems.
DLF Read-only Query API Reference
API Overview
All APIs are ROA-style (RESTful) and use AK signature authentication.
| API | HTTP | Path | SDK Method | Description |
|---|---|---|---|---|
| ListCatalogs | GET | /dlf/v1/catalogs | list_catalogs(request) | List Catalogs |
| GetCatalog | GET | /dlf/v1/catalogs/{catalog} | get_catalog(catalog) | Get Catalog by name |
| GetCatalogById | GET | /dlf/v1/catalogs/id/{id} | get_catalog_by_id(id) | Get Catalog by ID |
| ListDatabases | GET | /dlf/v1/{catalogId}/databases | list_databases(catalog_id, request) | List database names |
| ListDatabaseDetails | GET | /dlf/v1/{catalogId}/database-details | list_database_details(catalog_id, request) | List database details |
| GetDatabase | GET | /dlf/v1/{catalogId}/databases/{database} | get_database(catalog_id, database) | Get database details |
| ListTables | GET | /dlf/v1/{catalogId}/databases/{database}/tables | list_tables(catalog_id, database, request) | List table names |
| ListTableDetails | GET | /dlf/v1/{catalogId}/databases/{database}/table-details | list_table_details(catalog_id, database, request) | List table details |
| GetTable | GET | /dlf/v1/{catalogId}/databases/{database}/tables/{table} | get_table(catalog_id, database, table) | Get table details |
ListCatalogs
Request parameters (ListCatalogsRequest):
| Parameter | Type | Required | Description |
|---|---|---|---|
max_results | int | No | Max records per page, default 1000 |
page_token | str | No | Page token |
catalog_name_pattern | str | No | Name fuzzy match |
Returns: catalogs (list), next_page_token (str)
Catalog object fields: id, name, owner, status, type, is_shared, share_id, options, created_at, updated_at, created_by, updated_by
Catalog Status enum: NEW, INITIALIZING, RUNNING, TERMINATED, DELETED
GetCatalog / GetCatalogById
get_catalog(catalog: str)— by Catalog nameget_catalog_by_id(id: str)— by Catalog ID (e.g. clg-paimon-xxxx)
Returns the same fields as above.
ListDatabases
Request parameters (ListDatabasesRequest):
| Parameter | Type | Required | Description |
|---|---|---|---|
max_results | int | No | Max records per page, default 1000 |
page_token | str | No | Page token |
database_name_pattern | str | No | Name fuzzy match (e.g. database%) |
Returns: databases (list[str]), next_page_token (str)
ListDatabaseDetails
Request parameters are the same as ListDatabases.
Returns: database_details (list), next_page_token (str)
Database object fields: id, name, owner, location, options, created_at, created_by, updated_at, updated_by
GetDatabase
get_database(catalog_id: str, database: str) — returns the same fields as above.
ListTables
Request parameters (ListTablesRequest):
| Parameter | Type | Required | Description |
|---|---|---|---|
max_results | int | No | Max records per page |
page_token | str | No | Page token |
table_name_pattern | str | No | Name fuzzy match (e.g. user%) |
Returns: tables (list[str]), next_page_token (str)
ListTableDetails
Request parameters are the same as ListTables.
Returns: table_details (list), next_page_token (str)
GetTable
get_table(catalog_id: str, database: str, table: str)
Table object fields:
| Field | Type | Description |
|---|---|---|
id | str | Table ID |
name | str | Table name |
path | str | Storage path (oss://...) |
is_external | bool | Whether external table |
schema_id | int | Schema version |
schema | Schema | Schema object |
owner | str | Owner |
storage_class | str | Storage class |
created_at | int | Created timestamp (ms) |
updated_at | int | Updated timestamp (ms) |
created_by | str | Creator |
updated_by | str | Last updater |
Schema structure:
| Field | Type | Description |
|---|---|---|
fields | list | Column list (id, name, type) |
partition_keys | list[str] | Partition keys |
primary_keys | list[str] | Primary keys |
options | dict | Table properties |
comment | str | Table comment |
Error Codes
| HTTP status code | Description | Recommended action |
|---|---|---|
| 400 | Request parameter error | Check parameter format |
| 401 | Authentication failed | Check AK/SK |
| 403 | Insufficient permission | Grant DLF data permissions |
| 404 | Resource not found | Verify the resource name |
Success Verification Method
Scenario Goal Verification
Expected Outcome: Able to successfully query Catalog, database, and table metadata in the DLF data lake.
Step 1: Verify SDK installation
python3 -c "from alibabacloud_dlfnext20250310.client import Client; print('SDK OK')"Success Indicator: outputs SDK OK.
Step 2: Verify credential configuration
python3 -c "from alibabacloud_credentials.client import Client; Client(); print('Credentials OK')"Success Indicator: outputs Credentials OK (the default credential chain finds valid credentials).
Step 3: Verify Catalog list query
python3 scripts/dlf_metadata_query.py list-catalogs --region cn-hangzhouSuccess Indicator: returns the Catalog list in JSON format with count and items fields.
Step 4: Verify the end-to-end query chain
# 1. List Catalogs and get catalog_id
python3 scripts/dlf_metadata_query.py list-catalogs
# 2. Use catalog_id to list databases
python3 scripts/dlf_metadata_query.py list-databases --catalog-id <catalog_id>
# 3. Use catalog_id + database to list tables
python3 scripts/dlf_metadata_query.py list-tables --catalog-id <catalog_id> --database <db_name>
# 4. Use catalog_id + database + table to view the table Schema
python3 scripts/dlf_metadata_query.py get-table --catalog-id <catalog_id> --database <db_name> --table <table_name>Success Indicator: every step returns valid JSON; the final get-table returns the complete table structure including schema.fields.
Common Failure Causes
| Error message | Cause | Resolution |
|---|---|---|
No credentials found | Environment variables not configured | Set AK/SK environment variables |
Permission denied | Missing DLF data permissions | Grant access in the DLF console |
Resource not found | Wrong Catalog/DB/Table name | Verify the resource names |
SDK not installed | Python SDK not installed | pip install alibabacloud-dlfnext20250310 |
#!/usr/bin/env python3
"""DLF Metadata Query - Read-only CLI for querying DLF metadata.
Uses alibabacloud_dlfnext20250310 Python SDK.
Output: Always valid JSON to stdout.
List actions: {"count": N, "items": [...]}
Get actions: Single JSON object
Errors: {"error": "message", "hint": "what to do"}
"""
import json
import os
import re
import sys
try:
from alibabacloud_tea_openapi.models import Config
from alibabacloud_dlfnext20250310.client import Client
from alibabacloud_dlfnext20250310 import models as dlf_models
from alibabacloud_credentials.client import Client as CredentialClient
from Tea.exceptions import TeaException
except ImportError:
print(json.dumps({
"error": "SDK not installed",
"hint": "Run: pip install alibabacloud-dlfnext20250310 alibabacloud-credentials"
}))
sys.exit(1)
# Attribute lists for serialization (module-level constants)
_CATALOG_ATTRS = (
"id", "name", "owner", "status", "type", "is_shared",
"share_id", "created_at", "updated_at", "created_by",
"updated_by", "options",
)
_DATABASE_ATTRS = (
"id", "name", "owner", "location", "options",
"created_at", "created_by", "updated_at", "updated_by",
)
_TABLE_ATTRS = (
"id", "name", "path", "is_external", "schema_id",
"owner", "storage_class", "created_at", "updated_at",
"created_by", "updated_by",
)
def out_json(obj):
print(json.dumps(obj, ensure_ascii=False, indent=2, default=str))
def out_error(msg, hint=None):
obj = {"error": str(msg)}
if hint:
obj["hint"] = hint
out_json(obj)
sys.exit(1)
def extract_arg(args, name):
"""Extract a named argument from args list."""
for i, arg in enumerate(args):
if arg == name and i + 1 < len(args):
val = args[i + 1]
del args[i:i + 2]
return val
return None
# Validation patterns for input parameters
_NAME_PATTERN = re.compile(r'^[a-zA-Z0-9_\-\.]+$')
_ID_PATTERN = re.compile(r'^[a-zA-Z0-9_\-]+$')
_PATTERN_PARAM = re.compile(r'^[a-zA-Z0-9_\-\.%\*]+$')
_MAX_PARAM_LEN = 256
def _validate_param(val, name):
"""Validate parameter value for format and length."""
if len(val) > _MAX_PARAM_LEN:
out_error(f"Parameter {name} too long (max {_MAX_PARAM_LEN} chars)")
if val.startswith('-'):
out_error(f"Invalid value for {name}: '{val}' (looks like a flag, not a value)")
def _validate_name(val, name):
"""Validate a resource name (catalog, database, table)."""
_validate_param(val, name)
if not _NAME_PATTERN.match(val):
out_error(
f"Invalid {name}: '{val}'",
f"{name} must contain only alphanumeric characters, hyphens, underscores, or dots"
)
def _validate_id(val, name):
"""Validate a resource ID (catalog_id)."""
_validate_param(val, name)
if not _ID_PATTERN.match(val):
out_error(
f"Invalid {name}: '{val}'",
f"{name} must contain only alphanumeric characters, hyphens, or underscores"
)
def _validate_pattern(val, name):
"""Validate a search pattern parameter."""
_validate_param(val, name)
if not _PATTERN_PARAM.match(val):
out_error(
f"Invalid {name}: '{val}'",
f"{name} must contain only alphanumeric characters, hyphens, underscores, dots, or wildcards (% *)"
)
def require_arg(args, name, hint, validator=None):
"""Extract a required named argument, validate and exit with error if missing."""
val = extract_arg(args, name)
if not val:
out_error(f"Missing {name}", hint)
if validator:
validator(val, name)
return val
def extract_pagination_args(args):
"""Extract common pagination arguments: --pattern, --max-results, --page-token."""
pattern = extract_arg(args, "--pattern")
max_results = extract_arg(args, "--max-results")
page_token = extract_arg(args, "--page-token")
if pattern:
_validate_pattern(pattern, "--pattern")
if max_results:
if not max_results.isdigit() or int(max_results) < 1 or int(max_results) > 10000:
out_error("Invalid --max-results", "Must be an integer between 1 and 10000")
if page_token:
_validate_param(page_token, "--page-token")
return pattern, int(max_results) if max_results else None, page_token
def out_paginated(items, next_page_token):
"""Output a paginated list result."""
result = {"count": len(items), "items": items}
if next_page_token:
result["next_page_token"] = next_page_token
out_json(result)
def build_client(args):
"""Build DLF client using default credential chain."""
region = extract_arg(args, "--region") or "cn-hangzhou"
_validate_name(region, "--region")
try:
credential = CredentialClient()
except Exception as e:
out_error(
f"Failed to initialize credentials: {e}",
"Configure credentials via environment variables, config file, or instance role. "
"See https://help.aliyun.com/document_detail/378659.html"
)
config = Config(
credential=credential,
endpoint=f"dlfnext.{region}.aliyuncs.com",
region_id=region,
user_agent='AlibabaCloud-Agent-Skills/alibabacloud-dlf-manage',
connect_timeout=5000,
read_timeout=10000,
)
try:
return Client(config)
except Exception as e:
out_error(f"Failed to create DLF client: {e}",
"Check credentials and region.")
def _serialize(obj, attrs):
"""Generic serializer: extract non-None attributes from an SDK object."""
result = {}
for a in attrs:
v = getattr(obj, a, None)
if v is not None:
result[a] = v
return result
def serialize_catalog(cat):
return _serialize(cat, _CATALOG_ATTRS)
def serialize_database(db):
return _serialize(db, _DATABASE_ATTRS)
def serialize_table(tbl):
result = _serialize(tbl, _TABLE_ATTRS)
schema = getattr(tbl, "schema", None)
if schema:
schema_dict = {}
fields = getattr(schema, "fields", None)
if fields:
schema_dict["fields"] = [
{"id": getattr(f, "id", i), "name": getattr(f, "name", ""),
"type": str(getattr(f, "type", ""))}
for i, f in enumerate(fields)
]
for key in ("partition_keys", "primary_keys", "options", "comment"):
val = getattr(schema, key, None)
if val is not None:
schema_dict[key] = val
result["schema"] = schema_dict
return result
# ====== Action handlers ======
def action_list_catalogs(client, args):
pattern, max_results, page_token = extract_pagination_args(args)
request = dlf_models.ListCatalogsRequest(
catalog_name_pattern=pattern,
max_results=max_results,
page_token=page_token,
)
resp = client.list_catalogs(request)
catalogs = [serialize_catalog(c) for c in (resp.body.catalogs or [])]
out_paginated(catalogs, resp.body.next_page_token)
def action_get_catalog(client, args):
name = require_arg(args, "--catalog", "Specify catalog name, e.g. --catalog my_catalog", _validate_name)
resp = client.get_catalog(name)
out_json(serialize_catalog(resp.body))
def action_get_catalog_by_id(client, args):
cid = require_arg(args, "--id", "Specify catalog ID, e.g. --id clg-paimon-xxxx", _validate_id)
resp = client.get_catalog_by_id(cid)
out_json(serialize_catalog(resp.body))
def action_list_databases(client, args):
catalog_id = require_arg(args, "--catalog-id", "Specify catalog ID, e.g. --catalog-id clg-paimon-xxxx", _validate_id)
pattern, max_results, page_token = extract_pagination_args(args)
request = dlf_models.ListDatabasesRequest(
database_name_pattern=pattern,
max_results=max_results,
page_token=page_token,
)
resp = client.list_databases(catalog_id, request)
out_paginated(resp.body.databases or [], resp.body.next_page_token)
def action_list_database_details(client, args):
catalog_id = require_arg(args, "--catalog-id", "Specify catalog ID, e.g. --catalog-id clg-paimon-xxxx", _validate_id)
pattern, max_results, page_token = extract_pagination_args(args)
request = dlf_models.ListDatabaseDetailsRequest(
database_name_pattern=pattern,
max_results=max_results,
page_token=page_token,
)
resp = client.list_database_details(catalog_id, request)
dbs = [serialize_database(d) for d in (resp.body.database_details or [])]
out_paginated(dbs, resp.body.next_page_token)
def action_get_database(client, args):
catalog_id = require_arg(args, "--catalog-id", "Specify catalog ID, e.g. --catalog-id clg-paimon-xxxx", _validate_id)
database = require_arg(args, "--database", "Specify database name, e.g. --database my_db", _validate_name)
resp = client.get_database(catalog_id, database)
out_json(serialize_database(resp.body))
def action_list_tables(client, args):
catalog_id = require_arg(args, "--catalog-id", "Specify catalog ID, e.g. --catalog-id clg-paimon-xxxx", _validate_id)
database = require_arg(args, "--database", "Specify database name, e.g. --database my_db", _validate_name)
pattern, max_results, page_token = extract_pagination_args(args)
request = dlf_models.ListTablesRequest(
table_name_pattern=pattern,
max_results=max_results,
page_token=page_token,
)
resp = client.list_tables(catalog_id, database, request)
out_paginated(resp.body.tables or [], resp.body.next_page_token)
def action_list_table_details(client, args):
catalog_id = require_arg(args, "--catalog-id", "Specify catalog ID, e.g. --catalog-id clg-paimon-xxxx", _validate_id)
database = require_arg(args, "--database", "Specify database name, e.g. --database my_db", _validate_name)
pattern, max_results, page_token = extract_pagination_args(args)
request = dlf_models.ListTableDetailsRequest(
table_name_pattern=pattern,
max_results=max_results,
page_token=page_token,
)
resp = client.list_table_details(catalog_id, database, request)
tables = [serialize_table(t) for t in (resp.body.table_details or [])]
out_paginated(tables, resp.body.next_page_token)
def action_get_table(client, args):
catalog_id = require_arg(args, "--catalog-id", "Specify catalog ID, e.g. --catalog-id clg-paimon-xxxx", _validate_id)
database = require_arg(args, "--database", "Specify database name, e.g. --database my_db", _validate_name)
table = require_arg(args, "--table", "Specify table name, e.g. --table my_table", _validate_name)
resp = client.get_table(catalog_id, database, table)
out_json(serialize_table(resp.body))
ACTIONS = {
"list-catalogs": action_list_catalogs,
"get-catalog": action_get_catalog,
"get-catalog-by-id": action_get_catalog_by_id,
"list-databases": action_list_databases,
"list-database-details": action_list_database_details,
"get-database": action_get_database,
"list-tables": action_list_tables,
"list-table-details": action_list_table_details,
"get-table": action_get_table,
}
HELP_TEXT = """Usage: dlf_metadata_query.py <action> [options...]
Catalog Actions:
list-catalogs [--pattern <name>] List all catalogs
get-catalog --catalog <name> Get catalog details by name
get-catalog-by-id --id <catalog_id> Get catalog details by ID
Database Actions:
list-databases --catalog-id <id> [--pattern <name>] List database names
list-database-details --catalog-id <id> [--pattern <name>] List database details
get-database --catalog-id <id> --database <name> Get database details
Table Actions:
list-tables --catalog-id <id> --database <name> [--pattern <name>] List table names
list-table-details --catalog-id <id> --database <name> [--pattern <name>] List table details
get-table --catalog-id <id> --database <name> --table <name> Get table details
Global Options:
--region <region_id> Optional. Defaults to cn-hangzhou.
--max-results <N> Optional. Max records per page.
--page-token <token> Optional. Pagination token.
Authentication:
Uses default credential chain (CredentialClient). Supports:
- Environment variables (ALIBABA_CLOUD_ACCESS_KEY_ID / SECRET)
- Credentials file (~/.alibabacloud/credentials)
- ECS instance RAM role
See https://help.aliyun.com/document_detail/378659.html
"""
def main():
args = sys.argv[1:]
if not args or args[0] in ("--help", "-h", "help"):
print(HELP_TEXT)
sys.exit(0)
action = args.pop(0)
if action not in ACTIONS:
all_actions = ", ".join(sorted(ACTIONS.keys()))
out_error(f"Unknown action: {action}",
f"Valid actions: {all_actions}")
client = build_client(args)
try:
ACTIONS[action](client, args)
except TeaException as e:
error_msg = str(e)
# statusCode is only populated when `data` is a dict containing statusCode
# (see Tea.exceptions.TeaException). Fall back to matching the dotted code
# string (e.g. 'Forbidden.RAM', 'EntityNotExist.Catalog').
status_code = getattr(e, 'statusCode', None)
code_str = str(getattr(e, 'code', '') or '')
# Check 401 before 404: 'InvalidAccessKeyId.NotFound' contains "NotFound"
# but is a credential error, not a missing resource.
if (status_code == 401
or code_str.startswith('Unauthorized')
or code_str.startswith('InvalidAccessKey')
or code_str == 'SignatureDoesNotMatch'):
out_error(f"Authentication failed: {error_msg}",
"Check credential configuration. "
"See https://help.aliyun.com/document_detail/378659.html")
elif status_code == 403 or code_str.startswith('Forbidden') or 'NoPermission' in code_str:
out_error(f"Permission denied: {error_msg}",
"Grant DLF data permissions (LIST/DESCRIBE) in DLF console.")
elif status_code == 404 or 'NotExist' in code_str or 'NotFound' in code_str:
out_error(f"Resource not found: {error_msg}")
else:
out_error(f"API error: {error_msg}")
except Exception as e:
out_error(f"Unexpected error: {e}")
if __name__ == "__main__":
main()
Related skills
FAQ
Can this skill modify DLF resources?
No. It only contains read-only operations; there are no create, modify, or delete operations.
How does it authenticate to Alibaba Cloud?
It uses the default credential chain (environment variables, credentials file, ECS RAM role, or OIDC role ARN) and never reads or prints AK/SK values.