Merge pull request #1048 in YUNIONIO/onecloud from ~TANGBIN/onecloud:bugfix/tb-huawei-stage-2.2-fix to release/2.6.0

* commit 'fa49e138f26fbeb21a193ef48befb98dcbe37f99':
  update vendor & bugfix
  bugfix
  替换不必要的重复func
  remove useless todo
  rebuild root bugfix
  server get disks fix
  server delete etc fix
  server delete etc fix
This commit is contained in:
邱剑
2019-02-18 10:44:43 +08:00
14 changed files with 397 additions and 56 deletions
Generated
+3 -2
View File
@@ -1676,7 +1676,7 @@
[[projects]]
branch = "master"
digest = "1:6277045f9c9a63d84e1aa48c7ad3c1f42ea6523bc54ea92e772d541c78e37404"
digest = "1:37d4e9b3030c077522fc5669b497a25ce53429da77d5e592226658e57cc604bb"
name = "yunion.io/x/pkg"
packages = [
"gotypes",
@@ -1710,7 +1710,7 @@
"utils",
]
pruneopts = "UT"
revision = "e2e42205d868c26829b9322b40cdc48f24fed379"
revision = "5fbe7b39e5f599aa49f6f597d57da460d93c26ff"
[[projects]]
branch = "master"
@@ -1826,6 +1826,7 @@
"golang.org/x/net/ipv4",
"golang.org/x/sys/unix",
"gopkg.in/gin-gonic/gin.v1",
"gopkg.in/yaml.v2",
"k8s.io/api/core/v1",
"k8s.io/apimachinery/pkg/api/errors",
"k8s.io/apimachinery/pkg/apis/meta/v1",
+2 -2
View File
@@ -192,11 +192,11 @@ func (self *SHuaweiGuestDriver) RequestDeployGuestOnHost(ctx context.Context, gu
log.Debugf("VMrebuildRoot %s new diskID %s, wait status ready ...", iVM.GetGlobalId(), diskId)
err = cloudprovider.WaitStatus(iVM, models.VM_READY, time.Second*5, time.Second*1800)
err = cloudprovider.WaitStatus(iVM, models.VM_RUNNING, time.Second*5, time.Second*1800)
if err != nil {
return nil, err
}
log.Debugf("VMrebuildRoot %s, and status is ready", iVM.GetGlobalId())
log.Debugf("VMrebuildRoot %s, and status is %s", iVM.GetGlobalId(), iVM.GetStatus())
maxWaitSecs := 300
waited := 0
@@ -147,6 +147,12 @@ func (self *SManagedVirtualizedGuestDriver) RequestUndeployGuestOnHost(ctx conte
log.Errorf("host.GetIHost fail %s", err)
return nil, err
}
// 创建失败时external id为空。此时直接返回即可。不需要再调用公有云api
if len(guest.ExternalId) == 0 {
return nil, nil
}
ivm, err := ihost.GetIVMById(guest.ExternalId)
if err != nil {
if err == cloudprovider.ErrNotFound {
+9 -5
View File
@@ -52,11 +52,7 @@ func (self *SOrderManager) SetDomainId(domainId string) error {
// 查询客户包周期资源列表 https://support.huaweicloud.com/api-oce/zh-cn_topic_0084961226.html
func (self *SOrderManager) List(querys map[string]string) (*responses.ListResult, error) {
if self.orderCtx == nil {
return nil, fmt.Errorf("domainId is emtpy.Use SetDomainId method to set.")
}
return self.ListInContextWithSpec(self.orderCtx, "resources/detail", querys, "data")
return nil, fmt.Errorf("Not Suppport List Order")
}
// 查询订单的资源开通详情 https://support.huaweicloud.com/api-oce/api_order_00001.html
@@ -75,3 +71,11 @@ func (self *SOrderManager) PerformAction(action string, id string, params jsonut
return self._do(request, "")
}
func (self *SOrderManager) GetPeriodResourceList(querys map[string]string) (*responses.ListResult, error) {
if self.orderCtx == nil {
return nil, fmt.Errorf("domainId is emtpy.Use SetDomainId method to set.")
}
return self.ListInContextWithSpec(self.orderCtx, "resources/detail", querys, "data")
}
+36 -1
View File
@@ -4,7 +4,10 @@ package huawei
const DATETIME_FORMAT = "2006-01-02T15:04:05.999999999"
// Task status
const TASK_SUCCESS = "SUCCESS"
const (
TASK_SUCCESS = "SUCCESS"
TASK_FAIL = "FAIL"
)
// Charging Type
const (
@@ -20,3 +23,35 @@ const (
RESOURCE_TYPE_IP = "hws.resource.type.ip" // VPC公网IP
RESOURCE_TYPE_IMAGE = "hws.resource.type.marketplace" // 市场镜像
)
// Not Found Error code
// 网络等资源直接通过http code 404即可判断资源不存在。另外有些资源可能不是返回404这里单独列出来
const (
VM_NOT_FOUND = "Ecs.0114" // 云服务器不存在
ECS_NOT_FOUND = "Ecs.0614" // 弹性云服务器不存在
IMG_ID_NOT_FOUND = "IMG.0027" // 请求的镜像ID不存在
IMG_NOT_FOUND = "IMG.0027" // 镜像不存在
IMG_ERR_NOT_FOUND = "IMG.0057" // 镜像文件不存在或者为空或者不是允许格式的文件
IMG_BACKUP_NOT_FOUND = "IMG.0020" // 备份不存在
IMG_VM_BACKUP_NOT_FOUND = "IMG.0127" // 云服务器备份不存在
IMG_VM_NOT_FOUND = "IMG.0005" // 云主机不存在
JOB_NOT_FOUND = "Common.0011" // jobId为空
EVS_NOT_FOUND = "EVS.5404" // 磁盘、快照和备份等资源未找到。
FIP_NOT_FOUND = "VPC.0504" // 未找到弹性公网IP。
VPC_NOT_FOUND = "VPC.0012" // 未找到弹性公网VPC。
)
var NOT_FOUND_CODES = []string{
VM_NOT_FOUND,
ECS_NOT_FOUND,
IMG_ID_NOT_FOUND,
IMG_NOT_FOUND,
IMG_ERR_NOT_FOUND,
IMG_BACKUP_NOT_FOUND,
IMG_VM_BACKUP_NOT_FOUND,
IMG_VM_NOT_FOUND,
JOB_NOT_FOUND,
EVS_NOT_FOUND,
FIP_NOT_FOUND,
VPC_NOT_FOUND,
}
+58 -1
View File
@@ -233,6 +233,8 @@ func (self *SDisk) GetTemplateId() string {
return self.VolumeImageMetadata.ImageID
}
// Bootable 表示硬盘是否为启动盘。
// 启动盘 != 系统盘(必须是启动盘且挂载在root device上)
func (self *SDisk) GetDiskType() string {
if self.Bootable == "true" {
return models.DISK_TYPE_SYS
@@ -268,6 +270,14 @@ func (self *SDisk) GetMountpoint() string {
return ""
}
func (self *SDisk) GetMountServerId() string {
if len(self.Attachments) > 0 {
return self.Attachments[0].ServerID
}
return ""
}
func (self *SDisk) GetAccessPath() string {
return ""
}
@@ -323,8 +333,55 @@ func (self *SDisk) Resize(ctx context.Context, newSizeMB int64) error {
return self.storage.zone.region.resizeDisk(self.GetId(), sizeGb)
}
func (self *SDisk) Detach() error {
err := self.storage.zone.region.DetachDisk(self.GetMountServerId(), self.GetId())
if err != nil {
log.Debugf("detach server %s disk %s failed: %s", self.GetMountServerId(), self.GetId(), err)
return err
}
return cloudprovider.WaitStatus(self, models.DISK_READY, 5*time.Second, 60*time.Second)
}
func (self *SDisk) Attach(device string) error {
err := self.storage.zone.region.AttachDisk(self.GetMountServerId(), self.GetId(), device)
if err != nil {
log.Debugf("attach server %s disk %s failed: %s", self.GetMountServerId(), self.GetId(), err)
return err
}
return cloudprovider.WaitStatus(self, models.DISK_READY, 5*time.Second, 60*time.Second)
}
// 在线卸载磁盘 https://support.huaweicloud.com/usermanual-ecs/zh-cn_topic_0036046828.html
// 对于挂载在系统盘盘位(也就是“/dev/sda”或“/dev/vda”挂载点)上的磁盘,当前仅支持离线卸载
func (self *SDisk) Reset(ctx context.Context, snapshotId string) (string, error) {
return self.storage.zone.region.resetDisk(self.GetId(), snapshotId)
mountpoint := self.GetMountpoint()
if mountpoint == "/dev/sda" || mountpoint == "/dev/vda" {
err := self.Detach()
if err != nil {
return "", err
}
}
diskId, err := self.storage.zone.region.resetDisk(self.GetId(), snapshotId)
if err != nil {
return diskId, err
}
err = cloudprovider.WaitStatus(self, models.DISK_READY, 5*time.Second, 300*time.Second)
if err != nil {
return "", err
}
if mountpoint == "/dev/sda" || mountpoint == "/dev/vda" {
err := self.Attach(mountpoint)
if err != nil {
return "", err
}
}
return diskId, nil
}
// 华为云不支持重置
+110 -28
View File
@@ -91,12 +91,11 @@ type SInstance struct {
Progress string `json:"progress"`
HostID string `json:"hostId"`
Updated string `json:"updated"`
Created string `json:"created"`
Created time.Time `json:"created"`
Metadata VMMetadata `json:"metadata"`
Tags []string `json:"tags"`
Description string `json:"description"`
Locked bool `json:"locked"`
Image Image `json:"image"`
ConfigDrive string `json:"config_drive"`
TenantID string `json:"tenant_id"`
UserID string `json:"user_id"`
@@ -115,13 +114,13 @@ type SInstance struct {
OSEXTSRVATTRRamdiskID string `json:"OS-EXT-SRV-ATTR:ramdisk_id"`
EnterpriseProjectID string `json:"enterprise_project_id"`
OSEXTSRVATTRUserData string `json:"OS-EXT-SRV-ATTR:user_data"`
OSSRVUSGLaunchedAt string `json:"OS-SRV-USG:launched_at"`
OSSRVUSGLaunchedAt time.Time `json:"OS-SRV-USG:launched_at"`
OSEXTSRVATTRKernelID string `json:"OS-EXT-SRV-ATTR:kernel_id"`
OSEXTSRVATTRLaunchIndex int64 `json:"OS-EXT-SRV-ATTR:launch_index"`
HostStatus string `json:"host_status"`
OSEXTSRVATTRReservationID string `json:"OS-EXT-SRV-ATTR:reservation_id"`
OSEXTSRVATTRHostname string `json:"OS-EXT-SRV-ATTR:hostname"`
OSSRVUSGTerminatedAt string `json:"OS-SRV-USG:terminated_at"`
OSSRVUSGTerminatedAt time.Time `json:"OS-SRV-USG:terminated_at"`
SysTags []SysTag `json:"sys_tags"`
SecurityGroups []SecurityGroup `json:"security_groups"`
}
@@ -156,6 +155,21 @@ func compareSet(currentSet []string, newSet []string) (add []string, remove []st
return add, remove, keep
}
// 启动盘 != 系统盘(必须是启动盘且挂载在root device上)
func isBootDisk(server *SInstance, disk *SDisk) bool {
if disk.GetDiskType() != models.DISK_TYPE_SYS {
return false
}
for _, attachment := range disk.Attachments {
if attachment.ServerID == server.GetId() && attachment.Device == server.OSEXTSRVATTRRootDeviceName {
return true
}
}
return false
}
func (self *SInstance) GetId() string {
return self.ID
}
@@ -189,6 +203,12 @@ func (self *SInstance) Refresh() error {
if err != nil {
return err
}
if new.Status == InstanceStatusTerminated {
log.Debugf("Instance already terminated.")
return cloudprovider.ErrNotFound
}
return jsonutils.Update(self, new)
}
@@ -210,9 +230,9 @@ func (self *SInstance) GetMetadata() *jsonutils.JSONDict {
priceKey := fmt.Sprintf("%s::%s::%s", self.host.zone.region.GetId(), self.GetInstanceType(), lowerOs)
data.Add(jsonutils.NewString(priceKey), "price_key")
data.Add(jsonutils.NewString(self.host.zone.GetGlobalId()), "zone_ext_id")
if len(self.Image.ID) > 0 {
if image, err := self.host.zone.region.GetImage(self.Image.ID); err != nil {
log.Errorf("Failed to find image %s for instance %s zone %s", self.Image.ID, self.GetId(), self.OSEXTAZAvailabilityZone)
if len(self.Metadata.MeteringImageID) > 0 {
if image, err := self.host.zone.region.GetImage(self.Metadata.MeteringImageID); err != nil {
log.Errorf("Failed to find image %s for instance %s zone %s", self.Metadata.MeteringImageID, self.GetId(), self.OSEXTAZAvailabilityZone)
} else if meta := image.GetMetadata(); meta != nil {
data.Update(meta)
}
@@ -241,14 +261,23 @@ func (self *SInstance) GetBillingType() string {
}
}
// charging_mode “0”:按需计费 “1”:按包年包月计费
func (self *SInstance) GetExpiredAt() time.Time {
t, _ := time.Parse(DATETIME_FORMAT, self.OSSRVUSGTerminatedAt)
return t
var expiredTime time.Time
if self.Metadata.ChargingMode == "1" {
res, err := self.host.zone.region.GetOrderResourceDetail(self.GetId())
if err != nil {
log.Debugf(err.Error())
}
expiredTime = res.ExpireTime
}
return expiredTime
}
func (self *SInstance) GetCreateTime() time.Time {
t, _ := time.Parse(DATETIME_FORMAT, self.Created)
return t
return self.Created
}
func (self *SInstance) GetIHost() cloudprovider.ICloudHost {
@@ -275,6 +304,12 @@ func (self *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
}
disks[i].storage = storage
idisks[i] = &disks[i]
// 将系统盘放到第0个位置
if isBootDisk(self, &disks[i]) {
_temp := idisks[0]
idisks[0] = &disks[i]
idisks[i] = _temp
}
}
return idisks, nil
}
@@ -418,6 +453,11 @@ func (self *SInstance) StopVM(ctx context.Context, isForce bool) error {
return nil
}
if self.Status == InstanceStatusTerminated {
log.Debugf("Instance already terminated.")
return nil
}
err := self.host.zone.region.StopVM(self.GetId(), isForce)
if err != nil {
return err
@@ -426,6 +466,10 @@ func (self *SInstance) StopVM(ctx context.Context, isForce bool) error {
}
func (self *SInstance) DeleteVM(ctx context.Context) error {
if self.Status == InstanceStatusTerminated {
return nil
}
for {
err := self.host.zone.region.DeleteVM(self.GetId())
if err != nil && self.Status != InstanceStatusTerminated {
@@ -457,20 +501,40 @@ func (self *SInstance) UpdateUserData(userData string) error {
// todo: 支持注入user_data
func (self *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) {
var err error
if self.Image.ID == imageId {
err = self.host.zone.region.RebuildRoot(ctx, self.GetId(), passwd, publicKey)
var jobId string
if self.Metadata.MeteringImageID == imageId {
jobId, err = self.host.zone.region.RebuildRoot(ctx, self.GetId(), passwd, publicKey)
if err != nil {
return "", err
}
} else {
err = self.host.zone.region.ChangeRoot(ctx, self.GetId(), imageId, passwd, publicKey)
jobId, err = self.host.zone.region.ChangeRoot(ctx, self.GetId(), imageId, passwd, publicKey)
if err != nil {
return "", err
}
}
// todo: wait job finished here
return "", nil
err = self.host.zone.region.waitTaskStatus(self.host.zone.region.ecsClient.Servers.ServiceType(), jobId, TASK_SUCCESS, 15*time.Second, 900*time.Second)
if err != nil {
log.Errorf("RebuildRoot task error %s", err)
return "", err
}
err = self.Refresh()
if err != nil {
return "", err
}
idisks, err := self.GetIDisks()
if err != nil {
return "", err
}
if len(idisks) == 0 {
return "", fmt.Errorf("server %s has no volume attached.", self.GetId())
}
return idisks[0].GetId(), nil
}
func (self *SInstance) DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error {
@@ -670,6 +734,16 @@ func (self *SRegion) CreateInstance(name string, imageId string, instanceType st
} else {
// 包年包月
err = cloudprovider.WaitCreated(10*time.Second, 180*time.Second, func() bool {
order, e := self.GetOrder(_id)
if e != nil {
log.Debugf(err.Error())
return false
}
if order.TotalSize == 0 {
return false
}
ids, err = self.getAllResIdsByType(_id, RESOURCE_TYPE_VM)
if err != nil {
log.Debugf(err.Error())
@@ -827,10 +901,6 @@ func (self *SRegion) DeleteVM(instanceId string) error {
return err
}
if remoteStatus == InstanceStatusTerminated {
return nil
}
if remoteStatus != InstanceStatusStopped {
log.Errorf("DeleteVM vm status is %s expect %s", remoteStatus, InstanceStatusStopped)
return cloudprovider.ErrInvalidStatus
@@ -862,7 +932,9 @@ func (self *SRegion) UpdateVM(instanceId, name string) error {
return err
}
func (self *SRegion) RebuildRoot(ctx context.Context, instanceId, passwd, publicKeyName string) error {
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0067876349.html
// 返回job id
func (self *SRegion) RebuildRoot(ctx context.Context, instanceId, passwd, publicKeyName string) (string, error) {
params := jsonutils.NewDict()
reinstallObj := jsonutils.NewDict()
// meta := jsonutils.NewDict()
@@ -872,15 +944,21 @@ func (self *SRegion) RebuildRoot(ctx context.Context, instanceId, passwd, public
} else if len(publicKeyName) > 0 {
reinstallObj.Add(jsonutils.NewString(publicKeyName), "keyname")
} else {
return fmt.Errorf("both password and publicKey are empty.")
return "", fmt.Errorf("both password and publicKey are empty.")
}
params.Add(reinstallObj, "os-reinstall")
_, err := self.ecsClient.Servers.PerformAction2("reinstallos", instanceId, params, "")
return err
ret, err := self.ecsClient.Servers.PerformAction2("reinstallos", instanceId, params, "")
if err != nil {
return "", err
}
return ret.GetString("job_id")
}
func (self *SRegion) ChangeRoot(ctx context.Context, instanceId, imageId, passwd, publicKeyName string) error {
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0067876971.html
// 返回job id
func (self *SRegion) ChangeRoot(ctx context.Context, instanceId, imageId, passwd, publicKeyName string) (string, error) {
params := jsonutils.NewDict()
changeOsObj := jsonutils.NewDict()
// meta := jsonutils.NewDict()
@@ -890,14 +968,18 @@ func (self *SRegion) ChangeRoot(ctx context.Context, instanceId, imageId, passwd
} else if len(publicKeyName) > 0 {
changeOsObj.Add(jsonutils.NewString(publicKeyName), "keyname")
} else {
return fmt.Errorf("both password and publicKey are empty.")
return "", fmt.Errorf("both password and publicKey are empty.")
}
changeOsObj.Add(jsonutils.NewString(imageId), "imageid")
params.Add(changeOsObj, "os-change")
_, err := self.ecsClient.Servers.PerformAction2("changeos", instanceId, params, "")
return err
ret, err := self.ecsClient.Servers.PerformAction2("changeos", instanceId, params, "")
if err != nil {
return "", err
}
return ret.GetString("job_id")
}
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0020212692.html
+111 -12
View File
@@ -1,6 +1,19 @@
package huawei
import "fmt"
import (
"fmt"
"strings"
"time"
"yunion.io/x/onecloud/pkg/cloudprovider"
)
type SOrder struct {
ErrorCode *string `json:"error_code"` // 只有失败时才返回此参数
ErrorMsg *string `json:"error_msg"` //只有失败时才返回此参数
TotalSize int `json:"totalSize"` // 只有成功时才返回此参数
Resources []SResource `json:"resources"`
}
type SResource struct {
ResourceID string `json:"resourceId"`
@@ -11,37 +24,123 @@ type SResource struct {
Status int64 `json:"status"`
}
/*
获取订单信息 https://support.huaweicloud.com/api-oce/api_order_00001.html
*/
func (self *SRegion) GetAllResByOrderId(orderId string) ([]SResource, error) {
type SResourceDetail struct {
ID string `json:"id"`
Status int64 `json:"status"`
ResourceID string `json:"resource_id"`
ResourceName string `json:"resource_name"`
RegionCode string `json:"region_code"`
CloudServiceTypeCode string `json:"cloud_service_type_code"`
ResourceTypeCode string `json:"resource_type_code"`
ResourceSpecCode string `json:"resource_spec_code"`
ProjectCode string `json:"project_code"`
ProductID string `json:"product_id"`
MainResourceID string `json:"main_resource_id"`
IsMainResource int64 `json:"is_main_resource"`
ValidTime time.Time `json:"valid_time"`
ExpireTime time.Time `json:"expire_time"`
NextOperationPolicy string `json:"next_operation_policy"`
}
func (self *SRegion) getDomianId() (string, error) {
domains, err := self.client.getEnabledDomains()
if err != nil {
return nil, err
return "", err
}
if domains == nil || len(domains) == 0 {
return nil, fmt.Errorf("GetAllResByOrderId domain is empty")
return "", fmt.Errorf("GetAllResByOrderId domain is empty")
} else if len(domains) > 1 {
// not supported??
return nil, fmt.Errorf("GetAllResByOrderId mutliple domain(%d) found", len(domains))
return "", fmt.Errorf("GetAllResByOrderId mutliple domain(%d) found", len(domains))
}
err = self.ecsClient.Orders.SetDomainId(domains[0].ID)
return domains[0].ID, nil
}
/*
获取订单信息 https://support.huaweicloud.com/api-oce/api_order_00001.html
*/
func (self *SRegion) GetOrder(orderId string) (SOrder, error) {
var order SOrder
domain, err := self.getDomianId()
if err != nil {
return order, err
}
err = self.ecsClient.Orders.SetDomainId(domain)
if err != nil {
return order, err
}
err = DoGet(self.ecsClient.Orders.Get, orderId, nil, &order)
return order, err
}
/*
获取订单资源详情列表 https://support.huaweicloud.com/api-oce/zh-cn_topic_0084961226.html
*/
func (self *SRegion) GetOrderResources(orderId string, resource_ids []string, only_main_resource bool) ([]SResourceDetail, error) {
domain, err := self.getDomianId()
if err != nil {
return nil, err
}
resp, err := self.ecsClient.Orders.Get(orderId, nil)
err = self.ecsClient.Orders.SetDomainId(domain)
if err != nil {
return nil, err
}
resources := make([]SResource, 0)
err = resp.Unmarshal(&resources, "resources")
resources := make([]SResourceDetail, 0)
queries := map[string]string{"customer_id": domain}
if len(orderId) > 0 {
queries["order_id"] = orderId
}
if len(resource_ids) > 0 {
queries["resource_ids"] = strings.Join(resource_ids, ",")
}
if only_main_resource {
queries["only_main_resource"] = "1"
}
err = doListAll(self.ecsClient.Orders.GetPeriodResourceList, queries, &resources)
return resources, err
}
/*
获取资源详情 https://support.huaweicloud.com/api-oce/zh-cn_topic_0084961226.html
*/
func (self *SRegion) GetOrderResourceDetail(resourceId string) (SResourceDetail, error) {
var res SResourceDetail
if len(resourceId) == 0 {
return res, fmt.Errorf("GetOrderResourceDetail resource id should not be empty")
}
resources, err := self.GetOrderResources("", []string{resourceId}, false)
if err != nil {
return res, err
}
switch len(resources) {
case 0:
return res, cloudprovider.ErrNotFound
case 1:
return resources[0], nil
default:
return res, fmt.Errorf("%d resources with id %s found, Expect 1", len(resources), resourceId)
}
}
func (self *SRegion) GetAllResByOrderId(orderId string) ([]SResource, error) {
order, err := self.GetOrder(orderId)
if err != nil {
return nil, err
}
return order.Resources, nil
}
func (self *SRegion) getAllResByType(orderId string, resourceType string) ([]SResource, error) {
res, err := self.GetAllResByOrderId(orderId)
if err != nil {
+6 -2
View File
@@ -338,6 +338,8 @@ func (self *SRegion) SyncSecurityGroup(secgroupId string, vpcId string, name str
secgroupId = extID
}
// 华为云默认deny。不需要显式指定
rules = SecurityRuleSetToAllowSet(rules)
return secgroupId, self.syncSecgroupRules(secgroupId, rules)
}
@@ -608,6 +610,7 @@ func (self *SRegion) addSecurityGroupRules(secGrpId string, rule *secrules.Secur
return nil
}
// todo: icmp协议目前存在差异,华为云能指定icmp code,onecloud不支持
func (self *SRegion) addSecurityGroupRule(secGrpId, direction, portStart, portEnd, protocol, ipNet string) error {
params := jsonutils.NewDict()
secgroupObj := jsonutils.NewDict()
@@ -615,10 +618,11 @@ func (self *SRegion) addSecurityGroupRule(secGrpId, direction, portStart, portEn
secgroupObj.Add(jsonutils.NewString(direction), "direction")
secgroupObj.Add(jsonutils.NewString(ipNet), "remote_ip_prefix")
secgroupObj.Add(jsonutils.NewString("IPV4"), "ethertype")
if len(portStart) > 0 && portStart != "0" {
// 端口为空或者1-65535
if len(portStart) > 0 && portStart != "0" && portStart != "-1" {
secgroupObj.Add(jsonutils.NewString(portStart), "port_range_min")
}
if len(portEnd) > 0 && portEnd != "0" {
if len(portEnd) > 0 && portEnd != "0" && portEnd != "-1" {
secgroupObj.Add(jsonutils.NewString(portEnd), "port_range_max")
}
if len(protocol) > 0 {
+30 -1
View File
@@ -13,6 +13,8 @@ https://support.huaweicloud.com/usermanual-vpc/zh-cn_topic_0073379079.html
import (
"net"
"sort"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/util/secrules"
)
@@ -68,6 +70,33 @@ func compatibleSecurityGroupRule(r SecurityGroupRule) bool {
return true
}
// 将安全组规则全部转换为等价的allow规则
func SecurityRuleSetToAllowSet(srs secrules.SecurityRuleSet) secrules.SecurityRuleSet {
inRuleSet := secrules.SecurityRuleSet{}
outRuleSet := secrules.SecurityRuleSet{}
for _, rule := range srs {
if rule.Direction == secrules.SecurityRuleIngress {
inRuleSet = append(inRuleSet, rule)
}
if rule.Direction == secrules.SecurityRuleEgress {
outRuleSet = append(outRuleSet, rule)
}
}
sort.Sort(inRuleSet)
sort.Sort(outRuleSet)
inRuleSet = inRuleSet.AllowList()
outRuleSet = outRuleSet.AllowList()
ret := secrules.SecurityRuleSet{}
ret = append(ret, inRuleSet...)
ret = append(ret, outRuleSet...)
return ret
}
func (self *SSecurityGroup) GetId() string {
return self.ID
}
@@ -192,7 +221,7 @@ func (self *SSecurityGroup) GetSecurityRule(ruleId string, withRuleId bool) (sec
} else {
desc = remoteRule.Description
}
// todo: icmp 可能不兼容
// todo: icmp 可能不兼容。华为云能指定icmp code,但是onecloud端不支持
rule := secrules.SecurityRule{
Priority: 1,
Action: secrules.SecurityRuleAllow,
+11 -1
View File
@@ -1,7 +1,10 @@
package huawei
import (
"fmt"
"time"
"yunion.io/x/log"
)
func (self *SRegion) waitTaskStatus(serviceType string, taskId string, targetStatus string, interval time.Duration, timeout time.Duration) error {
@@ -13,6 +16,8 @@ func (self *SRegion) waitTaskStatus(serviceType string, taskId string, targetSta
}
if status == targetStatus {
break
} else if status == TASK_FAIL {
return fmt.Errorf("task %s failed", taskId)
} else {
time.Sleep(interval)
}
@@ -27,7 +32,12 @@ func (self *SRegion) GetTaskStatus(serviceType string, taskId string) (string, e
return "", err
}
return task.GetString("status")
status, err := task.GetString("status")
if status == TASK_FAIL {
log.Debugf("task %s failed: %s", taskId, task.String())
}
return status, err
}
// https://support.huaweicloud.com/api-ecs/zh-cn_topic_0022225398.html
+2 -1
View File
@@ -11,6 +11,7 @@ import (
"yunion.io/x/onecloud/pkg/util/httputils"
"yunion.io/x/onecloud/pkg/util/huawei/client/manager"
"yunion.io/x/onecloud/pkg/util/huawei/client/responses"
"yunion.io/x/pkg/utils"
)
// 常用的方法
@@ -28,7 +29,7 @@ func unmarshalResult(resp jsonutils.JSONObject, respErr error, result interface{
if respErr != nil {
switch e := respErr.(type) {
case *httputils.JSONClientError:
if e.Code == 404 {
if e.Code == 404 || utils.IsInStringArray(e.Class, NOT_FOUND_CODES) {
return cloudprovider.ErrNotFound
}
return e
+6
View File
@@ -27,6 +27,7 @@ var FULLISO_TIME_REG *regexp.Regexp
var COMPACT_TIME_REG *regexp.Regexp
var MYSQL_TIME_REG *regexp.Regexp
var NORMAL_TIME_REG *regexp.Regexp
var FULLNORMAL_TIME_REG *regexp.Regexp
var RFC2882_TIME_REG *regexp.Regexp
var EMAIL_REG *regexp.Regexp
var CHINA_MOBILE_REG *regexp.Regexp
@@ -56,6 +57,7 @@ func init() {
COMPACT_TIME_REG = regexp.MustCompile(`^\d{14}$`)
MYSQL_TIME_REG = regexp.MustCompile(`^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$`)
NORMAL_TIME_REG = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$`)
FULLNORMAL_TIME_REG = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}$`)
RFC2882_TIME_REG = regexp.MustCompile(`[A-Z][a-z]{2}, [0-9]{1,2} [A-Z][a-z]{2} [0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2} [A-Z]{3}`)
EMAIL_REG = regexp.MustCompile(`^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$`)
CHINA_MOBILE_REG = regexp.MustCompile(`^1[0-9-]{10}$`)
@@ -171,6 +173,10 @@ func MatchNormalTime(str string) bool {
return NORMAL_TIME_REG.MatchString(str)
}
func MatchFullNormalTime(str string) bool {
return FULLNORMAL_TIME_REG.MatchString(str)
}
func MatchRFC2882Time(str string) bool {
return RFC2882_TIME_REG.MatchString(str)
}
+7
View File
@@ -26,6 +26,7 @@ const (
FullIsoTimeFormat = "2006-01-02T15:04:05.000000Z"
MysqlTimeFormat = "2006-01-02 15:04:05"
NormalTimeFormat = "2006-01-02T15:04:05"
FullNormalTimeFormat = "2006-01-02T15:04:05.000000"
CompactTimeFormat = "20060102150405"
DateFormat = "2006-01-02"
ShortDateFormat = "20060102"
@@ -84,6 +85,10 @@ func ParseNormalTime(str string) (time.Time, error) {
return time.Parse(NormalTimeFormat, str)
}
func ParseFullNormalTime(str string) (time.Time, error) {
return time.Parse(FullNormalTimeFormat, str)
}
func ParseCompactTime(str string) (time.Time, error) {
return time.Parse(CompactTimeFormat, str)
}
@@ -111,6 +116,8 @@ func ParseTimeStr(str string) (time.Time, error) {
return ParseMysqlTime(str)
} else if regutils.MatchNormalTime(str) {
return ParseNormalTime(str)
} else if regutils.MatchFullNormalTime(str) {
return ParseFullNormalTime(str)
} else if regutils.MatchRFC2882Time(str) {
return ParseRFC2882Time(str)
} else if regutils.MatchCompactTime(str) {