
Huawei Cloud Cce Cluster Management
- 78 installs
- 19 repo stars
- Updated July 31, 2026
- huaweicloud/huaweicloud-skills
Manage Huawei Cloud CCE Kubernetes cluster lifecycle: create, delete, hibernate clusters, and manage node pools, nodes, and addons via Python SDK v3.
About
Manages CCE (Cloud Container Engine) cluster lifecycle including create/delete/hibernate/awake, node pool and node management, addons, EIP binding, and kubeconfig retrieval. A developer uses it to operate Kubernetes clusters on Huawei Cloud, with two-step confirmation on destructive actions.
- Cluster, node pool, node, and addon lifecycle via SDK v3
- Two-step confirm=true gate on all dangerous operations
Huawei Cloud Cce Cluster Management by the numbers
- 78 all-time installs (skills.sh)
- +4 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #627 of 1,042 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/huaweicloud/huaweicloud-skills --skill huawei-cloud-cce-cluster-managementAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 78 |
|---|---|
| repo stars | ★ 19 |
| Last updated | July 31, 2026 |
| Repository | huaweicloud/huaweicloud-skills ↗ |
What it does
Manage Huawei Cloud CCE Kubernetes cluster lifecycle: create, delete, hibernate clusters, and manage node pools, nodes, and addons via Python SDK v3.
Files
Huawei Cloud CCE Cluster Management
Overview
Manage CCE (Cloud Container Engine) cluster lifecycle, including cluster creation/deletion/hibernation/awakening, node pool management, node scheduling control, and addon management.
⛔ Security Constraints
Dangerous Operation Confirmation Mechanism
This skill strictly enforces a two-step confirmation mechanism for all dangerous operations to prevent accidental service disruption or data loss.
All dangerous operations require confirm=true parameter to execute. Otherwise, they return a preview and confirmation prompt.
Operations Requiring Confirmation
| Tool | Operation Type | Risk Level | Description |
|---|---|---|---|
huawei_delete_cce_cluster | Delete | 🔴 Critical | Deletes entire CCE cluster, irreversible |
huawei_hibernate_cce_cluster | Hibernate | 🟠 High | Stops all workloads, pauses control plane billing |
huawei_awake_cce_cluster | Awake | 🟠 High | Resumes cluster from hibernation |
huawei_resize_cce_nodepool | Scale | 🟡 Medium | Adjusts node pool size, affects capacity |
huawei_delete_cce_nodepool | Delete | 🟠 High | Deletes node pool, affects business capacity |
huawei_delete_cce_node | Delete | 🟠 High | Removes node from cluster, affects scheduling |
huawei_uninstall_cce_addon | Uninstall | 🟠 High | Removes addon, may affect cluster functionality |
huawei_cce_node_cordon | Cordon | 🟡 Medium | Marks node unschedulable, new pods won't be assigned |
huawei_cce_node_uncordon | Uncordon | 🟡 Medium | Marks node schedulable, new pods may be assigned immediately |
huawei_cce_node_drain | Drain | 🟠 High | Evicts all pods from node, affects running workloads |
Workflow
Step 1: Preview Operation - Call without confirm parameter
# Example: Preview cluster deletion
python3 huawei-cloud.py huawei_delete_cce_cluster \
region=cn-north-4 \
cluster_id=xxxReturns: operation preview, risk warning, confirmation example
Step 2: Confirm Execution - Call with confirm=true
# Example: Confirm and execute deletion
python3 huawei-cloud.py huawei_delete_cce_cluster \
region=cn-north-4 \
cluster_id=xxx \
confirm=trueCredential Security
✅ This skill strictly follows these security rules:
1. No persistent credential storage - Never saves AK/SK, tokens, or certificates to disk 2. No long-term memory cache - AK/SK exists only during API call, released afterward 3. Only project ID memory cache - Non-sensitive project ID cached in process memory 4. No credential leakage - Never includes AK/SK in logs, responses, or errors 5. Temporary file cleanup - If temporary cert files are created, they are deleted immediately after use
AK/SK usage methods:
- Environment variables
HW_ACCESS_KEY/HW_SECRET_KEY/HW_REGION_NAME(process-level, not saved) - Per-call parameter (valid only for that call)
---
Prerequisites
Python Environment
- Python 3.8+
- Install SDKs:
pip install huaweicloudsdkcce huaweicloudsdkcore - Optional for node operations:
pip install kubernetes
Environment Variables (Recommended)
export HW_ACCESS_KEY="your-access-key-id"
export HW_SECRET_KEY="your-secret-access-key"
export HW_REGION_NAME="cn-north-4"IAM Permission Policies
Ensure the IAM user has the minimum required permissions:
| Permission | Description |
|---|---|
cce:cluster:list | List clusters |
cce:cluster:get | Get cluster details |
cce:cluster:create | Create clusters |
cce:cluster:delete | Delete clusters |
cce:cluster:update | Update clusters (hibernate/awake/bind EIP) |
cce:node:list | List nodes |
cce:node:get | Get node details |
cce:node:create | Create nodes |
cce:node:delete | Delete nodes |
cce:node:update | Update nodes (cordon/uncordon/drain) |
cce:nodepool:list | List node pools |
cce:nodepool:create | Create node pools |
cce:nodepool:delete | Delete node pools |
cce:nodepool:update | Update node pools (resize) |
cce:addon:list | List addons |
cce:addon:get | Get addon details |
cce:addon:create | Install addons |
cce:addon:update | Update addons |
cce:addon:delete | Uninstall addons |
---
Core Commands
Cluster Query
| Tool | Function | Parameters |
|---|---|---|
huawei_list_cce_clusters | List all CCE clusters in region | region |
huawei_get_cce_nodes | Get detailed node information | region, cluster_id, node_id |
huawei_get_cce_kubeconfig | Get cluster kubeconfig | region, cluster_id, duration |
Cluster Management
| Tool | Function | Risk Level | Requires Confirmation |
|---|---|---|---|
huawei_create_cce_cluster | Create CCE cluster | 🟢 Low | No |
huawei_delete_cce_cluster | Delete CCE cluster | 🔴 Critical | Yes |
huawei_hibernate_cce_cluster | Hibernate cluster | 🟠 High | Yes |
huawei_awake_cce_cluster | Awake cluster | 🟠 High | Yes |
huawei_bind_cce_cluster_eip | Bind cluster EIP | 🟢 Low | No |
huawei_unbind_cce_cluster_eip | Unbind cluster EIP | 🟡 Medium | No |
Recommended defaults:
- Cluster type:
Turbo(best performance with ENI network) - Container network:
enifor Turbo clusters - Naming format:
<env>-<app>-cluster(e.g.,prod-web-cluster)
Node Pool Management
| Tool | Function | Risk Level | Requires Confirmation |
|---|---|---|---|
huawei_list_cce_nodepools | List node pools | 🟢 Low | No |
huawei_create_cce_nodepool | Create node pool | 🟢 Low | No |
huawei_delete_cce_nodepool | Delete node pool | 🟠 High | Yes |
huawei_resize_cce_nodepool | Resize node pool | 🟡 Medium | Yes |
Recommended defaults:
- Naming format:
<env>-<role>-pool(e.g.,prod-worker-pool) - Initial node count: 2 for HA, or 0 with autoscaling
- Enable autoscaling for dynamic scaling
Node Management
| Tool | Function | Risk Level | Requires Confirmation |
|---|---|---|---|
huawei_list_cce_nodes | List cluster nodes | 🟢 Low | No |
huawei_create_cce_node | Create nodes directly | 🟢 Low | No |
huawei_delete_cce_node | Delete node | 🟠 High | Yes |
huawei_cce_node_cordon | Mark node unschedulable | 🟡 Medium | Yes |
huawei_cce_node_uncordon | Mark node schedulable | 🟡 Medium | Yes |
huawei_cce_node_drain | Evict all pods from node | 🟠 High | Yes |
huawei_cce_node_status | Query node scheduling status | 🟢 Low | No |
Note: Prefer node pools for managed scaling. Direct node creation is for special cases.
Addon Management
| Tool | Function | Risk Level | Requires Confirmation |
|---|---|---|---|
huawei_list_cce_addons | List cluster addons | 🟢 Low | No |
huawei_get_cce_addon_detail | Get addon details | 🟢 Low | No |
huawei_install_cce_addon | Install addon | 🟢 Low | No |
huawei_uninstall_cce_addon | Uninstall addon | 🟠 High | Yes |
huawei_update_cce_addon | Update addon | 🟡 Medium | No |
Common addons:
coredns- DNS servicemetrics-server- Monitoring metricseverest- Storage driver
Network Prerequisites
| Tool | Function | Parameters |
|---|---|---|
huawei_list_vpc | List VPCs with CIDR info | region |
huawei_list_vpc_subnets | List subnets with AZ info | region, vpc_id |
Use these tools to find VPC/subnet IDs before cluster creation.
---
Supported Regions
| Region Code | Region Name |
|---|---|
| cn-north-4 | North China-Beijing 4 |
| cn-north-1 | North China-Beijing 1 |
| cn-north-2 | North China-Beijing 2 |
| cn-east-3 | East China-Shanghai 1 |
| cn-south-1 | South China-Guangzhou |
| cn-south-2 | South China-Guangzhou Friendly |
| cn-east-4 | East China II |
| cn-southwest-2 | Guiyang 1 |
| ap-southeast-1 | Asia-Pacific-Hong Kong |
| ap-southeast-2 | Asia-Pacific-Bangkok |
| ap-southeast-3 | Asia-Pacific-Singapore |
---
Output Format
All tools return JSON-formatted results containing:
status: operation result (success/error)data: operation-specific response (cluster info, node list, addon details, etc.)message: human-readable description of the resultwarning: risk warning for dangerous operations (preview mode only)
Verification
See verification-method.md for detailed verification steps. Quick checklist:
1. Verify AK/SK credentials are configured via environment variables 2. Run huawei_list_cce_clusters to confirm API connectivity 3. Test dangerous operation preview (call without confirm=true) 4. Verify Turbo cluster ENI network configuration
Best Practices
- Use environment variables (
HW_ACCESS_KEY/HW_SECRET_KEY) for credentials — avoid hardcoding - Always preview dangerous operations before confirming with
confirm=true - Use Turbo clusters (
container_network_type=eni) for high-performance workloads - Resize node pools during low-traffic periods to minimize business impact
- Keep node pools at ≥2 nodes for production workloads to ensure redundancy
- Regularly check cluster health via
huawei_list_cce_clustersandhuawei_show_cce_cluster
---
References
| Document | Description |
|---|---|
| task-cluster-management.md | Cluster lifecycle operations |
| task-nodepool-management.md | Node pool operations |
| task-node-management.md | Node scheduling operations |
| iam-policies.md | IAM permission policies |
| verification-method.md | Verification steps |
| troubleshooting.md | Troubleshooting guide |
| cce-api-guide.md | CCE Python SDK API reference |
| cce-cluster-parameters.md | Cluster/nodepool creation parameters |
---
Notes
- Ensure AK/SK has correct IAM permissions
- Different regions may have different resource availability
- All dangerous operations require confirmation
- Deletion operations are irreversible
- Hibernate cluster stops all workloads - use during non-business hours
- Node drain evicts all pods - ensure sufficient replicas
- Turbo clusters recommended for best performance with ENI network
CCE SDK API Reference
Overview
Huawei Cloud CCE Python SDK API call reference documentation, including key notes and common issues.
SDK Installation
pip install huaweicloudsdkcore huaweicloudsdkcce huaweicloudsdkiamNode password salted encryption requires installing passlib. The script reads the password from the CCE_NODE_PASSWORD environment variable and automatically validates complexity:
pip install passlib
export CCE_NODE_PASSWORD="your-password"Authentication Configuration
from huaweicloudsdkcore.auth.credentials import BasicCredentials
from huaweicloudsdkcce.v3.region.cce_region import CceRegion
credentials = BasicCredentials(ak, sk, project_id)
client = CceClient.new_builder() \
.with_credentials(credentials) \
.with_region(CceRegion.value_of("cn-north-4")) \
.build()Main APIs
| API | Description | Corresponding Tool |
|---|---|---|
| ListClusters | List clusters | huawei_list_cce_clusters |
| CreateCluster | Create cluster | huawei_create_cce_cluster |
| DeleteCluster | Delete cluster | huawei_delete_cce_cluster |
| UpdateCluster | Update cluster | huawei_hibernate/awake_cce_cluster |
| ListNodes | List nodes | huawei_list_cce_nodes |
| CreateNode | Create node | huawei_create_cce_node |
| DeleteNode | Delete node | huawei_delete_cce_node |
| ListNodePools | List node pools | huawei_list_cce_nodepools |
| CreateNodePool | Create node pool | huawei_create_cce_nodepool |
| UpdateNodePool | Update node pool | huawei_resize_cce_nodepool |
| CreateAddonInstance | Install addon | huawei_install_cce_addon |
| ShowAddonInstance | Get addon details | huawei_get_cce_addon_detail |
| ListAddonTemplates | List addon templates | No corresponding tool, needs direct SDK call |
Example Code
List Clusters
from huaweicloudsdkcce.v3 import ListClustersRequest
request = ListClustersRequest()
response = client.list_clusters(request)
for cluster in response.items:
print(f"Cluster: {cluster.metadata.name}, Status: {cluster.status.phase}")Create Node Pool (with Password Salting)
Important: The password is read from the `CCE_NODE_PASSWORD` environment variable. The `UserPassword.password` field must be passed a SHA-512 salted and base64-encoded value; the raw password cannot be passed directly.
import os
from huaweicloudsdkcce.v3 import (
CreateNodePoolRequest, NodePool, NodePoolMetadata, NodePoolSpec,
NodeSpec, Login, UserPassword, Volume
)
from passlib.hash import sha512_crypt
import base64
password = os.environ.get("CCE_NODE_PASSWORD")
hashed = sha512_crypt.using(rounds=5000).hash(password)
salted_b64 = base64.b64encode(hashed.encode("utf-8")).decode("utf-8")
login = Login()
login.user_password = UserPassword(password=salted_b64)
node_spec = NodeSpec(
flavor="c7.large.2",
az="cn-north-4a",
os="EulerOS",
root_volume=Volume(size=40, volumetype="GPSSD"),
login=login,
)
# Turbo cluster node pools also need data_volumes configured
node_spec.data_volumes = [Volume(size=100, volumetype="SSD")]
spec = NodePoolSpec(initial_node_count=1, node_template=node_spec)
body = NodePool(kind="NodePool", api_version="v3", metadata=NodePoolMetadata(name="my-pool"), spec=spec)
request = CreateNodePoolRequest(cluster_id="cluster-id", body=body)
response = client.create_node_pool(request)Create Node (with Password Salting)
Important: The SDK does not have a `CreateNodeRequestBody` class. Use the `Node` object as the request body. `NodeSpec.count` sets the node count. SDK attribute names use snake_case.
import os
from huaweicloudsdkcce.v3 import (
CreateNodeRequest, Node, NodeMetadata, NodeSpec,
Volume, Login, UserPassword, NodeNicSpec
)
from passlib.hash import sha512_crypt
import base64
password = os.environ.get("CCE_NODE_PASSWORD")
hashed = sha512_crypt.using(rounds=5000).hash(password)
salted_b64 = base64.b64encode(hashed.encode("utf-8")).decode("utf-8")
login = Login(user_password=UserPassword(username="root", password=salted_b64))
# Note: attribute names are snake_case: user_password, ssh_key, root_volume, data_volumes, node_nic_spec
node_spec = NodeSpec(
flavor="c7.large.2",
az="cn-north-4a",
os="EulerOS 2.9",
root_volume=Volume(size=40, volumetype="SSD"),
login=login,
data_volumes=[Volume(size=100, volumetype="SSD")], # snake_case, not dataVolumes
node_nic_spec=NodeNicSpec(primary_nic={"subnetId": "subnet-id"}), # Note: primary_nic is a dict
count=1, # node count is set in NodeSpec.count
)
body = Node(
kind="Node",
api_version="v3",
metadata=NodeMetadata(name="node-clusterid8"),
spec=node_spec,
)
request = CreateNodeRequest(cluster_id="cluster-id")
request.body = body
response = client.create_node(request)Install Addon
Important: `InstanceSpec` uses the `addon_template_name` field, not `template_name`.
from huaweicloudsdkcce.v3 import (
CreateAddonInstanceRequest, AddonInstance, AddonMetadata, InstanceSpec
)
spec = InstanceSpec(
cluster_id="cluster-id",
version="1.21.7",
addon_template_name="volcano", # Note: field name is addon_template_name, not template_name
values={"basic": {"category": "small", "flavor": 1},
"custom": {"default_scheduler": True}}
)
body = AddonInstance(
kind="Addon", api_version="v3",
metadata=AddonMetadata(annotations={"addon.install/type": "install"}),
spec=spec
)
request = CreateAddonInstanceRequest(body=body)
response = client.create_addon_instance(request)Get Addon Details
Important: `ShowAddonInstanceRequest` uses the `id` field, not `addon_name`. And use the `show_addon_instance` method, not `show_addon`.
from huaweicloudsdkcce.v3 import ShowAddonInstanceRequest
request = ShowAddonInstanceRequest()
request.cluster_id = "cluster-id"
request.id = "addon-instance-id" # Note: field name is id, not addon_name
response = client.show_addon_instance(request) # Note: method name is show_addon_instance, not show_addon
print(f"Addon: {response.metadata.name}, Status: {response.status}")List Addon Template Versions
Before installing an addon, query available versions:
from huaweicloudsdkcce.v3 import ListAddonTemplatesRequest
request = ListAddonTemplatesRequest()
request.cluster_id = "cluster-id"
request.addon_template_name = "volcano"
response = client.list_addon_templates(request)
for item in response.items:
for v in item.spec.versions:
print(f" Version: {v.version}, Stable: {v.stable}")Update Node Scheduling Status
from huaweicloudsdkcce.v3 import UpdateNodeRequest, NodeMetadata, NodeSpec
request = UpdateNodeRequest(
cluster_id="cluster-id",
node_id="node-id",
body=NodeUpdateRequest(
metadata=NodeMetadata(unschedulable=True)
)
)
client.update_node(request)SDK Key Notes
| Issue | Description |
|---|---|
InstanceSpec.template_name | ❌ Wrong field name, should use addon_template_name |
ShowAddonInstanceRequest.addon_name | ❌ Wrong field name, should use id (value is addon instance UID) |
client.show_addon() | ❌ Wrong method name, should use client.show_addon_instance() |
UserPassword.password raw password | ❌ Cannot pass raw password directly; must use SHA-512 salted + base64 encoding. The script reads from CCE_NODE_PASSWORD environment variable and handles it automatically |
CreateNodeRequestBody | ❌ This class does not exist; use Node object as the body of CreateNodeRequest |
| SDK attribute names camelCase | ❌ Some old examples use camelCase; the SDK actually uses snake_case: user_password, ssh_key, root_volume, data_volumes, node_nic_spec |
NodeNicSpec(subnetId=...) | ❌ Wrong construction; should use NodeNicSpec(primary_nic={"subnetId": "xxx"}) |
CreateNodeRequestBody.count | ❌ Does not exist; node count is set via NodeSpec.count |
| Turbo cluster node flavor | Must use ENI-compatible specs (e.g., c7 series); s6, c6, etc. do not support ENI |
| Non-local-disk node data_volumes | Some specs require data volumes to be configured; otherwise creation fails |
Official Documentation
Cluster Creation Parameter Reference
Overview
Detailed parameters required for creating CCE clusters, node pools, and nodes, including key constraints such as Turbo clusters, password salting/encryption, and ENI flavor compatibility.
Required Cluster Parameters
| Parameter | Description | Example Value |
|---|---|---|
region | Huawei Cloud region | cn-north-4 |
cluster_name | Cluster name | my-cluster |
cluster_version | Kubernetes version | v1.28 |
flavor_id | Cluster specification | cce.s1.small |
vpc_id | VPC ID | vpc-xxx |
subnet_id | Subnet ID | subnet-xxx |
Optional Cluster Parameters
| Parameter | Description | Default Value |
|---|---|---|
cluster_type | Cluster type | VirtualMachine |
container_network_type | Container network type | overlay_l2 |
container_network_cidr | Container network CIDR | Auto-assigned |
eni_subnet_id | ENI subnet ID (Turbo cluster) | Empty |
description | Cluster description | Empty |
Turbo Cluster
When creating a CCE Turbo (ENI network) cluster, set:
cluster_type=VirtualMachine(the Turbo cluster's category is automatically determined by the API based on container_network_type)container_network_type=eniflavor_idcan use any specification such ascce.s1.small
The spec.category returned by the API will automatically become Turbo.
Cluster Specifications
| Specification | Description | Applicable Scenario |
|---|---|---|
cce.s1.small | Small scale, 50 nodes | Development & testing |
cce.s1.medium | Medium scale, 200 nodes | Production environment |
cce.s1.large | Large scale, 1000 nodes | Large-scale applications |
cce.s2.small | HA small scale | HA testing |
cce.s2.medium | HA medium scale | HA production |
Node Pool Creation Parameters
Required Parameters
| Parameter | Description | Example Value |
|---|---|---|
region | Huawei Cloud region | cn-north-4 |
cluster_id | Cluster ID | xxx |
nodepool_name | Node pool name | dev-worker-pool |
flavor | Node specification | c7.large.2 |
availability_zone | Availability zone | cn-north-4a |
root_volume_size | System disk size (GB) | 40 |
root_volume_type | System disk type | GPSSD |
initial_node_count | Initial node count | 1 |
Login Authentication (one is required)
| Parameter | Description | Notes |
|---|---|---|
ssh_key | SSH key pair name | Mutually exclusive with password |
| Password | Read from CCE_NODE_PASSWORD environment variable | 8-26 characters, must include at least three of: uppercase, lowercase, digits, special characters |
Important: The password is passed via the `CCE_NODE_PASSWORD` environment variable. The script automatically performs SHA-512 salted encryption + base64 encoding. No manual processing is needed.
CCE_NODE_PASSWORD Environment Variable
When creating nodes/node pools without providing ssh_key, the script reads the password from the environment variable CCE_NODE_PASSWORD:
export CCE_NODE_PASSWORD="your_password"Password complexity requirements:
- Length: 8-26 characters
- Must include at least three of: uppercase letters, lowercase letters, digits, special characters
- Special characters:
!@$%^-_=+[]{}:,./?
The script automatically validates password complexity and returns an error message if requirements are not met.
Password Salting and Encryption
Important: The CCE API requires the password field to be SHA-512 salted encrypted and then base64 encoded. Raw passwords cannot be passed directly.
Encryption steps (Python):
from passlib.hash import sha512_crypt
import base64
hashed = sha512_crypt.using(rounds=5000).hash("raw_password")
salted_b64 = base64.b64encode(hashed.encode("utf-8")).decode("utf-8")
# salted_b64 is the value for the UserPassword.password fieldDependency required: pip install passlib
Optional Parameters
| Parameter | Description | Default Value |
|---|---|---|
os_type | Operating system | EulerOS |
data_volumes | Data volume configuration JSON | Required for some specifications |
subnet_id | Subnet ID | Uses cluster subnet |
autoscaling_enabled | Enable autoscaling | false |
min_node_count | Minimum node count for autoscaling | 0 |
max_node_count | Maximum node count for autoscaling | 0 |
Data Volumes (data_volumes)
Some node specifications (e.g., non-local disk types) must have data volumes configured, otherwise creation will fail. Format is a JSON array:
data_volumes='[{"size":100,"type":"SSD"}]'ENI Flavor Compatibility
Important: Node pools in Turbo (ENI network) clusters must use ENI-compatible flavors.
Flavors that do not support ENI (such as s6.large.2, c6.large.2) will produce an error: Flavor [xxx] 's subeni quota is 0, Eni network is not supported
Recommended for Turbo clusters: c7 series (e.g., c7.large.2), s7 series.
Node Creation Parameters
Parameters for direct node creation (non-node pool) are essentially the same as node pool, with the additional:
| Parameter | Description | Default Value |
|---|---|---|
node_count | Number of nodes to create | 1 |
The password also requires SHA-512 salted encryption + base64 encoding.
Kubernetes Versions
Common versions: v1.27, v1.28, v1.29, v1.30, v1.31
Password Salting and Encryption
When creating nodes/node pools, the password field must be SHA-512 salted encrypted and then base64 encoded. Raw passwords cannot be passed directly.
Salting Method
Reference: CCE Password Salting and Encryption
Python Example
from passlib.hash import sha512_crypt
import base64
hashed = sha512_crypt.using(rounds=5000).hash("raw_password")
salted_password = base64.b64encode(hashed.encode("utf-8")).decode("utf-8")Password Complexity Requirements
- Length: 8-26 characters
- Must include at least three of: uppercase letters, lowercase letters, digits, special characters
- Special characters:
!@$%^-_=+[]{}:,./?
Example
Create a Standard Cluster
python3 huawei-cloud.py huawei_create_cce_cluster \
region=cn-north-4 \
cluster_name=my-cluster \
cluster_version=v1.28 \
flavor_id=cce.s1.small \
vpc_id=vpc-xxx \
subnet_id=subnet-xxxCreate a Turbo Cluster
python3 huawei-cloud.py huawei_create_cce_cluster \
region=cn-north-4 \
cluster_name=dev-turbo-cluster \
cluster_version=v1.28 \
cluster_type=VirtualMachine \
container_network_type=eni \
flavor_id=cce.s1.small \
vpc_id=vpc-xxx \
subnet_id=subnet-xxxCreate a Node Pool
export CCE_NODE_PASSWORD="your_password"
python3 huawei-cloud.py huawei_create_cce_nodepool \
region=cn-north-4 \
cluster_id=xxx \
nodepool_name=dev-worker-pool \
flavor=c7.large.2 \
availability_zone=cn-north-4a \
root_volume_size=40 \
root_volume_type=GPSSD \
initial_node_count=1 \
'data_volumes=[{"size":100,"type":"SSD"}]'Notes
- Cluster name: 1-63 characters, letters, digits, and hyphens
- VPC and subnet must exist in the specified region
- Cluster creation may take 5-15 minutes
- Cluster specification cannot be changed after creation
- Turbo cluster nodes must use ENI-compatible flavors
- Non-local disk specifications must have data volumes configured
- The password field must be SHA-512 salted + base64 encoded; the script handles this automatically
CCE IAM Permission Configuration
Overview
IAM permission policy descriptions required for Huawei Cloud CCE cluster management.
Key Parameters
| Permission | Description |
|---|---|
cce:cluster:list | Query cluster list |
cce:cluster:get | Query cluster details |
cce:cluster:create | Create cluster |
cce:cluster:delete | Delete cluster |
cce:cluster:update | Update cluster (hibernate/wake/bind EIP) |
cce:node:list | Query node list |
cce:node:get | Query node details |
cce:node:delete | Delete node |
cce:node:update | Update node (cordon/uncordon/drain) |
cce:nodepool:list | Query node pool list |
cce:nodepool:update | Update node pool (scale up/down) |
cce:addon:list | Query addon list |
cce:addon:get | Query addon details |
Minimum Required Policy (JSON)
{
"Version": "5.0",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cce:cluster:list",
"cce:cluster:get",
"cce:cluster:create",
"cce:cluster:delete",
"cce:cluster:update",
"cce:node:list",
"cce:node:get",
"cce:node:delete",
"cce:node:update",
"cce:nodepool:list",
"cce:nodepool:update",
"cce:addon:list",
"cce:addon:get"
],
"Resource": ["CCE:*:*:cluster:*", "CCE:*:*:node:*", "CCE:*:*:nodepool:*"]
}
]
}System Policies
| System Policy | Applicable Scenario |
|---|---|
CCE Administrator | Full cluster management permissions |
CCE Viewer | Read-only permissions, view cluster information |
CES ReadOnlyAccess | Query monitoring metrics |
Example
# Recommended combination: CCE Administrator + CES ReadOnlyAccess
# Add the above policies to user groups in the IAM consoleCluster Management Task Details
Overview
CCE cluster lifecycle management operations, including creation, deletion, hibernation, awakening, and EIP binding.
Key Parameters
| Parameter | Description | Required |
|---|---|---|
region | Huawei Cloud region | Yes |
cluster_id | Cluster ID | Yes (except for creation) |
cluster_name | Cluster name | Required for creation |
cluster_version | K8s version | Required for creation |
flavor_id | Cluster specification | Required for creation |
vpc_id | VPC ID | Required for creation |
subnet_id | Subnet ID | Required for creation |
cluster_type | Cluster type | Optional for creation |
container_network_type | Container network type | Optional for creation |
eip_id | EIP ID | Required for binding/unbinding |
confirm | Confirm executing dangerous operations | Required for dangerous operations |
Operation Classification
| Operation | Risk Level | Requires Confirmation |
|---|---|---|
| Create cluster | 🟢 Low | No |
| Delete cluster | 🔴 Extremely High | Yes |
| Hibernate cluster | 🟠 High | Yes |
| Awaken cluster | 🟠 High | Yes |
| Bind EIP | 🟢 Low | No |
| Unbind EIP | 🟡 Medium | No |
Example
Create a Standard Cluster
python3 huawei-cloud.py huawei_create_cce_cluster \
region=cn-north-4 \
cluster_name=my-cluster \
cluster_version=v1.28 \
flavor_id=cce.s1.small \
vpc_id=xxx \
subnet_id=xxxCreate a Turbo Cluster
Turbo clusters use ENI container networking, suitable for high-performance scenarios. When creating, set cluster_type=VirtualMachine and container_network_type=eni; the API will automatically set spec.category to Turbo.
python3 huawei-cloud.py huawei_create_cce_cluster \
region=cn-north-4 \
cluster_name=dev-turbo-cluster \
cluster_version=v1.28 \
cluster_type=VirtualMachine \
container_network_type=eni \
flavor_id=cce.s1.small \
vpc_id=xxx \
subnet_id=xxxNote: Turbo cluster node pools must use ENI-compatible flavors (e.g., `c7.large.2`), and typically require configuring data volumes.
Delete a Cluster (Requires Double Confirmation)
# Preview deletion
python3 huawei-cloud.py huawei_delete_cce_cluster \
region=cn-north-4 \
cluster_id=xxx
# Confirm deletion
python3 huawei-cloud.py huawei_delete_cce_cluster \
region=cn-north-4 \
cluster_id=xxx \
confirm=trueHibernate a Cluster
python3 huawei-cloud.py huawei_hibernate_cce_cluster \
region=cn-north-4 \
cluster_id=xxx \
confirm=trueAwaken a Cluster
python3 huawei-cloud.py huawei_awake_cce_cluster \
region=cn-north-4 \
cluster_id=xxx \
confirm=trueNode Management Task Details
Overview
Cluster node lifecycle management, including creation, querying, cordon, uncordon, drain, and deletion operations.
Create Node Parameters
Required Parameters
| Parameter | Description | Example Value |
|---|---|---|
region | Huawei Cloud region | cn-north-4 |
cluster_id | Cluster ID | xxx |
flavor | Node specification | c7.large.2 |
availability_zone | Availability zone | cn-north-4a |
root_volume_size | System disk size (GB) | 40 |
root_volume_type | System disk type | GPSSD |
ssh_key or password | Login authentication (one required) | KeyPair-dev or CCE_NODE_PASSWORD environment variable |
Login Authentication
Either ssh_key or password is required (mutually exclusive):
ssh_key: SSH key pair name- Password: passed via
CCE_NODE_PASSWORDenvironment variable (8-26 characters, must contain at least three of: uppercase, lowercase, digits, special characters)
Important: The password is read from the `CCE_NODE_PASSWORD` environment variable, and the script automatically performs SHA-512 salted encryption + base64 encoding, no manual processing required.
export CCE_NODE_PASSWORD="your_password"Data Volumes (data_volumes)
Some node specifications (non-local disk types) must configure data volumes:
data_volumes='[{"size":100,"type":"SSD"}]'ENI Flavor Compatibility
Nodes in Turbo (ENI network) clusters must use flavors that support ENI (such as the c7 series); s6, c6, etc. do not support ENI.
Optional Parameters
| Parameter | Description | Default Value |
|---|---|---|
node_count | Number of nodes to create | 1 |
os_type | Operating system | EulerOS |
subnet_id | Subnet ID | Uses cluster subnet |
Scheduling Management Parameters
| Parameter | Description | Required |
|---|---|---|
region | Huawei Cloud region | Yes |
cluster_id | Cluster ID | Yes |
node_id | Node ID | Yes |
confirm | Confirm dangerous operations | Required for dangerous operations |
Node Scheduling Status
| Status | Description |
|---|---|
| Schedulable | Schedulable, new Pods can be assigned to this node |
| Unschedulable | Unschedulable, new Pods will not be assigned to this node |
Operation Description
| Operation | Function | Risk Level | Requires Confirmation |
|---|---|---|---|
| Create Node | Add node | 🟢 Low | No |
| Query Node List | Get all nodes | 🟢 Low | No |
| Query Node Status | Get scheduling status | 🟢 Low | No |
| cordon | Mark as unschedulable | 🟡 Medium | Yes |
| uncordon | Restore schedulable | 🟡 Medium | Yes |
| drain | Evict all Pods | 🟠 High | Yes |
| delete | Delete node | 🟠 High | Yes |
Create Node (Turbo Cluster)
export CCE_NODE_PASSWORD="your_password"
python3 huawei-cloud.py huawei_create_cce_node \
region=cn-north-4 \
cluster_id=xxx \
flavor=c7.large.2 \
availability_zone=cn-north-4a \
root_volume_size=40 \
root_volume_type=GPSSD \
node_count=1 \
'data_volumes=[{"size":100,"type":"SSD"}]'Node Maintenance Process
# 1. Mark node as unschedulable
python3 huawei-cloud.py huawei_cce_node_cordon \
region=cn-north-4 cluster_id=xxx node_id=xxx confirm=true
# 2. Evict Pods on the node
python3 huawei-cloud.py huawei_cce_node_drain \
region=cn-north-4 cluster_id=xxx node_id=xxx confirm=true
# 3. Perform maintenance operations...
# 4. Restore node scheduling
python3 huawei-cloud.py huawei_cce_node_uncordon \
region=cn-north-4 cluster_id=xxx node_id=xxx confirm=trueNode Pool Management Task Details
Overview
Node pool lifecycle management, including creating node pools, querying node pool lists, and adjusting node counts.
Create Node Pool Parameters
Required Parameters
| Parameter | Description | Example Value |
|---|---|---|
region | Huawei Cloud region | cn-north-4 |
cluster_id | Cluster ID | xxx |
nodepool_name | Node pool name | dev-worker-pool |
flavor | Node specification | c7.large.2 |
availability_zone | Availability zone | cn-north-4a |
root_volume_size | System disk size (GB) | 40 |
root_volume_type | System disk type | GPSSD |
initial_node_count | Initial node count | 1 |
ssh_key or password | Login authentication (one required) | KeyPair-dev or MyPass123! |
Login Authentication
One of ssh_key and password is required; they are mutually exclusive:
ssh_key: SSH key pair namepassword: Node login password (8-26 characters, must contain at least three of: uppercase, lowercase, digits, special characters)
Important: The script automatically performs SHA-512 salted encryption + base64 encoding on the password. No manual processing is needed. However, if calling the CCE API directly, you must encrypt it yourself.
Data Volumes (data_volumes)
Some node specifications (non-local disk types) must configure data volumes, otherwise creation will fail with the error: Data volume needed for non-local-disk flavor or non-system diskType
data_volumes='[{"size":100,"type":"SSD"}]'ENI Flavor Compatibility
Node pools in Turbo (ENI network) clusters must use ENI-compatible flavors. Incompatible flavors will result in the error: Flavor [xxx] 's subeni quota is 0, Eni network is not supported
| Flavor Series | ENI Support | Recommended Scenario |
|---|---|---|
c7 series (e.g., c7.large.2) | ✅ Supported | Recommended for Turbo clusters |
s7 series | ✅ Supported | Turbo clusters |
s6 series (e.g., s6.large.2) | ❌ Not supported | Standard clusters only |
c6 series (e.g., c6.large.2) | ❌ Not supported | Standard clusters only |
Optional Parameters
| Parameter | Description | Default Value |
|---|---|---|
os_type | Operating system | EulerOS |
subnet_id | Subnet ID | Uses cluster subnet |
autoscaling_enabled | Enable auto-scaling | false |
min_node_count | Minimum node count | 0 |
max_node_count | Maximum node count | 0 |
Scaling Parameters
| Parameter | Description | Required |
|---|---|---|
region | Huawei Cloud region | Yes |
cluster_id | Cluster ID | Yes |
nodepool_id | Node pool ID | Yes |
node_count | Target node count | Yes |
confirm | Confirm execution | Yes |
Node Pool States
| State | Description |
|---|---|
| Active | Running normally |
| Scaling | Scaling in progress |
| Deleting | Being deleted |
| Error | Abnormal state |
Operation Instructions
Create Node Pool (Standard Cluster)
python3 huawei-cloud.py huawei_create_cce_nodepool \
region=cn-north-4 \
cluster_id=xxx \
nodepool_name=dev-worker-pool \
flavor=s6.large.2 \
availability_zone=cn-north-4a \
root_volume_size=40 \
root_volume_type=GPSSD \
initial_node_count=2 \
ssh_key=KeyPair-devCreate Node Pool (Turbo Cluster)
export CCE_NODE_PASSWORD="your_password"
python3 huawei-cloud.py huawei_create_cce_nodepool \
region=cn-north-4 \
cluster_id=xxx \
nodepool_name=dev-worker-pool \
flavor=c7.large.2 \
availability_zone=cn-north-4a \
root_volume_size=40 \
root_volume_type=GPSSD \
initial_node_count=1 \
'data_volumes=[{"size":100,"type":"SSD"}]'Query Node Pools
python3 huawei-cloud.py huawei_list_cce_nodepools \
region=cn-north-4 \
cluster_id=xxxScale Up Node Pool
python3 huawei-cloud.py huawei_resize_cce_nodepool \
region=cn-north-4 \
cluster_id=xxx \
nodepool_id=xxx \
node_count=10 \
confirm=trueScale Down Node Pool
python3 huawei-cloud.py huawei_resize_cce_nodepool \
region=cn-north-4 \
cluster_id=xxx \
nodepool_id=xxx \
node_count=3 \
confirm=trueCommon Troubleshooting Issues
Overview
Common issues and solutions in CCE cluster management operations.
Issue Categories
| Error Type | Possible Cause | Solution |
|---|---|---|
| 403 Insufficient Permissions | Missing IAM permissions | Check IAM policy configuration |
| 404 Resource Not Found | Incorrect cluster/node ID | Verify resource ID is correct |
| 400 Parameter Error | Invalid parameter format | Check parameter format and values |
| 409 State Conflict | Operation not allowed in current resource state | Wait for resource state change and retry |
Common Issues
1. Cluster query returns empty list
Possible Causes:
- Incorrect region parameter
- Current account has no clusters
Solutions:
# Verify region is correct
python3 huawei-cloud.py huawei_list_cce_clusters region=cn-north-4
# Check other regions
python3 huawei-cloud.py huawei_list_cce_clusters region=cn-east-32. Node operation returns insufficient permissions
Possible Causes:
- IAM lacks
cce:node:updatepermission
Solutions: Add CCE-related permissions for the user in the IAM console.
3. Cluster sleep/awaken operation failed
Possible Causes:
- Cluster state does not support this operation
- Cluster is executing other tasks
Solutions:
# First query cluster status
python3 huawei-cloud.py huawei_list_cce_clusters region=cn-north-4
# Confirm status is Available before operating4. Node pool scaling not taking effect
Possible Causes:
- Forgot to add
confirm=trueparameter - Node pool is currently scaling
Solutions:
# Add confirm parameter
python3 huawei-cloud.py huawei_resize_cce_nodepool \
region=cn-north-4 \
cluster_id=xxx \
nodepool_id=xxx \
node_count=5 \
confirm=true5. Password-related errors when creating nodes/node pools
Error Messages:
CCE_CM.0004 - Request is invalid, Unexpected initial node password formatCCE_NODE_PASSWORD environment variable is not setCCE_NODE_PASSWORD length must be 8-26 charactersCCE_NODE_PASSWORD must contain at least 3 of: uppercase, lowercase, digits, special chars
Causes:
CCE_NODE_PASSWORDenvironment variable not set- Password complexity does not meet requirements (8-26 characters, must contain at least 3 of: uppercase, lowercase, digits, special characters)
- When calling CCE API directly, password not encrypted with SHA-512 salted encryption + base64 encoding
Solutions:
# Set password environment variable (must meet complexity requirements)
export CCE_NODE_PASSWORD="MyPass123!"
# The script automatically performs SHA-512 salted encryption + base64 encoding, no manual processing needed
python3 huawei-cloud.py huawei_create_cce_nodepool ...When calling CCE API directly, you need to encrypt yourself:
import os
from passlib.hash import sha512_crypt
import base64
password = os.environ.get("CCE_NODE_PASSWORD")
hashed = sha512_crypt.using(rounds=5000).hash(password)
salted_b64 = base64.b64encode(hashed.encode("utf-8")).decode("utf-8")6. "Flavor ENI network is not supported" error when creating node pool
Error Message: Flavor [xxx] 's subeni quota is 0, Eni network is not supported
Cause: Node pool in a Turbo (ENI network) cluster uses a node flavor that does not support ENI.
Flavors that do not support ENI: s6 series, c6 series, etc. Flavors that support ENI: c7 series (e.g., c7.large.2), s7 series
Solutions:
# Turbo cluster uses c7 series flavor
python3 huawei-cloud.py huawei_create_cce_nodepool \
flavor=c7.large.2 \
...7. "Data volume needed" error when creating node pool
Error Message: Data volume needed for non-local-disk flavor or non-system diskType
Cause: Some node flavors (non-local disk types) must configure data volumes.
Solutions:
python3 huawei-cloud.py huawei_create_cce_nodepool \
... \
'data_volumes=[{"size":100,"type":"SSD"}]'8. "instanceID is invalid" error when querying addon details
Error Message: CCE.03400001 - Invalid request., instanceID is invalid
Cause: Incorrect value passed to the id field of ShowAddonInstanceRequest.
Solutions:
- The
idfield should use the addon instance UID (obtained fromhuawei_list_cce_addonsor theuidfield in the creation response) - Do not use the
addon_namefield (deprecated), use theidfield instead - Call
client.show_addon_instance()method, notclient.show_addon()
9. "InstanceSpec got unexpected keyword argument 'template_name'" error when installing addon
Error Message: InstanceSpec.__init__() got an unexpected keyword argument 'template_name'
Cause: The correct field name for CCE SDK InstanceSpec class is addon_template_name, not template_name.
Solutions:
spec = InstanceSpec(
addon_template_name="volcano", # Correct
# template_name="volcano", # Incorrect
...
)10. "cannot import name 'CreateNodeRequestBody'" error when creating node
Error Message: cannot import name 'CreateNodeRequestBody' from 'huaweicloudsdkcce.v3'
Cause: CCE SDK does not have CreateNodeRequestBody class; use Node object as the body of CreateNodeRequest.
Solutions:
from huaweicloudsdkcce.v3 import CreateNodeRequest, Node, NodeMetadata, NodeSpec
body = Node(kind="Node", api_version="v3", metadata=NodeMetadata(name="my-node"), spec=node_spec)
request = CreateNodeRequest(cluster_id="cluster-id")
request.body = body
response = client.create_node(request)11. SDK attribute name error when creating node
Error Message: Login.__init__() got an unexpected keyword argument 'userPassword' or similar
Cause: CCE SDK Python package attribute names use snake_case, not camelCase.
Common error reference:
| Incorrect (camelCase) | Correct (snake_case) |
|---|---|
Login(userPassword=...) | Login(user_password=...) |
Login(sshkey=...) | Login(ssh_key=...) |
NodeSpec(rootVolume=...) | NodeSpec(root_volume=...) |
NodeSpec(dataVolumes=...) | NodeSpec(data_volumes=...) |
NodeSpec(nodeNicSpec=...) | NodeSpec(node_nic_spec=...) |
NodeNicSpec(subnetId=...) | NodeNicSpec(primary_nic={"subnetId": "xxx"}) |
12. NodeNicSpec construction error
Error Message: NodeNicSpec.__init__() got an unexpected keyword argument 'subnetId'
Cause: NodeNicSpec does not directly accept subnetId parameter; pass it through primary_nic dict.
Solutions:
node_nic_spec = NodeNicSpec(primary_nic={"subnetId": "subnet-id"}) # Correct
# NodeNicSpec(subnetId="subnet-id") # IncorrectFeature Verification Steps
Overview
Functional verification process for CCE cluster management skill.
Verification Checklist
| No. | Verification Item | Command Example |
|---|---|---|
| 1 | Query cluster list | huawei_list_cce_clusters region=cn-north-4 |
| 2 | Query node list | huawei_list_cce_nodes region=cn-north-4 cluster_id=xxx |
| 3 | Query node pool list | huawei_list_cce_nodepools region=cn-north-4 cluster_id=xxx |
| 4 | Get kubeconfig | huawei_get_cce_kubeconfig region=cn-north-4 cluster_id=xxx |
| 5 | Node scheduling status | huawei_cce_node_status region=cn-north-4 cluster_id=xxx node_id=xxx |
Verification Steps
Step 1: Environment Check
# Check Python environment
python3 --version
# Check dependencies
pip show huaweicloudsdkcceStep 2: Verify Query Functions
# Query cluster list
python3 huawei-cloud.py huawei_list_cce_clusters region=cn-north-4
# Expected result: Returns cluster list, including cluster_id, name, status, etc.Step 3: Verify Node Management
# Query node scheduling status
python3 huawei-cloud.py huawei_cce_node_status \
region=cn-north-4 \
cluster_id=<cluster_id> \
node_id=<node_id>
# Expected result: Returns "Schedulable" or "Unschedulable"Step 4: Verify Dangerous Operation Confirmation Mechanism
# Call delete command without confirm parameter
python3 huawei-cloud.py huawei_delete_cce_cluster \
region=cn-north-4 \
cluster_id=xxx
# Expected result: Returns preview and warning, does not execute deletionExample
# Complete verification flow
python3 huawei-cloud.py huawei_list_cce_clusters region=cn-north-4
python3 huawei-cloud.py huawei_list_cce_nodes region=cn-north-4 cluster_id=<cluster_id>"""Huawei Cloud service modules."""
"""CCE Addon management functions."""
from typing import Any, Dict, Optional
from huaweicloudsdkcore.exceptions.exceptions import ClientRequestException
from huaweicloudsdkcce.v3 import (
ShowAddonInstanceRequest,
ListAddonInstancesRequest,
CreateAddonInstanceRequest,
AddonInstance,
InstanceSpec,
AddonMetadata,
UpdateAddonInstanceRequest,
DeleteAddonInstanceRequest,
)
from .common import get_credentials, create_cce_client, SDK_AVAILABLE, IMPORT_ERROR
def get_cce_addon_detail(region: str, cluster_id: str, addon_name: str, ak: Optional[str] = None, sk: Optional[str] = None, project_id: Optional[str] = None) -> Dict[str, Any]:
"""Get detailed information of a specific CCE addon."""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not cluster_id:
return {
"success": False,
"error": "cluster_id is required"
}
if not SDK_AVAILABLE:
return {
"success": False,
"error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"
}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
request = ShowAddonInstanceRequest()
request.cluster_id = cluster_id
request.addon_name = addon_name
response = client.show_addon(request)
addon_info = {}
if hasattr(response, 'spec') and response.spec:
spec = response.spec
addon_info["name"] = getattr(spec, 'name', None)
addon_info["version"] = getattr(spec, 'version', None)
addon_info["status"] = getattr(spec, 'status', None)
addon_info["description"] = getattr(spec, 'description', None)
if hasattr(spec, 'custom') and spec.custom:
custom = spec.custom
addon_info["custom_params"] = {}
if hasattr(custom, 'aom_id'):
addon_info["custom_params"]["aom_id"] = custom.aom_id
if hasattr(custom, 'aom_instance_id'):
addon_info["custom_params"]["aom_instance_id"] = custom.aom_instance_id
if hasattr(custom, 'prom_instance_id'):
addon_info["custom_params"]["prom_instance_id"] = custom.prom_instance_id
if hasattr(custom, 'remote_write_url'):
addon_info["custom_params"]["remote_write_url"] = custom.remote_write_url
if hasattr(custom, 'remote_read_url'):
addon_info["custom_params"]["remote_read_url"] = custom.remote_read_url
if isinstance(custom, dict):
addon_info["custom_params"] = custom
if 'aom_id' in custom:
addon_info["aom_id"] = custom['aom_id']
if 'aom_instance_id' in custom:
addon_info["aom_instance_id"] = custom['aom_instance_id']
if 'prom_instance_id' in custom:
addon_info["aom_instance_id"] = custom['prom_instance_id']
if hasattr(response, 'metadata') and response.metadata:
metadata = response.metadata
addon_info["uid"] = getattr(metadata, 'uid', None)
addon_info["creation_timestamp"] = str(getattr(metadata, 'creation_timestamp', None))
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "get_cce_addon_detail",
"addon": addon_info
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, 'request_id', None)
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__
}
def list_cce_addons(region: str, cluster_id: str, ak: Optional[str] = None, sk: Optional[str] = None, project_id: Optional[str] = None) -> Dict[str, Any]:
"""List addons (plugins) in a CCE cluster
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with addon list
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not cluster_id:
return {
"success": False,
"error": "cluster_id is required"
}
if not SDK_AVAILABLE:
return {
"success": False,
"error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"
}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
request = ListAddonInstancesRequest()
request.cluster_id = cluster_id
response = client.list_addon_instances(request)
addons = []
if hasattr(response, 'items') and response.items:
for addon in response.items:
addon_info = {
"name": addon.metadata.name if hasattr(addon, 'metadata') and hasattr(addon.metadata, 'name') else None,
"uid": addon.metadata.uid if hasattr(addon, 'metadata') and hasattr(addon.metadata, 'uid') else None,
"template_name": addon.spec.template_name if hasattr(addon, 'spec') and hasattr(addon.spec, 'template_name') else None,
"version": addon.spec.version if hasattr(addon, 'spec') and hasattr(addon.spec, 'version') else None,
"status": addon.status.status if hasattr(addon, 'status') and hasattr(addon.status, 'status') else None,
"description": addon.spec.description if hasattr(addon, 'spec') and hasattr(addon.spec, 'description') else None,
"created_at": str(addon.metadata.creation_timestamp) if hasattr(addon, 'metadata') and hasattr(addon.metadata, 'creation_timestamp') else None,
}
addons.append(addon_info)
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "list_cce_addons",
"count": len(addons),
"addons": addons
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, 'request_id', None)
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__
}
def install_cce_addon(
region: str,
cluster_id: str,
addon_template_name: str,
addon_version: str,
values: Dict[str, Any],
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: Optional[str] = None
) -> Dict[str, Any]:
"""Install an addon to a CCE cluster
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID
addon_template_name: Addon template name (e.g., "coredns", "metrics-server")
addon_version: Addon version to install
values: Addon-specific configuration values
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with installation result
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not cluster_id:
return {
"success": False,
"error": "cluster_id is required"
}
if not addon_template_name:
return {
"success": False,
"error": "addon_template_name is required"
}
if not addon_version:
return {
"success": False,
"error": "addon_version is required"
}
if not SDK_AVAILABLE:
return {
"success": False,
"error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"
}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
metadata = AddonMetadata(
annotations={"addon.install/type": "install"}
)
spec = InstanceSpec(
cluster_id=cluster_id,
version=addon_version,
template_name=addon_template_name,
values=values
)
body = AddonInstance(
kind="Addon",
api_version="v3",
metadata=metadata,
spec=spec
)
request = CreateAddonInstanceRequest()
request.body = body
response = client.create_addon_instance(request)
addon_info = {}
if hasattr(response, 'metadata') and response.metadata:
addon_info["uid"] = getattr(response.metadata, 'uid', None)
addon_info["name"] = getattr(response.metadata, 'name', None)
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "install_cce_addon",
"addon_template_name": addon_template_name,
"addon_version": addon_version,
"addon": addon_info
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, 'request_id', None)
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__
}
def uninstall_cce_addon(
region: str,
cluster_id: str,
addon_id: str,
confirm: bool,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: Optional[str] = None
) -> Dict[str, Any]:
"""Uninstall an addon from a CCE cluster
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID
addon_id: Addon ID (name or UID)
confirm: Must be True to proceed with uninstallation
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with uninstallation result
"""
if not confirm:
return {
"success": False,
"error": "Uninstallation requires explicit confirmation. Set confirm=True to proceed."
}
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not cluster_id:
return {
"success": False,
"error": "cluster_id is required"
}
if not addon_id:
return {
"success": False,
"error": "addon_id is required"
}
if not SDK_AVAILABLE:
return {
"success": False,
"error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"
}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
request = DeleteAddonInstanceRequest()
request.cluster_id = cluster_id
request.addon_name = addon_id
client.delete_addon_instance(request)
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "uninstall_cce_addon",
"addon_id": addon_id,
"message": f"Addon {addon_id} uninstallation initiated"
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, 'request_id', None)
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__
}
def update_cce_addon(
region: str,
cluster_id: str,
addon_id: str,
addon_version: str,
values: Dict[str, Any],
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: Optional[str] = None
) -> Dict[str, Any]:
"""Update an addon in a CCE cluster
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID
addon_id: Addon ID (name or UID)
addon_version: New addon version
values: Addon-specific configuration values
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with update result
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not cluster_id:
return {
"success": False,
"error": "cluster_id is required"
}
if not addon_id:
return {
"success": False,
"error": "addon_id is required"
}
if not addon_version:
return {
"success": False,
"error": "addon_version is required"
}
if not SDK_AVAILABLE:
return {
"success": False,
"error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"
}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
metadata = AddonMetadata(
annotations={"addon.upgrade/type": "upgrade"}
)
spec = InstanceSpec(
cluster_id=cluster_id,
version=addon_version,
values=values
)
body = AddonInstance(
kind="Addon",
api_version="v3",
metadata=metadata,
spec=spec
)
request = UpdateAddonInstanceRequest()
request.addon_name = addon_id
request.body = body
response = client.update_addon_instance(request)
addon_info = {}
if hasattr(response, 'metadata') and response.metadata:
addon_info["uid"] = getattr(response.metadata, 'uid', None)
addon_info["name"] = getattr(response.metadata, 'name', None)
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "update_cce_addon",
"addon_id": addon_id,
"addon_version": addon_version,
"addon": addon_info
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, 'request_id', None)
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__
}"""CCE Cluster management functions."""
import hashlib
import hmac
import time as time_module
import urllib.parse
from urllib.parse import quote, unquote
from typing import Any, Dict, List, Optional
import requests
from huaweicloudsdkcce.v3 import (
CreateClusterRequest,
DeleteClusterRequest,
ListClustersRequest,
ShowClusterRequest,
HibernateClusterRequest,
AwakeClusterRequest,
Cluster,
ClusterMetadata,
ClusterSpec,
ContainerNetwork,
HostNetwork,
ServiceNetwork,
EniNetwork,
NetworkSubnet,
)
from huaweicloudsdkcce.v3.region.cce_region import CceRegion
from huaweicloudsdkcore.auth.credentials import BasicCredentials
from huaweicloudsdkcore.exceptions.exceptions import ClientRequestException
from huaweicloudsdkvpc.v2 import ShowSubnetRequest
from huaweicloudsdkvpc.v2.region.vpc_region import VpcRegion
from .common import (
get_credentials,
get_credentials_with_region,
create_cce_client,
SDK_AVAILABLE,
IMPORT_ERROR,
_register_cert_file,
_safe_delete_file,
)
def _resolve_neutron_subnet_id(region: str, vpc_subnet_id: str, ak: str, sk: str, project_id: Optional[str] = None) -> Optional[str]:
"""Resolve the Neutron subnet UUID from a VPC subnet UUID.
CCE Turbo (ENI) clusters require the Neutron subnet UUID in the
eniNetwork.subnets[].subnetID field, while HostNetwork.subnet uses
the VPC subnet UUID. This helper queries the VPC API to obtain the
neutron_subnet_id for a given VPC subnet.
Args:
region: Huawei Cloud region
vpc_subnet_id: VPC subnet UUID (e.g., b8a2c56a-...)
ak: Access Key
sk: Secret Key
project_id: Project ID (optional, will be resolved from region if not provided)
Returns:
Neutron subnet UUID string, or None if lookup fails
"""
try:
# Resolve project ID from region if not provided
access_key, secret_key, proj_id = get_credentials_with_region(region, ak, sk, project_id)
if not proj_id:
return None
from huaweicloudsdkvpc.v2 import VpcClient
creds = BasicCredentials(ak=access_key, sk=secret_key, project_id=proj_id)
vpc_client = VpcClient.new_builder() \
.with_credentials(creds) \
.with_endpoint(f'vpc.{region}.myhuaweicloud.com') \
.build()
req = ShowSubnetRequest(subnet_id=vpc_subnet_id)
resp = vpc_client.show_subnet(req)
if hasattr(resp, 'subnet') and hasattr(resp.subnet, 'neutron_subnet_id'):
return resp.subnet.neutron_subnet_id
except Exception:
pass
return None
def list_cce_clusters(region: str, ak: Optional[str] = None, sk: Optional[str] = None, project_id: Optional[str] = None, limit: int = 100, offset: int = 0) -> Dict[str, Any]:
"""List CCE clusters in the specified region with pagination"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not SDK_AVAILABLE:
return {
"success": False,
"error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"
}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
request = ListClustersRequest()
response = client.list_clusters(request)
clusters = []
if hasattr(response, 'items') and response.items:
for cluster in response.items:
cluster_info = {
"id": cluster.metadata.uid,
"name": cluster.metadata.name,
"status": cluster.status.phase if hasattr(cluster, 'status') and hasattr(cluster.status, 'phase') else 'Unknown',
"type": cluster.spec.type if hasattr(cluster, 'spec') and hasattr(cluster.spec, 'type') else 'Unknown',
"version": cluster.spec.version if hasattr(cluster, 'spec') and hasattr(cluster.spec, 'version') else 'Unknown',
"created_at": str(cluster.metadata.creation_timestamp) if hasattr(cluster, 'metadata') and hasattr(cluster.metadata, 'creation_timestamp') else None,
}
if hasattr(cluster, 'spec') and hasattr(cluster.spec, 'network'):
cluster_info["network"] = {
"vpc_id": getattr(cluster.spec.network, 'vpc_id', None),
"subnet_id": getattr(cluster.spec.network, 'subnet_id', None),
}
if hasattr(cluster, 'spec') and hasattr(cluster.spec, 'node'):
cluster_info["node_config"] = {
"flavor": getattr(cluster.spec.node, 'flavor', None),
"count": getattr(cluster.spec.node, 'initial_node_count', None),
}
clusters.append(cluster_info)
return {
"success": True,
"region": region,
"action": "list_cce_clusters",
"count": len(clusters),
"clusters": clusters
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, 'request_id', None)
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__
}
def get_cce_nodes(region: str, cluster_id: str, node_name: Optional[str] = None, ak: Optional[str] = None, sk: Optional[str] = None, project_id: Optional[str] = None) -> Dict[str, Any]:
"""Get detailed information about CCE cluster nodes
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID
node_name: Node name (optional, if not provided, returns all nodes)
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with node details
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not cluster_id:
return {
"success": False,
"error": "cluster_id is required"
}
if not SDK_AVAILABLE:
return {
"success": False,
"error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"
}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
request = ListNodesRequest()
request.cluster_id = cluster_id
response = client.list_nodes(request)
nodes = []
if hasattr(response, 'items') and response.items:
for node in response.items:
if node_name and node.metadata.name != node_name:
continue
node_info = {
"id": node.metadata.uid,
"name": node.metadata.name,
"status": node.status.phase if hasattr(node, 'status') and hasattr(node.status, 'phase') else 'Unknown',
"created_at": str(node.metadata.creation_timestamp) if hasattr(node, 'metadata') and hasattr(node.metadata, 'creation_timestamp') else None,
}
if hasattr(node, 'spec'):
node_info["flavor"] = getattr(node.spec, 'flavor', None)
node_info["server_id"] = getattr(node.status, 'server_id', None)
node_info["availability_zone"] = getattr(node.spec, 'az', None)
if hasattr(node, 'status') and hasattr(node.status, 'conditions'):
conditions = []
for cond in node.status.conditions:
conditions.append({
"type": cond.type,
"status": cond.status,
"reason": getattr(cond, 'reason', None),
"message": getattr(cond, 'message', None),
})
node_info["conditions"] = conditions
if hasattr(node, 'status'):
node_info["allocatable"] = getattr(node.status, 'allocatable', None)
node_info["capacity"] = getattr(node.status, 'capacity', None)
nodes.append(node_info)
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "get_cce_nodes",
"node_name": node_name,
"count": len(nodes),
"nodes": nodes
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, 'request_id', None)
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__
}
def get_cce_kubeconfig(region: str, cluster_id: str, ak: Optional[str] = None, sk: Optional[str] = None, project_id: Optional[str] = None, duration: int = 30) -> Dict[str, Any]:
"""Get kubeconfig for a CCE cluster
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
duration: Certificate validity duration in days (default: 30)
Returns:
Dictionary with kubeconfig content
"""
access_key, secret_key, proj_id = get_credentials_with_region(region, ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not proj_id:
return {
"success": False,
"error": "Project ID not found. Please provide project_id parameter."
}
if not cluster_id:
return {
"success": False,
"error": "cluster_id is required"
}
if not SDK_AVAILABLE:
return {
"success": False,
"error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"
}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
cert_duration = ClusterCertDuration(duration=duration)
request = CreateKubernetesClusterCertRequest(cluster_id=cluster_id)
request.body = cert_duration
response = client.create_kubernetes_cluster_cert(request)
result = {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "get_cce_kubeconfig",
"duration_days": duration,
}
if hasattr(response, 'to_dict'):
resp_dict = response.to_dict()
result["kubeconfig"] = resp_dict
if 'clusters' in resp_dict:
result["cluster_endpoints"] = []
for cluster in resp_dict['clusters']:
endpoint_info = {
"name": cluster.get('name'),
"server": cluster.get('cluster', {}).get('server')
}
result["cluster_endpoints"].append(endpoint_info)
if 'current_context' in resp_dict:
result["current_context"] = resp_dict['current_context']
import yaml
result["kubeconfig_yaml"] = yaml.dump(resp_dict, default_flow_style=False, allow_unicode=True)
return result
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__
}
def delete_cce_cluster(region: str, cluster_id: str, confirm: bool = False, delete_evs: bool = False, delete_net: bool = False, delete_obs: bool = False, ak: Optional[str] = None, sk: Optional[str] = None, project_id: Optional[str] = None) -> Dict[str, Any]:
"""Delete a CCE cluster
IMPORTANT: This operation will delete the cluster and all its resources.
User confirmation is required before deletion.
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID to delete
confirm: Must be set to True to confirm deletion (required)
delete_evs: Whether to delete associated EVS volumes (default: False)
delete_net: Whether to delete associated network resources (default: False)
delete_obs: Whether to delete associated OBS buckets (default: False)
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with deletion result
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not cluster_id:
return {
"success": False,
"error": "cluster_id is required"
}
if not confirm:
return {
"success": False,
"error": "Deletion not confirmed. To delete the cluster, please set confirm=true parameter.",
"warning": "This operation will delete the cluster and all its resources (nodes, workloads, etc.). Are you sure you want to delete this cluster?",
"hint": "Add confirm=true parameter to confirm deletion. Example: delete_cce_cluster region=cn-north-4 cluster_id=xxx confirm=true"
}
if not SDK_AVAILABLE:
return {
"success": False,
"error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"
}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
request = DeleteClusterRequest()
request.cluster_id = cluster_id
request.delete_evs = delete_evs
request.delete_net = delete_net
request.delete_obs = delete_obs
response = client.delete_cluster(request)
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "delete_cce_cluster",
"message": f"Cluster deletion request submitted successfully",
"delete_evs": delete_evs,
"delete_net": delete_net,
"delete_obs": delete_obs,
"response": response.to_dict() if hasattr(response, 'to_dict') else str(response)
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, 'request_id', None)
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__
}
def hibernate_cce_cluster(
region: str,
cluster_id: str,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: Optional[str] = None,
confirm: bool = False,
) -> Dict[str, Any]:
"""Hibernate a CCE cluster (pause billing + workloads)
Puts the cluster into hibernated state. Billing for control plane is paused.
Workloads are stopped. Use awake_cce_cluster to resume.
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
confirm: Must be True to confirm the operation
Returns:
Dictionary with result
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not cluster_id:
return {"success": False, "error": "cluster_id is required"}
if not SDK_AVAILABLE:
return {"success": False, "error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"}
if not confirm:
return {
"success": False,
"requires_confirmation": True,
"operation": "hibernate_cce_cluster",
"cluster_id": cluster_id,
"error": f"Hibernate will pause cluster {cluster_id} and stop all workloads. Billing for control plane is paused.",
"hint": f"Add confirm=true to confirm. Example: huawei_hibernate_cce_cluster region=cn-north-4 cluster_id=xxx confirm=true"
}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
request = HibernateClusterRequest()
request.cluster_id = cluster_id
response = client.hibernate_cluster(request)
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "hibernate_cce_cluster",
"message": "Cluster hibernation request submitted successfully",
"status_code": response.status_code if hasattr(response, "status_code") else None,
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, "request_id", None),
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__,
}
def awake_cce_cluster(
region: str,
cluster_id: str,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: Optional[str] = None,
confirm: bool = False,
) -> Dict[str, Any]:
"""Awake a hibernated CCE cluster (resume billing + workloads)
Wakes up a previously hibernated cluster. Billing for control plane resumes
and workloads will be restarted.
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
confirm: Must be True to confirm the operation
Returns:
Dictionary with result
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not cluster_id:
return {"success": False, "error": "cluster_id is required"}
if not SDK_AVAILABLE:
return {"success": False, "error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"}
if not confirm:
return {
"success": False,
"requires_confirmation": True,
"operation": "awake_cce_cluster",
"cluster_id": cluster_id,
"error": f"Awake will resume cluster {cluster_id}. Control plane billing resumes and workloads restart.",
"hint": f"Add confirm=true to confirm. Example: huawei_awake_cce_cluster region=cn-north-4 cluster_id=xxx confirm=true"
}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
request = AwakeClusterRequest()
request.cluster_id = cluster_id
response = client.awake_cluster(request)
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "awake_cce_cluster",
"message": "Cluster awake request submitted successfully",
"status_code": response.status_code if hasattr(response, "status_code") else None,
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, "request_id", None),
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__,
}
def bind_cce_cluster_eip(
region: str,
cluster_id: str,
eip_id: str,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: Optional[str] = None,
) -> Dict[str, Any]:
"""Bind an EIP to a CCE cluster master node for public API access
Associates an existing Elastic IP with the cluster's control plane,
enabling public access to the Kubernetes API server.
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID
eip_id: EIP resource ID to bind (use huawei_list_eip to find available EIPs)
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with result including the public endpoint URL
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not cluster_id:
return {"success": False, "error": "cluster_id is required"}
if not eip_id:
return {"success": False, "error": "eip_id is required"}
if not SDK_AVAILABLE:
return {"success": False, "error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
spec_spec = MasterEIPRequestSpecSpec(id=eip_id)
spec = MasterEIPRequestSpec(action="bind", spec=spec_spec)
body = MasterEIPRequest(spec=spec)
request = UpdateClusterEipRequest(cluster_id=cluster_id, body=body)
client.update_cluster_eip(request)
resp = client.show_cluster(ShowClusterRequest(cluster_id=cluster_id))
public_url = None
if hasattr(resp, 'status') and hasattr(resp.status, 'endpoints'):
for ep in resp.status.endpoints:
if ep.type == "External":
public_url = ep.url
break
result = {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "bind_cce_cluster_eip",
"eip_id": eip_id,
"message": "EIP bound to cluster master successfully",
}
if public_url:
result["public_endpoint"] = public_url
return result
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, "request_id", None),
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__,
}
def unbind_cce_cluster_eip(
region: str,
cluster_id: str,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: Optional[str] = None,
) -> Dict[str, Any]:
"""Unbind the EIP from a CCE cluster master node
Removes the Elastic IP association from the cluster's control plane,
disabling public access to the Kubernetes API server.
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with result
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not cluster_id:
return {"success": False, "error": "cluster_id is required"}
if not SDK_AVAILABLE:
return {"success": False, "error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
spec = MasterEIPRequestSpec(action="unbind")
body = MasterEIPRequest(spec=spec)
request = UpdateClusterEipRequest(cluster_id=cluster_id, body=body)
client.update_cluster_eip(request)
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "unbind_cce_cluster_eip",
"message": "EIP unbound from cluster master successfully",
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, "request_id", None),
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__,
}
def create_cce_cluster(
region: str,
cluster_name: str,
vpc_id: str,
subnet_id: str,
cluster_version: Optional[str] = None,
cluster_type: str = "VirtualMachine",
container_network_type: str = "overlay_l2",
container_network_cidr: Optional[str] = None,
service_network_cidr: Optional[str] = None,
flavor_id: Optional[str] = None,
description: Optional[str] = None,
eni_subnet_id: Optional[str] = None,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: Optional[str] = None,
) -> Dict[str, Any]:
"""Create a new CCE cluster
Creates a Cloud Container Engine cluster with specified configuration.
If cluster_version is not specified, CCE will use the latest supported version.
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_name: Name of the cluster to create
vpc_id: VPC ID where the cluster will be created
subnet_id: Subnet ID for the cluster
cluster_version: Kubernetes version (optional, defaults to latest if not specified)
cluster_type: Cluster type (default: "VirtualMachine", use "VirtualMachine" + eni network for Turbo)
container_network_type: Container network type (default: "overlay_l2", use "eni" for Turbo clusters)
container_network_cidr: Container network CIDR (optional, e.g., "172.16.0.0/16")
service_network_cidr: Service network CIDR (optional, e.g., "10.247.0.0/16")
flavor_id: Cluster flavor ID (optional, determines control plane specs)
description: Cluster description (optional)
eni_subnet_id: ENI subnet ID for Turbo clusters (required when container_network_type="eni")
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with cluster creation result
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters."
}
if not cluster_name:
return {"success": False, "error": "cluster_name is required"}
if not vpc_id:
return {"success": False, "error": "vpc_id is required"}
if not subnet_id:
return {"success": False, "error": "subnet_id is required"}
if not SDK_AVAILABLE:
return {"success": False, "error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"}
try:
client = create_cce_client(region, access_key, secret_key, proj_id)
cluster_metadata = ClusterMetadata(name=cluster_name)
if description:
cluster_metadata.annotations = {"description": description}
host_network = HostNetwork(vpc=vpc_id, subnet=subnet_id)
container_network = ContainerNetwork(mode=container_network_type)
if container_network_cidr:
container_network.cidr = container_network_cidr
cluster_spec = ClusterSpec(
type=cluster_type,
host_network=host_network,
container_network=container_network,
)
if cluster_version:
cluster_spec.version = cluster_version
if flavor_id:
cluster_spec.flavor_id = flavor_id
# Set eni_network for Turbo clusters (container_network_type="eni")
# IMPORTANT: CCE Turbo (ENI) clusters require the **Neutron subnet UUID**
# in eniNetwork.subnets[].subnetID, NOT the VPC subnet UUID.
# The HostNetwork.subnet field uses the VPC subnet UUID, but the ENI
# subnet validation checks against Neutron subnet IDs. If the VPC
# subnet ID is provided, we automatically resolve its Neutron UUID.
if container_network_type == "eni":
eni_net_subnet_id = eni_subnet_id or subnet_id
# If eni_net_subnet_id looks like a VPC subnet UUID (not a Neutron one),
# resolve the Neutron subnet UUID via the VPC API.
neutron_id = _resolve_neutron_subnet_id(
region, eni_net_subnet_id, access_key, secret_key, proj_id
)
if neutron_id:
cluster_spec.eni_network = EniNetwork(
subnets=[NetworkSubnet(subnet_id=neutron_id)]
)
else:
# Fallback: use the provided ID directly (may be a Neutron UUID already)
cluster_spec.eni_network = EniNetwork(
subnets=[NetworkSubnet(subnet_id=eni_net_subnet_id)]
)
if service_network_cidr:
cluster_spec.service_network = ServiceNetwork(i_pv4_cidr=service_network_cidr)
cluster_body = Cluster(
kind="Cluster",
api_version="v3",
metadata=cluster_metadata,
spec=cluster_spec,
)
request = CreateClusterRequest(body=cluster_body)
response = client.create_cluster(request)
cluster_id = None
cluster_name_result = cluster_name
actual_version = cluster_version or "latest (API default)"
if hasattr(response, 'metadata'):
cluster_id = getattr(response.metadata, 'uid', None)
cluster_name_result = getattr(response.metadata, 'name', cluster_name)
if hasattr(response, 'spec') and hasattr(response.spec, 'version'):
actual_version = response.spec.version
return {
"success": True,
"region": region,
"action": "create_cce_cluster",
"cluster_id": cluster_id,
"cluster_name": cluster_name_result,
"cluster_version": actual_version,
"cluster_type": cluster_type,
"vpc_id": vpc_id,
"subnet_id": subnet_id,
"container_network_type": container_network_type,
"container_network_cidr": container_network_cidr,
"service_network_cidr": service_network_cidr,
"flavor_id": flavor_id,
"message": "Cluster creation request submitted successfully",
"response": response.to_dict() if hasattr(response, 'to_dict') else str(response),
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, "request_id", None),
}
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__,
}"""CCE Node management functions."""
from typing import Any, Dict, List, Optional
from .common import (
get_credentials,
create_cce_client,
SDK_AVAILABLE,
IMPORT_ERROR,
K8S_AVAILABLE,
K8S_IMPORT_ERROR,
)
from huaweicloudsdkcore.exceptions.exceptions import ClientRequestException
def list_cce_nodes(
region: str,
cluster_id: str,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: Optional[str] = None,
limit: int = 100,
offset: int = 0,
) -> Dict[str, Any]:
"""List nodes in a CCE cluster with pagination"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters.",
}
if not cluster_id:
return {"success": False, "error": "cluster_id is required"}
if not SDK_AVAILABLE:
return {"success": False, "error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"}
try:
from huaweicloudsdkcce.v3 import ListNodesRequest
client = create_cce_client(region, access_key, secret_key, proj_id)
request = ListNodesRequest()
request.cluster_id = cluster_id
response = client.list_nodes(request)
nodes = []
if hasattr(response, "items") and response.items:
for node in response.items:
node_info = {
"id": node.metadata.uid,
"name": node.metadata.name,
"status": node.status.phase
if hasattr(node, "status") and hasattr(node.status, "phase")
else "Unknown",
"created_at": str(node.metadata.creation_timestamp)
if hasattr(node, "metadata") and hasattr(node.metadata, "creation_timestamp")
else None,
"labels": dict(node.metadata.labels)
if hasattr(node.metadata, "labels") and node.metadata.labels
else {},
}
if hasattr(node, "spec"):
node_info["flavor"] = getattr(node.spec, "flavor", None)
node_info["server_id"] = getattr(node.status, "server_id", None)
node_info["availability_zone"] = getattr(node.spec, "az", None)
if hasattr(node, "status") and hasattr(node.status, "conditions"):
conditions = []
for cond in node.status.conditions:
conditions.append({
"type": cond.type,
"status": cond.status,
"reason": getattr(cond, "reason", None),
})
node_info["conditions"] = conditions
nodes.append(node_info)
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "list_cce_nodes",
"count": len(nodes),
"nodes": nodes,
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, "request_id", None),
}
except Exception as e:
return {"success": False, "error": str(e), "error_type": type(e).__name__}
def delete_cce_node(
region: str,
cluster_id: str,
node_id: str,
confirm: bool = False,
scale_down: bool = True,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: Optional[str] = None,
) -> Dict[str, Any]:
"""Delete a node from CCE cluster
IMPORTANT: This operation will delete the node and all its pods.
User confirmation is required before deletion.
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID
node_id: Node ID to delete
confirm: Must be set to True to confirm deletion (required)
scale_down: Whether to scale down pods before deleting (default: True)
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with deletion result
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters.",
}
if not cluster_id:
return {"success": False, "error": "cluster_id is required"}
if not node_id:
return {"success": False, "error": "node_id is required"}
if not confirm:
return {
"success": False,
"error": "Deletion not confirmed. To delete the node, please set confirm=true parameter.",
"warning": f"This operation will delete the node '{node_id}' from cluster '{cluster_id}'. All pods on this node will be terminated. Are you sure?",
"hint": "Add confirm=true parameter to confirm deletion. Example: delete_cce_node region=cn-north-4 cluster_id=xxx node_id=yyy confirm=true",
}
if not SDK_AVAILABLE:
return {"success": False, "error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"}
try:
from huaweicloudsdkcce.v3 import DeleteNodeRequest
client = create_cce_client(region, access_key, secret_key, proj_id)
request = DeleteNodeRequest()
request.cluster_id = cluster_id
request.node_id = node_id
request.nodepool_scale_down = scale_down
response = client.delete_node(request)
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"node_id": node_id,
"action": "delete_cce_node",
"message": "Node deletion request submitted successfully",
"scale_down": scale_down,
"response": response.to_dict() if hasattr(response, "to_dict") else str(response),
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, "request_id", None),
}
except Exception as e:
return {"success": False, "error": str(e), "error_type": type(e).__name__}
def _node_operation(
region: str,
cluster_id: str,
node_name: str,
operation: str,
confirm: bool = False,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: str = None,
) -> Dict[str, Any]:
"""Internal helper for CCE node operations (cordon/uncordon/drain/status)
Args:
region: Huawei Cloud region
cluster_id: CCE cluster ID
node_name: Node name or IP address
operation: One of 'cordon', 'uncordon', 'drain', 'status'
confirm: Required for write operations (cordon/uncordon/drain)
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with operation result
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {"success": False, "error": "Credentials not provided."}
if not K8S_AVAILABLE:
return {"success": False, "error": f"Kubernetes SDK not installed: {K8S_IMPORT_ERROR}"}
ca_cert_file = client_cert_file = client_key_file = None
try:
cce_client = create_cce_client(region, access_key, secret_key, proj_id)
from huaweicloudsdkcce.v3 import CreateKubernetesClusterCertRequest, ClusterCertDuration
cert_req = CreateKubernetesClusterCertRequest(cluster_id=cluster_id)
cert_req.body = ClusterCertDuration(duration=1)
kubeconfig_data = cce_client.create_kubernetes_cluster_cert(cert_req).to_dict()
import tempfile
import os
import yaml
import base64
import kubernetes as k8s
from kubernetes.client import Configuration, CoreV1Api
raw_kc = kubeconfig_data.get("kubeconfig") or kubeconfig_data.get("content", "") or kubeconfig_data
kc = yaml.safe_load(raw_kc) if isinstance(raw_kc, str) and raw_kc else raw_kc
current_ctx = kc.get("current_context", "")
ctx_entry = next((c for c in kc.get("contexts", []) if c["name"] == current_ctx), None)
if not ctx_entry:
return {"success": False, "error": f"Context '{current_ctx}' not found in kubeconfig"}
cluster_name = ctx_entry["context"]["cluster"]
user_name = ctx_entry["context"]["user"]
cluster_entry = next((c for c in kc.get("clusters", []) if c["name"] == cluster_name), None)
if not cluster_entry:
return {"success": False, "error": f"Cluster '{cluster_name}' not found in kubeconfig"}
cluster_cfg = cluster_entry["cluster"]
user_entry = next((u for u in kc.get("users", []) if u["name"] == user_name), None)
if not user_entry:
return {"success": False, "error": f"User '{user_name}' not found in kubeconfig"}
user_cfg = user_entry["user"]
server = cluster_cfg.get("server", "")
ca_data = cluster_cfg.get("certificate_authority_data")
if ca_data:
with tempfile.NamedTemporaryFile(mode="w", suffix=".crt", delete=False) as f:
f.write(base64.b64decode(ca_data).decode())
ca_cert_file = f.name
cert_data = user_cfg.get("client_certificate_data")
key_data = user_cfg.get("client_key_data")
if cert_data and key_data:
with tempfile.NamedTemporaryFile(mode="w", suffix=".crt", delete=False) as f:
f.write(base64.b64decode(cert_data).decode())
client_cert_file = f.name
with tempfile.NamedTemporaryFile(mode="w", suffix=".key", delete=False) as f:
f.write(base64.b64decode(key_data).decode())
client_key_file = f.name
skip_tls = cluster_cfg.get("insecure_skip_tls_verify", False)
config = Configuration()
config.host = server
config.verify_ssl = not skip_tls
if ca_cert_file:
config.ssl_ca_cert = ca_cert_file
if client_cert_file:
config.cert_file = client_cert_file
if client_key_file:
config.key_file = client_key_file
api_client = k8s.client.ApiClient(config)
core_v1 = CoreV1Api(api_client)
if operation == "status":
node = core_v1.read_node(node_name)
conditions = {c.type: c.status for c in node.status.conditions}
node_labels = dict(node.metadata.labels) if node.metadata.labels else {}
return {
"success": True,
"operation": "status",
"node": node_name,
"schedulable": node.spec.unschedulable is None,
"ready": conditions.get("Ready") == "True",
"conditions": conditions,
"os_version": node_labels.get("node.kubernetes.io/os_version", ""),
"kernel_version": node_labels.get("node.kubernetes.io/kernel_version", ""),
}
elif operation == "cordon":
if not confirm:
return {
"success": False,
"requires_confirmation": True,
"operation": "cordon",
"node": node_name,
"error": f"Cordon will mark node {node_name} as unschedulable.",
"hint": f"Add confirm=true to confirm. Example: cce_node_cordon region=cn-north-4 cluster_id=xxx node_name=192.168.x.x confirm=true",
}
core_v1.patch_node(node_name, {"spec": {"unschedulable": True}})
return {
"success": True,
"operation": "cordon",
"node": node_name,
"message": "Node marked as unschedulable",
}
elif operation == "uncordon":
if not confirm:
return {
"success": False,
"requires_confirmation": True,
"operation": "uncordon",
"node": node_name,
"error": f"Uncordon will mark node {node_name} as schedulable. New pods may be immediately assigned.",
"hint": f"Add confirm=true to confirm. Example: cce_node_uncordon region=cn-north-4 cluster_id=xxx node_name=192.168.x.x confirm=true",
}
core_v1.patch_node(node_name, {"spec": {"unschedulable": None}})
return {
"success": True,
"operation": "uncordon",
"node": node_name,
"message": "Node marked as schedulable",
}
elif operation == "drain":
if not confirm:
pods_preview = core_v1.list_pod_for_all_namespaces(
field_selector=f"spec.nodeName={node_name}"
).items
affected = [
f"{p.metadata.namespace}/{p.metadata.name}"
for p in pods_preview
if p.metadata.namespace not in ("kube-system", "hss", "monitoring")
]
return {
"success": False,
"requires_confirmation": True,
"operation": "drain",
"node": node_name,
"affected_pods": affected,
"error": f"Drain will delete {len(affected)} non-system pods on node {node_name}.",
"hint": f"Add confirm=true to confirm. Example: cce_node_drain region=cn-north-4 cluster_id=xxx node_name=192.168.x.x confirm=true",
}
skip_ns = set(["kube-system", "hss", "monitoring"])
grace = 30
pods = core_v1.list_pod_for_all_namespaces(
field_selector=f"spec.nodeName={node_name}"
).items
deleted, skipped = [], []
for p in pods:
ns, pname = p.metadata.namespace, p.metadata.name
if ns in skip_ns:
skipped.append(f"{ns}/{pname}")
continue
try:
body = k8s.client.V1DeleteOptions(grace_period_seconds=grace)
core_v1.delete_namespaced_pod(pname, ns, body=body)
deleted.append(f"{ns}/{pname}")
except Exception as e:
skipped.append(f"{ns}/{pname} ({e})"[:100])
return {
"success": True,
"operation": "drain",
"node": node_name,
"deleted": deleted,
"skipped": skipped,
}
else:
return {"success": False, "error": f"Unknown operation: {operation}"}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, "request_id", None),
}
except Exception as e:
return {"success": False, "error": str(e), "error_type": type(e).__name__}
finally:
for f in [ca_cert_file, client_cert_file, client_key_file]:
if f and os.path.exists(f):
try:
os.unlink(f)
except Exception:
pass
def cce_node_cordon(
region: str,
cluster_id: str,
node_name: str,
confirm: bool = False,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: str = None,
) -> Dict[str, Any]:
"""Mark CCE node as unschedulable (cordon)
Args:
region: Huawei Cloud region
cluster_id: CCE cluster ID
node_name: Node name or IP address
confirm: Must be True to confirm the operation
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with operation result
"""
return _node_operation(
region,
cluster_id,
node_name,
"cordon",
confirm=confirm,
ak=ak,
sk=sk,
project_id=project_id,
)
def cce_node_uncordon(
region: str,
cluster_id: str,
node_name: str,
confirm: bool = False,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: str = None,
) -> Dict[str, Any]:
"""Mark CCE node as schedulable (uncordon)
Args:
region: Huawei Cloud region
cluster_id: CCE cluster ID
node_name: Node name or IP address
confirm: Must be True to confirm the operation
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with operation result
"""
return _node_operation(
region,
cluster_id,
node_name,
"uncordon",
confirm=confirm,
ak=ak,
sk=sk,
project_id=project_id,
)
def cce_node_drain(
region: str,
cluster_id: str,
node_name: str,
confirm: bool = False,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: str = None,
) -> Dict[str, Any]:
"""Drain CCE node (evict all non-system pods)
Args:
region: Huawei Cloud region
cluster_id: CCE cluster ID
node_name: Node name or IP address
confirm: Must be True to confirm the operation
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with operation result
"""
return _node_operation(
region,
cluster_id,
node_name,
"drain",
confirm=confirm,
ak=ak,
sk=sk,
project_id=project_id,
)
def cce_node_status(
region: str,
cluster_id: str,
node_name: str,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: str = None,
) -> Dict[str, Any]:
"""Query CCE node schedulability status
Args:
region: Huawei Cloud region
cluster_id: CCE cluster ID
node_name: Node name or IP address
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with node status (schedulable, ready, conditions)
"""
return _node_operation(
region,
cluster_id,
node_name,
"status",
confirm=True,
ak=ak,
sk=sk,
project_id=project_id,
)
def create_cce_node(
region: str,
cluster_id: str,
flavor: str,
availability_zone: str,
root_volume_size: int = 40,
root_volume_type: str = "SSD",
node_count: int = 1,
os_type: str = "EulerOS 2.9",
ssh_key: Optional[str] = None,
password: Optional[str] = None,
data_volumes: Optional[List[Dict[str, Any]]] = None,
subnet_id: Optional[str] = None,
ak: Optional[str] = None,
sk: Optional[str] = None,
project_id: Optional[str] = None,
) -> Dict[str, Any]:
"""Create nodes in a CCE cluster
Args:
region: Huawei Cloud region (e.g., cn-north-4)
cluster_id: CCE cluster ID
flavor: Node flavor (e.g., c6.large.2)
availability_zone: Availability zone (e.g., cn-north-4a)
root_volume_size: Root volume size in GB (default: 40)
root_volume_type: Root volume type (default: SSD)
node_count: Number of nodes to create (default: 1)
os_type: Operating system type (default: EulerOS 2.9)
ssh_key: SSH key pair name for login
password: Password for login (used if ssh_key not provided)
data_volumes: List of data volume configs [{"size": 100, "type": "SSD"}]
subnet_id: Subnet ID for the node
ak: Access Key ID (optional)
sk: Secret Access Key (optional)
project_id: Project ID (optional)
Returns:
Dictionary with creation result
"""
access_key, secret_key, proj_id = get_credentials(ak, sk, project_id)
if not access_key or not secret_key:
return {
"success": False,
"error": "Credentials not provided. Set HUAWEI_AK and HUAWEI_SK environment variables or pass as parameters.",
}
if not cluster_id:
return {"success": False, "error": "cluster_id is required"}
if not flavor:
return {"success": False, "error": "flavor is required"}
if not availability_zone:
return {"success": False, "error": "availability_zone is required"}
if not SDK_AVAILABLE:
return {"success": False, "error": f"Huawei Cloud SDK not installed: {IMPORT_ERROR}"}
try:
from huaweicloudsdkcce.v3 import (
CreateNodeRequest,
CreateNodeRequestBody,
NodeMetadata,
NodeSpec,
Volume,
Login,
UserPassword,
NodeNicSpec,
)
client = create_cce_client(region, access_key, secret_key, proj_id)
root_volume = Volume(size=root_volume_size, volumetype=root_volume_type)
data_volume_list = []
if data_volumes:
for vol in data_volumes:
data_volume_list.append(
Volume(
size=vol.get("size", 100),
volumetype=vol.get("type", "SSD"),
)
)
login = None
if ssh_key:
login = Login(sshkey=ssh_key)
elif password:
login = Login(userPassword=UserPassword(username="root", password=password))
node_spec = NodeSpec(
flavor=flavor,
az=availability_zone,
os=os_type,
login=login,
rootVolume=root_volume,
dataVolumes=data_volume_list if data_volume_list else None,
nodeNicSpec=NodeNicSpec(subnetId=subnet_id) if subnet_id else None,
)
node_metadata = NodeMetadata(name=f"node-{cluster_id[:8]}")
request_body = CreateNodeRequestBody(
metadata=node_metadata,
spec=node_spec,
count=node_count,
)
request = CreateNodeRequest(cluster_id=cluster_id)
request.body = request_body
response = client.create_node(request)
return {
"success": True,
"region": region,
"cluster_id": cluster_id,
"action": "create_cce_node",
"node_count": node_count,
"flavor": flavor,
"availability_zone": availability_zone,
"message": f"Node creation request submitted for {node_count} node(s)",
"response": response.to_dict() if hasattr(response, "to_dict") else str(response),
}
except ClientRequestException as e:
return {
"success": False,
"error": f"{e.error_code} - {e.error_msg}",
"request_id": getattr(e, "request_id", None),
}
except Exception as e:
return {"success": False, "error": str(e), "error_type": type(e).__name__}