@@ -18,16 +18,24 @@ package huaweicloud
18
18
19
19
import (
20
20
"fmt"
21
+ "math/rand"
22
+ "strconv"
23
+ "strings"
21
24
"time"
22
25
26
+ apiv1 "k8s.io/api/core/v1"
27
+ "k8s.io/apimachinery/pkg/api/resource"
28
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23
29
"k8s.io/apimachinery/pkg/util/wait"
24
30
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
25
31
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/huaweicloud/huaweicloud-sdk-go-v3/core/sdktime"
26
32
huaweicloudsdkas "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/huaweicloud/huaweicloud-sdk-go-v3/services/as/v1"
27
33
huaweicloudsdkasmodel "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/huaweicloud/huaweicloud-sdk-go-v3/services/as/v1/model"
28
34
huaweicloudsdkecs "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/huaweicloud/huaweicloud-sdk-go-v3/services/ecs/v2"
29
35
huaweicloudsdkecsmodel "k8s.io/autoscaler/cluster-autoscaler/cloudprovider/huaweicloud/huaweicloud-sdk-go-v3/services/ecs/v2/model"
36
+ "k8s.io/autoscaler/cluster-autoscaler/utils/gpu"
30
37
"k8s.io/klog/v2"
38
+ kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
31
39
)
32
40
33
41
// ElasticCloudServerService represents the elastic cloud server interfaces.
@@ -53,6 +61,12 @@ type AutoScalingService interface {
53
61
// The delta should be non-negative.
54
62
// IncreaseSizeInstance wait until instance number is updated.
55
63
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 )
56
70
}
57
71
58
72
// CloudServiceManager represents the cloud service interfaces.
@@ -71,6 +85,16 @@ type cloudServiceManager struct {
71
85
getASClientFunc func () * huaweicloudsdkas.AsClient
72
86
}
73
87
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
+
74
98
func newCloudServiceManager (cloudConfig * CloudConfig ) * cloudServiceManager {
75
99
return & cloudServiceManager {
76
100
cloudConfig : cloudConfig ,
@@ -389,3 +413,129 @@ func (csm *cloudServiceManager) deleteScalingPolicy(opts *huaweicloudsdkasmodel.
389
413
klog .V (1 ).Infof ("delete scaling policy succeed. policy id: %s" , opts .ScalingPolicyId )
390
414
return nil
391
415
}
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