Skip to content

Commit a414125

Browse files
authored
Merge pull request kubernetes#3671 from wangshiqi308/pr_implements_templatenodeinfo
implemented TemplateNodeInfo function
2 parents ce2c9e2 + e0f2a56 commit a414125

File tree

2 files changed

+161
-1
lines changed

2 files changed

+161
-1
lines changed

cluster-autoscaler/cloudprovider/huaweicloud/huaweicloud_auto_scaling_group.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,17 @@ func (asg *AutoScalingGroup) Nodes() ([]cloudprovider.Instance, error) {
158158
// capacity and allocatable information as well as all pods that are started on
159159
// the node by default, using manifest (most likely only kube-proxy). Implementation optional.
160160
func (asg *AutoScalingGroup) TemplateNodeInfo() (*schedulerframework.NodeInfo, error) {
161-
return nil, cloudprovider.ErrNotImplemented
161+
template, err := asg.cloudServiceManager.getAsgTemplate(asg.groupID)
162+
if err != nil {
163+
return nil, err
164+
}
165+
node, err := asg.cloudServiceManager.buildNodeFromTemplate(asg.groupName, template)
166+
if err != nil {
167+
return nil, err
168+
}
169+
nodeInfo := schedulerframework.NewNodeInfo(cloudprovider.BuildKubeProxy(asg.groupName))
170+
nodeInfo.SetNode(node)
171+
return nodeInfo, nil
162172
}
163173

164174
// Exist checks if the node group really exists on the cloud provider side. Allows to tell the

cluster-autoscaler/cloudprovider/huaweicloud/huaweicloud_service_manager.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,24 @@ package huaweicloud
1818

1919
import (
2020
"fmt"
21+
"math/rand"
22+
"strconv"
23+
"strings"
2124
"time"
2225

26+
apiv1 "k8s.io/api/core/v1"
27+
"k8s.io/apimachinery/pkg/api/resource"
28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2329
"k8s.io/apimachinery/pkg/util/wait"
2430
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
2531
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/huaweicloud/huaweicloud-sdk-go-v3/core/sdktime"
2632
huaweicloudsdkas "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/huaweicloud/huaweicloud-sdk-go-v3/services/as/v1"
2733
huaweicloudsdkasmodel "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/huaweicloud/huaweicloud-sdk-go-v3/services/as/v1/model"
2834
huaweicloudsdkecs "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/huaweicloud/huaweicloud-sdk-go-v3/services/ecs/v2"
2935
huaweicloudsdkecsmodel "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/huaweicloud/huaweicloud-sdk-go-v3/services/ecs/v2/model"
36+
"k8s.io/autoscaler/cluster-autoscaler/utils/gpu"
3037
"k8s.io/klog/v2"
38+
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
3139
)
3240

3341
// ElasticCloudServerService represents the elastic cloud server interfaces.
@@ -53,6 +61,12 @@ type AutoScalingService interface {
5361
// The delta should be non-negative.
5462
// IncreaseSizeInstance wait until instance number is updated.
5563
IncreaseSizeInstance(groupID string, delta int) error
64+
65+
// Get default auto scaling group template
66+
getAsgTemplate(groupID string) (*asgTemplate, error)
67+
68+
// buildNodeFromTemplate returns template from instance flavor
69+
buildNodeFromTemplate(asgName string, template *asgTemplate) (*apiv1.Node, error)
5670
}
5771

5872
// CloudServiceManager represents the cloud service interfaces.
@@ -71,6 +85,16 @@ type cloudServiceManager struct {
7185
getASClientFunc func() *huaweicloudsdkas.AsClient
7286
}
7387

88+
type asgTemplate struct {
89+
name string
90+
vcpu int64
91+
ram int64
92+
gpu int64
93+
region string
94+
zone string
95+
tags map[string]string
96+
}
97+
7498
func newCloudServiceManager(cloudConfig *CloudConfig) *cloudServiceManager {
7599
return &cloudServiceManager{
76100
cloudConfig: cloudConfig,
@@ -389,3 +413,129 @@ func (csm *cloudServiceManager) deleteScalingPolicy(opts *huaweicloudsdkasmodel.
389413
klog.V(1).Infof("delete scaling policy succeed. policy id: %s", opts.ScalingPolicyId)
390414
return nil
391415
}
416+
417+
func (csm *cloudServiceManager) getScalingGroupByID(groupID string) (*huaweicloudsdkasmodel.ScalingGroups, error) {
418+
asClient := csm.getASClientFunc()
419+
opts := &huaweicloudsdkasmodel.ShowScalingGroupRequest{
420+
ScalingGroupId: groupID,
421+
}
422+
response, err := asClient.ShowScalingGroup(opts)
423+
if err != nil {
424+
klog.Errorf("failed to show scaling group info. group: %s, error: %v", groupID, err)
425+
return nil, err
426+
}
427+
if response == nil || response.ScalingGroup == nil {
428+
return nil, fmt.Errorf("no scaling group found: %s", groupID)
429+
}
430+
431+
return response.ScalingGroup, nil
432+
}
433+
434+
func (csm *cloudServiceManager) getScalingGroupConfigByID(groupID, configID string) (*huaweicloudsdkasmodel.ScalingConfiguration, error) {
435+
asClient := csm.getASClientFunc()
436+
opts := &huaweicloudsdkasmodel.ShowScalingConfigRequest{
437+
ScalingConfigurationId: configID,
438+
}
439+
response, err := asClient.ShowScalingConfig(opts)
440+
if err != nil {
441+
klog.Errorf("failed to show scaling group config. config id: %s, error: %v", configID, err)
442+
return nil, err
443+
}
444+
if response == nil || response.ScalingConfiguration == nil {
445+
return nil, fmt.Errorf("no scaling configuration found, groupID: %s, configID: %s", groupID, configID)
446+
}
447+
return response.ScalingConfiguration, nil
448+
}
449+
450+
func (csm *cloudServiceManager) listFlavors(az string) (*[]huaweicloudsdkecsmodel.Flavor, error) {
451+
ecsClient := csm.getECSClientFunc()
452+
opts := &huaweicloudsdkecsmodel.ListFlavorsRequest{
453+
AvailabilityZone: &az,
454+
}
455+
response, err := ecsClient.ListFlavors(opts)
456+
if err != nil {
457+
klog.Errorf("failed to list flavors. availability zone: %s", az)
458+
return nil, err
459+
}
460+
461+
return response.Flavors, nil
462+
}
463+
464+
func (csm *cloudServiceManager) getAsgTemplate(groupID string) (*asgTemplate, error) {
465+
sg, err := csm.getScalingGroupByID(groupID)
466+
if err != nil {
467+
klog.Errorf("failed to get ASG by id:%s,because of %s", groupID, err.Error())
468+
return nil, err
469+
}
470+
471+
configuration, err := csm.getScalingGroupConfigByID(groupID, *sg.ScalingConfigurationId)
472+
473+
for _, az := range *sg.AvailableZones {
474+
flavors, err := csm.listFlavors(az)
475+
if err != nil {
476+
klog.Errorf("failed to list flavors, available zone is: %s, error: %v", az, err)
477+
return nil, err
478+
}
479+
480+
for _, flavor := range *flavors {
481+
if !strings.EqualFold(flavor.Name, *configuration.InstanceConfig.FlavorRef) {
482+
continue
483+
}
484+
485+
vcpus, _ := strconv.ParseInt(flavor.Vcpus, 10, 64)
486+
return &asgTemplate{
487+
name: flavor.Name,
488+
vcpu: vcpus,
489+
ram: int64(flavor.Ram),
490+
zone: az,
491+
}, nil
492+
}
493+
}
494+
return nil, nil
495+
}
496+
497+
func (csm *cloudServiceManager) buildNodeFromTemplate(asgName string, template *asgTemplate) (*apiv1.Node, error) {
498+
node := apiv1.Node{}
499+
nodeName := fmt.Sprintf("%s-asg-%d", asgName, rand.Int63())
500+
501+
node.ObjectMeta = metav1.ObjectMeta{
502+
Name: nodeName,
503+
SelfLink: fmt.Sprintf("/api/v1/nodes/%s", nodeName),
504+
Labels: map[string]string{},
505+
}
506+
507+
node.Status = apiv1.NodeStatus{
508+
Capacity: apiv1.ResourceList{},
509+
}
510+
511+
node.Status.Capacity[apiv1.ResourcePods] = *resource.NewQuantity(110, resource.DecimalSI)
512+
node.Status.Capacity[apiv1.ResourceCPU] = *resource.NewQuantity(template.vcpu, resource.DecimalSI)
513+
node.Status.Capacity[gpu.ResourceNvidiaGPU] = *resource.NewQuantity(template.gpu, resource.DecimalSI)
514+
node.Status.Capacity[apiv1.ResourceMemory] = *resource.NewQuantity(template.ram*1024*1024, resource.DecimalSI)
515+
516+
node.Status.Allocatable = node.Status.Capacity
517+
518+
node.Labels = cloudprovider.JoinStringMaps(node.Labels, buildGenericLabels(template, nodeName))
519+
520+
node.Status.Conditions = cloudprovider.BuildReadyConditions()
521+
return &node, nil
522+
}
523+
524+
func buildGenericLabels(template *asgTemplate, nodeName string) map[string]string {
525+
result := make(map[string]string)
526+
result[kubeletapis.LabelArch] = cloudprovider.DefaultArch
527+
result[kubeletapis.LabelOS] = cloudprovider.DefaultOS
528+
529+
result[apiv1.LabelInstanceType] = template.name
530+
531+
result[apiv1.LabelZoneRegion] = template.region
532+
result[apiv1.LabelZoneFailureDomain] = template.zone
533+
result[apiv1.LabelHostname] = nodeName
534+
535+
// append custom node labels
536+
for key, value := range template.tags {
537+
result[key] = value
538+
}
539+
540+
return result
541+
}

0 commit comments

Comments
 (0)