diff --git a/cloud-control-manager/cloud-driver/drivers/gcp/resources/CommonHandler.go b/cloud-control-manager/cloud-driver/drivers/gcp/resources/CommonHandler.go index 1c37d9e58..4c3d1c586 100644 --- a/cloud-control-manager/cloud-driver/drivers/gcp/resources/CommonHandler.go +++ b/cloud-control-manager/cloud-driver/drivers/gcp/resources/CommonHandler.go @@ -361,7 +361,12 @@ func WaitOperationComplete(client *compute.Service, project string, region strin func GetDiskInfo(client *compute.Service, credential idrv.CredentialInfo, region idrv.RegionInfo, diskName string) (*compute.Disk, error) { projectID := credential.ProjectID zone := region.Zone + targetZone := region.TargetZone + // 대상 zone이 다른경우 targetZone을 사용 + if targetZone != ""{ + zone = targetZone + } diskResp, err := client.Disks.Get(projectID, zone, diskName).Do() if err != nil { cblogger.Error(err) diff --git a/cloud-control-manager/cloud-driver/drivers/gcp/resources/DiskHandler.go b/cloud-control-manager/cloud-driver/drivers/gcp/resources/DiskHandler.go index 233398b6e..0f7fb3330 100644 --- a/cloud-control-manager/cloud-driver/drivers/gcp/resources/DiskHandler.go +++ b/cloud-control-manager/cloud-driver/drivers/gcp/resources/DiskHandler.go @@ -41,6 +41,13 @@ func (DiskHandler *GCPDiskHandler) CreateDisk(diskReqInfo irs.DiskInfo) (irs.Dis zone := DiskHandler.Region.Zone diskName := diskReqInfo.IId.NameId + if diskReqInfo.Zone != "" {// #1067 disk의 zone이 있으면 해당 zone 사용. + cblogger.Info("SetDisk zone before ", DiskHandler.Region) + zone = diskReqInfo.Zone + DiskHandler.Region.Zone = zone // Region은 동일할 것이고 zone을 새로 설정. + cblogger.Info("SetDisk zone after ", DiskHandler.Region) + } + disk := &compute.Disk{ Name: diskName, } @@ -79,7 +86,7 @@ func (DiskHandler *GCPDiskHandler) CreateDisk(diskReqInfo irs.DiskInfo) (irs.Dis // Disk 생성 대기 WaitOperationComplete(DiskHandler.Client, projectID, region, zone, op.Name, 3) - + cblogger.Info("GetDisk zone ", DiskHandler.Region) diskInfo, errDiskInfo := DiskHandler.GetDisk(irs.IID{NameId: diskName, SystemId: diskName}) if errDiskInfo != nil { cblogger.Error(errDiskInfo) @@ -96,25 +103,51 @@ func (DiskHandler *GCPDiskHandler) ListDisk() ([]*irs.DiskInfo, error) { diskInfoList := []*irs.DiskInfo{} projectID := DiskHandler.Credential.ProjectID - zone := DiskHandler.Region.Zone - - diskList, err := DiskHandler.Client.Disks.List(projectID, zone).Do() - hiscallInfo.ElapsedTime = call.Elapsed(start) + regionID := DiskHandler.Region.Region + //zone := DiskHandler.Region.Zone + + cblogger.Error("get ZoneInfo by region ") + //GetRegionZone(regionName string) (irs.RegionZoneInfo, error) + // #1067에 의해 connection의 zone -> region내 disk 조회로 변경 + regionZoneHandler := GCPRegionZoneHandler{ + Client: DiskHandler.Client, + Credential : DiskHandler.Credential, + Region:DiskHandler.Region, + Ctx : DiskHandler.Ctx, + } + regionZoneInfo, err := regionZoneHandler.GetRegionZone(regionID) if err != nil { + cblogger.Error("failed to get ZoneInfo by region ", err) + // failed to get ZoneInfo by region cblogger.Error(err) - LoggingError(hiscallInfo, err) return nil, err - } - calllogger.Info(call.String(hiscallInfo)) - - for _, disk := range diskList.Items { - diskInfo, err := convertDiskInfo(disk) - if err != nil { - cblogger.Error(err) - return nil, err + } else { + cblogger.Error("get region zone Info ", regionZoneInfo) + for _, zoneItem := range regionZoneInfo.ZoneList { + cblogger.Error("zone Info ", zoneItem) + // get Disks by Zone + hiscallInfo.ElapsedTime = call.Elapsed(start) + diskList, err := DiskHandler.Client.Disks.List(projectID, zoneItem.Name).Do() + if err != nil { + cblogger.Error(err) + LoggingError(hiscallInfo, err) + return nil, err + } + calllogger.Info(call.String(hiscallInfo)) + + for _, disk := range diskList.Items { + diskInfo, err := convertDiskInfo(disk) + if err != nil { + cblogger.Error(err) + return nil, err + } + diskInfoList = append(diskInfoList, &diskInfo) + } } - diskInfoList = append(diskInfoList, &diskInfo) + } + + return diskInfoList, nil } @@ -122,7 +155,7 @@ func (DiskHandler *GCPDiskHandler) ListDisk() ([]*irs.DiskInfo, error) { func (DiskHandler *GCPDiskHandler) GetDisk(diskIID irs.IID) (irs.DiskInfo, error) { hiscallInfo := GetCallLogScheme(DiskHandler.Region, call.DISK, diskIID.NameId, "GetDisk()") start := call.Start() - + cblogger.Info("GetDisk zone ", DiskHandler.Region) diskResp, err := GetDiskInfo(DiskHandler.Client, DiskHandler.Credential, DiskHandler.Region, diskIID.SystemId) hiscallInfo.ElapsedTime = call.Elapsed(start) if err != nil { @@ -192,8 +225,14 @@ func (DiskHandler *GCPDiskHandler) DeleteDisk(diskIID irs.IID) (bool, error) { projectID := DiskHandler.Credential.ProjectID region := DiskHandler.Region.Region zone := DiskHandler.Region.Zone + targetZone := DiskHandler.Region.TargetZone disk := diskIID.SystemId + // 대상 zone이 다른경우 targetZone을 사용 + if targetZone != ""{ + zone = targetZone + } + op, err := DiskHandler.Client.Disks.Delete(projectID, zone, disk).Do() hiscallInfo.ElapsedTime = call.Elapsed(start) if err != nil { @@ -210,12 +249,47 @@ func (DiskHandler *GCPDiskHandler) DeleteDisk(diskIID irs.IID) (bool, error) { } func (DiskHandler *GCPDiskHandler) AttachDisk(diskIID irs.IID, ownerVM irs.IID) (irs.DiskInfo, error) { + // disk와 vm의 zone valid check + attachDiskInfo, err := DiskHandler.GetDisk(diskIID) + if err != nil { + cblogger.Error(err) + return irs.DiskInfo{}, err + } + + // check disk status : "available" state only + if attachDiskInfo.Status != irs.DiskStatus("Available") { + return irs.DiskInfo{}, errors.New(string("The disk must be in the Available state : " + attachDiskInfo.Status)) + } + + vmHandler := GCPVMHandler{ + Client: DiskHandler.Client, + Region: DiskHandler.Region, + Ctx: DiskHandler.Ctx, + Credential: DiskHandler.Credential, + } + vmInfo, err := vmHandler.GetVmById(ownerVM) + if err != nil { + cblogger.Error(err.Error()) + return irs.DiskInfo{}, err + } + + + if vmInfo.Region.Zone != attachDiskInfo.Zone{ + cblogger.Error("The disk and the VM must be in the same zone." + vmInfo.Region.Zone, attachDiskInfo.Zone) + return irs.DiskInfo{}, errors.New(string("The disk and the VM must be in the same zone.")) + } + + // vmStatus는 다시 조회해야 하기 때문에 attach할 수 있는 상태가 아니면 오류로 return + // valid check end + + hiscallInfo := GetCallLogScheme(DiskHandler.Region, call.DISK, diskIID.NameId, "AttachDisk()") start := call.Start() projectID := DiskHandler.Credential.ProjectID region := DiskHandler.Region.Region - zone := DiskHandler.Region.Zone + //zone := DiskHandler.Region.Zone + zone := vmInfo.Region.Zone// vm의 zone으로 설정 instance := ownerVM.SystemId attachedDisk := &compute.AttachedDisk{ @@ -234,6 +308,7 @@ func (DiskHandler *GCPDiskHandler) AttachDisk(diskIID irs.IID, ownerVM irs.IID) WaitOperationComplete(DiskHandler.Client, projectID, region, zone, op.Name, 3) + // attach가 끝나면 disk정보 return diskInfo, errDiskInfo := DiskHandler.GetDisk(diskIID) if errDiskInfo != nil { cblogger.Error(errDiskInfo) @@ -429,6 +504,14 @@ func convertDiskInfo(diskResp *compute.Disk) (irs.DiskInfo, error) { diskInfo.IId = irs.IID{NameId: diskResp.Name, SystemId: diskResp.Name} diskInfo.DiskSize = strconv.FormatInt(diskResp.SizeGb, 10) diskInfo.CreatedTime, _ = time.Parse(time.RFC3339, diskResp.CreationTimestamp) + //diskInfo.Zone = diskResp.Zone // diskResp의 zone은 url 형태이므로 zone 만 추출 + index := strings.Index(diskResp.Zone, "zones/") // "zones/"의 인덱스를 찾음 + if index != -1 { + diskInfo.Zone = diskResp.Zone[index+len("zones/"):] // "zones/" 다음의 문자열을 추출 + }else{ + diskInfo.Zone = diskResp.Zone + } + // Users : the users of the disk (attached instances) if diskResp.Users != nil { diff --git a/cloud-control-manager/cloud-driver/drivers/gcp/resources/VPCHandler.go b/cloud-control-manager/cloud-driver/drivers/gcp/resources/VPCHandler.go index e331b39f8..78b28d04e 100644 --- a/cloud-control-manager/cloud-driver/drivers/gcp/resources/VPCHandler.go +++ b/cloud-control-manager/cloud-driver/drivers/gcp/resources/VPCHandler.go @@ -35,6 +35,7 @@ type GCPVPCHandler struct { } //@TODO : VPC 생성 로직 변경 필요 / 서브넷이 백그라운드로 생성되기 때문에 조회 시 모두 생성될 때까지 대기하는 로직 필요(그렇지 않으면 일부 정보가 누락됨) +// #1067 : gcp는 subnet 생성시 zone을 사용하지 않음. func (vVPCHandler *GCPVPCHandler) CreateVPC(vpcReqInfo irs.VPCReqInfo) (irs.VPCInfo, error) { cblogger.Info(vpcReqInfo) @@ -141,7 +142,7 @@ func (vVPCHandler *GCPVPCHandler) CreateVPC(vpcReqInfo irs.VPCReqInfo) (irs.VPCI return irs.VPCInfo{}, errors.New("Making Subnet Error - " + subnetName) } - spew.Dump(infoSubnet) + //spew.Dump(infoSubnet) //생성된 서브넷이 조회되는데 시간이 필요하기 때문에 홀딩 함. /* errChkSubnetStatus := vVPCHandler.WaitForRunSubnet(subnetName, true) diff --git a/cloud-control-manager/cloud-driver/drivers/tencent/connect/TencentCloudConnection.go b/cloud-control-manager/cloud-driver/drivers/tencent/connect/TencentCloudConnection.go index 12e651bec..01d299caa 100644 --- a/cloud-control-manager/cloud-driver/drivers/tencent/connect/TencentCloudConnection.go +++ b/cloud-control-manager/cloud-driver/drivers/tencent/connect/TencentCloudConnection.go @@ -64,7 +64,7 @@ func (cloudConn *TencentCloudConnection) CreateKeyPairHandler() (irs.KeyPairHand func (cloudConn *TencentCloudConnection) CreateVMHandler() (irs.VMHandler, error) { cblogger.Info("Start CreateVMHandler()") - vmHandler := trs.TencentVMHandler{Region: cloudConn.Region, Client: cloudConn.VMClient, DiskClient: cloudConn.DiskClient} + vmHandler := trs.TencentVMHandler{Region: cloudConn.Region, Client: cloudConn.VMClient, DiskClient: cloudConn.DiskClient, VPCClient: cloudConn.VNetworkClient} return &vmHandler, nil } diff --git a/cloud-control-manager/cloud-driver/drivers/tencent/resources/CommonHandler.go b/cloud-control-manager/cloud-driver/drivers/tencent/resources/CommonHandler.go index 2038c9007..50fca2902 100644 --- a/cloud-control-manager/cloud-driver/drivers/tencent/resources/CommonHandler.go +++ b/cloud-control-manager/cloud-driver/drivers/tencent/resources/CommonHandler.go @@ -3,12 +3,15 @@ package resources import ( "errors" "time" + "strconv" call "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/call-log" irs "github.com/cloud-barista/cb-spider/cloud-control-manager/cloud-driver/interfaces/resources" + cbs "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cbs/v20170312" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" + vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" tencentError "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" ) @@ -281,3 +284,77 @@ func DescribeZones(client *cvm.Client) (*cvm.DescribeZonesResponse, error) { return responseZones, nil } + +// subnet 목록 조회 by vpc +func ListSubnet(client *vpc.Client, reqVpcId string) ([]irs.SubnetInfo, error){ + cblogger.Infof("reqVpcId : [%s]", reqVpcId) + var arrSubnetInfoList []irs.SubnetInfo + + // logger for HisCall + callogger := call.GetLogger("HISCALL") + callLogInfo := call.CLOUDLOGSCHEMA{ + CloudOS: call.TENCENT, + RegionZone: client.GetRegion(), + ResourceType: call.VPCSUBNET, + ResourceName: "ListSubnet - VpcId:" + reqVpcId, + CloudOSAPI: "DescribeSubnets()", + ElapsedTime: "", + ErrorMSG: "", + } + + request := vpc.NewDescribeSubnetsRequest() + request.Filters = []*vpc.Filter{ + &vpc.Filter{ + Name: common.StringPtr("vpc-id"), + Values: common.StringPtrs([]string{reqVpcId}), + }, + } + + callLogStart := call.Start() + response, err := client.DescribeSubnets(request) + callLogInfo.ElapsedTime = call.Elapsed(callLogStart) + callogger.Info(call.String(callLogInfo)) + if err != nil { + cblogger.Error(err) + return nil, err + } + + for _, curSubnet := range response.Response.SubnetSet { + cblogger.Infof("[%s] Check Subnet Information", *curSubnet.SubnetId) + resSubnetInfo := irs.SubnetInfo{ + IId: irs.IID{SystemId: *curSubnet.SubnetId, NameId: *curSubnet.SubnetName}, + IPv4_CIDR: *curSubnet.CidrBlock, + Zone: *curSubnet.Zone, + //Status: *subnetInfo.State, + } + + keyValueList := []irs.KeyValue{ + {Key: "VpcId", Value: *curSubnet.VpcId}, + {Key: "IsDefault", Value: strconv.FormatBool(*curSubnet.IsDefault)}, + {Key: "AvailabilityZone", Value: *curSubnet.Zone}, + } + resSubnetInfo.KeyValueList = keyValueList + arrSubnetInfoList = append(arrSubnetInfoList, resSubnetInfo) + } + + return arrSubnetInfoList, nil +} + +// subnet 단건 조회 +func GetSubnet(client *vpc.Client, reqVpcId string, reqSubnetId string) (irs.SubnetInfo, error){ + resultSubnetInfo := irs.SubnetInfo{} + subnetList, err := ListSubnet(client, reqVpcId) + if err != nil { + return resultSubnetInfo, err + } + + + for _, curSubnet := range subnetList { + if curSubnet.IId.SystemId == reqSubnetId { + return curSubnet, nil + } + } + + // 못찾으면 not found error + return resultSubnetInfo, errors.New("cannot find the subnet") +} diff --git a/cloud-control-manager/cloud-driver/drivers/tencent/resources/DiskHandler.go b/cloud-control-manager/cloud-driver/drivers/tencent/resources/DiskHandler.go index 5821d9f32..e326ed0ad 100644 --- a/cloud-control-manager/cloud-driver/drivers/tencent/resources/DiskHandler.go +++ b/cloud-control-manager/cloud-driver/drivers/tencent/resources/DiskHandler.go @@ -43,8 +43,14 @@ func (DiskHandler *TencentDiskHandler) CreateDisk(diskReqInfo irs.DiskInfo) (irs return irs.DiskInfo{}, errors.New("A disk with the name " + diskReqInfo.IId.NameId + " already exists.") } + // region base이므로 특정 zone을 지정시 해당 zone에 생성. + zone := DiskHandler.Region.Zone + if diskReqInfo.Zone != ""{ + zone = diskReqInfo.Zone + } + request := cbs.NewCreateDisksRequest() - request.Placement = &cbs.Placement{Zone: common.StringPtr(DiskHandler.Region.Zone)} + request.Placement = &cbs.Placement{Zone: common.StringPtr(zone)} request.DiskChargeType = common.StringPtr("POSTPAID_BY_HOUR") diskErr := validateDisk(&diskReqInfo) @@ -251,6 +257,7 @@ func convertDiskInfo(diskResp *cbs.Disk) (irs.DiskInfo, error) { diskInfo.OwnerVM.SystemId = *diskResp.InstanceId diskInfo.CreatedTime, _ = time.Parse("2006-01-02 15:04:05", *diskResp.CreateTime) diskInfo.Status = convertTenStatusToDiskStatus(diskResp) + diskInfo.Zone = *diskResp.Placement.Zone return diskInfo, nil } diff --git a/cloud-control-manager/cloud-driver/drivers/tencent/resources/VMHandler.go b/cloud-control-manager/cloud-driver/drivers/tencent/resources/VMHandler.go index 4a1f6b4a8..9f259157d 100644 --- a/cloud-control-manager/cloud-driver/drivers/tencent/resources/VMHandler.go +++ b/cloud-control-manager/cloud-driver/drivers/tencent/resources/VMHandler.go @@ -25,6 +25,7 @@ import ( tencentcbs "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cbs/v20170312" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312" + tencentvpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" //lighthouse "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/lighthouse/v20200324" ) @@ -32,7 +33,9 @@ type TencentVMHandler struct { Region idrv.RegionInfo Client *cvm.Client DiskClient *tencentcbs.Client + VPCClient *tencentvpc.Client } +//Client *vpc.Client //type TencentCbsHandler struct { // Region idrv.RegionInfo @@ -68,8 +71,19 @@ type TencentVMHandler struct { // VM생성 시 Zone이 필수라서 Credential의 Zone에만 생성함. func (vmHandler *TencentVMHandler) StartVM(vmReqInfo irs.VMReqInfo) (irs.VMInfo, error) { cblogger.Info(vmReqInfo) - zoneId := vmHandler.Region.Zone + + vpcHandler := TencentVPCHandler{ + Region: vmHandler.Region, + Client: vmHandler.VPCClient, + } + subnetInfo, err := GetSubnet(vpcHandler.Client, vmReqInfo.VpcIID.SystemId, vmReqInfo.SubnetIID.SystemId) + if err != nil { + return irs.VMInfo{}, errors.New("there is no available subnet") + } + if subnetInfo.Zone != "" { + zoneId = subnetInfo.Zone + } cblogger.Debugf("Zone : %s", zoneId) if zoneId == "" { cblogger.Error("Connection information does not contain Zone information.") @@ -194,8 +208,9 @@ func (vmHandler *TencentVMHandler) StartVM(vmReqInfo irs.VMReqInfo) (irs.VMInfo, //============================= // Placement 처리 //============================= + // 이슈 #1097 : placement는 optional이며 vm생성시 subnet은 1개만 선택하므로 subnet에 정의된 zone 사용하도록. (subnet에서 선택할 zone과 다른 zone에 생성불가) request.Placement = &cvm.Placement{ - Zone: common.StringPtr(vmHandler.Region.Zone), + Zone: common.StringPtr(zoneId), } /* 이슈 #348에 의해 RootDisk 기능 지원하면서 기존 로직 제거 diff --git a/cloud-control-manager/cloud-driver/drivers/tencent/resources/VPCHandler.go b/cloud-control-manager/cloud-driver/drivers/tencent/resources/VPCHandler.go index d089a2d8d..0e1259bcb 100644 --- a/cloud-control-manager/cloud-driver/drivers/tencent/resources/VPCHandler.go +++ b/cloud-control-manager/cloud-driver/drivers/tencent/resources/VPCHandler.go @@ -43,12 +43,12 @@ func (VPCHandler *TencentVPCHandler) CreateVPC(vpcReqInfo irs.VPCReqInfo) (irs.V return irs.VPCInfo{}, errors.New("A VPC with the name " + vpcReqInfo.IId.NameId + " already exists.") } - zoneId := VPCHandler.Region.Zone + zoneId := VPCHandler.Region.Zone// default cblogger.Infof("Zone : %s", zoneId) - if zoneId == "" { - cblogger.Error("Connection information does not contain Zone information.") - return irs.VPCInfo{}, errors.New("Connection information does not contain Zone information.") - } + // if zoneId == "" { // vpc 자체는 region dependency임. + // cblogger.Error("Connection information does not contain Zone information.") + // return irs.VPCInfo{}, errors.New("Connection information does not contain Zone information.") + // } // logger for HisCall callogger := call.GetLogger("HISCALL") @@ -95,10 +95,15 @@ func (VPCHandler *TencentVPCHandler) CreateVPC(vpcReqInfo irs.VPCReqInfo) (irs.V for _, curSubnet := range vpcReqInfo.SubnetInfoList { cblogger.Infof("[%s] Subnet 처리", curSubnet.IId.NameId) + subnetZoneId := zoneId + if curSubnet.Zone != "" { + subnetZoneId = curSubnet.Zone + } + reqSubnet := &vpc.SubnetInput{ CidrBlock: common.StringPtr(curSubnet.IPv4_CIDR), SubnetName: common.StringPtr(curSubnet.IId.NameId), - Zone: common.StringPtr(zoneId), + Zone: common.StringPtr(subnetZoneId), //RouteTableId: common.StringPtr("route"), } requestSubnet.Subnets = append(requestSubnet.Subnets, reqSubnet) @@ -106,7 +111,7 @@ func (VPCHandler *TencentVPCHandler) CreateVPC(vpcReqInfo irs.VPCReqInfo) (irs.V responseSubnet, errSubnet := VPCHandler.Client.CreateSubnets(requestSubnet) cblogger.Debug(responseSubnet.ToJsonString()) - spew.Dump(responseSubnet) + //spew.Dump(responseSubnet) if errSubnet != nil { cblogger.Error(errSubnet) return irs.VPCInfo{}, errSubnet @@ -392,6 +397,9 @@ func (VPCHandler *TencentVPCHandler) AddSubnet(vpcIID irs.IID, subnetInfo irs.Su cblogger.Infof("[%s] Add Subnet - CIDR : %s", subnetInfo.IId.NameId, subnetInfo.IPv4_CIDR) zoneId := VPCHandler.Region.Zone + if subnetInfo.Zone != "" { + zoneId = subnetInfo.Zone + } cblogger.Infof("Zone : %s", zoneId) if zoneId == "" { cblogger.Error("Connection information does not contain Zone information.") @@ -433,7 +441,7 @@ func (VPCHandler *TencentVPCHandler) AddSubnet(vpcIID irs.IID, subnetInfo irs.Su request.VpcId = common.StringPtr(vpcIID.SystemId) request.SubnetName = common.StringPtr(subnetInfo.IId.NameId) request.CidrBlock = common.StringPtr(subnetInfo.IPv4_CIDR) - request.Zone = common.StringPtr(VPCHandler.Region.Zone) + request.Zone = common.StringPtr(zoneId) callLogStart := call.Start() response, err := VPCHandler.Client.CreateSubnet(request)