
Alicloud Network Alb
- 155 installs
- 396 repo stars
- Updated July 18, 2026
- cinience/alicloud-skills
Configure Alibaba Cloud Application Load Balancer (ALB) listeners, server groups, health checks, and routing rules for production HTTP/HTTPS traffic distribution.
About
Agent skill for cinience/alicloud-skills that guides configuration of Alibaba Cloud Application Load Balancer (ALB): listeners, forwarding rules, server groups, health checks, TLS, and backend attachment for reliable SaaS and API ingress.
- ALB listener and rule configuration
- Server group and health check setup
- HTTPS termination and certificate binding
- VPC and backend target integration
- Traffic routing for multi-service deployments
Alicloud Network Alb by the numbers
- 155 all-time installs (skills.sh)
- Ranked #495 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cinience/alicloud-skills --skill alicloud-network-albAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 155 |
|---|---|
| repo stars | ★ 396 |
| Last updated | July 18, 2026 |
| Repository | cinience/alicloud-skills ↗ |
What it does
Configure Alibaba Cloud Application Load Balancer (ALB) listeners, server groups, health checks, and routing rules for production HTTP/HTTPS traffic distribution.
Files
Category: service
Application Load Balancer (ALB)
Use this skill for end-to-end ALB operations via local Python scripts and OpenAPI-compatible workflows.
Validation
mkdir -p output/alicloud-network-alb
for f in skills/network/slb/alicloud-network-alb/scripts/*.py; do
python3 -m py_compile "$f"
done
echo "py_compile_ok" > output/alicloud-network-alb/validate.txtPass criteria: command exits 0 and output/alicloud-network-alb/validate.txt is generated.
Output And Evidence
- Save all command outputs, request parameters, and API responses under
output/alicloud-network-alb/. - For change operations, keep before/after snapshots plus health-check results.
Prerequisites
pip install alibabacloud_alb20200616 alibabacloud_tea_openapi alibabacloud_credentialsCredential priority:
1. ALICLOUD_ACCESS_KEY_ID / ALICLOUD_ACCESS_KEY_SECRET 2. Optional STS token: ALICLOUD_SECURITY_TOKEN 3. Shared config: ~/.alibabacloud/credentials
Workflow
1. Confirm region, VPC context, target ALB resource IDs, and expected change window. 2. Run inventory scripts first (list_*, get_*) and save baseline outputs. 3. Apply one change at a time (listener/server-group/rule/lb lifecycle). 4. Wait for async completion when needed (scripts/wait_for_job.py). 5. Validate final state with health checks and state re-query.
Top task playbooks
1) Read-only inventory and quick diagnosis
python3 scripts/list_instances.py --region cn-hangzhou --json --output output/alicloud-network-alb/instances.json
python3 scripts/list_server_groups.py --region cn-hangzhou --json --output output/alicloud-network-alb/server-groups.json
python3 scripts/list_acls.py --region cn-hangzhou --json --output output/alicloud-network-alb/acls.json2) Inspect one ALB and listener details
python3 scripts/get_instance_status.py --region cn-hangzhou --lb-id alb-xxx --view detail --output output/alicloud-network-alb/lb-detail.json
python3 scripts/list_listeners.py --region cn-hangzhou --lb-id alb-xxx --json --output output/alicloud-network-alb/listeners.json
python3 scripts/get_listener_attribute.py --region cn-hangzhou --listener-id lsn-xxx --output output/alicloud-network-alb/listener-attr.json3) Validate traffic path health
python3 scripts/check_health_status.py --region cn-hangzhou --listener-id lsn-xxx --output output/alicloud-network-alb/health.json
python3 scripts/list_server_group_servers.py --region cn-hangzhou --server-group-id sgp-xxx --output output/alicloud-network-alb/server-group-members.json4) Controlled change flow (example: update listener)
python3 scripts/update_listener.py --region cn-hangzhou --listener-id lsn-xxx --request-timeout 120 --output output/alicloud-network-alb/update-listener.json
python3 scripts/check_health_status.py --region cn-hangzhou --listener-id lsn-xxx --output output/alicloud-network-alb/health-after-update.json5) Resource lifecycle operations
- ALB lifecycle:
create_load_balancer.py,delete_load_balancer.py,deletion_protection.py - Listener lifecycle:
create_listener.py,start_listener.py,stop_listener.py,delete_listener.py - Server-group lifecycle:
create_server_group.py,add_servers.py,remove_servers.py,delete_server_group.py - Rule lifecycle:
create_rule.py,update_rule.py,delete_rule.py
References
- API quick map:
references/api_quick_map.md - Script catalog:
references/scripts_catalog.md - Troubleshooting:
references/troubleshooting.md - Logs and analysis:
references/log-analysis.md - Dependencies/order:
references/resource-dependencies.md - Sources:
references/sources.md
interface:
display_name: "Alibaba Cloud Network ALB"
short_description: "ALB instance, listener, server group, health check, and troubleshooting workflows"
default_prompt: "Use $alicloud-network-alb to complete this ALB task on Alibaba Cloud."
ALB API Quick Map (2020-06-16)
Regions & Zones (2)
DescribeRegions— Query available regionsDescribeZones— Query available zones in a region
Load Balancer Instances (16)
CreateLoadBalancer— Create an ALB instanceDeleteLoadBalancer— Delete an ALB instanceGetLoadBalancerAttribute— Query instance details (including ZoneMappings/VIP)ListLoadBalancers— List instancesUpdateLoadBalancerAttribute— Modify instance attributesUpdateLoadBalancerZones— Modify availability zone configurationUpdateLoadBalancerEdition— Change edition (Basic/Standard/StandardWithWaf)UpdateLoadBalancerAddressTypeConfig— Switch network type (public/private)EnableDeletionProtection— Enable deletion protectionDisableDeletionProtection— Disable deletion protectionStartShiftLoadBalancerZones— Remove a zone from DNS resolution (Zone Shift)CancelShiftLoadBalancerZones— Restore zone DNS resolutionEnableLoadBalancerIpv6Internet— Switch dual-stack IPv6 to publicDisableLoadBalancerIpv6Internet— Switch dual-stack IPv6 to privateLoadBalancerJoinSecurityGroup— Associate a security groupLoadBalancerLeaveSecurityGroup— Disassociate a security group
Listeners (9)
CreateListener— Create a listenerDeleteListener— Delete a listenerGetListenerAttribute— Query listener details (including certificates/ACL)ListListeners— List listenersStartListener— Start a listenerStopListener— Stop a listenerUpdateListenerAttribute— Modify listener configurationUpdateListenerLogConfig— Modify listener log configurationGetListenerHealthStatus— Query listener health check status
Server Groups (9)
CreateServerGroup— Create a server groupDeleteServerGroup— Delete a server groupUpdateServerGroupAttribute— Modify server group configuration (health check/session persistence/scheduling algorithm, etc.)UpdateServerGroupServersAttribute— Modify backend server weight and descriptionListServerGroups— List server groupsListServerGroupServers— List backend servers in a server groupAddServersToServerGroup— Add backend serversRemoveServersFromServerGroup— Remove backend serversReplaceServersInServerGroup— Replace backend servers
Forwarding Rules (7)
CreateRule— Create a forwarding ruleCreateRules— Create forwarding rules in batchDeleteRule— Delete a forwarding ruleDeleteRules— Delete forwarding rules in batchUpdateRuleAttribute— Modify a forwarding ruleUpdateRulesAttribute— Modify forwarding rules in batchListRules— List forwarding rules
Listener Certificates (3)
AssociateAdditionalCertificatesWithListener— Associate additional certificates (SNI)DissociateAdditionalCertificatesFromListener— Disassociate additional certificatesListListenerCertificates— List certificates associated with a listener
Common Bandwidth Packages (2)
AttachCommonBandwidthPackageToLoadBalancer— Associate a shared bandwidth packageDetachCommonBandwidthPackageFromLoadBalancer— Disassociate a shared bandwidth package
Access Logs (2)
EnableLoadBalancerAccessLog— Enable access logsDisableLoadBalancerAccessLog— Disable access logs
Health Check Templates (6)
CreateHealthCheckTemplate— Create a health check templateGetHealthCheckTemplateAttribute— Query health check template detailsDeleteHealthCheckTemplates— Delete health check templates in batchUpdateHealthCheckTemplateAttribute— Modify a health check templateListHealthCheckTemplates— List health check templatesApplyHealthCheckTemplateToServerGroup— Apply a template to a server group
Security Policies (6)
CreateSecurityPolicy— Create a custom TLS security policyDeleteSecurityPolicy— Delete a security policyUpdateSecurityPolicyAttribute— Modify a security policy (TLS versions/cipher suites)ListSecurityPolicies— List custom security policiesListSecurityPolicyRelations— Query listeners associated with a security policyListSystemSecurityPolicies— List system predefined security policies
Access Control / ACL (10)
CreateAcl— Create an access control listDeleteAcl— Delete an ACLUpdateAclAttribute— Modify ACL attributesListAcls— List ACLsListAclEntries— List IP entries in an ACLAddEntriesToAcl— Add IP entriesRemoveEntriesFromAcl— Remove IP entriesAssociateAclsWithListener— Associate ACLs with a listenerDissociateAclsFromListener— Disassociate ACLs from a listenerListAclRelations— Query listeners associated with an ACL
Tags (5)
TagResources— Tag resourcesUnTagResources— Remove tagsListTagResources— Query resource tagsListTagKeys— Query tag keysListTagValues— Query tag values
Resource Groups (1)
MoveResourceGroup— Move a resource to another resource group
Async Jobs (1)
ListAsynJobs— Query async jobs
Capacity Reservation (2)
ModifyCapacityReservation— Create or modify capacity reservationDescribeCapacityReservation— Query capacity reservation details
Programmable Scripts / AScript (4)
CreateAScripts— Create programmable scriptsUpdateAScripts— Modify programmable scriptsDeleteAScripts— Delete programmable scriptsListAScripts— List programmable scripts
ALB Access Log Analysis
Source: ALB Access Logs
Run log queries using the `alicloud-observability-sls-log-query` skill.
SetSLS_PROJECTandSLS_LOGSTOREto the ALB access log project/logstore.
Obtain fromGetLoadBalancerAttribute→AccessLogConfig.LogProject/AccessLogConfig.LogStore.
Log Fields
Log topic: alb_layer7_access_log
Request
| Field | Type | Description |
|---|---|---|
request_method | string | HTTP method |
request_uri | string | Request URI |
request_length | int | Total request length (startline + headers + body), bytes |
scheme | string | HTTP or HTTPS |
server_protocol | string | HTTP protocol version (HTTP/1.0, HTTP/1.1) |
host | string | Domain or IP — from request params, then Host header, then backend IP |
http_host | string | Host header value |
http_referer | string | Referer header |
http_user_agent | string | User-Agent header |
http_x_forwarded_for | string | X-Forwarded-For header |
http_x_real_ip | string | X-Real-IP header |
slb_headers | string | Custom headers (requires feature enablement, default 1 KB, max 4 KB) |
Client
| Field | Type | Description |
|---|---|---|
client_ip | string | Client IP. With "real client source IP" enabled, shows the real client IP; otherwise shows previous hop |
client_port | int | Client port |
tcpinfo_rtt | int | Client TCP RTT, microseconds |
ALB Instance
| Field | Type | Description |
|---|---|---|
app_lb_id | string | ALB instance ID |
vip_addr | string | Virtual IP address |
slb_vport | int | Listener port |
TLS
| Field | Type | Description |
|---|---|---|
ssl_protocol | string | TLS version (e.g. TLSv1.2) |
ssl_cipher | string | TLS cipher suite (e.g. ECDHE-RSA-AES128-GCM-SHA256) |
Response & Timing
| Field | Type | Description |
|---|---|---|
status | int | Status code returned by ALB to client |
upstream_status | int | Status code returned by backend |
upstream_addr | string | Backend server IP:port |
body_bytes_sent | int | HTTP body bytes sent to client |
request_time | float | Total request duration (first packet received → response sent), seconds |
read_request_time | int | Time ALB spent reading the request, milliseconds |
upstream_response_time | float | Time from ALB connecting to backend through receiving data and closing, seconds |
write_response_time | int | ALB response write time, milliseconds |
Tracing
| Field | Type | Description |
|---|---|---|
slb_xtrace | string | Trace link TraceId |
xtrace_type | string | Trace type (currently only Zipkin) |
Meta
| Field | Type | Description |
|---|---|---|
time | string | Log timestamp, YYYY-MM-DDThh:mm:ssZ |
__topic__ | string | Always alb_layer7_access_log |
SLS Query Templates
5xx error distribution
status >= 500 | SELECT status, upstream_status, count(*) AS cnt
GROUP BY status, upstream_status ORDER BY cnt DESC LIMIT 204xx error distribution
status >= 400 AND status < 500 | SELECT status, request_uri, count(*) AS cnt
GROUP BY status, request_uri ORDER BY cnt DESC LIMIT 20502 errors with backend detail
status:502 | SELECT upstream_addr, upstream_status, count(*) AS cnt
GROUP BY upstream_addr, upstream_status ORDER BY cnt DESCSlow requests Top 20 (by upstream_response_time)
* | SELECT request_uri, upstream_addr,
avg(upstream_response_time) AS avg_rt,
max(upstream_response_time) AS max_rt,
count(*) AS cnt
GROUP BY request_uri, upstream_addr
ORDER BY avg_rt DESC LIMIT 20Latency breakdown (read / upstream / write)
* | SELECT
avg(read_request_time) AS avg_read_ms,
avg(upstream_response_time * 1000) AS avg_upstream_ms,
avg(write_response_time) AS avg_write_ms,
avg(request_time * 1000) AS avg_total_msTop client IPs
* | SELECT client_ip, count(*) AS cnt
GROUP BY client_ip ORDER BY cnt DESC LIMIT 20Traffic by URI path
* | SELECT request_uri, count(*) AS pv,
sum(body_bytes_sent) AS total_bytes
GROUP BY request_uri ORDER BY pv DESC LIMIT 20Backend server error rate
* | SELECT upstream_addr,
count_if(upstream_status >= 500) AS err_5xx,
count(*) AS total,
round(count_if(upstream_status >= 500) * 100.0 / count(*), 2) AS err_rate
GROUP BY upstream_addr ORDER BY err_rate DESC LIMIT 20Request volume trend (per minute)
* | SELECT date_trunc('minute', __time__) AS t, count(*) AS qps
GROUP BY t ORDER BY tRequests for a specific backend server
upstream_addr:172.16.0.x | SELECT time, client_ip, request_uri,
status, upstream_status, request_time, upstream_response_time
ORDER BY time DESC LIMIT 100High-RTT clients
* | SELECT client_ip, avg(tcpinfo_rtt / 1000.0) AS avg_rtt_ms, count(*) AS cnt
GROUP BY client_ip HAVING avg_rtt_ms > 100
ORDER BY avg_rtt_ms DESC LIMIT 20ALB Resource Dependencies and Creation Order
Dependency Diagram
VPC + VSwitch (prerequisite, already exists)
│
├── ACL ← Only requires Region, no other dependencies
│
├── Security Policy ← Only requires Region, no other dependencies
│
├── Server Group ← Requires VpcId
│ └── Add Servers ← Requires ServerGroupId + backend instances (ECS/ENI/ECI/IP)
│
└── ALB Instance ← Requires VpcId + ZoneMappings(ZoneId + VSwitchId)
│
├── Access Log (enable) ← Requires LoadBalancerId + SLS Project/LogStore
│
└── Listener ← Requires LoadBalancerId + ServerGroupId (default forwarding action)
│ + CertificateId (required for HTTPS)
│ + SecurityPolicyId (optional for HTTPS)
│
├── Forwarding Rule ← Requires ListenerId + ServerGroupId (forwarding target)
│
├── Associate Certificate (SNI) ← Requires ListenerId + CertificateId
│
└── Associate ACL ← Requires ListenerId + AclId + AclType(White/Black)Creation Order (Building a Complete ALB from Scratch)
Must strictly follow the order below, otherwise it will fail due to missing dependency IDs:
Step 1: Prepare resources with no dependencies (can be parallelized)
├── Create Server Group → Obtain ServerGroupId
├── Create ACL (if needed) → Obtain AclId
└── Create Security Policy (if needed) → Obtain SecurityPolicyId
Step 2: Add backends to Server Group (can be done at any time)
└── Add Servers to ServerGroup ← ServerGroupId + backend instance info
Step 3: Create ALB instance
└── Create LoadBalancer → Obtain LoadBalancerId
⚠️ ALB creation is an async operation, wait until status becomes Active
Step 4: Enable Access Log (optional)
└── EnableLoadBalancerAccessLog ← LoadBalancerId
Step 5: Create Listener
└── Create Listener → Obtain ListenerId
← LoadBalancerId + ServerGroupId (default forwarding)
← CertificateId (required for HTTPS)
← SecurityPolicyId (optional for HTTPS, defaults to tls_cipher_policy_1_0)
⚠️ Listener creation is also an async operation
Step 6: Configure Listener associated resources (can be parallelized)
├── Create Forwarding Rule ← ListenerId + ServerGroupId
├── Associate SNI Certificate ← ListenerId + CertificateId
└── Associate ACL ← ListenerId + AclIdDeletion Order (Reverse of Creation)
Deletion must start from leaf resources; disassociate first, then delete:
Step 1: Delete/disassociate Listener associated resources (can be parallelized)
├── Delete Forwarding Rules
├── Disassociate SNI Certificates (DissociateAdditionalCertificatesFromListener)
└── Disassociate ACLs (DissociateAclsFromListener)
Step 2: Delete Listener
└── Delete Listener
Step 3: Delete ALB instance
├── Disable Deletion Protection (DisableDeletionProtection) (if enabled)
└── Delete LoadBalancer
Step 4: Delete resources with no dependencies (can be parallelized)
├── Remove backends → Delete Server Group
├── Delete ACL
└── Delete Security PolicyAsync Operation Notes
The following operations are asynchronous. They return a JobId upon invocation, and you need to poll until completion:
| Operation | Wait Condition |
|---|---|
| CreateLoadBalancer | GetLoadBalancerAttribute → LoadBalancerStatus == Active |
| DeleteLoadBalancer | GetLoadBalancerAttribute → 404 (resource does not exist) |
| CreateListener | GetListenerAttribute → ListenerStatus == Running |
| DeleteListener | GetListenerAttribute → 404 |
| CreateRule / CreateRules | ListRules to confirm the rule exists |
You can query async task status via ListAsynJobs.
ID Passing Quick Reference
| Current Operation | Required IDs | Where to Obtain |
|---|---|---|
| CreateServerGroup | VpcId | Known or from VPC console |
| AddServersToServerGroup | ServerGroupId | Returned by CreateServerGroup |
| CreateLoadBalancer | VpcId, ZoneId, VSwitchId | Known or from VPC/ECS console |
| CreateListener | LoadBalancerId, ServerGroupId | Returned by previous steps |
| CreateListener (HTTPS) | CertificateId | SSL Certificate Management console |
| CreateRule | ListenerId, ServerGroupId | Returned by previous steps |
| AssociateAclsWithListener | ListenerId, AclId | Returned by previous steps |
| EnableLoadBalancerAccessLog | LoadBalancerId, LogProject, LogStore | SLS console |
Common Topology Patterns
Pattern 1: HTTP → HTTPS Redirect
ServerGroup-A (with backends attached)
ALB
├── Listener HTTP:80
│ └── DefaultAction: Redirect → HTTPS:443
└── Listener HTTPS:443
├── DefaultAction: Forward → ServerGroup-A
└── Certificate: cert-xxxCreation order: ServerGroup-A → ALB → Listener HTTPS:443 (bindServerGroup-A) → Listener HTTP:80 (DefaultAction=Redirect)
Pattern 2: Multi-Domain Routing
ServerGroup-API (API backend)
ServerGroup-Web (frontend static)
ServerGroup-Default (fallback)
ALB
└── Listener HTTPS:443
├── Rule: Host(api.example.com) → ServerGroup-API
├── Rule: Host(www.example.com) → ServerGroup-Web
└── DefaultAction → ServerGroup-DefaultCreation order: 3 ServerGroups → ALB → Listener (bind ServerGroup-Default) → 2 Rules
Pattern 3: Blue-Green Deployment
ServerGroup-Blue (current version, weight 100)
ServerGroup-Green (new version, weight 0)
ALB
└── Listener HTTPS:443
└── DefaultAction: ForwardGroup
├── ServerGroup-Blue weight=100
└── ServerGroup-Green weight=0Adjust weights via UpdateRuleAttribute to switch traffic.
ALB Scripts Catalog
This file groups all local scripts by operation so SKILL.md can stay focused on the decision workflow.
Inventory and inspection
scripts/list_instances.py- list ALB instances with filters.scripts/get_instance_status.py- inspect one ALB instance (tree/detail view).scripts/list_listeners.py- list listeners under one ALB.scripts/get_listener_attribute.py- inspect listener details.scripts/list_server_groups.py- list server groups.scripts/list_server_group_servers.py- list backend servers in one group.scripts/list_rules.py- list forwarding rules.scripts/check_health_status.py- query listener health status.scripts/list_listener_certificates.py- list listener certificates.scripts/list_security_policies.py- list custom/system TLS policies.scripts/list_acls.py- list ACL resources.scripts/list_acl_entries.py- list ACL entries.
Create and update resources
scripts/create_load_balancer.py/scripts/delete_load_balancer.pyscripts/deletion_protection.pyscripts/create_listener.py/scripts/update_listener.py/scripts/start_listener.py/scripts/stop_listener.py/scripts/delete_listener.pyscripts/create_server_group.py/scripts/delete_server_group.pyscripts/add_servers.py/scripts/remove_servers.pyscripts/create_rule.py/scripts/update_rule.py/scripts/delete_rule.py
Async and operation tracking
scripts/wait_for_job.py- wait for ALB async job completion and return final state.
Suggested sequence for production changes
1. Snapshot current state with list_* and get_* scripts. 2. Apply one change operation (create/update/delete). 3. Wait with wait_for_job.py if the API returns an async job id. 4. Re-check listeners, server groups, and health status. 5. Save all evidence under output/alicloud-network-alb/.
ALB Official Sources
- ALB API overview (2020-06-16):
- https://api.aliyun.com/document/Alb/2020-06-16/overview
- ALB product page in API Explorer:
- https://api.aliyun.com/product/Alb
- ALB OpenAPI metadata (machine-readable):
- https://api.aliyun.com/meta/v1/products/Alb/versions/2020-06-16/api-docs.json
- Alibaba Cloud API Explorer:
- https://api.aliyun.com/
ALB Troubleshooting Guide
Source: ALB FAQ, Status Code Reference, Health Check Troubleshooting
1. Cannot Access Service Through ALB
Diagnostic checklist:
1. Verify CNAME — ALB DNS names cannot be accessed directly; custom domain must CNAME to ALB DNS name. Verify with nslookup or dig. 2. Check network type — Private (Intranet) ALB only works within VPC; switch to Internet + bind EIP for public access. 3. Check ICP filing — Domains must be ICP-filed for China mainland public access (403 or connection reset otherwise). If ICP was done at another provider, transfer filing to Alibaba Cloud is also required. 4. Check listeners / forwarding rules — Verify port, protocol, domain/path matching. 5. Check health status — Unhealthy backends prevent proper forwarding. See Section 3 below. 6. Verify backend directly — curl -I http://<backend-IP>:<port> from the server. 7. Check security groups — Ensure listener port and source IPs are allowed.
2. High Latency Through ALB
1. Enable access logs, analyze request_time and upstream_response_time. 2. If upstream_response_time is high → backend processing is slow (check app performance, DB, CPU/memory). 3. If request_time >> upstream_response_time → network issue between client and ALB (use ping/MTR). 4. Cross-region: use Global Accelerator (GA) to reduce latency.
3. Health Check Failures
3.1 First-time configuration failures
| Cause | Fix |
|---|---|
| Health check parameter misconfiguration | Compare with default parameters as baseline |
| Port/path mismatch | Probe from backend: curl -X $METHOD -H "Host: $DOMAIN" -I http://$IP:$PORT$PATH |
| Status code mismatch | Backend returns a valid code (e.g. 302) not in the expected list → update health check success codes |
3.2 Previously working, now failing
| Cause | Diagnostic | Fix |
|---|---|---|
| Security software / iptables blocking | iptables -nL on backend ECS | Remove blocking rule: iptables -t filter -D INPUT -s 100.64.0.0/10 -j DROP |
| Routing misconfiguration (pre-upgrade ALB) | route -n — look for 100.64.0.0/10 pointing to wrong gateway | route del -net 100.64.0.0/10 |
| Backend overloaded | Check CPU/memory/IO on backend | Address resource bottleneck |
3.3 ALB health check source IPs
- Upgraded ALB instances: use private addresses from the ALB's VSwitch subnet (Local IP).
- Pre-upgrade ALB instances: use
100.64.0.0/10range. Must NOT be blocked by security groups or iptables.
3.4 API-based diagnosis
1. GetListenerHealthStatus(ListenerId) → NonNormalServers list with ReasonCode
2. ListServerGroupServers(ServerGroupId) → confirm backend IP/port/weight
3. Probe from backend ECS: curl -v http://127.0.0.1:{port}{health_check_path}3.5 Health check passes but requests return 502
Health checks use lightweight probes that may succeed while actual requests fail under heavy backend load. Check backend resource utilization.
3.6 All backends fail health check
ALB still attempts to forward requests per the scheduling algorithm to minimize business impact. Check logs and health check configuration.
4. HTTP Status Codes — ALB Error Reference
Key diagnostic principle: always compare status (ALB-returned) vs upstream_status (backend-returned) in access logs. When they match, the issue originates from the backend, not ALB.
4xx Client Errors
| Code | Cause | Resolution |
|---|---|---|
| 400 | Malformed request; HTTP sent to HTTPS port; request header > 32 KB; incomplete request transmission | Fix client request format; check protocol match; reduce header size |
| 405 | ALB blocks TRACE method; other methods depend on backend support | Use alternative method; verify backend supports the method |
| 408 | Client too slow sending data (default timeout 60s); poor network quality; ALB bandwidth throttling | Check request_time and tcpinfo_rtt in access logs; increase timeout; check Cloud Monitor bandwidth metrics |
| 414 | URI > 32 KB (ALB limit, not adjustable) | Shorten URI or use POST body (supports up to 50 GB) |
| 463 | Request routing loop — ALB detects duplicate ALICLOUD-ALB-TRACE header | Backend is redirecting back to ALB; fix backend config or network architecture |
| 499 | Client closed connection before receiving response | Poor network; backend too slow (upstream_response_time); client timeout too short |
5xx Server Errors
500 Internal Server Error
| Scenario | Diagnostic |
|---|---|
| Backend returns 500 | upstream_status = 500 in access logs → investigate backend |
| Backend abnormally closes connection before completing response | Packet capture on backend |
502 Bad Gateway
| Scenario | Diagnostic |
|---|---|
| Backend returns 502 directly | upstream_status = 502 → backend issue |
| Backend returns other codes (504, 444) but ALB shows 502 | Compare status vs upstream_status in logs |
| TCP communication failure with backend | Check service status, port listening, TCP handshake |
| Backend backlog full (new connections dropped) | `netstat -s \ |
| Client packet exceeds backend MTU | Small health check packets succeed but larger packets fail |
| Malformed response or illegal HTTP headers from backend | Packet capture on backend |
| Backend not processing requests in time | Check backend logs, CPU, memory |
503 Service Temporarily Unavailable
| Scenario | Diagnostic |
|---|---|
| Backend returns 503 | upstream_status = 503 → backend issue |
| ALB rate limiting triggered | upstream_status = - (request never reached backend); response header ALB-QPS-Limited: Limited confirms throttling; check Cloud Monitor "requests per second" metric |
| Direct IP access / DNS anomaly | Traffic concentrates on few IPs → access via ALB domain name with proper CNAME |
| No backends configured or all weights = 0 | Ensure at least one backend with non-zero weight |
504 Gateway Timeout
| Scenario | Diagnostic |
|---|---|
| Backend returns 504 | upstream_status = 504 → backend issue |
| ALB-to-backend connection timeout | Default 5s, not modifiable; packet capture to find root cause |
| Backend response timeout | Default 60s; check upstream_response_time in access logs |
SLS log query for status code analysis
Use alicloud-observability-sls-log-query skill:
-- Top error status codes with upstream correlation
status >= 400 | SELECT status, upstream_status, count(*) AS cnt
GROUP BY status, upstream_status ORDER BY cnt DESC LIMIT 20
-- 502 errors with backend detail
status:502 | SELECT upstream_addr, upstream_status, count(*) AS cnt
GROUP BY upstream_addr, upstream_status ORDER BY cnt DESC5. Certificate & HTTPS Issues
| Issue | Resolution |
|---|---|
| Certificate expired | Check validity in SSL Certificate Service console; replace with new certificate |
| Certificate-domain mismatch | Check SNI config with ListListenerCertificates; wildcard certs (*.example.com) match same-level subdomains only |
| Wildcard cert doesn't match | Only one * allowed, must be leftmost; *.example.com does NOT match test.test.example.com; * matches 0-9, letters, hyphens only (no underscores) |
| Cannot upload cert directly to ALB | Upload to SSL Certificate Service console first; ALB references certs from there |
| Browser shows old cert after update | Likely WAF 2.0 transparent integration — force sync by toggling WAF traffic redirection |
| CA mutual auth not working | Only Standard and WAF Enhanced editions support CA mutual auth; Basic edition does not |
6. Forwarding Rule Conflicts
ListRules(ListenerId) → check all rules' Priority and Conditions- Same listener: Priority values must be unique.
- When conditions overlap, lower Priority value (higher precedence) matches first.
- Check host + path combinations for unintended overlaps.
7. ACL Access Control Issues
GetListenerAttribute(ListenerId) → AclConfig { AclType, AclRelations }
ListAclEntries(AclId) → check IP entriesAclType: White(whitelist): only listed IPs can access.AclType: Black(blacklist): listed IPs are denied.- Verify the client's real IP is (or is not) in the ACL list.
8. ALB Request Limits
| Parameter | Limit | Adjustable |
|---|---|---|
| URI length | 32 KB max | No |
| Request header | 32 KB max | No |
| Custom header in access logs | 1 KB default, 4 KB max | Via account manager |
| POST body | 50 GB max | — |
| Keep-alive requests per connection | 100 (HTTP); 1000 (HTTPS + HTTP/2.0) | No |
| QUIC Client Hello | ≥ 1024 bytes | Pad with null characters |
Exceeding URI or header limits returns 400 or 414.
9. WAF Integration Notes
| Architecture | Behavior |
|---|---|
| WAF 2.0 transparent | Request → WAF → ALB (two gateways); requires syncing timeouts and certificates on both sides |
| WAF 3.0 service-based | Request → ALB → WAF inspection (single gateway); no sync issues |
- Recommended: WAF 3.0 service-based (WAF Enhanced ALB edition).
- After releasing WAF 2.0, manually enable
X-Forwarded-Protoheader in ALB listener to avoid protocol detection issues (e.g. infinite redirects).
10. EIP & Bandwidth
| Issue | Resolution |
|---|---|
| How to increase ALB public bandwidth | Default 400 Mbps per instance (dual AZ); purchase shared bandwidth package and add ALB-bound EIPs |
| EIP traffic unevenly distributed | Domain resolved to single EIP instead of ALB DNS name; front-end proxy using IPHash; clients caching DNS A records |
| Supported EIP types | Pay-as-you-go with usage-based billing only; BGP multi-line, BGP Premium, BGP DDoS Enhanced; subscription and fixed-bandwidth EIPs not supported |
| Private ALB → public | Change network type from Intranet to Internet (binds EIP, incurs public network fees) |
#!/usr/bin/env python3
"""Add backend servers to an ALB server group.
Supports ECS instances, ENI, ECI, and IP-based servers.
Examples:
# Add a single ECS server
python add_servers.py --region cn-hangzhou --sg-id sgp-xxx \\
--server ecs:i-xxx:8080
# Add multiple servers with weight
python add_servers.py --region cn-hangzhou --sg-id sgp-xxx \\
--server ecs:i-xxx:8080:100 \\
--server ecs:i-yyy:8080:50
# Add ENI server
python add_servers.py --region cn-hangzhou --sg-id sgp-xxx \\
--server eni:eni-xxx:8080
# Add IP-based server (for Ip-type server group)
python add_servers.py --region cn-hangzhou --sg-id sgp-xxx \\
--server ip:10.0.1.100:8080
# Dry run
python add_servers.py --region cn-hangzhou --sg-id sgp-xxx \\
--server ecs:i-xxx:8080 --dry-run
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from pathlib import Path
from typing import Any
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
"""Create ALB client with credentials from environment."""
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
SERVER_TYPE_MAP = {
"ecs": "Ecs",
"eni": "Eni",
"eci": "Eci",
"ip": "Ip",
}
def parse_server(spec: str) -> dict[str, Any]:
"""Parse server spec: type:id:port[:weight[:description]].
For IP type: ip:address:port[:weight[:description]]
"""
parts = spec.split(":")
if len(parts) < 3:
raise ValueError(
f"Invalid server spec '{spec}'. "
"Format: type:id:port[:weight[:description]] "
"e.g. ecs:i-xxx:8080 or ecs:i-xxx:8080:100:web-server"
)
raw_type = parts[0].lower()
server_type = SERVER_TYPE_MAP.get(raw_type)
if not server_type:
raise ValueError(
f"Unknown server type '{raw_type}'. Supported: {', '.join(SERVER_TYPE_MAP.keys())}"
)
server: dict[str, Any] = {"server_type": server_type, "port": int(parts[2])}
if server_type == "Ip":
server["server_ip"] = parts[1]
else:
server["server_id"] = parts[1]
if len(parts) >= 4 and parts[3]:
server["weight"] = int(parts[3])
else:
server["weight"] = 100
if len(parts) >= 5 and parts[4]:
server["description"] = parts[4]
return server
def main() -> int:
parser = argparse.ArgumentParser(
description="Add backend servers to ALB server group",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Server spec format: type:id:port[:weight[:description]]
type: ecs | eni | eci | ip
id: instance/ENI/ECI ID, or IP address for 'ip' type
port: backend port
weight: 0-100, default 100
description: optional
Examples:
%(prog)s --region cn-hangzhou --sg-id sgp-xxx --server ecs:i-xxx:8080
%(prog)s --region cn-hangzhou --sg-id sgp-xxx --server ecs:i-xxx:8080:100:web
%(prog)s --region cn-hangzhou --sg-id sgp-xxx --server ip:10.0.1.100:8080:50
""",
)
parser.add_argument("--region", required=True, help="Region ID")
parser.add_argument("--sg-id", required=True, dest="server_group_id", help="Server group ID")
parser.add_argument(
"--server", required=True, action="append", dest="servers",
help="Server spec: type:id:port[:weight[:description]]",
)
parser.add_argument("--dry-run", action="store_true", help="Print request without executing")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
try:
parsed_servers = [parse_server(s) for s in args.servers]
except ValueError as e:
print(f"Error: {e}", file=sys.stderr)
return 1
if args.dry_run:
output = json.dumps({
"server_group_id": args.server_group_id,
"servers": parsed_servers,
}, indent=2, ensure_ascii=False)
if args.output:
Path(args.output).write_text(output + "\n", encoding="utf-8")
print(f"Dry-run request written to {args.output}")
else:
print("Dry-run mode - would add servers:")
print(output)
return 0
client = create_client(args.region)
sdk_servers = []
for s in parsed_servers:
srv = alb_models.AddServersToServerGroupRequestServers(
server_type=s["server_type"],
port=s["port"],
weight=s.get("weight", 100),
)
if "server_id" in s:
srv.server_id = s["server_id"]
if "server_ip" in s:
srv.server_ip = s["server_ip"]
if "description" in s:
srv.description = s["description"]
sdk_servers.append(srv)
try:
resp = client.add_servers_to_server_group(alb_models.AddServersToServerGroupRequest(
server_group_id=args.server_group_id,
servers=sdk_servers,
))
result = {"job_id": resp.body.job_id, "request_id": resp.body.request_id}
except Exception as e:
print(f"Error adding servers: {e}", file=sys.stderr)
return 1
if args.json:
output = json.dumps(result, indent=2, ensure_ascii=False)
else:
output = f"Servers added successfully ({len(parsed_servers)} server(s)):\n Job ID: {result['job_id']}"
if args.output:
Path(args.output).write_text(output + "\n", encoding="utf-8")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Check ALB listener health status and report unhealthy backends."""
from __future__ import annotations
import argparse
import json
import os
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def list_all_listeners(client: Alb20200616Client, lb_id: str) -> list:
"""Fetch all listeners for a given ALB instance."""
listeners: list = []
next_token: str | None = None
while True:
req = alb_models.ListListenersRequest(
load_balancer_ids=[lb_id],
max_results=100,
next_token=next_token,
)
resp = client.list_listeners(req)
listeners.extend(resp.body.listeners or [])
next_token = resp.body.next_token
if not next_token:
break
return listeners
def _get_all_health_status(client: Alb20200616Client, listener_id: str):
"""Fetch all health status pages for a listener."""
all_status = []
all_rule_status = []
next_token: str | None = None
while True:
req = alb_models.GetListenerHealthStatusRequest(
listener_id=listener_id,
include_rule=True,
max_results=30,
next_token=next_token,
)
resp = client.get_listener_health_status(req)
all_status.extend(resp.body.listener_health_status or [])
all_rule_status.extend(resp.body.rule_health_status or [])
next_token = resp.body.next_token
if not next_token:
break
return all_status, all_rule_status
def _format_unhealthy_servers(non_normal, indent: str = " ") -> list[str]:
"""Format unhealthy server entries."""
lines: list[str] = []
for srv in non_normal:
server_ip = srv.server_ip or "?"
port = srv.port or "?"
status = srv.status or "?"
reason_str = ""
if srv.reason:
reason_code = srv.reason.reason_code or ""
actual = srv.reason.actual_response or ""
expected = srv.reason.expected_response or ""
reason_str = reason_code
if actual or expected:
reason_str += f" (expected:{expected}, actual:{actual})"
line = f"{indent}- {server_ip}:{port} [{status}]"
if reason_str:
line += f" {reason_str}"
lines.append(line)
return lines
def check_listener_health(client: Alb20200616Client, listener_id: str) -> list[str]:
"""Check health status for a single listener, return formatted lines."""
lines: list[str] = []
health_status, rule_health_status = _get_all_health_status(client, listener_id)
if not health_status:
lines.append(" (No health status data available)")
return lines
for lhs in health_status:
for sg_info in lhs.server_group_infos or []:
sg_id = sg_info.server_group_id or "?"
# health_check_enabled is a str: "on" means enabled
hc_enabled = sg_info.health_check_enabled
if hc_enabled is not None and hc_enabled != "on":
lines.append(f" ServerGroup: {sg_id}")
lines.append(" ⏸️ Health check disabled")
continue
non_normal = sg_info.non_normal_servers or []
if not non_normal:
lines.append(f" ServerGroup: {sg_id}")
lines.append(" ✅ All backends healthy")
else:
lines.append(f" ServerGroup: {sg_id}")
lines.append(f" ⚠️ Unhealthy backends detected ({len(non_normal)}):")
lines.extend(_format_unhealthy_servers(non_normal))
# Rule-level health status
if rule_health_status:
lines.append("")
lines.append(" Rule-level health status:")
for rhs in rule_health_status:
rule_id = rhs.rule_id or "?"
for sg_info in rhs.server_group_infos or []:
sg_id = sg_info.server_group_id or "?"
hc_enabled = sg_info.health_check_enabled
if hc_enabled is not None and hc_enabled != "on":
lines.append(f" Rule {rule_id} → ServerGroup {sg_id}: health check disabled")
continue
non_normal = sg_info.non_normal_servers or []
if not non_normal:
lines.append(f" Rule {rule_id} → ServerGroup {sg_id}: all healthy")
else:
lines.append(f" Rule {rule_id} → ServerGroup {sg_id}: {len(non_normal)} unhealthy")
lines.extend(_format_unhealthy_servers(non_normal, indent=" "))
return lines
def main() -> int:
parser = argparse.ArgumentParser(description="Check ALB listener health status")
parser.add_argument("--region", required=True, help="Region ID, e.g. cn-hangzhou")
parser.add_argument("--lb-id", required=True, help="LoadBalancer ID, e.g. alb-xxx")
parser.add_argument("--listener-id", help="Specific listener ID. If omitted, checks all listeners.")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
client = create_client(args.region)
# Determine which listeners to check
if args.listener_id:
listener_ids = [args.listener_id]
else:
listeners = list_all_listeners(client, args.lb_id)
listener_ids = [ls.listener_id for ls in listeners if ls.listener_id]
if args.json:
# JSON mode: collect raw health status data for all listeners
result = []
for lid in listener_ids:
health_status, rule_health_status = _get_all_health_status(client, lid)
entry = {
"listener_id": lid,
"listener_health_status": [s.to_map() for s in health_status],
"rule_health_status": [r.to_map() for r in rule_health_status],
}
result.append(entry)
output = json.dumps(result, indent=2, ensure_ascii=False, default=str)
else:
lines: list[str] = []
if args.listener_id:
lines.append(f"Listener: {args.listener_id}")
lines.extend(check_listener_health(client, args.listener_id))
else:
if not listener_ids:
lines.append(f"No listeners found for ALB {args.lb_id}")
else:
lines.append(f"Health check for ALB: {args.lb_id} ({len(listener_ids)} listener(s))")
lines.append("")
for ls in listeners:
lid = ls.listener_id or "?"
proto = ls.listener_protocol or "?"
port = ls.listener_port or "?"
lstatus = ls.listener_status or "?"
lines.append(f"Listener: {lid} ({proto}:{port}) [{lstatus}]")
lines.extend(check_listener_health(client, lid))
lines.append("")
output = "\n".join(lines).rstrip()
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output + "\n")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Create ALB listener with support for HTTP/HTTPS/QUIC and various default actions."""
from __future__ import annotations
import argparse
import json
import os
from typing import Any
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def build_default_action(args: argparse.Namespace) -> alb_models.CreateListenerRequestDefaultActions:
"""Build default action from CLI arguments."""
action_type = args.action_type
if action_type == "ForwardGroup":
if not args.forward_server_groups:
raise ValueError("--forward-server-groups required for ForwardGroup action")
tuples = [
alb_models.CreateListenerRequestDefaultActionsForwardGroupConfigServerGroupTuples(
server_group_id=sg_id,
)
for sg_id in args.forward_server_groups
]
return alb_models.CreateListenerRequestDefaultActions(
type="ForwardGroup",
forward_group_config=alb_models.CreateListenerRequestDefaultActionsForwardGroupConfig(
server_group_tuples=tuples,
),
)
elif action_type == "Redirect":
if not args.redirect_protocol and not args.redirect_port:
raise ValueError("--redirect-protocol or --redirect-port required for Redirect action")
return alb_models.CreateListenerRequestDefaultActions(
type="Redirect",
redirect_config=alb_models.CreateListenerRequestDefaultActionsRedirectConfig(
protocol=args.redirect_protocol,
port=str(args.redirect_port) if args.redirect_port else None,
http_redirect_code=str(args.redirect_code) if args.redirect_code else "301",
),
)
elif action_type == "FixedResponse":
if not args.fixed_response_code:
raise ValueError("--fixed-response-code required for FixedResponse action")
return alb_models.CreateListenerRequestDefaultActions(
type="FixedResponse",
fixed_response_config=alb_models.CreateListenerRequestDefaultActionsFixedResponseConfig(
http_code=str(args.fixed_response_code),
content=args.fixed_response_content or "",
content_type=args.fixed_response_content_type or "text/plain",
),
)
else:
raise ValueError(f"Unsupported action type: {action_type}")
def build_certificates(args: argparse.Namespace) -> list | None:
"""Build certificate list for HTTPS/QUIC listeners."""
if args.protocol not in ("HTTPS", "QUIC"):
return None
if not args.certificate_ids:
raise ValueError(f"--certificate-ids required for {args.protocol} listener")
return [
alb_models.CreateListenerRequestCertificates(certificate_id=cert_id)
for cert_id in args.certificate_ids
]
def create_listener(client: Alb20200616Client, args: argparse.Namespace) -> dict[str, Any]:
"""Create listener and return result."""
action = build_default_action(args)
certificates = build_certificates(args)
req = alb_models.CreateListenerRequest(
load_balancer_id=args.load_balancer_id,
listener_protocol=args.protocol,
listener_port=args.port,
default_actions=[action],
listener_description=args.description,
)
if certificates:
req.certificates = certificates
if args.security_policy_id:
req.security_policy_id = args.security_policy_id
if args.http2_enabled is not None and args.protocol == "HTTPS":
req.http_2enabled = args.http2_enabled
if args.idle_timeout:
req.idle_timeout = args.idle_timeout
if args.request_timeout:
req.request_timeout = args.request_timeout
resp = client.create_listener(req)
return {
"listener_id": resp.body.listener_id,
"job_id": resp.body.job_id,
}
def main() -> int:
parser = argparse.ArgumentParser(
description="Create ALB listener",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# HTTP listener forwarding to server group
python create_listener.py --region cn-hangzhou --lb-id alb-xxx \\
--protocol HTTP --port 80 --action-type ForwardGroup \\
--forward-server-groups sgp-xxx
# HTTPS listener with certificate
python create_listener.py --region cn-hangzhou --lb-id alb-xxx \\
--protocol HTTPS --port 443 --action-type ForwardGroup \\
--forward-server-groups sgp-xxx --certificate-ids cert-xxx
# HTTP to HTTPS redirect
python create_listener.py --region cn-hangzhou --lb-id alb-xxx \\
--protocol HTTP --port 80 --action-type Redirect \\
--redirect-protocol HTTPS --redirect-port 443
# QUIC listener
python create_listener.py --region cn-hangzhou --lb-id alb-xxx \\
--protocol QUIC --port 443 --forward-server-groups sgp-xxx \\
--certificate-ids cert-xxx
""",
)
# Required
parser.add_argument("--region", required=True, help="Region ID, e.g. cn-hangzhou")
parser.add_argument("--lb-id", required=True, dest="load_balancer_id", help="Load balancer ID")
parser.add_argument("--protocol", required=True, choices=["HTTP", "HTTPS", "QUIC"], help="Listener protocol")
parser.add_argument("--port", required=True, type=int, help="Listener port")
# Action type
parser.add_argument("--action-type", required=True, choices=["ForwardGroup", "Redirect", "FixedResponse"], help="Default action type")
# ForwardGroup action options
parser.add_argument("--forward-server-groups", nargs="+", help="Server group IDs for forwarding")
# Redirect action options
parser.add_argument("--redirect-protocol", choices=["HTTP", "HTTPS"], help="Target protocol for redirect")
parser.add_argument("--redirect-port", type=int, help="Target port for redirect")
parser.add_argument("--redirect-code", type=int, default=301, help="HTTP redirect code (default: 301)")
# FixedResponse action options
parser.add_argument("--fixed-response-code", type=int, help="HTTP status code for fixed response")
parser.add_argument("--fixed-response-content", help="Response body content")
parser.add_argument("--fixed-response-content-type", default="text/plain", help="Content type (default: text/plain)")
# HTTPS/QUIC options
parser.add_argument("--certificate-ids", nargs="+", help="Certificate IDs for HTTPS/QUIC")
parser.add_argument("--security-policy-id", help="TLS security policy ID")
parser.add_argument("--http2-enabled", type=lambda x: x.lower() == "true", help="Enable HTTP/2 (HTTPS only)")
# General options
parser.add_argument("--description", help="Listener description")
parser.add_argument("--idle-timeout", type=int, help="Idle timeout in seconds")
parser.add_argument("--request-timeout", type=int, help="Request timeout in seconds")
# Output
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--dry-run", action="store_true", help="Print request without executing")
args = parser.parse_args()
try:
action = build_default_action(args)
certificates = build_certificates(args)
if args.dry_run:
print("Dry run - would create listener with:")
print(f" LoadBalancerId: {args.load_balancer_id}")
print(f" Protocol: {args.protocol}")
print(f" Port: {args.port}")
print(f" Action: {args.action_type}")
if certificates:
print(f" Certificates: {args.certificate_ids}")
return 0
client = create_client(args.region)
result = create_listener(client, args)
if args.json:
print(json.dumps(result, indent=2))
else:
print(f"Listener created: {result['listener_id']}")
print(f"Job ID: {result['job_id']}")
print(f"Note: Listener creation is async. Use get_listener_attribute.py to check status.")
return 0
except ValueError as e:
print(f"Error: {e}")
return 1
except Exception as e:
print(f"API Error: {e}")
return 2
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Create an ALB (Application Load Balancer) instance.
Examples:
# Internet-facing ALB in two zones
python create_load_balancer.py --region cn-hangzhou --name my-alb \\
--vpc-id vpc-xxx --address-type Internet \\
--zone cn-hangzhou-h:vsw-aaa --zone cn-hangzhou-i:vsw-bbb
# Internal ALB
python create_load_balancer.py --region cn-hangzhou --name my-alb \\
--vpc-id vpc-xxx --address-type Intranet \\
--zone cn-hangzhou-h:vsw-aaa --zone cn-hangzhou-i:vsw-bbb
# Standard edition with deletion protection
python create_load_balancer.py --region cn-hangzhou --name my-alb \\
--vpc-id vpc-xxx --address-type Internet --edition Standard \\
--deletion-protection \\
--zone cn-hangzhou-h:vsw-aaa --zone cn-hangzhou-i:vsw-bbb
# Dry run
python create_load_balancer.py --region cn-hangzhou --name my-alb \\
--vpc-id vpc-xxx --address-type Internet \\
--zone cn-hangzhou-h:vsw-aaa --zone cn-hangzhou-i:vsw-bbb \\
--dry-run
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from pathlib import Path
from typing import Any
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def parse_zone(spec: str) -> dict[str, str]:
"""Parse zone spec: zone_id:vswitch_id[:eip_allocation_id]."""
parts = spec.split(":")
if len(parts) < 2:
raise ValueError(
f"Invalid zone spec '{spec}'. "
"Format: zone_id:vswitch_id e.g. cn-hangzhou-h:vsw-xxx"
)
result: dict[str, str] = {"zone_id": parts[0], "v_switch_id": parts[1]}
if len(parts) >= 3 and parts[2]:
result["allocation_id"] = parts[2]
return result
def main() -> int:
parser = argparse.ArgumentParser(
description="Create an ALB instance",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Zone spec format: zone_id:vswitch_id[:eip_allocation_id]
Examples:
%(prog)s --region cn-hangzhou --name my-alb \\
--vpc-id vpc-xxx --address-type Internet \\
--zone cn-hangzhou-h:vsw-aaa --zone cn-hangzhou-i:vsw-bbb
""",
)
parser.add_argument("--region", required=True, help="Region ID")
parser.add_argument("--name", required=True, help="Load balancer name")
parser.add_argument("--vpc-id", required=True, help="VPC ID")
parser.add_argument(
"--address-type", required=True, choices=["Internet", "Intranet"],
help="Network type",
)
parser.add_argument(
"--zone", required=True, action="append", dest="zones",
help="Zone mapping: zone_id:vswitch_id[:eip_allocation_id] (at least 2 zones)",
)
parser.add_argument(
"--edition", default="Standard",
choices=["Basic", "Standard", "StandardWithWaf"],
help="ALB edition (default: Standard)",
)
parser.add_argument(
"--address-ip-version", default="IPv4",
choices=["IPv4", "DualStack"],
help="IP version (default: IPv4)",
)
parser.add_argument(
"--pay-type", default="PostPay",
choices=["PostPay"],
help="Billing type (default: PostPay)",
)
parser.add_argument("--deletion-protection", action="store_true", help="Enable deletion protection")
parser.add_argument("--resource-group-id", help="Resource group ID")
parser.add_argument("--dry-run", action="store_true", help="Print request without executing")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
try:
parsed_zones = [parse_zone(z) for z in args.zones]
except ValueError as e:
print(f"Error: {e}", file=sys.stderr)
return 1
if len(parsed_zones) < 2:
print("Error: at least 2 zones are required for ALB.", file=sys.stderr)
return 1
if args.dry_run:
request: dict[str, Any] = {
"load_balancer_name": args.name,
"vpc_id": args.vpc_id,
"address_type": args.address_type,
"load_balancer_edition": args.edition,
"address_ip_version": args.address_ip_version,
"pay_type": args.pay_type,
"deletion_protection_enabled": args.deletion_protection,
"zone_mappings": parsed_zones,
}
output = json.dumps(request, indent=2, ensure_ascii=False)
if args.output:
Path(args.output).write_text(output + "\n", encoding="utf-8")
print(f"Dry-run request written to {args.output}")
else:
print("Dry-run mode - would create ALB:")
print(output)
return 0
client = create_client(args.region)
zone_mappings = [
alb_models.CreateLoadBalancerRequestZoneMappings(
zone_id=z["zone_id"],
v_switch_id=z["v_switch_id"],
**({"allocation_id": z["allocation_id"]} if "allocation_id" in z else {}),
)
for z in parsed_zones
]
req = alb_models.CreateLoadBalancerRequest(
load_balancer_name=args.name,
vpc_id=args.vpc_id,
address_type=args.address_type,
load_balancer_edition=args.edition,
address_ip_version=args.address_ip_version,
load_balancer_billing_config=alb_models.CreateLoadBalancerRequestLoadBalancerBillingConfig(
pay_type=args.pay_type,
),
zone_mappings=zone_mappings,
deletion_protection_enabled=args.deletion_protection,
)
if args.resource_group_id:
req.resource_group_id = args.resource_group_id
try:
resp = client.create_load_balancer(req)
result = {
"load_balancer_id": resp.body.load_balancer_id,
"request_id": resp.body.request_id,
}
except Exception as e:
print(f"Error creating ALB: {e}", file=sys.stderr)
return 1
if args.json:
output = json.dumps(result, indent=2, ensure_ascii=False)
else:
output = (
f"ALB created successfully:\n"
f" Load Balancer ID: {result['load_balancer_id']}\n"
f" Note: ALB creation is async. Use get_instance_status.py to check status."
)
if args.output:
Path(args.output).write_text(output + "\n", encoding="utf-8")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Create ALB forwarding rule with flexible conditions and actions.
Supports common patterns like blocking HTTP methods, host-based routing,
path-based routing, and fixed responses.
Examples:
# Block DELETE method with 405 response
python create_rule.py --region cn-hangzhou --listener-id lsn-xxx \\
--name "block-delete" --priority 10 \\
--condition-method DELETE \\
--action-fixed-response 405 "Method Not Allowed"
# Host-based routing to server group
python create_rule.py --region cn-hangzhou --listener-id lsn-xxx \\
--name "api-route" --priority 20 \\
--condition-host "api.example.com" \\
--action-forward-to sgp-xxx
# Path-based routing with multiple conditions
python create_rule.py --region cn-hangzhou --listener-id lsn-xxx \\
--name "api-v1-route" --priority 30 \\
--condition-host "api.example.com" \\
--condition-path "/v1/*" \\
--action-forward-to sgp-xxx
# HTTP to HTTPS redirect
python create_rule.py --region cn-hangzhou --listener-id lsn-xxx \\
--name "force-https" --priority 5 \\
--action-redirect https 443
# Dry run - validate without creating
python create_rule.py --region cn-hangzhou --listener-id lsn-xxx \\
--name "test" --priority 100 \\
--condition-method POST \\
--action-fixed-response 200 "OK" \\
--dry-run
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from pathlib import Path
from typing import Any
def create_client(region_id: str):
"""Create ALB client with credentials from environment."""
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def parse_conditions(args: argparse.Namespace) -> list[dict]:
"""Parse condition arguments into ALB condition structures."""
conditions: list[dict] = []
if args.condition_method:
conditions.append({
"type": "Method",
"method_config": {"values": args.condition_method.split(",")},
})
if args.condition_host:
conditions.append({
"type": "Host",
"host_config": {"values": args.condition_host.split(",")},
})
if args.condition_path:
conditions.append({
"type": "Path",
"path_config": {"values": args.condition_path.split(",")},
})
if args.condition_header:
for header in args.condition_header:
# Format: "X-Api-Key: value1,value2" or "X-Api-Key=value1,value2"
if ":" in header:
key, values = header.split(":", 1)
elif "=" in header:
key, values = header.split("=", 1)
else:
key, values = header, ""
conditions.append({
"type": "Header",
"header_config": {
"key": key.strip(),
"values": [v.strip() for v in values.split(",") if v.strip()],
},
})
if args.condition_query:
for query in args.condition_query:
# Format: "version=1.0,2.0"
if "=" in query:
key, values = query.split("=", 1)
else:
key, values = query, ""
conditions.append({
"type": "QueryString",
"query_string_config": {
"values": [{"key": key.strip(), "value": v.strip()}
for v in values.split(",") if v.strip()],
},
})
if args.condition_cookie:
for cookie in args.condition_cookie:
# Format: "session=abc,def"
if "=" in cookie:
key, values = cookie.split("=", 1)
else:
key, values = cookie, ""
conditions.append({
"type": "Cookie",
"cookie_config": {
"values": [{"key": key.strip(), "value": v.strip()}
for v in values.split(",") if v.strip()],
},
})
if args.condition_source_ip:
conditions.append({
"type": "SourceIp",
"source_ip_config": {"values": args.condition_source_ip.split(",")},
})
return conditions
def parse_actions(args: argparse.Namespace) -> list[dict]:
"""Parse action arguments into ALB action structures."""
actions: list[dict] = []
order = 1
if args.action_forward_to:
actions.append({
"type": "ForwardGroup",
"order": order,
"forward_group_config": {
"server_group_tuples": [
{"server_group_id": sg_id.strip()}
for sg_id in args.action_forward_to.split(",")
],
},
})
order += 1
if args.action_redirect:
# Format: "https 443" or "https" or "443"
parts = args.action_redirect.split()
protocol = None
port = None
for part in parts:
if part.lower() in ("http", "https"):
protocol = part.upper()
elif part.isdigit():
port = part
actions.append({
"type": "Redirect",
"order": order,
"redirect_config": {
**({"protocol": protocol} if protocol else {}),
**({"port": port} if port else {}),
"http_redirect_code": args.action_redirect_code,
},
})
order += 1
if args.action_fixed_response:
code, *content_parts = args.action_fixed_response.split(None, 1)
content = content_parts[0] if content_parts else ""
actions.append({
"type": "FixedResponse",
"order": order,
"fixed_response_config": {
"http_code": code,
"content": content,
**({"content_type": args.action_fixed_response_type} if args.action_fixed_response_type else {}),
},
})
order += 1
if args.action_rewrite:
# Format: "host=/new-host" or "path=/new-path" or both
config: dict[str, str] = {}
for part in args.action_rewrite.split():
if "=" in part:
key, value = part.split("=", 1)
if key == "host":
config["host"] = value
elif key == "path":
config["path"] = value
if config:
actions.append({
"type": "Rewrite",
"order": order,
"rewrite_config": config,
})
order += 1
if args.action_insert_header:
for header in args.action_insert_header:
# Format: "X-Custom-Header: value" or "X-Custom-Header=value"
if ":" in header:
key, value = header.split(":", 1)
elif "=" in header:
key, value = header.split("=", 1)
else:
key, value = header, ""
actions.append({
"type": "InsertHeader",
"order": order,
"insert_header_config": {
"key": key.strip(),
"value": value.strip(),
},
})
order += 1
return actions
def build_request(args: argparse.Namespace) -> dict[str, Any]:
"""Build the CreateRule request structure."""
conditions = parse_conditions(args)
actions = parse_actions(args)
if not conditions and not args.allow_no_conditions:
raise ValueError(
"No conditions specified. Use --condition-* options or --allow-no-conditions "
"to create a catch-all rule."
)
if not actions:
raise ValueError("No actions specified. Use --action-* options.")
request: dict[str, Any] = {
"listener_id": args.listener_id,
"rule_name": args.name,
"priority": args.priority,
"rule_conditions": conditions,
"rule_actions": actions,
}
return request
def execute_create(client, request: dict[str, Any]) -> dict[str, Any]:
"""Execute the CreateRule API call."""
from alibabacloud_alb20200616 import models as alb_models
# Convert dict to model objects
conditions = []
for c in request["rule_conditions"]:
cond = alb_models.CreateRuleRequestRuleConditions(type=c["type"])
if "host_config" in c:
cond.host_config = alb_models.CreateRuleRequestRuleConditionsHostConfig(**c["host_config"])
elif "path_config" in c:
cond.path_config = alb_models.CreateRuleRequestRuleConditionsPathConfig(**c["path_config"])
elif "method_config" in c:
cond.method_config = alb_models.CreateRuleRequestRuleConditionsMethodConfig(**c["method_config"])
elif "header_config" in c:
cond.header_config = alb_models.CreateRuleRequestRuleConditionsHeaderConfig(**c["header_config"])
elif "query_string_config" in c:
values = [alb_models.CreateRuleRequestRuleConditionsQueryStringConfigValues(**v)
for v in c["query_string_config"]["values"]]
cond.query_string_config = alb_models.CreateRuleRequestRuleConditionsQueryStringConfig(values=values)
elif "cookie_config" in c:
values = [alb_models.CreateRuleRequestRuleConditionsCookieConfigValues(**v)
for v in c["cookie_config"]["values"]]
cond.cookie_config = alb_models.CreateRuleRequestRuleConditionsCookieConfig(values=values)
elif "source_ip_config" in c:
cond.source_ip_config = alb_models.CreateRuleRequestRuleConditionsSourceIpConfig(**c["source_ip_config"])
conditions.append(cond)
actions = []
for a in request["rule_actions"]:
act = alb_models.CreateRuleRequestRuleActions(type=a["type"], order=a["order"])
if "forward_group_config" in a:
tuples = [alb_models.CreateRuleRequestRuleActionsForwardGroupConfigServerGroupTuples(**t)
for t in a["forward_group_config"]["server_group_tuples"]]
act.forward_group_config = alb_models.CreateRuleRequestRuleActionsForwardGroupConfig(
server_group_tuples=tuples
)
elif "redirect_config" in a:
act.redirect_config = alb_models.CreateRuleRequestRuleActionsRedirectConfig(**a["redirect_config"])
elif "fixed_response_config" in a:
act.fixed_response_config = alb_models.CreateRuleRequestRuleActionsFixedResponseConfig(
**a["fixed_response_config"]
)
elif "rewrite_config" in a:
act.rewrite_config = alb_models.CreateRuleRequestRuleActionsRewriteConfig(**a["rewrite_config"])
elif "insert_header_config" in a:
act.insert_header_config = alb_models.CreateRuleRequestRuleActionsInsertHeaderConfig(
**a["insert_header_config"]
)
actions.append(act)
req = alb_models.CreateRuleRequest(
listener_id=request["listener_id"],
rule_name=request["rule_name"],
priority=request["priority"],
rule_conditions=conditions,
rule_actions=actions,
)
resp = client.create_rule(req)
return {
"rule_id": resp.body.rule_id,
"job_id": resp.body.job_id,
"request_id": resp.body.request_id,
}
def main() -> int:
parser = argparse.ArgumentParser(
description="Create ALB forwarding rule",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Block DELETE method
%(prog)s --region cn-hangzhou --listener-id lsn-xxx \\
--name "block-delete" --priority 10 \\
--condition-method DELETE \\
--action-fixed-response 405 "Method Not Allowed"
# Route by host
%(prog)s --region cn-hangzhou --listener-id lsn-xxx \\
--name "api-route" --priority 20 \\
--condition-host "api.example.com" \\
--action-forward-to sgp-xxx
# HTTP to HTTPS redirect
%(prog)s --region cn-hangzhou --listener-id lsn-xxx \\
--name "force-https" --priority 5 \\
--action-redirect https 443
""",
)
parser.add_argument("--region", required=True, help="Region ID, e.g. cn-hangzhou")
parser.add_argument("--listener-id", required=True, help="Listener ID (lsn-xxx)")
parser.add_argument("--name", required=True, help="Rule name")
parser.add_argument("--priority", type=int, required=True, help="Rule priority (1-10000, lower = higher priority)")
# Condition options
cond_group = parser.add_argument_group("Conditions (at least one required)")
cond_group.add_argument("--condition-method", help="HTTP method(s), comma-separated: GET,POST,DELETE")
cond_group.add_argument("--condition-host", help="Host(s), comma-separated: api.example.com,*.example.com")
cond_group.add_argument("--condition-path", help="Path(s), comma-separated: /api/*,/v1/*")
cond_group.add_argument("--condition-header", action="append", help="Header condition: 'X-Api-Key: value1,value2'")
cond_group.add_argument("--condition-query", action="append", help="Query string condition: 'version=1.0,2.0'")
cond_group.add_argument("--condition-cookie", action="append", help="Cookie condition: 'session=abc,def'")
cond_group.add_argument("--condition-source-ip", help="Source IP(s), comma-separated: 10.0.0.0/8,192.168.1.1")
cond_group.add_argument("--allow-no-conditions", action="store_true", help="Allow creating a catch-all rule")
# Action options
act_group = parser.add_argument_group("Actions (at least one required)")
act_group.add_argument("--action-forward-to", help="Forward to server group ID(s), comma-separated")
act_group.add_argument("--action-redirect", help="Redirect: 'https 443' or 'https' or '443'")
act_group.add_argument("--action-redirect-code", default="301", help="Redirect HTTP code (default: 301)")
act_group.add_argument("--action-fixed-response", help="Fixed response: '404 Not Found'")
act_group.add_argument("--action-fixed-response-type", help="Content-Type for fixed response")
act_group.add_argument("--action-rewrite", help="Rewrite: 'host=newhost' or 'path=/newpath' or 'host=newhost path=/new'")
act_group.add_argument("--action-insert-header", action="append", help="Insert header: 'X-Custom: value'")
# Other options
parser.add_argument("--dry-run", action="store_true", help="Validate and print request without creating")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
try:
request = build_request(args)
except ValueError as e:
print(f"Error: {e}", file=sys.stderr)
return 1
if args.dry_run:
output = json.dumps(request, indent=2, ensure_ascii=False)
if args.output:
Path(args.output).write_text(output + "\n", encoding="utf-8")
print(f"Dry-run request written to {args.output}")
else:
print("Dry-run mode - request structure:")
print(output)
return 0
# Execute the create
client = create_client(args.region)
try:
result = execute_create(client, request)
except Exception as e:
print(f"Error creating rule: {e}", file=sys.stderr)
return 1
# Output
if args.json:
output = json.dumps(result, indent=2, ensure_ascii=False)
else:
output = f"Rule created successfully:\n Rule ID: {result['rule_id']}\n Job ID: {result['job_id']}"
if args.output:
Path(args.output).write_text(output + "\n", encoding="utf-8")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Create ALB server group with health check and sticky session configuration."""
from __future__ import annotations
import argparse
import json
import os
from typing import Any
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def build_health_check_config(args: argparse.Namespace) -> alb_models.CreateServerGroupRequestHealthCheckConfig | None:
"""Build health check config from CLI arguments."""
if args.health_check_disabled:
return alb_models.CreateServerGroupRequestHealthCheckConfig(
health_check_enabled=False,
)
return alb_models.CreateServerGroupRequestHealthCheckConfig(
health_check_enabled=True,
health_check_protocol=args.health_check_protocol or "HTTP",
health_check_method=args.health_check_method or "HEAD",
health_check_path=args.health_check_path or "/",
health_check_host=args.health_check_host,
health_check_http_version=args.health_check_http_version or "HTTP1.1",
health_check_interval=args.health_check_interval or 5,
health_check_timeout=args.health_check_timeout or 3,
healthy_threshold=args.healthy_threshold or 3,
unhealthy_threshold=args.unhealthy_threshold or 3,
health_check_codes=args.health_check_codes or ["http_2xx"],
)
def build_sticky_session_config(args: argparse.Namespace) -> alb_models.CreateServerGroupRequestStickySessionConfig:
"""Build sticky session config from CLI arguments.
ALB API requires StickySessionConfig to be present even when disabled.
"""
if not args.sticky_session_enabled:
return alb_models.CreateServerGroupRequestStickySessionConfig(
sticky_session_enabled=False,
)
return alb_models.CreateServerGroupRequestStickySessionConfig(
sticky_session_enabled=True,
sticky_session_type=args.sticky_session_type or "Server",
cookie=args.sticky_session_cookie,
cookie_timeout=args.sticky_session_timeout,
)
def create_server_group(client: Alb20200616Client, args: argparse.Namespace) -> dict[str, Any]:
"""Create server group and return result."""
health_check_config = build_health_check_config(args)
sticky_session_config = build_sticky_session_config(args)
req = alb_models.CreateServerGroupRequest(
server_group_name=args.name,
vpc_id=args.vpc_id,
protocol=args.protocol or "HTTP",
scheduler=args.scheduler or "Wrr",
server_group_type=args.server_group_type or "Instance",
)
if health_check_config:
req.health_check_config = health_check_config
# ALB API requires StickySessionConfig to always be present
req.sticky_session_config = sticky_session_config
if args.connection_drain_timeout is not None:
req.connection_drain_config = alb_models.CreateServerGroupRequestConnectionDrainConfig(
connection_drain_enabled=args.connection_drain_timeout > 0,
connection_drain_timeout=max(0, args.connection_drain_timeout),
)
resp = client.create_server_group(req)
return {
"server_group_id": resp.body.server_group_id,
}
def main() -> int:
parser = argparse.ArgumentParser(
description="Create ALB server group",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Basic HTTP server group
python create_server_group.py --region cn-hangzhou --name my-sg \\
--vpc-id vpc-xxx --protocol HTTP
# With health check customization
python create_server_group.py --region cn-hangzhou --name my-sg \\
--vpc-id vpc-xxx --protocol HTTP \\
--health-check-path /health --health-check-interval 10
# With sticky sessions
python create_server_group.py --region cn-hangzhou --name my-sg \\
--vpc-id vpc-xxx --protocol HTTP \\
--sticky-session-enabled --sticky-session-type Server --sticky-session-cookie SERVERID
# Disable health check (not recommended for production)
python create_server_group.py --region cn-hangzhou --name my-sg \\
--vpc-id vpc-xxx --health-check-disabled
""",
)
# Required
parser.add_argument("--region", required=True, help="Region ID, e.g. cn-hangzhou")
parser.add_argument("--name", required=True, help="Server group name")
parser.add_argument("--vpc-id", required=True, help="VPC ID")
# Basic config
parser.add_argument("--protocol", choices=["HTTP", "HTTPS", "GRPC"], default="HTTP", help="Protocol (default: HTTP)")
parser.add_argument("--scheduler", choices=["Wrr", "Wlc", "Sch", "Uch"], default="Wrr", help="Scheduling algorithm (default: Wrr)")
parser.add_argument("--server-group-type", choices=["Instance", "Ip"], default="Instance", help="Server group type (default: Instance)")
# Health check options
parser.add_argument("--health-check-disabled", action="store_true", help="Disable health check")
parser.add_argument("--health-check-protocol", choices=["HTTP", "HTTPS", "TCP"], default="HTTP", help="Health check protocol")
parser.add_argument("--health-check-method", choices=["HEAD", "GET"], default="HEAD", help="Health check method")
parser.add_argument("--health-check-path", default="/", help="Health check path (default: /)")
parser.add_argument("--health-check-host", help="Health check host header")
parser.add_argument("--health-check-http-version", choices=["HTTP1.0", "HTTP1.1"], default="HTTP1.1", help="Health check HTTP version (default: HTTP1.1)")
parser.add_argument("--health-check-interval", type=int, default=5, help="Check interval in seconds (default: 5)")
parser.add_argument("--health-check-timeout", type=int, default=3, help="Check timeout in seconds (default: 3)")
parser.add_argument("--healthy-threshold", type=int, default=3, help="Healthy threshold (default: 3)")
parser.add_argument("--unhealthy-threshold", type=int, default=3, help="Unhealthy threshold (default: 3)")
parser.add_argument("--health-check-codes", nargs="+", default=["http_2xx"], help="Expected status codes (default: http_2xx)")
# Sticky session options
parser.add_argument("--sticky-session-enabled", action="store_true", help="Enable sticky sessions")
parser.add_argument("--sticky-session-type", choices=["Server", "Insert"], help="Sticky session type")
parser.add_argument("--sticky-session-cookie", help="Cookie name for Server type")
parser.add_argument("--sticky-session-timeout", type=int, help="Cookie timeout for Insert type (seconds)")
# Connection draining
parser.add_argument("--connection-drain-timeout", type=int, default=300, help="Connection drain timeout in seconds (default: 300, 0 to disable)")
# Output
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--dry-run", action="store_true", help="Print request without executing")
args = parser.parse_args()
try:
if args.dry_run:
print("Dry run - would create server group with:")
print(f" Name: {args.name}")
print(f" VPC: {args.vpc_id}")
print(f" Protocol: {args.protocol}")
print(f" Scheduler: {args.scheduler}")
print(f" Health check: {'disabled' if args.health_check_disabled else 'enabled'}")
if args.sticky_session_enabled:
print(f" Sticky session: enabled (type={args.sticky_session_type})")
return 0
client = create_client(args.region)
result = create_server_group(client, args)
if args.json:
print(json.dumps(result, indent=2))
else:
print(f"Server group created: {result['server_group_id']}")
return 0
except Exception as e:
print(f"Error: {e}")
return 1
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Delete an ALB listener.
WARNING: This is a destructive operation. The listener and its associated
forwarding rules will be deleted.
Examples:
python delete_listener.py --region cn-hangzhou --listener-id lsn-xxx
python delete_listener.py --region cn-hangzhou --listener-id lsn-xxx --json
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from pathlib import Path
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def main() -> int:
parser = argparse.ArgumentParser(description="Delete an ALB listener")
parser.add_argument("--region", required=True, help="Region ID")
parser.add_argument("--listener-id", required=True, help="Listener ID (lsn-xxx)")
parser.add_argument("--yes", action="store_true", help="Skip confirmation prompt")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
if not args.yes:
confirm = input(f"Delete listener {args.listener_id}? This will also delete associated rules. [y/N] ")
if confirm.lower() not in ("y", "yes"):
print("Cancelled.")
return 0
client = create_client(args.region)
try:
resp = client.delete_listener(alb_models.DeleteListenerRequest(
listener_id=args.listener_id,
))
result = {"job_id": resp.body.job_id, "request_id": resp.body.request_id}
except Exception as e:
print(f"Error deleting listener: {e}", file=sys.stderr)
return 1
if args.json:
output = json.dumps(result, indent=2, ensure_ascii=False)
else:
output = f"Listener {args.listener_id} deleted.\n Job ID: {result['job_id']}"
if args.output:
Path(args.output).write_text(output + "\n", encoding="utf-8")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Delete an ALB (Application Load Balancer) instance.
WARNING: This is a destructive operation. All listeners and rules under the ALB
will be deleted. You must disable deletion protection first if it is enabled.
Examples:
python delete_load_balancer.py --region cn-hangzhou --lb-id alb-xxx
python delete_load_balancer.py --region cn-hangzhou --lb-id alb-xxx --yes --json
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from pathlib import Path
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def main() -> int:
parser = argparse.ArgumentParser(description="Delete an ALB instance")
parser.add_argument("--region", required=True, help="Region ID")
parser.add_argument("--lb-id", required=True, dest="load_balancer_id", help="Load balancer ID (alb-xxx)")
parser.add_argument("--yes", action="store_true", help="Skip confirmation prompt")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
if not args.yes:
confirm = input(
f"Delete ALB {args.load_balancer_id}? "
"All listeners and rules will be deleted. [y/N] "
)
if confirm.lower() not in ("y", "yes"):
print("Cancelled.")
return 0
client = create_client(args.region)
try:
resp = client.delete_load_balancer(alb_models.DeleteLoadBalancerRequest(
load_balancer_id=args.load_balancer_id,
))
result = {"job_id": resp.body.job_id, "request_id": resp.body.request_id}
except Exception as e:
err_msg = str(e)
if "DeletionProtectionEnabled" in err_msg or "DeletionProtection" in err_msg:
print(
f"Error: Deletion protection is enabled for {args.load_balancer_id}.\n"
f"Run: python deletion_protection.py --region {args.region} "
f"--resource-id {args.load_balancer_id} --disable",
file=sys.stderr,
)
else:
print(f"Error deleting ALB: {e}", file=sys.stderr)
return 1
if args.json:
output = json.dumps(result, indent=2, ensure_ascii=False)
else:
output = f"ALB {args.load_balancer_id} deleted.\n Job ID: {result['job_id']}"
if args.output:
Path(args.output).write_text(output + "\n", encoding="utf-8")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Delete an ALB forwarding rule.
Examples:
python delete_rule.py --region cn-hangzhou --rule-id rule-xxx
python delete_rule.py --region cn-hangzhou --rule-id rule-xxx --yes --json
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from pathlib import Path
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def main() -> int:
parser = argparse.ArgumentParser(description="Delete an ALB forwarding rule")
parser.add_argument("--region", required=True, help="Region ID")
parser.add_argument("--rule-id", required=True, help="Rule ID (rule-xxx)")
parser.add_argument("--yes", action="store_true", help="Skip confirmation prompt")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
if not args.yes:
confirm = input(f"Delete rule {args.rule_id}? [y/N] ")
if confirm.lower() not in ("y", "yes"):
print("Cancelled.")
return 0
client = create_client(args.region)
try:
resp = client.delete_rule(alb_models.DeleteRuleRequest(rule_id=args.rule_id))
result = {"job_id": resp.body.job_id, "request_id": resp.body.request_id}
except Exception as e:
print(f"Error deleting rule: {e}", file=sys.stderr)
return 1
if args.json:
output = json.dumps(result, indent=2, ensure_ascii=False)
else:
output = f"Rule {args.rule_id} deleted.\n Job ID: {result['job_id']}"
if args.output:
Path(args.output).write_text(output + "\n", encoding="utf-8")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Delete an ALB server group.
WARNING: The server group must not be referenced by any listener or rule.
Remove all references first.
Examples:
python delete_server_group.py --region cn-hangzhou --sg-id sgp-xxx
python delete_server_group.py --region cn-hangzhou --sg-id sgp-xxx --yes --json
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from pathlib import Path
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def main() -> int:
parser = argparse.ArgumentParser(description="Delete an ALB server group")
parser.add_argument("--region", required=True, help="Region ID")
parser.add_argument("--sg-id", required=True, dest="server_group_id", help="Server group ID (sgp-xxx)")
parser.add_argument("--yes", action="store_true", help="Skip confirmation prompt")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
if not args.yes:
confirm = input(f"Delete server group {args.server_group_id}? [y/N] ")
if confirm.lower() not in ("y", "yes"):
print("Cancelled.")
return 0
client = create_client(args.region)
try:
resp = client.delete_server_group(alb_models.DeleteServerGroupRequest(
server_group_id=args.server_group_id,
))
result = {"job_id": resp.body.job_id, "request_id": resp.body.request_id}
except Exception as e:
print(f"Error deleting server group: {e}", file=sys.stderr)
return 1
if args.json:
output = json.dumps(result, indent=2, ensure_ascii=False)
else:
output = f"Server group {args.server_group_id} deleted.\n Job ID: {result['job_id']}"
if args.output:
Path(args.output).write_text(output + "\n", encoding="utf-8")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Enable or disable deletion protection for an ALB instance.
Examples:
# Enable deletion protection
python deletion_protection.py --region cn-hangzhou --resource-id alb-xxx --enable
# Disable deletion protection (required before deleting an ALB)
python deletion_protection.py --region cn-hangzhou --resource-id alb-xxx --disable
# JSON output
python deletion_protection.py --region cn-hangzhou --resource-id alb-xxx --enable --json
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from pathlib import Path
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def main() -> int:
parser = argparse.ArgumentParser(description="Enable or disable ALB deletion protection")
parser.add_argument("--region", required=True, help="Region ID")
parser.add_argument("--resource-id", required=True, help="ALB instance ID (alb-xxx)")
action = parser.add_mutually_exclusive_group(required=True)
action.add_argument("--enable", action="store_true", help="Enable deletion protection")
action.add_argument("--disable", action="store_true", help="Disable deletion protection")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
client = create_client(args.region)
try:
if args.enable:
resp = client.enable_deletion_protection(alb_models.EnableDeletionProtectionRequest(
resource_id=args.resource_id,
))
action_desc = "enabled"
else:
resp = client.disable_deletion_protection(alb_models.DisableDeletionProtectionRequest(
resource_id=args.resource_id,
))
action_desc = "disabled"
result = {"request_id": resp.body.request_id, "action": action_desc}
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
return 1
if args.json:
output = json.dumps(result, indent=2, ensure_ascii=False)
else:
output = f"Deletion protection {action_desc} for {args.resource_id}."
if args.output:
Path(args.output).write_text(output + "\n", encoding="utf-8")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Get ALB instance status with overview (tree) or detail (JSON) view."""
from __future__ import annotations
import argparse
import json
import os
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
# ---------------------------------------------------------------------------
# Pagination helpers
# ---------------------------------------------------------------------------
def list_all_listeners(client: Alb20200616Client, lb_id: str) -> list:
"""Fetch all listeners for a given ALB instance."""
listeners: list = []
next_token: str | None = None
while True:
req = alb_models.ListListenersRequest(
load_balancer_ids=[lb_id],
max_results=100,
next_token=next_token,
)
resp = client.list_listeners(req)
listeners.extend(resp.body.listeners or [])
next_token = resp.body.next_token
if not next_token:
break
return listeners
def list_all_rules(client: Alb20200616Client, listener_id: str) -> list:
"""Fetch all forwarding rules for a listener."""
rules: list = []
next_token: str | None = None
while True:
req = alb_models.ListRulesRequest(
listener_ids=[listener_id],
max_results=100,
next_token=next_token,
)
resp = client.list_rules(req)
rules.extend(resp.body.rules or [])
next_token = resp.body.next_token
if not next_token:
break
return rules
# ---------------------------------------------------------------------------
# Tree rendering helpers
# ---------------------------------------------------------------------------
def _format_zone(zm) -> str:
"""Format a single ZoneMapping entry."""
zone_id = zm.zone_id or "?"
vsw = zm.v_switch_id or "?"
# Extract the first load balancer address if available
addr = "-"
if zm.load_balancer_addresses:
first = zm.load_balancer_addresses[0]
addr = first.address or first.ipv_6address or "-"
status_tag = ""
if hasattr(zm, "status") and zm.status and zm.status != "Active":
status_tag = f" ({zm.status})"
return f"Zone {zone_id} (vsw: {vsw}) → {addr}{status_tag}"
def _summarize_conditions(rule) -> str:
"""Build a short description of rule match conditions."""
parts: list[str] = []
for cond in rule.rule_conditions or []:
ctype = cond.type or ""
if ctype == "Host" and cond.host_config:
values = cond.host_config.values or []
parts.append(",".join(values))
elif ctype == "Path" and cond.path_config:
values = cond.path_config.values or []
parts.append(",".join(values))
elif ctype == "Header" and cond.header_config:
key = cond.header_config.key or ""
values = cond.header_config.values or []
parts.append(f"Header({key}={','.join(values)})")
elif ctype == "Method" and cond.method_config:
values = cond.method_config.values or []
parts.append(f"Method({','.join(values)})")
elif ctype == "QueryString" and cond.query_string_config:
qs = cond.query_string_config.values or []
qs_str = "&".join(f"{v.key}={v.value}" for v in qs if v.key)
parts.append(f"QS({qs_str})")
elif ctype == "Cookie" and cond.cookie_config:
cookies = cond.cookie_config.values or []
ck_str = ",".join(f"{c.key}={c.value}" for c in cookies if c.key)
parts.append(f"Cookie({ck_str})")
elif ctype == "SourceIp" and cond.source_ip_config:
values = cond.source_ip_config.values or []
parts.append(f"SrcIP({','.join(values)})")
else:
parts.append(ctype)
return " & ".join(parts) if parts else "match-all"
def _summarize_actions(rule) -> str:
"""Build a short description of rule actions."""
parts: list[str] = []
for act in rule.rule_actions or []:
atype = act.type or ""
if atype == "ForwardGroup" and act.forward_group_config:
sgcs = act.forward_group_config.server_group_tuples or []
sg_ids = [s.server_group_id for s in sgcs if s.server_group_id]
parts.append("→ " + ",".join(sg_ids))
elif atype == "Redirect" and act.redirect_config:
rc = act.redirect_config
target = f"{rc.protocol or ''}:{rc.port or ''}" if rc.protocol else str(rc.port or "")
parts.append(f"Redirect {target}")
elif atype == "FixedResponse" and act.fixed_response_config:
fc = act.fixed_response_config
parts.append(f"FixedResponse({fc.http_code or ''})")
elif atype == "Rewrite" and act.rewrite_config:
rw = act.rewrite_config
parts.append(f"Rewrite({rw.host or ''}{rw.path or ''})")
elif atype == "InsertHeader" and act.insert_header_config:
ih = act.insert_header_config
parts.append(f"InsertHeader({ih.key or ''})")
elif atype == "TrafficMirror":
parts.append("TrafficMirror")
elif atype == "TrafficLimit":
parts.append("TrafficLimit")
elif atype == "CORS":
parts.append("CORS")
else:
parts.append(atype)
return " ".join(parts) if parts else "-"
def build_overview(client: Alb20200616Client, lb_id: str) -> str:
"""Build an ASCII tree view of the ALB instance."""
# 1. Get instance attributes
attr_resp = client.get_load_balancer_attribute(
alb_models.GetLoadBalancerAttributeRequest(load_balancer_id=lb_id)
)
lb = attr_resp.body
lines: list[str] = []
name = lb.load_balancer_name or ""
status = lb.load_balancer_status or "?"
edition = lb.load_balancer_edition or ""
addr_type = lb.address_type or ""
edition_tag = f" ({edition})" if edition else ""
addr_tag = f" [{addr_type}]" if addr_type else ""
lines.append(f"ALB: {lb.load_balancer_id} ({name}) [{status}]{edition_tag}{addr_tag}")
# 2. Zone mappings
zones = lb.zone_mappings or []
listeners = list_all_listeners(client, lb_id)
total_children = len(zones) + len(listeners)
child_idx = 0
for zm in zones:
child_idx += 1
is_last_child = child_idx == total_children
prefix = "└── " if is_last_child else "├── "
lines.append(prefix + _format_zone(zm))
# 3. Listeners & their rules
for listener in listeners:
child_idx += 1
is_last_listener = child_idx == total_children
l_prefix = "└── " if is_last_listener else "├── "
l_cont = " " if is_last_listener else "│ "
lid = listener.listener_id or "?"
proto = listener.listener_protocol or "?"
port = listener.listener_port or "?"
lstatus = listener.listener_status or "?"
lines.append(f"{l_prefix}Listener: {lid} ({proto}:{port}) [{lstatus}]")
# Fetch rules
rules = list_all_rules(client, lid)
# Separate default action from non-default rules
default_rules = [r for r in rules if r.priority and r.priority >= 99999]
custom_rules = [r for r in rules if not r.priority or r.priority < 99999]
custom_rules.sort(key=lambda r: r.priority or 0)
all_rule_items = custom_rules + default_rules
for ri, rule in enumerate(all_rule_items):
is_last_rule = ri == len(all_rule_items) - 1
r_prefix = l_cont + ("└── " if is_last_rule else "├── ")
if rule.priority and rule.priority >= 99999:
# Default action
actions = _summarize_actions(rule)
lines.append(f"{r_prefix}DefaultAction {actions}")
else:
priority = rule.priority or "?"
conds = _summarize_conditions(rule)
actions = _summarize_actions(rule)
rule_id = rule.rule_id or ""
lines.append(f"{r_prefix}Rule: {rule_id} (Priority:{priority}, {conds}) {actions}")
return "\n".join(lines)
def build_detail(client: Alb20200616Client, lb_id: str) -> str:
"""Return full GetLoadBalancerAttribute response as formatted JSON."""
resp = client.get_load_balancer_attribute(
alb_models.GetLoadBalancerAttributeRequest(load_balancer_id=lb_id)
)
return json.dumps(resp.body.to_map(), indent=2, ensure_ascii=False, default=str)
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main() -> int:
parser = argparse.ArgumentParser(description="Get ALB instance status")
parser.add_argument("--region", required=True, help="Region ID, e.g. cn-hangzhou")
parser.add_argument("--lb-id", required=True, help="LoadBalancer ID, e.g. alb-xxx")
parser.add_argument(
"--view",
choices=["overview", "detail"],
default="overview",
help="View mode: overview (tree) or detail (full JSON). Default: overview",
)
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
client = create_client(args.region)
if args.view == "detail":
output = build_detail(client, args.lb_id)
else:
output = build_overview(client, args.lb_id)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output + "\n")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Get detailed attributes of an ALB listener (certificates, ACL, config)."""
from __future__ import annotations
import argparse
import json
import os
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def main() -> int:
parser = argparse.ArgumentParser(description="Get ALB listener attributes")
parser.add_argument("--region", required=True, help="Region ID, e.g. cn-hangzhou")
parser.add_argument("--listener-id", required=True, help="Listener ID, e.g. lsn-xxx")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
client = create_client(args.region)
resp = client.get_listener_attribute(
alb_models.GetListenerAttributeRequest(listener_id=args.listener_id)
)
output = json.dumps(resp.body.to_map(), indent=2, ensure_ascii=False, default=str)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output + "\n")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""List IP entries in an ALB access control list (ACL)."""
from __future__ import annotations
import argparse
import json
import os
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def iter_acl_entries(client: Alb20200616Client, acl_id: str):
"""Yield all entries in an ACL."""
next_token: str | None = None
while True:
req = alb_models.ListAclEntriesRequest(
acl_id=acl_id,
max_results=100,
next_token=next_token,
)
resp = client.list_acl_entries(req)
for entry in resp.body.acl_entries or []:
yield entry
next_token = resp.body.next_token
if not next_token:
break
def main() -> int:
parser = argparse.ArgumentParser(description="List entries in an ALB ACL")
parser.add_argument("--region", required=True, help="Region ID")
parser.add_argument("--acl-id", required=True, help="ACL ID, e.g. acl-xxx")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
client = create_client(args.region)
entries = list(iter_acl_entries(client, args.acl_id))
if args.json:
output = json.dumps(
[e.to_map() for e in entries], indent=2, ensure_ascii=False, default=str
)
else:
header = f"{'Entry (CIDR)':<45} {'Description'}"
sep = "-" * len(header)
lines = [header, sep]
for e in entries:
lines.append(
f"{e.entry or '-':<45} "
f"{e.description or '-'}"
)
lines.append(sep)
lines.append(f"Total: {len(entries)} entry(ies) in {args.acl_id}")
output = "\n".join(lines)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output + "\n")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""List ALB access control lists (ACLs)."""
from __future__ import annotations
import argparse
import json
import os
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def iter_acls(client: Alb20200616Client, acl_ids: list[str] | None = None,
acl_names: list[str] | None = None):
"""Yield all ACLs with optional filters."""
next_token: str | None = None
while True:
req = alb_models.ListAclsRequest(
max_results=100,
next_token=next_token,
acl_ids=acl_ids,
acl_names=acl_names,
)
resp = client.list_acls(req)
for acl in resp.body.acls or []:
yield acl
next_token = resp.body.next_token
if not next_token:
break
def main() -> int:
parser = argparse.ArgumentParser(description="List ALB ACLs")
parser.add_argument("--region", required=True, help="Region ID")
parser.add_argument("--acl-ids", nargs="+", help="Filter by ACL IDs")
parser.add_argument("--acl-names", nargs="+", help="Filter by ACL names")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
client = create_client(args.region)
acls = list(iter_acls(client, acl_ids=args.acl_ids, acl_names=args.acl_names))
if args.json:
output = json.dumps(
[a.to_map() for a in acls], indent=2, ensure_ascii=False, default=str
)
else:
header = (
f"{'AclId':<30} {'AclName':<30} "
f"{'AddressIPVersion':<18} {'AclStatus':<12} {'CreateTime'}"
)
sep = "-" * len(header)
lines = [header, sep]
for a in acls:
lines.append(
f"{a.acl_id or '-':<30} "
f"{(a.acl_name or '-'):<30} "
f"{(a.address_ipversion or '-'):<18} "
f"{(a.acl_status or '-'):<12} "
f"{a.create_time or '-'}"
)
lines.append(sep)
lines.append(f"Total: {len(acls)} ACL(s)")
output = "\n".join(lines)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output + "\n")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""List ALB instances in a single region."""
from __future__ import annotations
import argparse
import json
import os
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def iter_load_balancers(client: Alb20200616Client, *,
lb_ids: list[str] | None = None,
vpc_id: str | None = None,
address_type: str | None = None,
status: str | None = None):
"""Yield ALB instances using NextToken-based pagination with optional filters."""
next_token: str | None = None
while True:
req = alb_models.ListLoadBalancersRequest(
max_results=100,
next_token=next_token,
load_balancer_ids=lb_ids,
vpc_ids=[vpc_id] if vpc_id else None,
address_type=address_type,
load_balancer_status=status,
)
resp = client.list_load_balancers(req)
for lb in resp.body.load_balancers or []:
yield lb
next_token = resp.body.next_token
if not next_token:
break
def main() -> int:
parser = argparse.ArgumentParser(description="List ALB instances in a region")
parser.add_argument("--region", required=True, help="Region ID, e.g. cn-hangzhou")
parser.add_argument("--lb-ids", nargs="+", help="Filter by load balancer IDs")
parser.add_argument("--vpc-id", help="Filter by VPC ID")
parser.add_argument("--address-type", choices=["Internet", "Intranet"], help="Filter by address type")
parser.add_argument("--status", help="Filter by status, e.g. Active, Provisioning")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
client = create_client(args.region)
instances = list(iter_load_balancers(
client,
lb_ids=args.lb_ids,
vpc_id=args.vpc_id,
address_type=args.address_type,
status=args.status,
))
if args.json:
output = json.dumps(
[lb.to_map() for lb in instances], indent=2, ensure_ascii=False, default=str
)
else:
header = f"{'LoadBalancerId':<30} {'Name':<30} {'AddressType':<12} {'Status':<12} {'VpcId':<25} {'CreateTime'}"
sep = "-" * len(header)
lines = [header, sep]
for lb in instances:
lines.append(
f"{lb.load_balancer_id or '-':<30} "
f"{(lb.load_balancer_name or '-'):<30} "
f"{(lb.address_type or '-'):<12} "
f"{(lb.load_balancer_status or '-'):<12} "
f"{(lb.vpc_id or '-'):<25} "
f"{lb.create_time or '-'}"
)
lines.append(sep)
lines.append(f"Total: {len(instances)} ALB instance(s) in {args.region}")
output = "\n".join(lines)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output + "\n")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())
#!/usr/bin/env python3
"""Start an ALB listener.
Examples:
python start_listener.py --region cn-hangzhou --listener-id lsn-xxx
python start_listener.py --region cn-hangzhou --listener-id lsn-xxx --json
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from pathlib import Path
from alibabacloud_alb20200616.client import Client as Alb20200616Client
from alibabacloud_alb20200616 import models as alb_models
from alibabacloud_tea_openapi import models as open_api_models
def create_client(region_id: str) -> Alb20200616Client:
config = open_api_models.Config(
region_id=region_id,
endpoint=f"alb.{region_id}.aliyuncs.com",
)
ak = os.getenv("ALICLOUD_ACCESS_KEY_ID") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
sk = os.getenv("ALICLOUD_ACCESS_KEY_SECRET") or os.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
token = os.getenv("ALICLOUD_SECURITY_TOKEN") or os.getenv("ALIBABA_CLOUD_SECURITY_TOKEN")
if not ak or not sk:
raise RuntimeError("ALICLOUD_ACCESS_KEY_ID and ALICLOUD_ACCESS_KEY_SECRET must be set")
config.access_key_id = ak
config.access_key_secret = sk
if token:
config.security_token = token
return Alb20200616Client(config)
def main() -> int:
parser = argparse.ArgumentParser(description="Start an ALB listener")
parser.add_argument("--region", required=True, help="Region ID")
parser.add_argument("--listener-id", required=True, help="Listener ID (lsn-xxx)")
parser.add_argument("--json", action="store_true", help="Output as JSON")
parser.add_argument("--output", help="Write output to file")
args = parser.parse_args()
client = create_client(args.region)
try:
resp = client.start_listener(alb_models.StartListenerRequest(
listener_id=args.listener_id,
))
result = {"job_id": resp.body.job_id, "request_id": resp.body.request_id}
except Exception as e:
print(f"Error starting listener: {e}", file=sys.stderr)
return 1
if args.json:
output = json.dumps(result, indent=2, ensure_ascii=False)
else:
output = f"Listener {args.listener_id} started.\n Job ID: {result['job_id']}"
if args.output:
Path(args.output).write_text(output + "\n", encoding="utf-8")
print(f"Output written to {args.output}")
else:
print(output)
return 0
if __name__ == "__main__":
raise SystemExit(main())