From ff2cca86112e926d276cab2a4e0fc53af926ecfe Mon Sep 17 00:00:00 2001 From: ioito Date: Thu, 9 May 2019 20:57:21 +0800 Subject: [PATCH] rebase master --- Gopkg.lock | 4 - cmd/climc/shell/cloudregions.go | 2 +- cmd/climc/shell/elasticips.go | 16 +- cmd/climc/shell/networks.go | 14 +- cmd/climc/shell/skus.go | 2 +- cmd/climc/shell/usages.go | 4 +- pkg/apis/compute/cloudaccount_const.go | 1 + pkg/apis/compute/guest_const.go | 5 +- pkg/apis/compute/host_const.go | 1 + pkg/apis/compute/storage_const.go | 6 +- pkg/cloudprovider/eip.go | 10 + pkg/cloudprovider/fakeregion.go | 2 +- pkg/cloudprovider/resources.go | 3 +- pkg/cloutpost/options/options.go | 2 +- pkg/compute/guestdrivers/base.go | 5 + pkg/compute/guestdrivers/esxi.go | 5 + pkg/compute/guestdrivers/managedvirtual.go | 4 + pkg/compute/guestdrivers/openstack.go | 8 + pkg/compute/guestdrivers/zstack.go | 26 +- pkg/compute/hostdrivers/zstack.go | 4 - pkg/compute/models/cloudsync.go | 5 +- pkg/compute/models/elasticips.go | 29 +- pkg/compute/models/guest_actions.go | 20 +- pkg/compute/models/guestdrivers.go | 1 + pkg/compute/models/guests.go | 2 + pkg/compute/models/networks.go | 12 + pkg/compute/models/purge.go | 20 ++ pkg/compute/models/regiondrivers.go | 2 + pkg/compute/models/skus.go | 2 +- pkg/compute/regiondrivers/kvm.go | 6 + pkg/compute/regiondrivers/managedvirtual.go | 4 + pkg/compute/regiondrivers/zstack.go | 73 +++++ pkg/compute/tasks/eip_allocate_task.go | 49 +++- pkg/mcclient/options/base.go | 2 +- pkg/mcclient/options/servers.go | 4 +- pkg/util/aliyun/eip.go | 11 +- pkg/util/aws/eip.go | 10 +- pkg/util/azure/classic_eip.go | 4 + pkg/util/azure/eip.go | 8 +- pkg/util/httputils/httputils.go | 2 +- pkg/util/huawei/eip.go | 4 + pkg/util/huawei/region.go | 22 +- pkg/util/openstack/region.go | 2 +- pkg/util/qcloud/eip.go | 10 +- pkg/util/ucloud/eip.go | 20 +- pkg/util/ucloud/shell/eip.go | 8 +- pkg/util/zstack/configuration.go | 14 + pkg/util/zstack/disk.go | 81 +++--- pkg/util/zstack/eip.go | 74 +++-- pkg/util/zstack/host.go | 225 +++++++++++++-- pkg/util/zstack/image.go | 108 ++++++- pkg/util/zstack/image_server.go | 46 +++ pkg/util/zstack/instance.go | 298 ++++++++++++++++++-- pkg/util/zstack/instancenic.go | 11 +- pkg/util/zstack/network.go | 79 ++++-- pkg/util/zstack/network_service.go | 117 ++++++++ pkg/util/zstack/offering.go | 34 ++- pkg/util/zstack/region.go | 67 +++-- pkg/util/zstack/securitygroup.go | 217 +++++++++++--- pkg/util/zstack/shell/configration.go | 19 ++ pkg/util/zstack/shell/disk.go | 25 +- pkg/util/zstack/shell/image.go | 27 ++ pkg/util/zstack/shell/image_server.go | 21 ++ pkg/util/zstack/shell/instance.go | 35 +++ pkg/util/zstack/shell/instance_offering.go | 23 ++ pkg/util/zstack/shell/network.go | 98 +++++++ pkg/util/zstack/shell/offering.go | 20 -- pkg/util/zstack/shell/storage.go | 4 +- pkg/util/zstack/shell/vip.go | 37 +++ pkg/util/zstack/snapshot.go | 17 +- pkg/util/zstack/storage.go | 237 +++++++++++++++- pkg/util/zstack/storage_ceph.go | 172 ----------- pkg/util/zstack/storage_local.go | 66 ++--- pkg/util/zstack/storage_primary.go | 79 ------ pkg/util/zstack/storagecache.go | 52 +++- pkg/util/zstack/vip.go | 70 +++++ pkg/util/zstack/vpc.go | 6 +- pkg/util/zstack/wire.go | 35 +-- pkg/util/zstack/zone.go | 37 +-- pkg/util/zstack/zstack.go | 50 +++- pkg/util/zstack/zstack_const.go | 4 - pkg/webconsole/session/remote_console.go | 3 +- 82 files changed, 2247 insertions(+), 717 deletions(-) create mode 100644 pkg/cloudprovider/eip.go create mode 100644 pkg/compute/regiondrivers/zstack.go create mode 100644 pkg/util/zstack/configuration.go create mode 100644 pkg/util/zstack/image_server.go create mode 100644 pkg/util/zstack/network_service.go create mode 100644 pkg/util/zstack/shell/configration.go create mode 100644 pkg/util/zstack/shell/image_server.go create mode 100644 pkg/util/zstack/shell/instance_offering.go delete mode 100644 pkg/util/zstack/shell/offering.go create mode 100644 pkg/util/zstack/shell/vip.go delete mode 100644 pkg/util/zstack/storage_ceph.go delete mode 100644 pkg/util/zstack/storage_primary.go create mode 100644 pkg/util/zstack/vip.go diff --git a/Gopkg.lock b/Gopkg.lock index 23446f995b..5631db5c25 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1799,11 +1799,7 @@ "utils", ] pruneopts = "UT" -<<<<<<< HEAD - revision = "740ce1e70d2417c195f669bbddf8ade8ec967419" -======= revision = "91e7c6f49631cea6aa6b65d6d3e9e779f13ebc1e" ->>>>>>> 细化存储信息 [[projects]] branch = "master" diff --git a/cmd/climc/shell/cloudregions.go b/cmd/climc/shell/cloudregions.go index 2cfe30953c..2d319f59bf 100644 --- a/cmd/climc/shell/cloudregions.go +++ b/cmd/climc/shell/cloudregions.go @@ -47,7 +47,7 @@ func init() { type CloudregionCityListOptions struct { Manager string `help:"List objects belonging to the cloud provider"` Account string `help:"List objects belonging to the cloud account"` - Provider string `help:"List objects from the provider" choices:"VMware|Aliyun|Qcloud|Azure|Aws|Huawei|Openstack"` + Provider string `help:"List objects from the provider" choices:"VMware|Aliyun|Qcloud|Azure|Aws|Huawei|Openstack|Ucloud|ZStack"` City string `help:"List regions in the specified city"` PublicCloud *bool `help:"List objects belonging to public cloud" json:"public_cloud"` diff --git a/cmd/climc/shell/elasticips.go b/cmd/climc/shell/elasticips.go index 89aa48efb1..e20fd0d62c 100644 --- a/cmd/climc/shell/elasticips.go +++ b/cmd/climc/shell/elasticips.go @@ -47,7 +47,9 @@ func init() { MANAGER string `help:"cloud provider"` REGION string `help:"cloud region in which EIP is allocated"` NAME string `help:"name of the EIP"` - BW int `help:"Bandwidth in Mbps"` + Bandwidth int `help:"Bandwidth in Mbps"` + Ip string `help:"IP address of the EIP"` + Network string `help:"Network of the EIP"` ChargeType string `help:"bandwidth charge type, either traffic or bandwidth" choices:"traffic|bandwidth"` } R(&EipCreateOptions{}, "eip-create", "Create an EIP", func(s *mcclient.ClientSession, args *EipCreateOptions) error { @@ -55,12 +57,22 @@ func init() { params.Add(jsonutils.NewString(args.MANAGER), "manager") params.Add(jsonutils.NewString(args.REGION), "region") params.Add(jsonutils.NewString(args.NAME), "name") - params.Add(jsonutils.NewInt(int64(args.BW)), "bandwidth") + if args.Bandwidth != 0 { + params.Add(jsonutils.NewInt(int64(args.Bandwidth)), "bandwidth") + } if len(args.ChargeType) > 0 { params.Add(jsonutils.NewString(args.ChargeType), "charge_type") } + if len(args.Network) > 0 { + params.Add(jsonutils.NewString(args.Network), "network") + } + + if len(args.Ip) > 0 { + params.Add(jsonutils.NewString(args.Ip), "ip") + } + result, err := modules.Elasticips.Create(s, params) if err != nil { return err diff --git a/cmd/climc/shell/networks.go b/cmd/climc/shell/networks.go index f907ff3450..d32b52fc32 100644 --- a/cmd/climc/shell/networks.go +++ b/cmd/climc/shell/networks.go @@ -234,16 +234,16 @@ func init() { }) type NetworkCreateOptions2 struct { - Wire string `help:"ID or Name of wire in which the network is created"` - Vpc string `help:"ID or Name of vpc in which the network is created"` - Zone string `help:"ID or Name of zone in which the network is created"` - NETWORK string `help:"Name of new network"` - PREFIX string `help:"Start of IPv4 address range"` - Desc string `help:"Description" metavar:"DESCRIPTION"` + Wire string `help:"ID or Name of wire in which the network is created"` + Vpc string `help:"ID or Name of vpc in which the network is created"` + Zone string `help:"ID or Name of zone in which the network is created"` + NAME string `help:"Name of new network"` + PREFIX string `help:"Start of IPv4 address range"` + Desc string `help:"Description" metavar:"DESCRIPTION"` } R(&NetworkCreateOptions2{}, "network-create2", "Create a virtual network", func(s *mcclient.ClientSession, args *NetworkCreateOptions2) error { params := jsonutils.NewDict() - params.Add(jsonutils.NewString(args.NETWORK), "name") + params.Add(jsonutils.NewString(args.NAME), "name") params.Add(jsonutils.NewString(args.PREFIX), "guest_ip_prefix") if len(args.Desc) > 0 { params.Add(jsonutils.NewString(args.Desc), "description") diff --git a/cmd/climc/shell/skus.go b/cmd/climc/shell/skus.go index a0a13bae66..b377fc2a4c 100644 --- a/cmd/climc/shell/skus.go +++ b/cmd/climc/shell/skus.go @@ -150,7 +150,7 @@ func init() { }) type ServerSkuSpecsListOptions struct { - Provider string `help:"List objects from the provider" choices:"OneCloud|VMware|Aliyun|Qcloud|Azure|Aws|Huawei|Openstack|Ucloud" json:"provider"` + Provider string `help:"List objects from the provider" choices:"OneCloud|VMware|Aliyun|Qcloud|Azure|Aws|Huawei|Openstack|Ucloud|ZStack" json:"provider"` PublicCloud *bool `help:"List objects belonging to public cloud" json:"public_cloud"` Zone string `help:"zone Id or name"` PostpaidStatus *string `help:"skus available status for postpaid instance" choices:"available|soldout"` diff --git a/cmd/climc/shell/usages.go b/cmd/climc/shell/usages.go index bc2cdda190..863f76f00f 100644 --- a/cmd/climc/shell/usages.go +++ b/cmd/climc/shell/usages.go @@ -22,8 +22,8 @@ import ( ) type GeneralUsageOptions struct { - HostType []string `help:"Host types" choices:"hypervisor|baremetal|esxi|xen|kubelet|hyperv|aliyun|azure|aws|huawei|qcloud"` - Provider []string `help:"Provider" choices:"VMware|Aliyun|Azure|Aws|Qcloud|Huawei"` + HostType []string `help:"Host types" choices:"hypervisor|baremetal|esxi|xen|kubelet|hyperv|aliyun|azure|aws|huawei|qcloud|openstack|ucloud|zstack"` + Provider []string `help:"Provider" choices:"VMware|Aliyun|Azure|Aws|Qcloud|Huawei|OpenStack|Ucloud|ZStack"` Project string } diff --git a/pkg/apis/compute/cloudaccount_const.go b/pkg/apis/compute/cloudaccount_const.go index 8d2e17d67b..b45fa2552c 100644 --- a/pkg/apis/compute/cloudaccount_const.go +++ b/pkg/apis/compute/cloudaccount_const.go @@ -59,6 +59,7 @@ var ( CLOUD_PROVIDER_HUAWEI, CLOUD_PROVIDER_OPENSTACK, CLOUD_PROVIDER_UCLOUD, + CLOUD_PROVIDER_ZSTACK, } ) diff --git a/pkg/apis/compute/guest_const.go b/pkg/apis/compute/guest_const.go index 75d351f8cc..da6d5678ef 100644 --- a/pkg/apis/compute/guest_const.go +++ b/pkg/apis/compute/guest_const.go @@ -191,8 +191,9 @@ var HOSTTYPE_HYPERVISOR = map[string]string{ } const ( - VM_AWS_DEFAULT_LOGIN_USER = "ec2user" - VM_AZURE_DEFAULT_LOGIN_USER = "toor" + VM_AWS_DEFAULT_LOGIN_USER = "ec2user" + VM_AZURE_DEFAULT_LOGIN_USER = "toor" + VM_ZSTACK_DEFAULT_LOGIN_USER = "root" VM_METADATA_APP_TAGS = "app_tags" VM_METADATA_CREATE_PARAMS = "create_params" diff --git a/pkg/apis/compute/host_const.go b/pkg/apis/compute/host_const.go index 099f25d971..39ea8db354 100644 --- a/pkg/apis/compute/host_const.go +++ b/pkg/apis/compute/host_const.go @@ -87,6 +87,7 @@ var HOST_TYPES = []string{ HOST_TYPE_QCLOUD, HOST_TYPE_HUAWEI, HOST_TYPE_OPENSTACK, + HOST_TYPE_UCLOUD, HOST_TYPE_ZSTACK, } diff --git a/pkg/apis/compute/storage_const.go b/pkg/apis/compute/storage_const.go index 83aa529dcd..bd8cadc65d 100644 --- a/pkg/apis/compute/storage_const.go +++ b/pkg/apis/compute/storage_const.go @@ -66,9 +66,7 @@ const ( // Zstack storage type STORAGE_ZSTACK_LOCAL_STORAGE = "localstorage" - STORAGE_ZSTACK_ROOT = "root" - STORAGE_ZSTACK_DATA = "data" - STORAGE_ZSTACK_IMAGECACHE = "imagecache" + STORAGE_ZSTACK_CEPH = "ceph" ) const ( @@ -100,7 +98,7 @@ var ( STORAGE_HUAWEI_SSD, STORAGE_HUAWEI_SAS, STORAGE_HUAWEI_SATA, STORAGE_OPENSTACK_ISCSI, STORAGE_UCLOUD_CLOUD_NORMAL, STORAGE_UCLOUD_CLOUD_SSD, STORAGE_UCLOUD_LOCAL_NORMAL, STORAGE_UCLOUD_LOCAL_SSD, STORAGE_UCLOUD_EXCLUSIVE_LOCAL_DISK, - STORAGE_ZSTACK_LOCAL_STORAGE, STORAGE_ZSTACK_ROOT, STORAGE_ZSTACK_DATA, STORAGE_ZSTACK_IMAGECACHE, + STORAGE_ZSTACK_LOCAL_STORAGE, STORAGE_ZSTACK_CEPH, } STORAGE_LIMITED_TYPES = []string{STORAGE_LOCAL, STORAGE_BAREMETAL, STORAGE_NAS, STORAGE_RBD, STORAGE_NFS} diff --git a/pkg/cloudprovider/eip.go b/pkg/cloudprovider/eip.go new file mode 100644 index 0000000000..6153f448bc --- /dev/null +++ b/pkg/cloudprovider/eip.go @@ -0,0 +1,10 @@ +package cloudprovider + +type SEip struct { + Name string + BandwidthMbps int + ChargeType string + BGPType string + NetworkExternalId string + IP string +} diff --git a/pkg/cloudprovider/fakeregion.go b/pkg/cloudprovider/fakeregion.go index c056d82b4c..ce3c10326f 100644 --- a/pkg/cloudprovider/fakeregion.go +++ b/pkg/cloudprovider/fakeregion.go @@ -82,7 +82,7 @@ func (region *SFakeOnPremiseRegion) CreateIVpc(name string, desc string, cidr st return nil, ErrNotSupported } -func (region *SFakeOnPremiseRegion) CreateEIP(name string, bwMbps int, chargeType string, bgpType string) (ICloudEIP, error) { +func (region *SFakeOnPremiseRegion) CreateEIP(eip *SEip) (ICloudEIP, error) { return nil, ErrNotSupported } diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index f05cf2a719..30d2d5930e 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -67,7 +67,7 @@ type ICloudRegion interface { SyncSecurityGroup(secgroupId string, vpcId string, name string, desc string, rules []secrules.SecurityRule) (string, error) CreateIVpc(name string, desc string, cidr string) (ICloudVpc, error) - CreateEIP(name string, bwMbps int, chargeType string, bgpType string) (ICloudEIP, error) + CreateEIP(eip *SEip) (ICloudEIP, error) GetISnapshots() ([]ICloudSnapshot, error) GetISnapshotById(snapshotId string) (ICloudSnapshot, error) @@ -275,6 +275,7 @@ type ICloudEIP interface { GetIpAddr() string GetMode() string + GetINetworkId() string GetAssociationType() string GetAssociationExternalId() string diff --git a/pkg/cloutpost/options/options.go b/pkg/cloutpost/options/options.go index 925b83b799..99ce6b1320 100644 --- a/pkg/cloutpost/options/options.go +++ b/pkg/cloutpost/options/options.go @@ -20,7 +20,7 @@ import ( ) type CloudSyncOptions struct { - Provider string `help:"Public cloud provider" choices:"Aliyun|Azure|Aws|Qcloud"` + Provider string `help:"Public cloud provider" choices:"Aliyun|Azure|Aws|Qcloud|OpenStack|Ucloud|Huawei|ZStack"` Environment string `help:"environment of public cloud"` Cloudregion string `help:"region of public cloud"` Zone string `help:"availability zone of public cloud"` diff --git a/pkg/compute/guestdrivers/base.go b/pkg/compute/guestdrivers/base.go index 1c7952f61e..f880054d10 100644 --- a/pkg/compute/guestdrivers/base.go +++ b/pkg/compute/guestdrivers/base.go @@ -27,6 +27,7 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/billing" ) @@ -120,6 +121,10 @@ func (self *SBaseGuestDriver) GetChangeConfigStatus() ([]string, error) { return []string{}, fmt.Errorf("This Guest driver dose not implement GetChangeConfigStatus") } +func (self *SBaseGuestDriver) ValidateCreateEip(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) error { + return httperrors.NewInputParameterError("Not Implement ValidateCreateEip") +} + func (self *SBaseGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *models.SDisk, storage *models.SStorage) error { return fmt.Errorf("This Guest driver dose not implement ValidateResizeDisk") } diff --git a/pkg/compute/guestdrivers/esxi.go b/pkg/compute/guestdrivers/esxi.go index b66f74f822..469c2864b9 100644 --- a/pkg/compute/guestdrivers/esxi.go +++ b/pkg/compute/guestdrivers/esxi.go @@ -27,6 +27,7 @@ import ( "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/compute/options" + "yunion.io/x/onecloud/pkg/httperrors" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/util/billing" "yunion.io/x/onecloud/pkg/util/httputils" @@ -104,6 +105,10 @@ func (self *SESXiGuestDriver) GetDeployStatus() ([]string, error) { return []string{api.VM_READY}, nil } +func (self *SESXiGuestDriver) ValidateCreateEip(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) error { + return httperrors.NewInputParameterError("%s not support create eip", self.GetHypervisor()) +} + func (self *SESXiGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *models.SDisk, storage *models.SStorage) error { if !utils.IsInStringArray(guest.Status, []string{api.VM_READY}) { return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status) diff --git a/pkg/compute/guestdrivers/managedvirtual.go b/pkg/compute/guestdrivers/managedvirtual.go index f69353899f..b480b66281 100644 --- a/pkg/compute/guestdrivers/managedvirtual.go +++ b/pkg/compute/guestdrivers/managedvirtual.go @@ -165,6 +165,10 @@ func (self *SManagedVirtualizedGuestDriver) ValidateCreateData(ctx context.Conte return input, nil } +func (self *SManagedVirtualizedGuestDriver) ValidateCreateEip(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) error { + return nil +} + func (self *SManagedVirtualizedGuestDriver) RequestDetachDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error { return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId()) } diff --git a/pkg/compute/guestdrivers/openstack.go b/pkg/compute/guestdrivers/openstack.go index f1b43cd348..2b948d448f 100644 --- a/pkg/compute/guestdrivers/openstack.go +++ b/pkg/compute/guestdrivers/openstack.go @@ -17,6 +17,7 @@ package guestdrivers import ( "context" + "yunion.io/x/jsonutils" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/compute/options" @@ -90,6 +91,10 @@ func (self *SOpenStackGuestDriver) GetDeployStatus() ([]string, error) { return []string{api.VM_RUNNING}, nil } +func (self *SOpenStackGuestDriver) ValidateCreateEip(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) error { + return httperrors.NewInputParameterError("%s not support create eip, it only support bind eip", self.GetHypervisor()) +} + func (self *SOpenStackGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput) (*api.ServerCreateInput, error) { var err error input, err = self.SManagedVirtualizedGuestDriver.ValidateCreateData(ctx, userCred, input) @@ -99,6 +104,9 @@ func (self *SOpenStackGuestDriver) ValidateCreateData(ctx context.Context, userC if len(input.Networks) >= 2 { return nil, httperrors.NewInputParameterError("cannot support more than 1 nic") } + if len(input.Eip) > 0 || input.EipBw > 0 { + return nil, httperrors.NewUnsupportOperationError("%s not support create virtual machine with eip", self.GetHypervisor()) + } return input, nil } diff --git a/pkg/compute/guestdrivers/zstack.go b/pkg/compute/guestdrivers/zstack.go index 01d5c915cd..18041225e3 100644 --- a/pkg/compute/guestdrivers/zstack.go +++ b/pkg/compute/guestdrivers/zstack.go @@ -18,6 +18,7 @@ import ( "context" "fmt" + "yunion.io/x/jsonutils" "yunion.io/x/pkg/utils" api "yunion.io/x/onecloud/pkg/apis/compute" @@ -42,6 +43,10 @@ func (self *SZStackGuestDriver) GetHypervisor() string { return api.HYPERVISOR_ZSTACK } +func (self *SZStackGuestDriver) GetProvider() string { + return api.CLOUD_PROVIDER_ZSTACK +} + func (self *SZStackGuestDriver) GetDefaultSysDiskBackend() string { return api.STORAGE_ZSTACK_LOCAL_STORAGE } @@ -53,11 +58,14 @@ func (self *SZStackGuestDriver) GetMinimalSysDiskSizeGb() int { func (self *SZStackGuestDriver) GetStorageTypes() []string { return []string{ api.STORAGE_ZSTACK_LOCAL_STORAGE, - api.STORAGE_ZSTACK_DATA, - api.STORAGE_ZSTACK_ROOT, + api.STORAGE_ZSTACK_CEPH, } } +func (self *SZStackGuestDriver) GetMaxSecurityGroupCount() int { + return 1 +} + func (self *SZStackGuestDriver) ChooseHostStorage(host *models.SHost, backend string, storageIds []string) *models.SStorage { return self.chooseHostStorage(self, host, backend, storageIds) } @@ -86,9 +94,6 @@ func (self *SZStackGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk *m if !utils.IsInStringArray(guest.Status, []string{api.VM_READY, api.VM_RUNNING}) { return fmt.Errorf("Cannot resize disk when guest in status %s", guest.Status) } - if !utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_ZSTACK_LOCAL_STORAGE, api.STORAGE_ZSTACK_ROOT, api.STORAGE_ZSTACK_DATA}) { - return fmt.Errorf("Cannot resize %s disk", storage.StorageType) - } return nil } @@ -96,6 +101,10 @@ func (self *SZStackGuestDriver) RequestDetachDisk(ctx context.Context, guest *mo return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId()) } +func (self *SZStackGuestDriver) ValidateCreateEip(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) error { + return httperrors.NewInputParameterError("%s not support create eip, it only support bind eip", self.GetHypervisor()) +} + func (self *SZStackGuestDriver) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *api.ServerCreateInput) (*api.ServerCreateInput, error) { input, err := self.SManagedVirtualizedGuestDriver.ValidateCreateData(ctx, userCred, input) if err != nil { @@ -104,6 +113,9 @@ func (self *SZStackGuestDriver) ValidateCreateData(ctx context.Context, userCred if len(input.Networks) > 2 { return nil, httperrors.NewInputParameterError("cannot support more than 1 nic") } + if len(input.Eip) > 0 || input.EipBw > 0 { + return nil, httperrors.NewUnsupportOperationError("%s not support create virtual machine with eip", self.GetHypervisor()) + } return input, nil } @@ -115,6 +127,10 @@ func (self *SZStackGuestDriver) GetGuestInitialStateAfterRebuild() string { return api.VM_READY } +func (self *SZStackGuestDriver) IsNeedInjectPasswordByCloudInit() bool { + return true +} + func (self *SZStackGuestDriver) GetLinuxDefaultAccount(desc cloudprovider.SManagedVMCreateConfig) string { userName := "root" if desc.ImageType == "system" && desc.OsType == "Windows" { diff --git a/pkg/compute/hostdrivers/zstack.go b/pkg/compute/hostdrivers/zstack.go index f87e635e5d..0abd38f5f6 100644 --- a/pkg/compute/hostdrivers/zstack.go +++ b/pkg/compute/hostdrivers/zstack.go @@ -16,7 +16,6 @@ package hostdrivers import ( "yunion.io/x/jsonutils" - "yunion.io/x/pkg/utils" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/compute/models" @@ -41,8 +40,5 @@ func (self *SZStackHostDriver) ValidateAttachStorage(host *models.SHost, storage } func (self *SZStackHostDriver) ValidateDiskSize(storage *models.SStorage, sizeGb int) error { - if utils.IsInStringArray(storage.StorageType, []string{api.STORAGE_ZSTACK_IMAGECACHE, api.STORAGE_ZSTACK_ROOT}) { - return httperrors.NewUnsupportOperationError("Not support create %s disk", storage.StorageType) - } return nil } diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index 3ec8a0b010..5c058489ff 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -793,10 +793,11 @@ func syncPublicCloudProviderInfo( // no need to lock public cloud region as cloud region for public cloud is readonly - syncRegionEips(ctx, userCred, syncResults, provider, localRegion, remoteRegion, syncRange) - + // 需要先同步vpc,避免私有云eip找不到network syncRegionVPCs(ctx, userCred, syncResults, provider, localRegion, remoteRegion, syncRange) + syncRegionEips(ctx, userCred, syncResults, provider, localRegion, remoteRegion, syncRange) + for j := 0; j < len(localZones); j += 1 { if len(syncRange.Zone) > 0 && !utils.IsInStringArray(localZones[j].Id, syncRange.Zone) { diff --git a/pkg/compute/models/elasticips.go b/pkg/compute/models/elasticips.go index b040acfe5b..f571d36570 100644 --- a/pkg/compute/models/elasticips.go +++ b/pkg/compute/models/elasticips.go @@ -19,6 +19,7 @@ import ( "database/sql" "fmt" + "github.com/golang-plus/errors" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/tristate" @@ -60,14 +61,15 @@ type SElasticip struct { SManagedResourceBase SBillingResourceBase - Mode string `width:"32" charset:"ascii" list:"user"` + NetworkId string `width:"36" charset:"ascii" nullable:"false" get:"user" list:"user" create:"optional"` + Mode string `width:"32" charset:"ascii" list:"user"` - IpAddr string `width:"17" charset:"ascii" list:"user"` + IpAddr string `width:"17" charset:"ascii" list:"user" create:"optional"` AssociateType string `width:"32" charset:"ascii" list:"user"` AssociateId string `width:"256" charset:"ascii" list:"user"` - Bandwidth int `list:"user" create:"required"` + Bandwidth int `list:"user" create:"optional" default:"0"` ChargeType string `name:"charge_type" list:"user" create:"required"` BgpType string `list:"user" create:"optional"` // 目前只有华为云此字段是必需填写的。 @@ -362,6 +364,15 @@ func (manager *SElasticipManager) newFromCloudEip(ctx context.Context, userCred eip.ManagerId = provider.Id eip.CloudregionId = region.Id eip.ChargeType = extEip.GetInternetChargeType() + if networkId := extEip.GetINetworkId(); len(networkId) > 0 { + network, err := NetworkManager.FetchByExternalId(networkId) + if err != nil { + msg := fmt.Sprintf("failed to found network by externalId %s error: %v", networkId, err) + log.Errorf(msg) + return nil, errors.New(msg) + } + eip.NetworkId = network.GetId() + } err = manager.TableSpec().Insert(&eip) if err != nil { @@ -484,7 +495,7 @@ func (manager *SElasticipManager) ValidateCreateData(ctx context.Context, userCr if len(regionStr) == 0 { return nil, httperrors.NewMissingParameterError("region_id") } - region, err := CloudregionManager.FetchByIdOrName(nil, regionStr) + _region, err := CloudregionManager.FetchByIdOrName(nil, regionStr) if err != nil { if err != sql.ErrNoRows { return nil, httperrors.NewGeneralError(err) @@ -492,6 +503,7 @@ func (manager *SElasticipManager) ValidateCreateData(ctx context.Context, userCr return nil, httperrors.NewResourceNotFoundError("Region %s not found", regionStr) } } + region := _region.(*SCloudregion) data.Add(jsonutils.NewString(region.GetId()), "cloudregion_id") managerStr := jsonutils.GetAnyString(data, []string{"manager", "manager_id"}) @@ -532,13 +544,18 @@ func (manager *SElasticipManager) ValidateCreateData(ctx context.Context, userCr return nil, httperrors.NewOutOfQuotaError("Out of eip quota: %s", err) } - return data, nil + return region.GetDriver().ValidateCreateEipData(ctx, userCred, data) } func (self *SElasticip) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data jsonutils.JSONObject) { self.SVirtualResourceBase.PostCreate(ctx, userCred, ownerProjId, query, data) + params := jsonutils.NewDict() + if data.Contains("ip") { + ip, _ := data.GetString("ip") + params.Add(jsonutils.NewString(ip), "ip") + } eipPendingUsage := &SQuota{Eip: 1} - self.startEipAllocateTask(ctx, userCred, nil, eipPendingUsage) + self.startEipAllocateTask(ctx, userCred, params, eipPendingUsage) } func (self *SElasticip) startEipAllocateTask(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, pendingUsage quotas.IQuota) error { diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index c32b4637a7..4abf5a7d8c 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -2434,16 +2434,19 @@ func (self *SGuest) AllowPerformCreateEip(ctx context.Context, userCred mcclient } func (self *SGuest) PerformCreateEip(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { - bw, err := data.Int("bandwidth") - if err != nil { - return nil, httperrors.NewMissingParameterError("bandwidth") - } - + var bw int64 chargeType, _ := data.GetString("charge_type") if len(chargeType) == 0 { chargeType = api.EIP_CHARGE_TYPE_DEFAULT } + if chargeType == api.EIP_CHARGE_TYPE_BY_BANDWIDTH { + bw, _ = data.Int("bandwidth") + if bw == 0 { + return nil, httperrors.NewMissingParameterError("bandwidth") + } + } + if len(self.ExternalId) == 0 { return nil, httperrors.NewInvalidStatusError("Not a managed VM") } @@ -2452,7 +2455,7 @@ func (self *SGuest) PerformCreateEip(ctx context.Context, userCred mcclient.Toke return nil, httperrors.NewInvalidStatusError("No host???") } - _, err = host.GetDriver() + _, err := host.GetDriver() if err != nil { return nil, httperrors.NewInvalidStatusError("No valid cloud provider") } @@ -2462,6 +2465,11 @@ func (self *SGuest) PerformCreateEip(ctx context.Context, userCred mcclient.Toke return nil, httperrors.NewInvalidStatusError("No cloudregion???") } + err = self.GetDriver().ValidateCreateEip(ctx, userCred, data) + if err != nil { + return nil, err + } + eipPendingUsage := &SQuota{Eip: 1} err = QuotaManager.CheckSetPendingQuota(ctx, userCred, userCred.GetProjectId(), eipPendingUsage) if err != nil { diff --git a/pkg/compute/models/guestdrivers.go b/pkg/compute/models/guestdrivers.go index 004827a6d2..e2219d4848 100644 --- a/pkg/compute/models/guestdrivers.go +++ b/pkg/compute/models/guestdrivers.go @@ -151,6 +151,7 @@ type IGuestDriver interface { RequestSyncToBackup(ctx context.Context, guest *SGuest, task taskman.ITask) error IsSupportEip() bool + ValidateCreateEip(ctx context.Context, userCred mcclient.TokenCredential, data jsonutils.JSONObject) error NeedStopForChangeSpec(guest *SGuest) bool diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index 6d652da488..b727e5ccc2 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -3056,6 +3056,8 @@ func (self *SGuest) GetDeployConfigOnHost(ctx context.Context, userCred mcclient if strings.HasSuffix(host.Name, "-classic") { registerVpcId, externalVpcId = "classic", "classic" } + case api.HYPERVISOR_ZSTACK: + break default: return nil, fmt.Errorf("Unknown guest %s hypervisor %s for sync secgroup", self.Name, self.Hypervisor) } diff --git a/pkg/compute/models/networks.go b/pkg/compute/models/networks.go index 7afe75d154..20e64df3a9 100644 --- a/pkg/compute/models/networks.go +++ b/pkg/compute/models/networks.go @@ -171,6 +171,11 @@ func (self *SNetwork) GetTotalNicCount() (int, error) { return -1, err } total += cnt + cnt, err = self.GetEipsCount() + if err != nil { + return -1, err + } + total += cnt return total, nil } @@ -194,6 +199,10 @@ func (self *SNetwork) GetLoadbalancerIpsCount() (int, error) { return LoadbalancernetworkManager.Query().Equals("network_id", self.Id).CountWithError() } +func (self *SNetwork) GetEipsCount() (int, error) { + return ElasticipManager.Query().Equals("network_id", self.Id).CountWithError() +} + func (self *SNetwork) GetUsedAddresses() map[string]bool { used := make(map[string]bool) @@ -203,6 +212,7 @@ func (self *SNetwork) GetUsedAddresses() map[string]bool { HostnetworkManager.Query().SubQuery(), ReservedipManager.Query().SubQuery(), LoadbalancernetworkManager.Query().SubQuery(), + ElasticipManager.Query().SubQuery(), } { q := tbl.Query(tbl.Field("ip_addr")).Equals("network_id", self.Id) rows, err := q.Rows() @@ -883,6 +893,8 @@ func (self *SNetwork) getMoreDetails(ctx context.Context, extra *jsonutils.JSOND extra.Add(jsonutils.NewInt(int64(bmVnics)), "bm_vnics") lbVnics, _ := self.GetLoadbalancerIpsCount() extra.Add(jsonutils.NewInt(int64(lbVnics)), "lb_vnics") + eips, _ := self.GetEipsCount() + extra.Add(jsonutils.NewInt(int64(eips)), "eip_vnics") groupVnics, _ := self.GetGroupNicsCount() extra.Add(jsonutils.NewInt(int64(groupVnics)), "group_vnics") reserveVnics, _ := self.GetReservedNicsCount() diff --git a/pkg/compute/models/purge.go b/pkg/compute/models/purge.go index e638c2da7b..a5f049c090 100644 --- a/pkg/compute/models/purge.go +++ b/pkg/compute/models/purge.go @@ -552,6 +552,22 @@ func (net *SNetwork) purgeLoadbalancernetworks(ctx context.Context, userCred mcc return nil } +func (net *SNetwork) purgeEipnetworks(ctx context.Context, userCred mcclient.TokenCredential) error { + q := ElasticipManager.Query().Equals("network_id", net.Id) + eips := make([]SElasticip, 0) + err := db.FetchModelObjects(ElasticipManager, q, &eips) + if err != nil { + return err + } + for i := range eips { + err = eips[i].RealDelete(ctx, userCred) + if err != nil { + return err + } + } + return nil +} + func (net *SNetwork) purgeReservedIps(ctx context.Context, userCred mcclient.TokenCredential) error { q := ReservedipManager.Query().Equals("network_id", net.Id) rips := make([]SReservedip, 0) @@ -588,6 +604,10 @@ func (net *SNetwork) purge(ctx context.Context, userCred mcclient.TokenCredentia if err != nil { return err } + err = net.purgeEipnetworks(ctx, userCred) + if err != nil { + return err + } err = net.purgeReservedIps(ctx, userCred) if err != nil { return err diff --git a/pkg/compute/models/regiondrivers.go b/pkg/compute/models/regiondrivers.go index c761603f28..e5f55b1d31 100644 --- a/pkg/compute/models/regiondrivers.go +++ b/pkg/compute/models/regiondrivers.go @@ -73,6 +73,8 @@ type IRegionDriver interface { RequestDeleteLoadbalancerListenerRule(ctx context.Context, userCred mcclient.TokenCredential, lbr *SLoadbalancerListenerRule, task taskman.ITask) error ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) + + ValidateCreateEipData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) } var regionDrivers map[string]IRegionDriver diff --git a/pkg/compute/models/skus.go b/pkg/compute/models/skus.go index 89ae41ffa0..14c536cfb6 100644 --- a/pkg/compute/models/skus.go +++ b/pkg/compute/models/skus.go @@ -541,7 +541,7 @@ func providerFilter(q *sqlchemy.SQuery, provider string, public_cloud bool) *sql q = q.Equals("provider", provider) } else if public_cloud { q = q.IsNotEmpty("provider") - q = q.NotIn("provider", []string{api.CLOUD_PROVIDER_OPENSTACK}) + q = q.NotIn("provider", []string{api.CLOUD_PROVIDER_OPENSTACK, api.CLOUD_PROVIDER_ZSTACK}) } else { q = q.Filter(sqlchemy.OR( sqlchemy.IsNull(q.Field("provider")), diff --git a/pkg/compute/regiondrivers/kvm.go b/pkg/compute/regiondrivers/kvm.go index 5a36b23446..fc6252c729 100644 --- a/pkg/compute/regiondrivers/kvm.go +++ b/pkg/compute/regiondrivers/kvm.go @@ -18,6 +18,8 @@ import ( "context" "fmt" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/pkg/utils" @@ -304,3 +306,7 @@ func (self *SKVMRegionDriver) RequestDeleteLoadbalancerListenerRule(ctx context. func (self *SKVMRegionDriver) ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { return data, nil } + +func (self *SKVMRegionDriver) ValidateCreateEipData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewNotImplementedError("Not Implement EIP") +} diff --git a/pkg/compute/regiondrivers/managedvirtual.go b/pkg/compute/regiondrivers/managedvirtual.go index c9f96efa77..b4e2e94f20 100644 --- a/pkg/compute/regiondrivers/managedvirtual.go +++ b/pkg/compute/regiondrivers/managedvirtual.go @@ -828,3 +828,7 @@ func (self *SManagedVirtualizationRegionDriver) RequestDeleteLoadbalancerListene func (self *SManagedVirtualizationRegionDriver) ValidateCreateVpcData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { return data, nil } + +func (self *SManagedVirtualizationRegionDriver) ValidateCreateEipData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return data, nil +} diff --git a/pkg/compute/regiondrivers/zstack.go b/pkg/compute/regiondrivers/zstack.go new file mode 100644 index 0000000000..191032938b --- /dev/null +++ b/pkg/compute/regiondrivers/zstack.go @@ -0,0 +1,73 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package regiondrivers + +import ( + "context" + + "yunion.io/x/jsonutils" + api "yunion.io/x/onecloud/pkg/apis/compute" + "yunion.io/x/onecloud/pkg/cloudcommon/validators" + "yunion.io/x/onecloud/pkg/compute/models" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/onecloud/pkg/mcclient" +) + +type SZStackRegionDriver struct { + SManagedVirtualizationRegionDriver +} + +func init() { + driver := SZStackRegionDriver{} + models.RegisterRegionDriver(&driver) +} + +func (self *SZStackRegionDriver) GetProvider() string { + return api.CLOUD_PROVIDER_ZSTACK +} + +func (self *SZStackRegionDriver) ValidateCreateLoadbalancerData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewNotImplementedError("%s does not currently support creating loadbalancer", self.GetProvider()) +} + +func (self *SZStackRegionDriver) ValidateCreateLoadbalancerAclData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewNotImplementedError("%s does not currently support creating loadbalancer acl", self.GetProvider()) +} + +func (self *SZStackRegionDriver) ValidateCreateLoadbalancerCertificateData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + return nil, httperrors.NewNotImplementedError("%s does not currently support creating loadbalancer certificate", self.GetProvider()) +} + +func (self *SZStackRegionDriver) ValidateCreateEipData(ctx context.Context, userCred mcclient.TokenCredential, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { + networkV := validators.NewModelIdOrNameValidator("network", "network", "") + err := networkV.Validate(data) + if err != nil { + return nil, err + } + network := networkV.Model.(*models.SNetwork) + + vpc := network.GetVpc() + if vpc == nil { + return nil, httperrors.NewInputParameterError("failed to found vpc for network %s(%s)", network.Name, network.Id) + } + region, err := vpc.GetRegion() + if err != nil { + return nil, err + } + if region.GetDriver().GetProvider() != self.GetProvider() { + return nil, httperrors.NewUnsupportOperationError("network %s(%s) does not belong to %s", network.Name, network.Id, self.GetProvider()) + } + return data, nil +} diff --git a/pkg/compute/tasks/eip_allocate_task.go b/pkg/compute/tasks/eip_allocate_task.go index b4126f0714..93debe527a 100644 --- a/pkg/compute/tasks/eip_allocate_task.go +++ b/pkg/compute/tasks/eip_allocate_task.go @@ -18,11 +18,14 @@ import ( "context" "fmt" + "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/jsonutils" "yunion.io/x/log" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/db" + "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/compute/models" "yunion.io/x/onecloud/pkg/util/logclient" @@ -79,7 +82,51 @@ func (self *EipAllocateTask) OnInit(ctx context.Context, obj db.IStandaloneModel return } - extEip, err := iregion.CreateEIP(eip.Name, eip.Bandwidth, eip.ChargeType, eip.BgpType) + args := &cloudprovider.SEip{ + Name: eip.Name, + BandwidthMbps: eip.Bandwidth, + ChargeType: eip.ChargeType, + BGPType: eip.BgpType, + IP: eip.IpAddr, + } + + if len(eip.NetworkId) > 0 { + _network, err := models.NetworkManager.FetchById(eip.NetworkId) + if err != nil { + msg := fmt.Sprintf("failed to found network %s error: %v", eip.NetworkId, err) + self.onFailed(ctx, eip, msg) + return + } + network := _network.(*models.SNetwork) + ip, _ := self.GetParams().GetString("ip") + if len(ip) > 0 { + lockman.LockObject(ctx, network) + defer lockman.ReleaseObject(ctx, network) + + addrTable := network.GetUsedAddresses() + ipAddr, err := network.GetFreeIP(ctx, self.UserCred, addrTable, nil, ip, models.IPAllocationNone, false) + if err != nil { + self.onFailed(ctx, eip, err.Error()) + return + } + if ipAddr != ip { + msg := fmt.Sprintf("candidate ip %s is occupoed!", ip) + self.onFailed(ctx, eip, msg) + return + } + _, err = db.Update(eip, func() error { + eip.IpAddr = ipAddr + return nil + }) + if err != nil { + self.onFailed(ctx, eip, err.Error()) + return + } + } + args.NetworkExternalId = network.ExternalId + } + + extEip, err := iregion.CreateEIP(args) if err != nil { msg := fmt.Sprintf("create eip fail %s", err) eip.SetStatus(self.UserCred, api.EIP_STATUS_ALLOCATE_FAIL, msg) diff --git a/pkg/mcclient/options/base.go b/pkg/mcclient/options/base.go index 838af7dd64..555a656dc4 100644 --- a/pkg/mcclient/options/base.go +++ b/pkg/mcclient/options/base.go @@ -207,7 +207,7 @@ type BaseListOptions struct { Manager string `help:"List objects belonging to the cloud provider" json:"manager,omitempty"` Account string `help:"List objects belonging to the cloud account" json:"account,omitempty"` - Provider string `help:"List objects from the provider" choices:"OneCloud|VMware|Aliyun|Qcloud|Azure|Aws|Huawei|Openstack|Ucloud" json:"provider,omitempty"` + Provider string `help:"List objects from the provider" choices:"OneCloud|VMware|Aliyun|Qcloud|Azure|Aws|Huawei|Openstack|Ucloud|ZStack" json:"provider,omitempty"` Brand string `help:"List objects belonging to a special brand"` CloudEnv string `help:"Cloud environment" choices:"public|private|onpremise|private_or_onpremise" json:"cloud_env,omitempty"` PublicCloud *bool `help:"List objects belonging to public cloud" json:"public_cloud"` diff --git a/pkg/mcclient/options/servers.go b/pkg/mcclient/options/servers.go index 48c68c2c7d..a7f94f6ca8 100644 --- a/pkg/mcclient/options/servers.go +++ b/pkg/mcclient/options/servers.go @@ -39,7 +39,7 @@ type ServerListOptions struct { Gpu *bool `help:"Show gpu servers"` Secgroup string `help:"Secgroup ID or Name"` AdminSecgroup string `help:"AdminSecgroup ID or Name"` - Hypervisor string `help:"Show server of hypervisor" choices:"kvm|esxi|container|baremetal|aliyun|azure|aws|huawei|ucloud"` + Hypervisor string `help:"Show server of hypervisor" choices:"kvm|esxi|container|baremetal|aliyun|azure|aws|huawei|ucloud|zstack"` Region string `help:"Show servers in cloudregion"` WithEip *bool `help:"Show Servers with EIP"` WithoutEip *bool `help:"Show Servers without EIP"` @@ -126,7 +126,7 @@ type ServerConfigs struct { Host string `help:"Preferred host where virtual server should be created" json:"prefer_host"` BackupHost string `help:"Perfered host where virtual backup server should be created"` - Hypervisor string `help:"Hypervisor type" choices:"kvm|esxi|baremetal|container|aliyun|azure|qcloud|aws|huawei|openstack|ucloud"` + Hypervisor string `help:"Hypervisor type" choices:"kvm|esxi|baremetal|container|aliyun|azure|qcloud|aws|huawei|openstack|ucloud|zstack"` ResourceType string `help:"Resource type" choices:"shared|prepaid|dedicated"` Backup bool `help:"Create server with backup server"` diff --git a/pkg/util/aliyun/eip.go b/pkg/util/aliyun/eip.go index 2685b7ec77..916edb0d61 100644 --- a/pkg/util/aliyun/eip.go +++ b/pkg/util/aliyun/eip.go @@ -182,6 +182,10 @@ func (self *SEipAddress) GetBandwidth() int { return self.Bandwidth } +func (self *SEipAddress) GetINetworkId() string { + return "" +} + func (self *SEipAddress) GetInternetChargeType() string { switch self.InternetChargeType { case InternetChargeByTraffic: @@ -282,16 +286,15 @@ func (region *SRegion) AllocateEIP(bwMbps int, chargeType TInternetChargeType) ( return region.GetEip(eipId) } -func (region *SRegion) CreateEIP(name string, bwMbps int, chargeType string, bgpType string) (cloudprovider.ICloudEIP, error) { +func (region *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { var ctype TInternetChargeType - switch chargeType { + switch eip.ChargeType { case api.EIP_CHARGE_TYPE_BY_TRAFFIC: ctype = InternetChargeByTraffic case api.EIP_CHARGE_TYPE_BY_BANDWIDTH: ctype = InternetChargeByBandwidth } - eip, err := region.AllocateEIP(bwMbps, ctype) - return eip, err + return region.AllocateEIP(eip.BandwidthMbps, ctype) } func (region *SRegion) DeallocateEIP(eipId string) error { diff --git a/pkg/util/aws/eip.go b/pkg/util/aws/eip.go index 4393eb1c67..b5580a0d33 100644 --- a/pkg/util/aws/eip.go +++ b/pkg/util/aws/eip.go @@ -124,6 +124,10 @@ func (self *SEipAddress) GetBandwidth() int { return self.Bandwidth } +func (self *SEipAddress) GetINetworkId() string { + return "" +} + func (self *SEipAddress) GetInternetChargeType() string { // todo : implement me return api.EIP_CHARGE_TYPE_BY_TRAFFIC @@ -250,14 +254,14 @@ func (self *SRegion) AllocateEIP(domainType string) (*SEipAddress, error) { return self.GetEip(*eip.AllocationId) } -func (self *SRegion) CreateEIP(name string, bwMbps int, chargeType string, bgpType string) (cloudprovider.ICloudEIP, error) { +func (self *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { // todo: aws 不支持指定bwMbps, chargeType ? log.Debugf("CreateEip: aws not support specific params name/bwMbps/chargeType.") ieip, err := self.AllocateEIP("vpc") - if err == nil && len(name) > 0 { + if err == nil && len(eip.Name) > 0 { eipId := ieip.GetId() k := "Name" - nameTag := &ec2.Tag{Key: &k, Value: &name} + nameTag := &ec2.Tag{Key: &k, Value: &eip.Name} params := &ec2.CreateTagsInput{} params.SetResources([]*string{&eipId}) params.SetTags([]*ec2.Tag{nameTag}) diff --git a/pkg/util/azure/classic_eip.go b/pkg/util/azure/classic_eip.go index 6597ed5db9..1e51968237 100644 --- a/pkg/util/azure/classic_eip.go +++ b/pkg/util/azure/classic_eip.go @@ -77,6 +77,10 @@ func (self *SClassicEipAddress) GetBandwidth() int { return 0 } +func (self *SClassicEipAddress) GetINetworkId() string { + return "" +} + func (self *SClassicEipAddress) GetGlobalId() string { return strings.ToLower(self.ID) } diff --git a/pkg/util/azure/eip.go b/pkg/util/azure/eip.go index b32d4b8eed..9df8eb0041 100644 --- a/pkg/util/azure/eip.go +++ b/pkg/util/azure/eip.go @@ -77,8 +77,8 @@ func (region *SRegion) AllocateEIP(eipName string) (*SEipAddress, error) { return &eip, cloudprovider.WaitStatus(&eip, api.EIP_STATUS_READY, 10*time.Second, 300*time.Second) } -func (region *SRegion) CreateEIP(eipName string, bwMbps int, chargeType string, bgpType string) (cloudprovider.ICloudEIP, error) { - return region.AllocateEIP(eipName) +func (region *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { + return region.AllocateEIP(eip.Name) } func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) { @@ -199,6 +199,10 @@ func (self *SEipAddress) GetBandwidth() int { return 0 } +func (self *SEipAddress) GetINetworkId() string { + return "" +} + func (self *SEipAddress) GetGlobalId() string { return strings.ToLower(self.ID) } diff --git a/pkg/util/httputils/httputils.go b/pkg/util/httputils/httputils.go index bf489a3eeb..dba0a2955a 100644 --- a/pkg/util/httputils/httputils.go +++ b/pkg/util/httputils/httputils.go @@ -297,7 +297,7 @@ func ParseJSONResponse(resp *http.Response, err error, debug bool) (http.Header, if ce.Code == 0 { ce.Code = resp.StatusCode } - if edetail := jsonutils.GetAnyString(jrbody2, []string{"message", "detail", "error_msg"}); len(edetail) > 0 { + if edetail := jsonutils.GetAnyString(jrbody2, []string{"message", "detail", "details", "error_msg"}); len(edetail) > 0 { ce.Details = edetail } if eclass := jsonutils.GetAnyString(jrbody2, []string{"title", "type", "error_code"}); len(eclass) > 0 { diff --git a/pkg/util/huawei/eip.go b/pkg/util/huawei/eip.go index 4399a9a2aa..f6eb28db26 100644 --- a/pkg/util/huawei/eip.go +++ b/pkg/util/huawei/eip.go @@ -178,6 +178,10 @@ func (self *SEipAddress) GetBandwidth() int { return int(self.BandwidthSize) // Mb } +func (self *SEipAddress) GetINetworkId() string { + return "" +} + func (self *SEipAddress) GetInternetChargeType() string { // https://support.huaweicloud.com/api-vpc/zh-cn_topic_0020090603.html bandwidth, err := self.region.GetEipBandwidth(self.BandwidthID) diff --git a/pkg/util/huawei/region.go b/pkg/util/huawei/region.go index d088e1377d..fa7c2e132d 100644 --- a/pkg/util/huawei/region.go +++ b/pkg/util/huawei/region.go @@ -379,9 +379,9 @@ func (self *SRegion) CreateIVpc(name string, desc string, cidr string) (cloudpro // 华东-上海二:5_sbgp // 华北-北京一:5_bgp、5_sbgp // 亚太-香港:5_bgp -func (self *SRegion) CreateEIP(name string, bwMbps int, chargeType string, bgpType string) (cloudprovider.ICloudEIP, error) { +func (self *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { var ctype TInternetChargeType - switch chargeType { + switch eip.ChargeType { case api.EIP_CHARGE_TYPE_BY_TRAFFIC: ctype = InternetChargeByTraffic case api.EIP_CHARGE_TYPE_BY_BANDWIDTH: @@ -389,27 +389,27 @@ func (self *SRegion) CreateEIP(name string, bwMbps int, chargeType string, bgpTy } // todo: 如何避免hardcode。集成到cloudmeta服务中? - if len(bgpType) == 0 { + if len(eip.BGPType) == 0 { switch self.GetId() { case "cn-north-1", "cn-east-2", "cn-south-1": - bgpType = "5_sbgp" + eip.BGPType = "5_sbgp" case "cn-northeast-1": - bgpType = "5_telcom" + eip.BGPType = "5_telcom" case "cn-north-4", "ap-southeast-1", "ap-southeast-2", "eu-west-0": - bgpType = "5_bgp" + eip.BGPType = "5_bgp" default: - bgpType = "5_bgp" + eip.BGPType = "5_bgp" } } - eip, err := self.AllocateEIP(name, bwMbps, ctype, bgpType) - eip.region = self + ieip, err := self.AllocateEIP(eip.Name, eip.BandwidthMbps, ctype, eip.BGPType) + ieip.region = self if err != nil { return nil, err } - err = cloudprovider.WaitStatus(eip, api.EIP_STATUS_READY, 5*time.Second, 60*time.Second) - return eip, err + err = cloudprovider.WaitStatus(ieip, api.EIP_STATUS_READY, 5*time.Second, 60*time.Second) + return ieip, err } func (self *SRegion) GetISnapshots() ([]cloudprovider.ICloudSnapshot, error) { diff --git a/pkg/util/openstack/region.go b/pkg/util/openstack/region.go index d5032b2f2c..1c92b1edea 100644 --- a/pkg/util/openstack/region.go +++ b/pkg/util/openstack/region.go @@ -402,7 +402,7 @@ func (region *SRegion) GetIEips() ([]cloudprovider.ICloudEIP, error) { return nil, cloudprovider.ErrNotSupported } -func (region *SRegion) CreateEIP(name string, bwMbps int, chargeType string, bgpType string) (cloudprovider.ICloudEIP, error) { +func (region *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { return nil, cloudprovider.ErrNotSupported } diff --git a/pkg/util/qcloud/eip.go b/pkg/util/qcloud/eip.go index 81274e38c3..cb7de2c26f 100644 --- a/pkg/util/qcloud/eip.go +++ b/pkg/util/qcloud/eip.go @@ -161,6 +161,10 @@ func (self *SEipAddress) GetBandwidth() int { return 0 } +func (self *SEipAddress) GetINetworkId() string { + return "" +} + func (self *SEipAddress) GetBillingType() string { return billing_api.BILLING_TYPE_POSTPAID } @@ -292,15 +296,15 @@ func (region *SRegion) AllocateEIP(name string, bwMbps int, chargeType TInternet return nil, cloudprovider.ErrNotFound } -func (region *SRegion) CreateEIP(name string, bwMbps int, chargeType string, bgpType string) (cloudprovider.ICloudEIP, error) { +func (region *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { var ctype TInternetChargeType - switch chargeType { + switch eip.ChargeType { case api.EIP_CHARGE_TYPE_BY_TRAFFIC: ctype = InternetChargeByTraffic case api.EIP_CHARGE_TYPE_BY_BANDWIDTH: ctype = InternetChargeByBandwidth } - return region.AllocateEIP(name, bwMbps, ctype) + return region.AllocateEIP(eip.Name, eip.BandwidthMbps, ctype) } func (region *SRegion) DeallocateEIP(eipId string) error { diff --git a/pkg/util/ucloud/eip.go b/pkg/util/ucloud/eip.go index 1910485d8e..d8d8917a86 100644 --- a/pkg/util/ucloud/eip.go +++ b/pkg/util/ucloud/eip.go @@ -177,6 +177,10 @@ func (self *SEip) GetBandwidth() int { return self.BandwidthMb } +func (self *SEip) GetINetworkId() string { + return "" +} + // 弹性IP的计费模式, 枚举值为: "Bandwidth", 带宽计费; "Traffic", 流量计费; "ShareBandwidth",共享带宽模式. 默认为 "Bandwidth". func (self *SEip) GetInternetChargeType() string { switch self.PayMode { @@ -207,21 +211,21 @@ func (self *SEip) ChangeBandwidth(bw int) error { // https://docs.ucloud.cn/api/unet-api/allocate_eip // 增加共享带宽模式ShareBandwidth -func (self *SRegion) CreateEIP(name string, bwMbps int, chargeType string, bgpType string) (cloudprovider.ICloudEIP, error) { - if len(bgpType) == 0 { +func (self *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { + if len(eip.BGPType) == 0 { if strings.HasPrefix(self.GetId(), "cn-") { - bgpType = "Bgp" + eip.BGPType = "Bgp" } else { - bgpType = "International" + eip.BGPType = "International" } } params := NewUcloudParams() - params.Set("OperatorName", bgpType) - params.Set("Bandwidth", bwMbps) - params.Set("Name", name) + params.Set("OperatorName", eip.BGPType) + params.Set("Bandwidth", eip.BandwidthMbps) + params.Set("Name", eip.Name) var payMode string - switch chargeType { + switch eip.ChargeType { case api.EIP_CHARGE_TYPE_BY_TRAFFIC: payMode = "Traffic" case api.EIP_CHARGE_TYPE_BY_BANDWIDTH: diff --git a/pkg/util/ucloud/shell/eip.go b/pkg/util/ucloud/shell/eip.go index 60708bcda6..5a602330bf 100644 --- a/pkg/util/ucloud/shell/eip.go +++ b/pkg/util/ucloud/shell/eip.go @@ -15,6 +15,7 @@ package shell import ( + "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/util/shellutils" "yunion.io/x/onecloud/pkg/util/ucloud" ) @@ -37,7 +38,12 @@ func init() { BGP string `help:"bgp type" choices:"Bgp|International"` } shellutils.R(&EipAllocateOptions{}, "eip-create", "Allocate an EIP", func(cli *ucloud.SRegion, args *EipAllocateOptions) error { - eip, err := cli.CreateEIP(args.NAME, args.BW, ucloud.EIP_CHARGE_TYPE_BY_TRAFFIC, args.BGP) + option := cloudprovider.SEip{ + Name: args.NAME, + BandwidthMbps: args.BW, + BGPType: args.BGP, + } + eip, err := cli.CreateEIP(&option) if err != nil { return err } diff --git a/pkg/util/zstack/configuration.go b/pkg/util/zstack/configuration.go new file mode 100644 index 0000000000..dbbb29212c --- /dev/null +++ b/pkg/util/zstack/configuration.go @@ -0,0 +1,14 @@ +package zstack + +type SConfiguration struct { + Name string + Category string + Description string + DefaultValue string + Value string +} + +func (region *SRegion) GetConfigrations() ([]SConfiguration, error) { + configrations := []SConfiguration{} + return configrations, region.client.listAll("global-configurations", []string{}, &configrations) +} diff --git a/pkg/util/zstack/disk.go b/pkg/util/zstack/disk.go index 15722f71b6..c23efbab3d 100644 --- a/pkg/util/zstack/disk.go +++ b/pkg/util/zstack/disk.go @@ -15,7 +15,7 @@ import ( type SDisk struct { localStorage *SLocalStorage - cephStorage *SCephStorage + storage *SStorage region *SRegion ZStackBasic @@ -24,7 +24,7 @@ type SDisk struct { DiskOfferingUUID string `json:"diskOfferingUuid"` RootImageUUID string `json:"rootImageUuid"` InstallPath string `json:"installPath"` - Type string `json:"Type"` + Type string `json:"type"` Format string `json:"format"` Size int `json:"size"` ActualSize int `json:"actualSize"` @@ -36,36 +36,38 @@ type SDisk struct { } func (region *SRegion) GetDisk(diskId string) (*SDisk, error) { - disks, err := region.GetDisks("", []string{diskId}, "") - if err != nil { - return nil, err - } - if len(disks) == 1 { - if disks[0].UUID == diskId { - return &disks[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(disks) == 0 || len(diskId) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId + disk := &SDisk{region: region} + return disk, region.client.getResource("volumes", diskId, disk) } func (region *SRegion) GetDiskWithStorage(diskId string) (*SDisk, error) { disk, err := region.GetDisk(diskId) if err != nil { + log.Errorf("Get Disk %s error: %v", diskId, err) return nil, err } - disk.region = region - storage, err := region.GetPrimaryStorage(disk.PrimaryStorageUUID) + storage, err := region.GetStorage(disk.PrimaryStorageUUID) if err != nil { + log.Errorf("Get primary storage %s error: %v", disk.PrimaryStorageUUID, err) return nil, err } switch storage.Type { case StorageTypeLocal: + if len(disk.VMInstanceUUID) > 0 { + instance, err := region.GetInstance(disk.VMInstanceUUID) + if err != nil { + return nil, err + } + hostId := instance.LastHostUUID + if len(hostId) == 0 { + hostId = instance.HostUUID + } + disk.localStorage = &SLocalStorage{region: region, primaryStorageID: storage.UUID, HostUUID: hostId} + return disk, nil + } tags, err := region.GetSysTags("", "VolumeVO", disk.UUID, "") if err != nil { + log.Errorf("get disk tag error: %v", err) return nil, err } for i := 0; i < len(tags); i++ { @@ -84,23 +86,8 @@ func (region *SRegion) GetDiskWithStorage(diskId string) (*SDisk, error) { } return nil, cloudprovider.ErrNotFound case StorageTypeCeph: - storage, err := region.GetPrimaryStorage(disk.PrimaryStorageUUID) - if err != nil { - return nil, err - } - for i := 0; i < len(storage.Pools); i++ { - if strings.Contains(disk.InstallPath, storage.Pools[i].PoolName) { - zone, err := region.GetZone(storage.ZoneUUID) - if err != nil { - return nil, err - } - cephStorage := storage.Pools[i] - cephStorage.zone = zone - disk.cephStorage = &cephStorage - return disk, nil - } - } - return nil, fmt.Errorf("failed to found ceph storage for disk %s", disk.Name) + disk.storage = storage + return disk, nil default: return nil, fmt.Errorf("Unsupport StorageType %s", storage.Type) } @@ -156,8 +143,8 @@ func (disk *SDisk) GetIStorage() (cloudprovider.ICloudStorage, error) { if disk.localStorage != nil { return disk.localStorage, nil } - if disk.cephStorage != nil { - return disk.cephStorage, nil + if disk.storage != nil { + return disk.storage, nil } return nil, cloudprovider.ErrNotFound } @@ -167,7 +154,10 @@ func (disk *SDisk) GetStatus() string { case "Ready": return api.DISK_READY case "NotInstantiated": - return api.DISK_READY + //数据云盘特有的状态。在这个连接状态中,数据云盘只存在于数据库的表记录中。NotInstantiated状态的数据云盘可以挂载到任何类型虚拟机管理程序管理的云主机上;当挂载到云主机上后,数据云盘的hypervisorType域会存储云主机对应的虚拟机管理程序类型,在主存储上被实例化为虚拟机管理程序类型的实际二进制文件,同时连接状态会改为就绪(Ready);在这之后,这些数据云盘就只能被重新挂载到相同类型虚拟机管理程序管理的云主机上了。 + return api.DISK_INIT + case "Creating": + return api.DISK_ALLOCATING default: log.Errorf("Unknown disk %s(%s) status %s", disk.Name, disk.UUID, disk.Status) return api.DISK_UNKNOWN @@ -258,12 +248,23 @@ func (region *SRegion) CreateDisk(name string, storageId string, hostId string, params["systemTags"] = []string{"ceph::pool::" + poolName} } resp, err := region.client.post("volumes/data", jsonutils.Marshal(params)) - disk := &SDisk{} + if err != nil { + return nil, err + } + disk := &SDisk{region: region} return disk, resp.Unmarshal(disk, "inventory") } func (region *SRegion) DeleteDisk(diskId string) error { - return region.client.delete("volumes", diskId, "Enforcing") + err := region.client.delete("volumes", diskId, "Enforcing") + if err != nil { + return err + } + params := map[string]interface{}{ + "expungeDataVolume": jsonutils.NewDict(), + } + _, err = region.client.put("volumes", diskId, jsonutils.Marshal(params)) + return err } func (region *SRegion) ResizeDisk(diskId string, sizeMb int64) error { diff --git a/pkg/util/zstack/eip.go b/pkg/util/zstack/eip.go index ec77c0eef9..3d07f44400 100644 --- a/pkg/util/zstack/eip.go +++ b/pkg/util/zstack/eip.go @@ -1,6 +1,7 @@ package zstack import ( + "fmt" "time" "yunion.io/x/jsonutils" @@ -22,20 +23,8 @@ type SEipAddress struct { } func (region *SRegion) GetEip(eipId string) (*SEipAddress, error) { - eips, err := region.GetEips(eipId, "") - if err != nil { - return nil, err - } - if len(eips) == 1 { - if eips[0].UUID == eipId { - return &eips[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(eips) == 0 || len(eipId) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId + eip := &SEipAddress{region: region} + return eip, region.client.getResource("eips", eipId, eip) } func (region *SRegion) GetEips(eipId, instanceId string) ([]SEipAddress, error) { @@ -104,17 +93,13 @@ func (eip *SEipAddress) GetAssociationType() string { func (eip *SEipAddress) GetAssociationExternalId() string { if len(eip.VMNicUUID) > 0 { instances, err := eip.region.GetInstances("", "", eip.VMNicUUID) - if err != nil && len(instances) == 1 { + if err == nil && len(instances) == 1 { return instances[0].UUID } } return "" } -func (eip *SEipAddress) GetManagerId() string { - return eip.region.client.providerID -} - func (eip *SEipAddress) GetBillingType() string { return "" } @@ -128,23 +113,31 @@ func (eip *SEipAddress) GetExpiredAt() time.Time { } func (eip *SEipAddress) Delete() error { - return cloudprovider.ErrNotImplemented + return eip.region.DeleteVirtualIP(eip.VipUUID) } func (eip *SEipAddress) GetBandwidth() int { return 0 } +func (eip *SEipAddress) GetINetworkId() string { + vip, err := eip.region.GetVirtualIP(eip.VipUUID) + if err == nil { + return eip.region.GetNetworkId(vip) + } + return "" +} + func (eip *SEipAddress) GetInternetChargeType() string { return api.EIP_CHARGE_TYPE_BY_TRAFFIC } func (eip *SEipAddress) Associate(instanceId string) error { - return cloudprovider.ErrNotImplemented + return eip.region.AssociateEip(instanceId, eip.UUID) } func (eip *SEipAddress) Dissociate() error { - return cloudprovider.ErrNotImplemented + return eip.region.DisassociateEip(eip.UUID) } func (eip *SEipAddress) ChangeBandwidth(bw int) error { @@ -154,3 +147,40 @@ func (eip *SEipAddress) ChangeBandwidth(bw int) error { func (eip *SEipAddress) GetProjectId() string { return "" } + +func (region *SRegion) CreateEip(name string, vipId string, desc string) (*SEipAddress, error) { + params := map[string]map[string]string{ + "params": { + "name": name, + "description": desc, + "vipUuid": vipId, + }, + } + eip := &SEipAddress{region: region} + resp, err := region.client.post("eips", jsonutils.Marshal(params)) + if err != nil { + return nil, err + } + return eip, resp.Unmarshal(eip, "inventory") +} + +func (region *SRegion) AssociateEip(instanceId, eipId string) error { + instance, err := region.GetInstance(instanceId) + if err != nil { + return err + } + if len(instance.VMNics) == 0 { + return fmt.Errorf("instance %s does not have any nic", instance.Name) + } + resource := fmt.Sprintf("eips/%s/vm-instances/nics/%s", eipId, instance.VMNics[0].UUID) + params := map[string]interface{}{ + "params": jsonutils.NewDict(), + } + _, err = region.client.post(resource, jsonutils.Marshal(params)) + return err +} + +func (region *SRegion) DisassociateEip(eipId string) error { + resource := fmt.Sprintf("eips/%s/vm-instances/nics", eipId) + return region.client.delete(resource, "", "") +} diff --git a/pkg/util/zstack/host.go b/pkg/util/zstack/host.go index b23dd7fcbc..0ad502f8b4 100644 --- a/pkg/util/zstack/host.go +++ b/pkg/util/zstack/host.go @@ -1,7 +1,12 @@ package zstack import ( + "fmt" + "strings" + "time" + "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" api "yunion.io/x/onecloud/pkg/apis/compute" @@ -28,6 +33,35 @@ type SHost struct { ZStackTime } +func (region *SRegion) GetHosts(zoneId string, hostId string) ([]SHost, error) { + hosts := []SHost{} + params := []string{} + if len(zoneId) > 0 { + params = append(params, "q=zone.uuid="+zoneId) + } + if len(hostId) > 0 { + params = append(params, "q=uuid="+hostId) + } + if SkipEsxi { + params = append(params, "q=hypervisorType!=ESX") + } + return hosts, region.client.listAll("hosts", params, &hosts) +} + +func (region *SRegion) GetHost(hostId string) (*SHost, error) { + host := &SHost{} + err := region.client.getResource("hosts", hostId, host) + if err != nil { + return nil, err + } + zone, err := region.GetZone(host.ZoneUUID) + if err != nil { + return nil, err + } + host.zone = zone + return host, nil +} + func (host *SHost) GetMetadata() *jsonutils.JSONDict { return nil } @@ -45,25 +79,21 @@ func (host *SHost) GetIWires() ([]cloudprovider.ICloudWire, error) { } func (host *SHost) GetIStorages() ([]cloudprovider.ICloudStorage, error) { - primaryStorages, err := host.zone.region.GetPrimaryStorages(host.zone.UUID, host.ClusterUUID, "") + storages, err := host.zone.region.GetStorages(host.zone.UUID, host.ClusterUUID, "") if err != nil { return nil, err } istorages := []cloudprovider.ICloudStorage{} - for i := 0; i < len(primaryStorages); i++ { - switch primaryStorages[i].Type { + for i := 0; i < len(storages); i++ { + switch storages[i].Type { case StorageTypeLocal: - storages, err := host.zone.region.getILocalStorages(host.zone, primaryStorages[i].UUID, host.UUID) + localStorages, err := host.zone.region.getILocalStorages(storages[i].UUID, host.UUID) if err != nil { return nil, err } - istorages = append(istorages, storages...) + istorages = append(istorages, localStorages...) case StorageTypeCeph: - storages, err := host.zone.region.getICephStorages(host.zone, primaryStorages[i].UUID) - if err != nil { - return nil, err - } - istorages = append(istorages, storages...) + istorages = append(istorages, &storages[i]) case StorageTypeVCenter: } } @@ -88,21 +118,12 @@ func (host *SHost) GetIVMs() ([]cloudprovider.ICloudVM, error) { } func (host *SHost) GetIVMById(instanceId string) (cloudprovider.ICloudVM, error) { - instances, err := host.zone.region.GetInstances(host.UUID, instanceId, "") + instance, err := host.zone.region.GetInstance(instanceId) if err != nil { return nil, err } - if len(instances) == 1 { - if instances[0].UUID == instanceId { - instances[0].host = host - return &instances[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(instances) == 0 || len(instanceId) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId + instance.host = host + return instance, nil } func (host *SHost) GetId() string { @@ -179,7 +200,7 @@ func (host *SHost) GetMemSizeMB() int { } func (host *SHost) GetStorageSizeMB() int { - storages, err := host.zone.region.GetPrimaryStorages(host.zone.UUID, host.ClusterUUID, "") + storages, err := host.zone.region.GetStorages(host.zone.UUID, host.ClusterUUID, "") if err != nil { return 0 } @@ -206,12 +227,164 @@ func (host *SHost) GetHostType() string { return api.HOST_TYPE_ZSTACK } -func (host *SHost) GetManagerId() string { - return host.zone.region.client.providerID +func (region *SRegion) cleanDisks(diskIds []string) { + for i := 0; i < len(diskIds); i++ { + err := region.DeleteDisk(diskIds[i]) + if err != nil { + log.Errorf("clean disk %s error: %v", diskIds[i], err) + } + } +} + +func (region *SRegion) createDataDisks(name, hostId string, storages []cloudprovider.ICloudStorage, disks []cloudprovider.SDiskInfo) ([]string, error) { + diskIds := []string{} + for i := 0; i < len(disks); i++ { + for j := 0; j < len(storages); j++ { + poolName := "" + if storages[i].GetStorageType() == disks[i].StorageType { + switch disks[i].StorageType { + case "localstorage": + poolName = "" + case "ceph": + hostId = "" + storage := storages[j].(*SStorage) + poolName, _ := storage.GetDataPoolName() + if len(poolName) == 0 { + return []string{}, fmt.Errorf("failed to found data pool for ceph storage %s", storage.Name) + } + default: + return diskIds, fmt.Errorf("not support storageType %s", disks[i].StorageType) + } + name := fmt.Sprintf("vdisk_%s_%d", name, time.Now().UnixNano()) + disk, err := region.CreateDisk(name, storages[j].GetId(), hostId, poolName, disks[i].SizeGB, "") + if err != nil { + return diskIds, err + } + diskIds = append(diskIds, disk.UUID) + } + } + } + return diskIds, nil } func (host *SHost) CreateVM(desc *cloudprovider.SManagedVMCreateConfig) (cloudprovider.ICloudVM, error) { - return nil, cloudprovider.ErrNotImplemented + storages, err := host.GetIStorages() + if err != nil { + return nil, err + } + diskIds, err := host.zone.region.createDataDisks(desc.Name, host.UUID, storages, desc.DataDisks) + if err != nil { + defer host.zone.region.cleanDisks(diskIds) + return nil, err + } + rootStorageId := "" + for i := 0; i < len(storages); i++ { + if storages[i].GetStorageType() == desc.SysDisk.StorageType { + rootStorageId = storages[i].GetId() + } + } + if len(rootStorageId) == 0 { + return nil, fmt.Errorf("failed to found appropriate storage for root disk") + } + instance, err := host.zone.region._createVM(desc, host.UUID, rootStorageId) + if err != nil { + defer host.zone.region.cleanDisks(diskIds) + return nil, err + } + for i := 0; i < len(diskIds); i++ { + err = host.zone.region.AttachDisk(instance.UUID, diskIds[i]) + if err != nil { + log.Errorf("failed to attach disk %s into instance %s error: %v", diskIds[i], instance.Name, err) + } + } + err = host.zone.region.AssignSecurityGroup(instance.UUID, desc.ExternalSecgroupId) + if err != nil { + return nil, err + } + return host.GetIVMById(instance.UUID) +} + +func (region *SRegion) _createVM(desc *cloudprovider.SManagedVMCreateConfig, hostId string, rootStorageId string) (*SInstance, error) { + l3Id := strings.Split(desc.ExternalNetworkId, "/")[0] + if len(l3Id) == 0 { + return nil, fmt.Errorf("invalid networkid: %s", desc.ExternalNetworkId) + } + _, err := region.GetL3Network(l3Id) + if err != nil { + log.Errorf("failed to found l3network %s error: %v", l3Id, err) + return nil, err + } + offerings := map[string]string{} + if len(desc.InstanceType) > 0 { + offering, err := region.GetInstanceOfferingByType(desc.InstanceType) + if err != nil { + return nil, err + } + offerings[offering.Name] = offering.UUID + } else { + _offerings, err := region.GetInstanceOfferings("", "", desc.Cpu, desc.MemoryMB) + if err != nil { + return nil, err + } + for _, offering := range _offerings { + offerings[offering.Name] = offering.UUID + } + if len(offerings) == 0 { + return nil, fmt.Errorf("instance type %dC%dMB not avaiable", desc.Cpu, desc.MemoryMB) + } + } + return region.CreateInstance(desc, l3Id, hostId, rootStorageId, offerings) +} + +func (region *SRegion) CreateInstance(desc *cloudprovider.SManagedVMCreateConfig, l3Id, hostId, rootStorageId string, offerings map[string]string) (*SInstance, error) { + instance := &SInstance{} + systemTags := []string{ + "cdroms::Empty::None::None", + "usbRedirect::false", + fmt.Sprintf("staticIp::%s::%s", l3Id, desc.IpAddr), + "vmConsoleMode::vnc", + "cleanTraffic::false", + } + if len(desc.UserData) > 0 { + systemTags = append(systemTags, "userdata::"+desc.UserData) + } + if len(desc.PublicKey) > 0 { + systemTags = append(systemTags, "sshkey::"+desc.PublicKey) + } + var err error + for offerName, offerId := range offerings { + params := map[string]interface{}{ + "params": map[string]interface{}{ + "name": desc.Name, + "description": desc.Description, + "instanceOfferingUuid": offerId, + "imageUuid": desc.ExternalImageId, + "l3NetworkUuids": []string{ + l3Id, + }, + "hostUuid": hostId, + "dataVolumeSystemTags": []string{}, + "rootVolumeSystemTags": []string{}, + "vmMachineType": "", + "tagUuids": []string{}, + "defaultL3NetworkUuid": l3Id, + "primaryStorageUuidForRootVolume": rootStorageId, + "dataDiskOfferingUuids": []string{}, + "systemTags": systemTags, + "vmNicConfig": []string{}, + }, + } + log.Debugf("Try instanceOffering : %s", offerName) + err = region.client.create("vm-instances", jsonutils.Marshal(params), instance) + if err == nil { + return instance, nil + } + log.Errorf("create %s instance failed error: %v", offerName, err) + } + if err != nil { + return nil, err + } + return nil, fmt.Errorf("instance type %dC%dMB not avaiable", desc.Cpu, desc.MemoryMB) } func (host *SHost) GetIHostNics() ([]cloudprovider.ICloudHostNetInterface, error) { diff --git a/pkg/util/zstack/image.go b/pkg/util/zstack/image.go index ee885ebef5..75532eaf0c 100644 --- a/pkg/util/zstack/image.go +++ b/pkg/util/zstack/image.go @@ -1,12 +1,19 @@ package zstack import ( + "bytes" "context" + "fmt" + "io" + "mime/multipart" + "net/http" + "sort" "time" "yunion.io/x/jsonutils" "yunion.io/x/log" "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/util/httputils" api "yunion.io/x/onecloud/pkg/apis/compute" ) @@ -79,6 +86,8 @@ func (image *SImage) GetStatus() string { switch image.Status { case "Ready": return api.CACHED_IMAGE_STATUS_READY + case "Downloading": + return api.CACHED_IMAGE_STATUS_CACHING default: log.Errorf("Unknown image status: %s", image.Status) return api.CACHED_IMAGE_STATUS_CACHE_FAILED @@ -142,20 +151,8 @@ func (image *SImage) GetCreateTime() time.Time { } func (region *SRegion) GetImage(imageId string) (*SImage, error) { - images, err := region.GetImages(imageId) - if err != nil { - return nil, err - } - if len(images) == 1 { - if images[0].UUID == imageId { - return &images[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(images) == 0 || len(imageId) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId + image := &SImage{storageCache: region.getStorageCache()} + return image, region.client.getResource("images", imageId, image) } func (region *SRegion) GetImages(imageId string) ([]SImage, error) { @@ -169,3 +166,86 @@ func (region *SRegion) GetImages(imageId string) ([]SImage, error) { } return images, region.client.listAll("images", params, &images) } + +func (region *SRegion) GetBackupStorageUUID() ([]string, error) { + imageServers, err := region.GetImageServers("") + if err != nil { + return nil, err + } + if len(imageServers) == 0 { + return nil, fmt.Errorf("failed to found any image servers") + } + servers := ImageServers(imageServers) + sort.Sort(servers) + return []string{servers[0].UUID}, nil +} + +func (region *SRegion) CreateImage(imageName, format, osType, desc string, reader io.Reader, size int64) (*SImage, error) { + backupStorageUUIDs, err := region.GetBackupStorageUUID() + if err != nil { + return nil, err + } + platform := "" + switch osType { + case "linux": + platform = "Linux" + case "windows": + platform = "Windows" + default: + platform = "Other" + } + parmas := map[string]interface{}{ + "params": map[string]interface{}{ + "name": imageName, + "url": fmt.Sprintf("upload://%s", imageName), + "description": desc, + "mediaType": "RootVolumeTemplate", + "system": false, + "format": format, + "platform": platform, + "backupStorageUuids": backupStorageUUIDs, + "systemTags": []string{"qemuga", "bootMode::Legacy"}, + }, + } + + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("", imageName) + if err != nil { + return nil, err + } + + if reader == nil { + return nil, fmt.Errorf("invalid reader") + } + + if size == 0 { + return nil, fmt.Errorf("invalid image size") + } + + _, err = io.Copy(part, reader) + if err != nil { + return nil, err + } + + err = writer.Close() + if err != nil { + return nil, err + } + + image := &SImage{storageCache: region.getStorageCache()} + err = region.client.create("images", jsonutils.Marshal(parmas), image) + if err != nil { + return nil, err + } + + if len(image.BackupStorageRefs) < 0 { + return nil, fmt.Errorf("no InstallPath reture") + } + header := http.Header{} + header.Add("X-IMAGE-UUID", image.UUID) + header.Add("X-IMAGE-SIZE", fmt.Sprintf("%d", size)) + header.Add("Content-Type", writer.FormDataContentType()) + _, err = httputils.Request(httputils.GetDefaultClient(), context.Background(), "POST", image.BackupStorageRefs[0].InstallPath, header, body, false) + return image, err +} diff --git a/pkg/util/zstack/image_server.go b/pkg/util/zstack/image_server.go new file mode 100644 index 0000000000..e4bbfb0a0e --- /dev/null +++ b/pkg/util/zstack/image_server.go @@ -0,0 +1,46 @@ +package zstack + +type ImageServers []SImageServer + +type SImageServer struct { + ZStackBasic + + Hostname string `json:"hostname"` + Username string `json:"username"` + SSHPort int `json:"sshPort"` + URL string `json:"url"` + TotalCapacity int `json:"totalCapacity"` + AvailableCapacity int `json:"availableCapacity"` + Type string `json:"type"` + State string `json:"state"` + Status string `json:"status"` + AttachedZoneUUIDs []string `json:"attachedZoneUuids"` + ZStackTime +} + +func (v ImageServers) Len() int { + return len(v) +} + +func (v ImageServers) Swap(i, j int) { + v[i], v[j] = v[j], v[i] +} + +func (v ImageServers) Less(i, j int) bool { + if v[i].AvailableCapacity < v[j].AvailableCapacity { + return false + } + return true +} + +func (region *SRegion) GetImageServers(zoneId string) ([]SImageServer, error) { + servers := []SImageServer{} + params := []string{"q=state=Enabled", "q=status=Connected", "q=type=ImageStoreBackupStorage"} + if SkipEsxi { + params = append(params, "q=type!=VCenter") + } + if len(zoneId) > 0 { + params = append(params, "q=zone.uuid="+zoneId) + } + return servers, region.client.listAll("backup-storage", params, &servers) +} diff --git a/pkg/util/zstack/instance.go b/pkg/util/zstack/instance.go index 36901dcad2..a7da7532b7 100644 --- a/pkg/util/zstack/instance.go +++ b/pkg/util/zstack/instance.go @@ -11,6 +11,7 @@ import ( "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/onecloud/pkg/util/billing" "yunion.io/x/pkg/util/osprofile" + "yunion.io/x/pkg/utils" api "yunion.io/x/onecloud/pkg/apis/compute" ) @@ -45,20 +46,8 @@ type SInstance struct { } func (region *SRegion) GetInstance(instanceId string) (*SInstance, error) { - instances, err := region.GetInstances("", instanceId, "") - if err != nil { - return nil, err - } - if len(instances) == 1 { - if instances[0].UUID == instanceId { - return &instances[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(instances) == 0 || len(instanceId) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId + instance := &SInstance{} + return instance, region.client.getResource("vm-instances", instanceId, instance) } func (region *SRegion) GetInstances(hostId string, instanceId string, nicId string) ([]SInstance, error) { @@ -168,7 +157,31 @@ func (instance *SInstance) GetVmemSizeMB() int { } func (instance *SInstance) GetBootOrder() string { - return "dcn" + return instance.host.zone.region.GetBootOrder(instance.UUID) +} + +func (region *SRegion) GetBootOrder(instanceId string) string { + resp, err := region.client.get("vm-instances", instanceId, "boot-orders") + if err != nil { + return "dcn" + } + orders := []string{} + err = resp.Unmarshal(&orders, "orders") + if err != nil { + return "dcn" + } + order := "" + for _, _order := range orders { + switch _order { + case "CdRom": + order += "c" + case "HardDisk": + order += "d" + default: + log.Errorf("Unknown BootOrder %s for instance %s", _order, instanceId) + } + } + return order } func (instance *SInstance) GetVga() string { @@ -222,11 +235,41 @@ func (instance *SInstance) GetHypervisor() string { } func (instance *SInstance) StartVM(ctx context.Context) error { - return cloudprovider.ErrNotImplemented + err := instance.host.zone.region.StartVM(instance.UUID) + if err != nil { + return err + } + return cloudprovider.WaitStatus(instance, api.VM_RUNNING, 5*time.Second, 5*time.Minute) +} + +func (region *SRegion) StartVM(instanceId string) error { + params := map[string]interface{}{ + "startVmInstance": jsonutils.NewDict(), + } + _, err := region.client.put("vm-instances", instanceId, jsonutils.Marshal(params)) + return err } func (instance *SInstance) StopVM(ctx context.Context, isForce bool) error { - return cloudprovider.ErrNotImplemented + err := instance.host.zone.region.StopVM(instance.UUID, isForce) + if err != nil { + return err + } + return cloudprovider.WaitStatus(instance, api.VM_READY, 5*time.Second, 5*time.Minute) +} + +func (region *SRegion) StopVM(instanceId string, isForce bool) error { + option := "grace" + if isForce { + option = "cold" + } + params := map[string]interface{}{ + "stopVmInstance": map[string]string{ + "type": option, + }, + } + _, err := region.client.put("vm-instances", instanceId, jsonutils.Marshal(params)) + return err } func (instance *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) { @@ -235,43 +278,172 @@ func (instance *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) { return nil, err } authURL, _ := url.Parse(instance.host.zone.region.client.authURL) + url := fmt.Sprintf("%s://%s:5000/thirdparty/vnc_auto.html?host=%s&port=%d&token=%s&title=%s", info.Scheme, authURL.Host, info.Hostname, info.Port, info.Token, instance.Name) + password, _ := instance.host.zone.region.GetInstanceConsolePassword(instance.UUID) + if len(password) > 0 { + url = url + fmt.Sprintf("&password=%s", password) + } return jsonutils.Marshal(map[string]string{ - "url": fmt.Sprintf("%s://%s:5000/thirdparty/vnc_auto.html?host=%s&port=%d&token=%s&title=%s", info.Scheme, authURL.Host, info.Hostname, info.Port, info.Token, instance.Name), + "url": url, "protocol": "zstack", "instance_id": instance.UUID, }), nil } func (instance *SInstance) UpdateVM(ctx context.Context, name string) error { - return cloudprovider.ErrNotImplemented + params := map[string]interface{}{ + "updateVmInstance": map[string]string{ + "name": name, + }, + } + return instance.host.zone.region.UpdateVM(instance.UUID, jsonutils.Marshal(params)) +} + +func (region *SRegion) UpdateVM(instanceId string, params jsonutils.JSONObject) error { + _, err := region.client.put("vm-instances", instanceId, params) + return err } func (instance *SInstance) DeployVM(ctx context.Context, name string, password string, publicKey string, deleteKeypair bool, description string) error { - return cloudprovider.ErrNotImplemented + if instance.Name != name || instance.Description != description { + params := map[string]interface{}{ + "updateVmInstance": map[string]string{ + "name": name, + "description": description, + }, + } + err := instance.host.zone.region.UpdateVM(instance.UUID, jsonutils.Marshal(params)) + if err != nil { + return err + } + } + if len(password) > 0 { + params := map[string]interface{}{ + "changeVmPassword": map[string]string{ + "account": api.VM_ZSTACK_DEFAULT_LOGIN_USER, + "password": password, + }, + } + err := instance.host.zone.region.UpdateVM(instance.UUID, jsonutils.Marshal(params)) + if err != nil { + return err + } + } + if len(publicKey) > 0 { + params := map[string]interface{}{ + "setVmSshKey": map[string]string{ + "SshKey": publicKey, + }, + } + err := instance.host.zone.region.UpdateVM(instance.UUID, jsonutils.Marshal(params)) + if err != nil { + return err + } + } + if deleteKeypair { + err := instance.host.zone.region.client.delete("vm-instances", fmt.Sprintf("%s/ssh-keys", instance.UUID), "") + if err != nil { + return err + } + } + return nil } func (instance *SInstance) RebuildRoot(ctx context.Context, imageId string, passwd string, publicKey string, sysSizeGB int) (string, error) { - return "", cloudprovider.ErrNotImplemented + return instance.host.zone.region.RebuildRoot(instance.UUID, imageId, sysSizeGB) +} + +func (region *SRegion) RebuildRoot(instanceId, imageId string, sysSizeGB int) (string, error) { + params := map[string]interface{}{ + "changeVmImage": map[string]string{ + "imageUuid": imageId, + }, + } + instance := &SInstance{} + resp, err := region.client.put("vm-instances", instanceId, jsonutils.Marshal(params)) + if err != nil { + return "", err + } + err = resp.Unmarshal(instance, "inventory") + if err != nil { + return "", err + } + disk, err := region.GetDisk(instance.RootVolumeUUID) + if err != nil { + return "", err + } + if sysSizeGB > disk.GetDiskSizeMB()*1024 { + return instance.RootVolumeUUID, region.ResizeDisk(disk.UUID, int64(sysSizeGB)*1024) + } + return instance.RootVolumeUUID, nil } func (instance *SInstance) ChangeConfig(ctx context.Context, ncpu int, vmem int) error { - return cloudprovider.ErrNotImplemented + offerings, err := instance.host.zone.region.GetInstanceOfferings("", "", ncpu, vmem) + if err != nil { + return err + } + for _, offering := range offerings { + log.Debugf("try instance offering %s(%s) ...", offering.Name, offering.UUID) + err := instance.host.zone.region.ChangeConfig(instance.UUID, offering.UUID) + if err != nil { + log.Errorf("failed to change config for instance %s(%s) error: %v", instance.Name, instance.UUID, err) + } else { + return nil + } + } + return fmt.Errorf("Failed to change vm config, specification not supported") } func (instance *SInstance) ChangeConfig2(ctx context.Context, instanceType string) error { - return cloudprovider.ErrNotImplemented + offering, err := instance.host.zone.region.GetInstanceOfferingByType(instanceType) + if err != nil { + return err + } + return instance.host.zone.region.ChangeConfig(instance.UUID, offering.UUID) +} + +func (region *SRegion) ChangeConfig(instanceId, offeringId string) error { + params := map[string]interface{}{ + "changeInstanceOffering": map[string]string{ + "instanceOfferingUuid": offeringId, + }, + } + return region.UpdateVM(instanceId, jsonutils.Marshal(params)) } func (instance *SInstance) AttachDisk(ctx context.Context, diskId string) error { - return cloudprovider.ErrNotImplemented + return instance.host.zone.region.AttachDisk(instance.UUID, diskId) +} + +func (region *SRegion) AttachDisk(instanceId string, diskId string) error { + _, err := region.client.post(fmt.Sprintf("volumes/%s/vm-instances/%s", diskId, instanceId), jsonutils.NewDict()) + return err } func (instance *SInstance) DetachDisk(ctx context.Context, diskId string) error { - return cloudprovider.ErrNotImplemented + return instance.host.zone.region.DetachDisk(instance.UUID, diskId) +} + +func (region *SRegion) DetachDisk(instanceId, diskId string) error { + url := fmt.Sprintf("volumes/%s/vm-instances?vmUuid=%s", diskId, instanceId) + return region.client.delete(url, "", "") } func (instance *SInstance) DeleteVM(ctx context.Context) error { - return cloudprovider.ErrNotImplemented + return instance.host.zone.region.DeleteVM(instance.UUID) +} + +func (region *SRegion) DeleteVM(instanceId string) error { + err := region.client.delete("vm-instances", instanceId, "Enforcing") + if err != nil { + return err + } + params := map[string]interface{}{ + "expungeVmInstance": jsonutils.NewDict(), + } + _, err = region.client.put("vm-instances", instanceId, jsonutils.Marshal(params)) + return err } func (instance *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) { @@ -289,11 +461,75 @@ func (instance *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) { } func (instance *SInstance) AssignSecurityGroup(secgroupId string) error { - return cloudprovider.ErrNotImplemented + return instance.host.zone.region.AssignSecurityGroup(instance.UUID, secgroupId) } func (instance *SInstance) SetSecurityGroups(secgroupIds []string) error { - return cloudprovider.ErrNotImplemented + currentIds, err := instance.GetSecurityGroupIds() + if err != nil { + return err + } + for _, id := range currentIds { + if !utils.IsInStringArray(id, secgroupIds) { + err := instance.host.zone.region.RevokeSecurityGroup(instance.UUID, id) + if err != nil { + return err + } + } + } + for _, id := range secgroupIds { + if !utils.IsInStringArray(id, currentIds) { + err := instance.host.zone.region.AssignSecurityGroup(instance.UUID, id) + if err != nil { + return err + } + } + } + return nil +} + +func (region *SRegion) AssignSecurityGroup(instanceId, secgroupId string) error { + instance, err := region.GetInstance(instanceId) + if err != nil { + return err + } + secgroup, err := region.GetSecurityGroup(secgroupId) + if err != nil { + return err + } + if len(instance.VMNics) > 0 { + if !utils.IsInStringArray(instance.VMNics[0].L3NetworkUUID, secgroup.AttachedL3NetworkUUIDs) { + resource := fmt.Sprintf("security-groups/%s/l3-networks/%s", secgroupId, instance.VMNics[0].L3NetworkUUID) + _, err := region.client.post(resource, jsonutils.NewDict()) + if err != nil { + return err + } + } + params := map[string]interface{}{ + "params": map[string]interface{}{ + "vmNicUuids": []string{instance.VMNics[0].UUID}, + }, + } + resource := fmt.Sprintf("security-groups/%s/vm-instances/nics", secgroupId) + _, err = region.client.post(resource, jsonutils.Marshal(params)) + return err + } + return nil +} + +func (region *SRegion) RevokeSecurityGroup(instanceId, secgroupId string) error { + instance, err := region.GetInstance(instanceId) + if err != nil { + return err + } + for _, nic := range instance.VMNics { + resource := fmt.Sprintf("security-groups/%s/vm-instances/nics?vmNicUuids=%s", secgroupId, nic.UUID) + err := region.client.delete(resource, "", "") + if err != nil { + return err + } + } + return nil } func (instance *SInstance) GetBillingType() string { @@ -309,15 +545,15 @@ func (instance *SInstance) GetExpiredAt() time.Time { } func (instance *SInstance) UpdateUserData(userData string) error { - return cloudprovider.ErrNotImplemented + return cloudprovider.ErrNotSupported } func (instance *SInstance) CreateDisk(ctx context.Context, sizeMb int, uuid string, driver string) error { - return cloudprovider.ErrNotImplemented + return cloudprovider.ErrNotSupported } func (instance *SInstance) Renew(bc billing.SBillingCycle) error { - return cloudprovider.ErrNotImplemented + return cloudprovider.ErrNotSupported } func (instance *SInstance) GetProjectId() string { diff --git a/pkg/util/zstack/instancenic.go b/pkg/util/zstack/instancenic.go index ba2e103617..9b2f8518c0 100644 --- a/pkg/util/zstack/instancenic.go +++ b/pkg/util/zstack/instancenic.go @@ -1,8 +1,6 @@ package zstack import ( - "yunion.io/x/pkg/util/netutils" - "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -43,14 +41,9 @@ func (nic *SInstanceNic) GetINetwork() cloudprovider.ICloudNetwork { log.Errorf("failed to found networks for nic %v error: %v", jsonutils.Marshal(nic).String(), err) return nil } - ip, err := netutils.NewIPV4Addr(nic.IP) - if err != nil { - log.Errorf("Invalid ip address %s error: %v", nic.IP, err) - return nil - } for i := 0; i < len(networks); i++ { - if networks[i].GetIPRange().Contains(ip) { - l3Network, err := nic.instance.host.zone.region.GetL3Network(nic.instance.host.zone.UUID, "", networks[i].L3NetworkUUID) + if networks[i].Contains(nic.IP) { + l3Network, err := nic.instance.host.zone.region.GetL3Network(networks[i].L3NetworkUUID) if err != nil { log.Errorf("failed to found l3Network for network %v error: %v", jsonutils.Marshal(networks[i]).String(), err) return nil diff --git a/pkg/util/zstack/network.go b/pkg/util/zstack/network.go index 6c3e06a640..1d539059df 100644 --- a/pkg/util/zstack/network.go +++ b/pkg/util/zstack/network.go @@ -1,6 +1,8 @@ package zstack import ( + "fmt" + "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/cloudprovider" "yunion.io/x/pkg/util/netutils" @@ -54,21 +56,9 @@ func (region *SRegion) GetNetwork(zoneId, wireId, l3Id, networkId string) (*SNet return nil, cloudprovider.ErrDuplicateId } -func (region *SRegion) GetL3Network(zoneId string, wireId string, l3Id string) (*SL3Network, error) { - l3Networks, err := region.GetL3Networks(zoneId, wireId, l3Id) - if err != nil { - return nil, err - } - if len(l3Networks) == 1 { - if l3Networks[0].UUID == l3Id { - return &l3Networks[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(l3Networks) == 0 || len(l3Id) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId +func (region *SRegion) GetL3Network(l3Id string) (*SL3Network, error) { + l3network := &SL3Network{} + return l3network, region.client.getResource("l3-networks", l3Id, l3network) } func (region *SRegion) GetL3Networks(zoneId string, wireId string, l3Id string) ([]SL3Network, error) { @@ -115,7 +105,7 @@ func (network *SNetwork) GetName() string { } func (network *SNetwork) GetGlobalId() string { - return network.UUID + return fmt.Sprintf("%s/%s", network.L3NetworkUUID, network.UUID) } func (network *SNetwork) IsEmulated() bool { @@ -131,7 +121,18 @@ func (network *SNetwork) Delete() error { } func (region *SRegion) DeleteNetwork(networkId string) error { - return cloudprovider.ErrNotImplemented + network, err := region.GetNetwork("", "", "", networkId) + if err != nil { + return err + } + l3, err := region.GetL3Network(network.L3NetworkUUID) + if err != nil { + return err + } + if len(l3.Networks) == 1 { + return region.client.delete("l3-networks", l3.UUID, "") + } + return region.client.delete("l3-networks/ip-ranges", networkId, "") } func (network *SNetwork) GetIWire() cloudprovider.ICloudWire { @@ -160,6 +161,14 @@ func (network *SNetwork) GetIPRange() netutils.IPV4AddrRange { return netutils.NewIPV4AddrRange(start, end) } +func (network *SNetwork) Contains(ipAddr string) bool { + ip, err := netutils.NewIPV4Addr(ipAddr) + if err != nil { + return false + } + return network.GetIPRange().Contains(ip) +} + func (network *SNetwork) GetIpMask() int8 { return int8(network.PrefixLen) } @@ -184,3 +193,39 @@ func (network *SNetwork) Refresh() error { func (network *SNetwork) GetProjectId() string { return "" } + +func (region *SRegion) CreateNetwork(name string, cidr string, wireId string, desc string) (*SNetwork, error) { + params := map[string]interface{}{ + "params": map[string]interface{}{ + "name": name, + "type": "L3BasicNetwork", + "l2NetworkUuid": wireId, + "category": "Private", + "system": false, + }, + } + l3 := &SL3Network{} + resp, err := region.client.post("l3-networks", jsonutils.Marshal(params)) + if err != nil { + return nil, err + } + err = resp.Unmarshal(l3, "inventory") + if err != nil { + return nil, err + } + region.AttachServiceForl3Network(l3.UUID, []string{"Flat", "SecurityGroup"}) + params = map[string]interface{}{ + "params": map[string]interface{}{ + "name": name, + "networkCidr": cidr, + }, + } + resource := fmt.Sprintf("l3-networks/%s/ip-ranges/by-cidr", l3.UUID) + resp, err = region.client.post(resource, jsonutils.Marshal(params)) + if err != nil { + region.client.delete("l3-networks", l3.UUID, "") + return nil, err + } + network := &SNetwork{} + return network, resp.Unmarshal(network, "inventory") +} diff --git a/pkg/util/zstack/network_service.go b/pkg/util/zstack/network_service.go new file mode 100644 index 0000000000..d29b012d39 --- /dev/null +++ b/pkg/util/zstack/network_service.go @@ -0,0 +1,117 @@ +package zstack + +import ( + "fmt" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + "yunion.io/x/pkg/utils" +) + +type SNetworkService struct { + IPsec []string `json:"IPsec"` + VRouterRoute []string `json:"VRouterRoute"` + VipQos []string `json:"VipQos"` + DNS []string `json:"DNS"` + SNAT []string `json:"SNAT"` + LoadBalancer []string `json:"LoadBalancer"` + Userdata []string `json:"Userdata"` + SecurityGroup []string `json:"SecurityGroup"` + Eip []string `json:"Eip"` + DHCP []string `json:"DHCP"` + CentralizedDNS []string `json:"CentralizedDNS"` + HostRoute []string `json:"HostRoute"` + PortForwarding []string `json:"PortForwarding"` +} + +type SNetworkServiceProvider struct { + ZStackBasic + ZStackTime + Type string `json:"type"` + NetworkServiceTypes []string `json:"networkServiceTypes"` + AttachedL2NetworkUUIDs []string `json:"attachedL2NetworkUuids"` +} + +type SNetworkServiceRef struct { + L3NetworkUUID string `json:"l3NetworkUuid"` + NetworkServiceProviderUUID string `json:"networkServiceProviderUuid"` + NetworkServiceType string `json:"networkServiceType"` +} + +func (region *SRegion) GetNetworkServices() (*SNetworkService, error) { + service := &SNetworkService{} + resp, err := region.client.get("network-services/types", "", "") + if err != nil { + return nil, err + } + return service, resp.Unmarshal(service, "types") +} + +func (region *SRegion) GetNetworkServiceProviders(Type string) ([]SNetworkServiceProvider, error) { + providers := []SNetworkServiceProvider{} + params := []string{} + if len(Type) > 0 { + params = append(params, "q=type="+Type) + } + return providers, region.client.listAll("network-services/providers", params, &providers) +} + +func (region *SRegion) GetNetworkServiceRef(l3Id string, Type string) ([]SNetworkServiceRef, error) { + refs := []SNetworkServiceRef{} + params := []string{} + if len(l3Id) > 0 { + params = append(params, "q=l3NetworkUuid="+l3Id) + } + if len(Type) > 0 { + params = append(params, "q=networkServiceType="+Type) + } + return refs, region.client.listAll("l3-networks/network-services/refs", params, &refs) +} + +func (region *SRegion) AttachServiceForl3Network(l3Id string, services []string) error { + networkServices := map[string][]string{} + refs, err := region.GetNetworkServiceRef(l3Id, "") + if err != nil { + return err + } + currentServices := []string{} + for i := 0; i < len(refs); i++ { + currentServices = append(currentServices, refs[i].NetworkServiceType) + } + for _, service := range services { + networkServiceProviders, err := region.GetNetworkServiceProviders(service) + if err != nil || len(networkServiceProviders) == 0 { + msg := fmt.Sprintf("failed to find network services %s error: %v", service, err) + log.Errorln(msg) + return fmt.Errorf(msg) + } + attachServices := []string{} + for i := 0; i < len(networkServiceProviders[0].NetworkServiceTypes); i++ { + if !utils.IsInStringArray(networkServiceProviders[0].NetworkServiceTypes[i], currentServices) { + attachServices = append(attachServices, networkServiceProviders[0].NetworkServiceTypes[i]) + } + } + if len(attachServices) > 0 { + networkServices[networkServiceProviders[0].UUID] = attachServices + } + } + + if len(networkServices) == 0 { + return nil + } + params := map[string]interface{}{ + "params": map[string]interface{}{ + "networkServices": networkServices, + }, + } + resource := fmt.Sprintf("l3-networks/%s/network-services", l3Id) + _, err = region.client.post(resource, jsonutils.Marshal(params)) + if err != nil { + log.Errorf("failed to attach network services %s to l3network %s error: %v", services, l3Id, err) + } + return err +} + +func (region *SRegion) RemoveNetworkService(l3Id string, service string) error { + return region.client.delete("l3-networks", fmt.Sprintf("%s/network-services?networkServices=%s", l3Id, service), "") +} diff --git a/pkg/util/zstack/offering.go b/pkg/util/zstack/offering.go index 141594114b..1775721027 100644 --- a/pkg/util/zstack/offering.go +++ b/pkg/util/zstack/offering.go @@ -1,8 +1,9 @@ package zstack import ( + "fmt" + "yunion.io/x/jsonutils" - "yunion.io/x/onecloud/pkg/cloudprovider" api "yunion.io/x/onecloud/pkg/apis/compute" ) @@ -22,28 +23,39 @@ type SInstanceOffering struct { } func (region *SRegion) GetInstanceOffering(offerId string) (*SInstanceOffering, error) { - offerings, err := region.GetInstanceOfferings(offerId) + offer := &SInstanceOffering{region: region} + return offer, region.client.getResource("instance-offerings", offerId, offer) +} + +func (region *SRegion) GetInstanceOfferingByType(instanceType string) (*SInstanceOffering, error) { + offerings, err := region.GetInstanceOfferings("", instanceType, 0, 0) if err != nil { return nil, err } if len(offerings) == 1 { - if offerings[0].UUID == offerId { - return &offerings[0], nil - } - return nil, cloudprovider.ErrNotFound + return &offerings[0], nil } - if len(offerings) == 0 || len(offerId) == 0 { - return nil, cloudprovider.ErrNotFound + if len(offerings) == 0 { + return nil, fmt.Errorf("instanceType %s not found", instanceType) } - return nil, cloudprovider.ErrDuplicateId + return nil, fmt.Errorf("duplicate instanceType %s", instanceType) } -func (region *SRegion) GetInstanceOfferings(offerId string) ([]SInstanceOffering, error) { +func (region *SRegion) GetInstanceOfferings(offerId string, name string, cpu int, memorySizeMb int) ([]SInstanceOffering, error) { offerings := []SInstanceOffering{} - params := []string{"q=type=UserVM"} + params := []string{"q=type=UserVM", "q=state=Enabled"} if len(offerId) > 0 { params = append(params, "q=uuid="+offerId) } + if len(name) > 0 { + params = append(params, "q=name="+name) + } + if cpu != 0 { + params = append(params, fmt.Sprintf("q=cpuNum=%d", cpu)) + } + if memorySizeMb != 0 { + params = append(params, fmt.Sprintf("q=memorySize=%d", memorySizeMb*1024*1024)) + } if err := region.client.listAll("instance-offerings", params, &offerings); err != nil { return nil, err } diff --git a/pkg/util/zstack/region.go b/pkg/util/zstack/region.go index 61617fd1ea..cb4867cae9 100644 --- a/pkg/util/zstack/region.go +++ b/pkg/util/zstack/region.go @@ -2,6 +2,7 @@ package zstack import ( "fmt" + "strings" "yunion.io/x/jsonutils" "yunion.io/x/log" @@ -35,11 +36,11 @@ func (region *SRegion) GetId() string { } func (region *SRegion) GetName() string { - return fmt.Sprintf("%s %s", CLOUD_PROVIDER_ZSTACK, region.Name) + return region.client.providerName } func (region *SRegion) GetGlobalId() string { - return fmt.Sprintf("%s/%s", CLOUD_PROVIDER_ZSTACK, region.Name) + return fmt.Sprintf("%s/%s", CLOUD_PROVIDER_ZSTACK, region.client.providerID) } func (region *SRegion) IsEmulated() bool { @@ -64,7 +65,7 @@ func (region *SRegion) Refresh() error { } func (region *SRegion) GetIHostById(id string) (cloudprovider.ICloudHost, error) { - return region.GetHost("", id) + return region.GetHost(id) } func (region *SRegion) GetIStorageById(id string) (cloudprovider.ICloudStorage, error) { @@ -114,6 +115,10 @@ func (region *SRegion) GetIStoragecaches() ([]cloudprovider.ICloudStoragecache, return []cloudprovider.ICloudStoragecache{region.storageCache}, nil } +func (region *SRegion) getStorageCache() *SStoragecache { + return &SStoragecache{region: region} +} + func (region *SRegion) GetIVpcById(vpcId string) (cloudprovider.ICloudVpc, error) { return &SVpc{region: region}, nil } @@ -132,20 +137,8 @@ func (region *SRegion) GetIZoneById(id string) (cloudprovider.ICloudZone, error) } func (region *SRegion) GetZone(zoneId string) (*SZone, error) { - zones, err := region.GetZones(zoneId) - if err != nil { - return nil, err - } - if len(zones) == 1 { - if zones[0].UUID == zoneId { - return &zones[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(zones) == 0 || len(zoneId) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId + zone := &SZone{region: region} + return zone, region.client.getResource("zones", zoneId, zone) } func (region *SRegion) GetZones(zoneId string) ([]SZone, error) { @@ -206,8 +199,23 @@ func (region *SRegion) CreateIVpc(name string, desc string, cidr string) (cloudp return nil, cloudprovider.ErrNotSupported } -func (region *SRegion) CreateEIP(name string, bwMbps int, chargeType string, bgpType string) (cloudprovider.ICloudEIP, error) { - return nil, cloudprovider.ErrNotImplemented +func (region *SRegion) CreateEIP(eip *cloudprovider.SEip) (cloudprovider.ICloudEIP, error) { + if len(eip.NetworkExternalId) == 0 { + return nil, fmt.Errorf("networkId cannot be empty") + } + networkInfo := strings.Split(eip.NetworkExternalId, "/") + if len(networkInfo) != 2 { + return nil, fmt.Errorf("invalid network externalId, it should be `l3networId/networkId` format") + } + _, err := region.GetL3Network(networkInfo[0]) + if err != nil { + return nil, err + } + vip, err := region.CreateVirtualIP(eip.Name, "", eip.IP, networkInfo[0]) + if err != nil { + return nil, err + } + return region.CreateEip(eip.Name, vip.UUID, "") } func (region *SRegion) GetIEipById(eipId string) (cloudprovider.ICloudEIP, error) { @@ -284,7 +292,7 @@ func (region *SRegion) GetISnapshotById(snapshotId string) (cloudprovider.ICloud } func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) { - offerings, err := region.GetInstanceOfferings("") + offerings, err := region.GetInstanceOfferings("", "", 0, 0) if err != nil { return nil, err } @@ -296,5 +304,22 @@ func (region *SRegion) GetSkus(zoneId string) ([]cloudprovider.ICloudSku, error) } func (region *SRegion) SyncSecurityGroup(secgroupId string, vpcId string, name string, desc string, rules []secrules.SecurityRule) (string, error) { - return "", cloudprovider.ErrNotImplemented + if len(secgroupId) > 0 { + _, err := region.GetSecurityGroup(secgroupId) + if err != nil { + if err == cloudprovider.ErrNotFound { + secgroupId = "" + } else { + return "", err + } + } + } + if len(secgroupId) == 0 { + secgroup, err := region.CreateSecurityGroup(name, desc) + if err != nil { + return "", err + } + secgroupId = secgroup.UUID + } + return secgroupId, region.syncSecgroupRules(secgroupId, rules) } diff --git a/pkg/util/zstack/securitygroup.go b/pkg/util/zstack/securitygroup.go index 8decbb9921..85d957b20f 100644 --- a/pkg/util/zstack/securitygroup.go +++ b/pkg/util/zstack/securitygroup.go @@ -1,11 +1,11 @@ package zstack import ( + "fmt" "net" + "sort" "strings" - "yunion.io/x/onecloud/pkg/cloudprovider" - "yunion.io/x/jsonutils" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/pkg/util/secrules" @@ -25,6 +25,28 @@ type SSecurityGroupRule struct { ZStackTime } +type SSecurityGroupRuleSet []SSecurityGroupRule + +func (v SSecurityGroupRuleSet) Len() int { + return len(v) +} + +func (v SSecurityGroupRuleSet) Swap(i, j int) { + v[i], v[j] = v[j], v[i] +} + +func (v SSecurityGroupRuleSet) Less(i, j int) bool { + rule, err := v[i].toRule() + if err != nil { + return false + } + _rule, err := v[j].toRule() + if err != nil { + return false + } + return strings.Compare(rule.String(), _rule.String()) <= 0 +} + type SSecurityGroup struct { region *SRegion @@ -38,20 +60,8 @@ type SSecurityGroup struct { } func (region *SRegion) GetSecurityGroup(secgroupId string) (*SSecurityGroup, error) { - secgroups, err := region.GetSecurityGroups(secgroupId, "") - if err != nil { - return nil, err - } - if len(secgroups) == 1 { - if secgroups[0].UUID == secgroupId { - return &secgroups[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(secgroups) == 0 || len(secgroupId) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId + secgroup := &SSecurityGroup{region: region} + return secgroup, region.client.getResource("security-groups", secgroupId, secgroup) } func (region *SRegion) GetSecurityGroups(secgroupId string, instanceId string) ([]SSecurityGroup, error) { @@ -94,33 +104,44 @@ func (self *SSecurityGroup) GetDescription() string { return self.Description } +func (rule *SSecurityGroupRule) toRule() (*secrules.SecurityRule, error) { + r := &secrules.SecurityRule{ + Direction: secrules.DIR_IN, + Action: secrules.SecurityRuleAllow, + Priority: 1, + Protocol: secrules.PROTO_ANY, + PortStart: rule.StartPort, + PortEnd: rule.EndPort, + } + _, ipNet, err := net.ParseCIDR(rule.AllowedCIDR) + if err != nil { + return nil, err + } + r.IPNet = ipNet + if rule.Type == "Egress" { + r.Direction = secrules.DIR_OUT + } + if rule.Protocol != "ALL" { + r.Protocol = strings.ToLower(rule.Protocol) + } + return r, nil +} + func (self *SSecurityGroup) GetRules() ([]secrules.SecurityRule, error) { rules := []secrules.SecurityRule{} priority := 100 outRuleCount := 0 for i := 0; i < len(self.Rules); i++ { if self.Rules[i].IPVersion == 4 { - rule := secrules.SecurityRule{ - Direction: secrules.DIR_IN, - Action: secrules.SecurityRuleAllow, - Priority: priority, - Protocol: secrules.PROTO_ANY, - PortStart: self.Rules[i].StartPort, - PortEnd: self.Rules[i].EndPort, - } - _, ipNet, err := net.ParseCIDR(self.Rules[i].AllowedCIDR) + rule, err := self.Rules[i].toRule() if err != nil { return nil, err } - rule.IPNet = ipNet - if self.Rules[i].Type == "Egress" { - rule.Direction = secrules.DIR_OUT + if rule.Direction == secrules.DIR_OUT { outRuleCount++ } - if self.Rules[i].Protocol != "ALL" { - rule.Protocol = strings.ToLower(self.Rules[i].Protocol) - } - rules = append(rules, rule) + rule.Priority = priority + rules = append(rules, *rule) priority-- } } @@ -155,3 +176,135 @@ func (self *SSecurityGroup) Refresh() error { func (self *SSecurityGroup) GetProjectId() string { return "" } + +func (region *SRegion) AddSecurityGroupRule(secgroupId string, rules []secrules.SecurityRule) error { + ruleParam := []map[string]interface{}{} + for _, rule := range rules { + Type := "Ingress" + if rule.Direction == secrules.DIR_OUT { + Type = "Egress" + } + protocol := "ALL" + if rule.Protocol != secrules.PROTO_ANY { + protocol = strings.ToUpper(rule.Protocol) + } + if rule.Protocol == secrules.PROTO_ICMP { + rule.PortStart = -1 + rule.PortEnd = -1 + rule.Ports = []int{} + } + if len(rule.Ports) > 0 { + for _, port := range rule.Ports { + ruleParam = append(ruleParam, map[string]interface{}{ + "type": Type, + "startPort": port, + "endPort": port, + "protocol": protocol, + "allowedCidr": rule.IPNet.String(), + }) + } + } else { + ruleParam = append(ruleParam, map[string]interface{}{ + "type": Type, + "startPort": rule.PortStart, + "endPort": rule.PortEnd, + "protocol": protocol, + "allowedCidr": rule.IPNet.String(), + }) + } + } + if len(ruleParam) > 0 { + params := map[string]interface{}{ + "parms": map[string]interface{}{ + "rules": ruleParam, + }, + } + return region.client.create(fmt.Sprintf("security-groups/%s/rules", secgroupId), jsonutils.Marshal(params), nil) + } + return nil +} + +func (region *SRegion) DeleteSecurityGroupRules(ruleIds []string) error { + resource := fmt.Sprintf("security-groups/rules?ruleUuids=%s", strings.Join(ruleIds, ",")) + return region.client.delete(resource, "", "") +} + +func (region *SRegion) CreateSecurityGroup(name, desc string) (*SSecurityGroup, error) { + secgroup := &SSecurityGroup{} + params := map[string]map[string]string{ + "params": { + "name": name, + "description": desc, + }, + } + return secgroup, region.client.create("security-groups", jsonutils.Marshal(params), secgroup) +} + +func (region *SRegion) syncSecgroupRules(secgroupId string, rules []secrules.SecurityRule) error { + secgroup, err := region.GetSecurityGroup(secgroupId) + if err != nil { + return err + } + + inRules, outRules := secrules.SecurityRuleSet{}, secrules.SecurityRuleSet{} + for i := 0; i < len(rules); i++ { + if rules[i].Direction == secrules.DIR_IN { + inRules = append(inRules, rules[i]) + } else { + outRules = append(outRules, rules[i]) + } + } + + if len(outRules) > 0 { + rule := secrules.MustParseSecurityRule("out:allow any") + outRules = append(outRules, *rule) + } + + rules = inRules.AllowList() + rules = append(rules, outRules.AllowList()...) + for i := 0; i < len(rules); i++ { + rules[i].Priority = 1 + } + + sort.Sort(secrules.SecurityRuleSet(rules)) + sort.Sort(SSecurityGroupRuleSet(secgroup.Rules)) + + delRuleIds := []string{} + addRules := []secrules.SecurityRule{} + + i, j := 0, 0 + for i < len(rules) || j < len(secgroup.Rules) { + if i < len(rules) && j < len(secgroup.Rules) { + _rule, err := secgroup.Rules[j].toRule() + if err != nil { + return err + } + _ruleStr := _rule.String() + ruleStr := rules[i].String() + cmp := strings.Compare(_ruleStr, ruleStr) + if cmp == 0 { + i++ + j++ + } else if cmp > 0 { + delRuleIds = append(delRuleIds, secgroup.Rules[j].UUID) + j++ + } else { + addRules = append(addRules, rules[i]) + i++ + } + } else if i >= len(rules) { + delRuleIds = append(delRuleIds, secgroup.Rules[j].UUID) + j++ + } else if j >= len(secgroup.Rules) { + addRules = append(addRules, rules[i]) + i++ + } + } + if len(delRuleIds) > 0 { + err = region.DeleteSecurityGroupRules(delRuleIds) + if err != nil { + return err + } + } + return region.AddSecurityGroupRule(secgroupId, addRules) +} diff --git a/pkg/util/zstack/shell/configration.go b/pkg/util/zstack/shell/configration.go new file mode 100644 index 0000000000..f29eed2ce4 --- /dev/null +++ b/pkg/util/zstack/shell/configration.go @@ -0,0 +1,19 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/shellutils" + "yunion.io/x/onecloud/pkg/util/zstack" +) + +func init() { + type ConfigrationListOptions struct { + } + shellutils.R(&ConfigrationListOptions{}, "configration-list", "List configration", func(cli *zstack.SRegion, args *ConfigrationListOptions) error { + configrations, err := cli.GetConfigrations() + if err != nil { + return err + } + printList(configrations, len(configrations), 0, 0, []string{}) + return nil + }) +} diff --git a/pkg/util/zstack/shell/disk.go b/pkg/util/zstack/shell/disk.go index 1bbf95d890..c5aa945200 100644 --- a/pkg/util/zstack/shell/disk.go +++ b/pkg/util/zstack/shell/disk.go @@ -20,6 +20,23 @@ func init() { return nil }) + type DiskIdOptions struct { + ID string + } + + shellutils.R(&DiskIdOptions{}, "disk-show", "Show disk", func(cli *zstack.SRegion, args *DiskIdOptions) error { + disk, err := cli.GetDisk(args.ID) + if err != nil { + return err + } + printObject(disk) + return nil + }) + + shellutils.R(&DiskIdOptions{}, "disk-delete", "Delete disk", func(cli *zstack.SRegion, args *DiskIdOptions) error { + return cli.DeleteDisk(args.ID) + }) + type DiskCreateOptions struct { NAME string Description string @@ -38,14 +55,6 @@ func init() { return nil }) - type DiskDelete struct { - ID string - } - - shellutils.R(&DiskDelete{}, "disk-delete", "Delete disk", func(cli *zstack.SRegion, args *DiskDelete) error { - return cli.DeleteDisk(args.ID) - }) - type DiskResize struct { ID string SIZEGB int64 diff --git a/pkg/util/zstack/shell/image.go b/pkg/util/zstack/shell/image.go index f48baa0d54..ca1edb2249 100644 --- a/pkg/util/zstack/shell/image.go +++ b/pkg/util/zstack/shell/image.go @@ -1,6 +1,8 @@ package shell import ( + "os" + "yunion.io/x/onecloud/pkg/util/shellutils" "yunion.io/x/onecloud/pkg/util/zstack" ) @@ -18,4 +20,29 @@ func init() { return nil }) + type ImageCreateOptions struct { + FILE string + FORMAT string `choices:"qcow2|raw|iso"` + PLATFORM string `choices:"Linux|Windows|Other"` + Desc string + } + + shellutils.R(&ImageCreateOptions{}, "image-create", "Create image", func(cli *zstack.SRegion, args *ImageCreateOptions) error { + f, err := os.Open(args.FILE) + if err != nil { + return err + } + defer f.Close() + finfo, err := f.Stat() + if err != nil { + return err + } + image, err := cli.CreateImage(args.FILE, args.FORMAT, args.PLATFORM, args.Desc, f, finfo.Size()) + if err != nil { + return err + } + printObject(image) + return nil + }) + } diff --git a/pkg/util/zstack/shell/image_server.go b/pkg/util/zstack/shell/image_server.go new file mode 100644 index 0000000000..fda97bdfb0 --- /dev/null +++ b/pkg/util/zstack/shell/image_server.go @@ -0,0 +1,21 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/shellutils" + "yunion.io/x/onecloud/pkg/util/zstack" +) + +func init() { + type ImageServerListOptions struct { + ZoneId string + } + shellutils.R(&ImageServerListOptions{}, "image-server-list", "List image servers", func(cli *zstack.SRegion, args *ImageServerListOptions) error { + servers, err := cli.GetImageServers(args.ZoneId) + if err != nil { + return err + } + printList(servers, 0, 0, 0, []string{}) + return nil + }) + +} diff --git a/pkg/util/zstack/shell/instance.go b/pkg/util/zstack/shell/instance.go index 6ea83a42e3..d3e35c5ac6 100644 --- a/pkg/util/zstack/shell/instance.go +++ b/pkg/util/zstack/shell/instance.go @@ -26,6 +26,10 @@ func init() { ID string } + shellutils.R(&InstanceOperation{}, "instance-delete", "Delete instance", func(cli *zstack.SRegion, args *InstanceOperation) error { + return cli.DeleteVM(args.ID) + }) + shellutils.R(&InstanceOperation{}, "instance-console-password", "Show instance console password", func(cli *zstack.SRegion, args *InstanceOperation) error { password, err := cli.GetInstanceConsolePassword(args.ID) if err != nil { @@ -44,4 +48,35 @@ func init() { return nil }) + shellutils.R(&InstanceOperation{}, "instance-start", "Start instance", func(cli *zstack.SRegion, args *InstanceOperation) error { + return cli.StartVM(args.ID) + }) + + shellutils.R(&InstanceOperation{}, "instance-boot-order", "Show instance boot order", func(cli *zstack.SRegion, args *InstanceOperation) error { + fmt.Println(cli.GetBootOrder(args.ID)) + return nil + }) + + type InstanceStopOption struct { + ID string + IsForce bool + } + + shellutils.R(&InstanceStopOption{}, "instance-stop", "Start instance", func(cli *zstack.SRegion, args *InstanceStopOption) error { + return cli.StopVM(args.ID, args.IsForce) + }) + + type InstanceSecgroupOption struct { + ID string + SECGRPID string + } + + shellutils.R(&InstanceSecgroupOption{}, "instance-assign-secgroup", "Assign secgroup for a instance", func(cli *zstack.SRegion, args *InstanceSecgroupOption) error { + return cli.AssignSecurityGroup(args.ID, args.SECGRPID) + }) + + shellutils.R(&InstanceSecgroupOption{}, "instance-revoke-secgroup", "Assign secgroup for a instance", func(cli *zstack.SRegion, args *InstanceSecgroupOption) error { + return cli.RevokeSecurityGroup(args.ID, args.SECGRPID) + }) + } diff --git a/pkg/util/zstack/shell/instance_offering.go b/pkg/util/zstack/shell/instance_offering.go new file mode 100644 index 0000000000..7757e644e0 --- /dev/null +++ b/pkg/util/zstack/shell/instance_offering.go @@ -0,0 +1,23 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/shellutils" + "yunion.io/x/onecloud/pkg/util/zstack" +) + +func init() { + type InstanceOfferingListOptions struct { + OfferId string + Name string + Cpu int + MemoryMb int + } + shellutils.R(&InstanceOfferingListOptions{}, "instance-offering-list", "List instance offerings", func(cli *zstack.SRegion, args *InstanceOfferingListOptions) error { + offerings, err := cli.GetInstanceOfferings(args.OfferId, args.Name, args.Cpu, args.MemoryMb) + if err != nil { + return err + } + printList(offerings, len(offerings), 0, 0, []string{}) + return nil + }) +} diff --git a/pkg/util/zstack/shell/network.go b/pkg/util/zstack/shell/network.go index 8afde8b702..e7c5faf1bb 100644 --- a/pkg/util/zstack/shell/network.go +++ b/pkg/util/zstack/shell/network.go @@ -20,4 +20,102 @@ func init() { printList(networks, len(networks), 0, 0, []string{}) return nil }) + + type NetworkDeleteOptions struct { + ID string + } + + shellutils.R(&NetworkDeleteOptions{}, "network-delete", "Delete network", func(cli *zstack.SRegion, args *NetworkDeleteOptions) error { + return cli.DeleteNetwork(args.ID) + }) + + type NetworkCreateOptions struct { + NAME string + Desc string + WIRE string + CIDR string + } + + shellutils.R(&NetworkCreateOptions{}, "network-create", "Create network", func(cli *zstack.SRegion, args *NetworkCreateOptions) error { + network, err := cli.CreateNetwork(args.NAME, args.CIDR, args.WIRE, args.Desc) + if err != nil { + return err + } + printObject(network) + return nil + }) + + type L3NetworkListOptions struct { + ZoneId string + WireId string + Id string + } + + shellutils.R(&L3NetworkListOptions{}, "l3network-list", "List networks", func(cli *zstack.SRegion, args *L3NetworkListOptions) error { + l3networks, err := cli.GetL3Networks(args.ZoneId, args.WireId, args.Id) + if err != nil { + return err + } + printList(l3networks, len(l3networks), 0, 0, []string{}) + return nil + }) + + type NetworkServicesOptions struct { + } + + shellutils.R(&NetworkServicesOptions{}, "network-service-list", "List network services", func(cli *zstack.SRegion, args *NetworkServicesOptions) error { + service, err := cli.GetNetworkServices() + if err != nil { + return err + } + printObject(service) + return nil + }) + + type NetworkServiceProviderOptions struct { + Type string + } + + shellutils.R(&NetworkServiceProviderOptions{}, "network-service-provider-list", "List network service providers", func(cli *zstack.SRegion, args *NetworkServiceProviderOptions) error { + providers, err := cli.GetNetworkServiceProviders(args.Type) + if err != nil { + return err + } + printList(providers, len(providers), 0, 0, []string{}) + return nil + }) + + type NetworkServiceAttachOptions struct { + Services []string + ID string + } + + shellutils.R(&NetworkServiceAttachOptions{}, "network-service-attach", "Attach network service to l3network", func(cli *zstack.SRegion, args *NetworkServiceAttachOptions) error { + cli.AttachServiceForl3Network(args.ID, args.Services) + return nil + }) + + type NetworkServiceRefOptions struct { + L3Id string + Type string + } + + shellutils.R(&NetworkServiceRefOptions{}, "network-service-ref", "List network service ref", func(cli *zstack.SRegion, args *NetworkServiceRefOptions) error { + refs, err := cli.GetNetworkServiceRef(args.L3Id, args.Type) + if err != nil { + return err + } + printList(refs, len(refs), 0, 0, []string{}) + return nil + }) + + type NetworkServiceRemoveOptions struct { + L3ID string + SERVICE string `choices:"Userdata|DHCP|VipQos|HostRoute|Eip|SecurityGroup|DNS|SNAT"` + } + + shellutils.R(&NetworkServiceRemoveOptions{}, "remove-network-service", "Remove l3network service", func(cli *zstack.SRegion, args *NetworkServiceRemoveOptions) error { + return cli.RemoveNetworkService(args.L3ID, args.SERVICE) + }) + } diff --git a/pkg/util/zstack/shell/offering.go b/pkg/util/zstack/shell/offering.go deleted file mode 100644 index b2df1290a7..0000000000 --- a/pkg/util/zstack/shell/offering.go +++ /dev/null @@ -1,20 +0,0 @@ -package shell - -import ( - "yunion.io/x/onecloud/pkg/util/shellutils" - "yunion.io/x/onecloud/pkg/util/zstack" -) - -func init() { - type OfferingListOptions struct { - OfferId string - } - shellutils.R(&OfferingListOptions{}, "offering-list", "List instance offerings", func(cli *zstack.SRegion, args *OfferingListOptions) error { - offerings, err := cli.GetInstanceOfferings(args.OfferId) - if err != nil { - return err - } - printList(offerings, len(offerings), 0, 0, []string{}) - return nil - }) -} diff --git a/pkg/util/zstack/shell/storage.go b/pkg/util/zstack/shell/storage.go index c3d3867dee..c54c766f96 100644 --- a/pkg/util/zstack/shell/storage.go +++ b/pkg/util/zstack/shell/storage.go @@ -11,8 +11,8 @@ func init() { ClusterId string Id string } - shellutils.R(&StorageListOptions{}, "primary-storage-list", "List storages", func(cli *zstack.SRegion, args *StorageListOptions) error { - storages, err := cli.GetPrimaryStorages(args.ZoneId, args.ClusterId, args.Id) + shellutils.R(&StorageListOptions{}, "storage-list", "List storages", func(cli *zstack.SRegion, args *StorageListOptions) error { + storages, err := cli.GetStorages(args.ZoneId, args.ClusterId, args.Id) if err != nil { return err } diff --git a/pkg/util/zstack/shell/vip.go b/pkg/util/zstack/shell/vip.go new file mode 100644 index 0000000000..0f27845f30 --- /dev/null +++ b/pkg/util/zstack/shell/vip.go @@ -0,0 +1,37 @@ +package shell + +import ( + "yunion.io/x/onecloud/pkg/util/shellutils" + "yunion.io/x/onecloud/pkg/util/zstack" +) + +func init() { + type VipListOptions struct { + VipId string + } + shellutils.R(&VipListOptions{}, "vip-list", "List vips", func(cli *zstack.SRegion, args *VipListOptions) error { + vips, err := cli.GetVirtualIPs(args.VipId) + if err != nil { + return err + } + printList(vips, 0, 0, 0, []string{}) + return nil + }) + + type VipCreateOptions struct { + NAME string + Desc string + Ip string + L3ID string + } + + shellutils.R(&VipCreateOptions{}, "vip-create", "Create vip", func(cli *zstack.SRegion, args *VipCreateOptions) error { + vip, err := cli.CreateVirtualIP(args.NAME, args.Desc, args.Ip, args.L3ID) + if err != nil { + return err + } + printObject(vip) + return nil + }) + +} diff --git a/pkg/util/zstack/snapshot.go b/pkg/util/zstack/snapshot.go index 2a94821c59..e9bab53f83 100644 --- a/pkg/util/zstack/snapshot.go +++ b/pkg/util/zstack/snapshot.go @@ -5,7 +5,6 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/log" - "yunion.io/x/onecloud/pkg/cloudprovider" api "yunion.io/x/onecloud/pkg/apis/compute" ) @@ -76,20 +75,8 @@ func (snapshot *SSnapshot) IsEmulated() bool { } func (region *SRegion) GetSnapshot(snapshotId string) (*SSnapshot, error) { - snapshots, err := region.GetSnapshots(snapshotId, "") - if err != nil { - return nil, err - } - if len(snapshots) == 1 { - if snapshots[0].UUID == snapshotId { - return &snapshots[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(snapshots) == 0 || len(snapshotId) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId + snapshot := &SSnapshot{region: region} + return snapshot, region.client.getResource("volume-snapshots", snapshotId, snapshot) } func (region *SRegion) GetSnapshots(snapshotId string, diskId string) ([]SSnapshot, error) { diff --git a/pkg/util/zstack/storage.go b/pkg/util/zstack/storage.go index b1ccc9f4f6..5da8dfa0ca 100644 --- a/pkg/util/zstack/storage.go +++ b/pkg/util/zstack/storage.go @@ -1,35 +1,248 @@ package zstack import ( + "fmt" + "strings" + + "yunion.io/x/jsonutils" + "yunion.io/x/log" + api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" ) +type TStorageType string + +type TCephPoolType string + +const ( + StorageTypeCeph = TStorageType("Ceph") + StorageTypeLocal = TStorageType("LocalStorage") + StorageTypeVCenter = TStorageType("VCenter") + + CephPoolTypeData = TCephPoolType("Data") + CephPoolTypeRoot = TCephPoolType("Root") + CephPoolTypeImageCache = TCephPoolType("ImageCache") +) + +type SPool struct { + UUID string `json:"uuid"` + PrimaryStorageUUID string `json:"primaryStorageUuid"` + PoolName string `json:"poolName"` + Type TCephPoolType `json:"type"` + AvailableCapacity int64 `json:"availableCapacity"` + UsedCapacity int64 `json:"usedCapacity"` + ReplicatedSize int64 `json:"replicatedSize"` + TotalCapacity int64 `json:"totalCapacity"` + ZStackTime +} + +type SStorage struct { + region *SRegion + + ZStackBasic + VCenterUUID string `json:"VCenterUuid"` + Datastore string `json:"datastore"` + ZoneUUID string `json:"zoneUuid"` + URL string `json:"url"` + TotalCapacity int64 `json:"totalCapacity"` + AvailableCapacity int `json:"availableCapacity"` + TotalPhysicalCapacity int `json:"totalPhysicalCapacity"` + AvailablePhysicalCapacity int `json:"availablePhysicalCapacity"` + Type TStorageType `json:"type"` + State string `json:"state"` + Status string `json:"status"` + MountPath string `json:"mountPath"` + AttachedClusterUUIDs []string `json:"attachedClusterUuids"` + + Pools []SPool `json:"pools"` + + ZStackTime +} + func (region *SRegion) getIStorages(zondId string) ([]cloudprovider.ICloudStorage, error) { - primaryStorages, err := region.GetPrimaryStorages(zondId, "", "") + primaryStorages, err := region.GetStorages(zondId, "", "") if err != nil { return nil, err } istorage := []cloudprovider.ICloudStorage{} - for _, primaryStorage := range primaryStorages { - zone, err := region.GetZone(primaryStorage.ZoneUUID) - if err != nil { - return nil, err - } + for i := 0; i < len(primaryStorages); i++ { + primaryStorage := primaryStorages[i] switch primaryStorage.Type { case StorageTypeLocal: - ilocalStorages, err := region.getILocalStorages(zone, primaryStorage.UUID, "") + ilocalStorages, err := region.getILocalStorages(primaryStorage.UUID, "") if err != nil { return nil, err } istorage = append(istorage, ilocalStorages...) case StorageTypeCeph: - icephStorage, err := region.getICephStorages(zone, primaryStorage.UUID) - if err != nil { - return nil, err - } - istorage = append(istorage, icephStorage...) + primaryStorage.region = region + istorage = append(istorage, &primaryStorage) case StorageTypeVCenter: } } return istorage, nil } + +func (region *SRegion) GetStorage(storageId string) (*SStorage, error) { + if len(storageId) == 0 { + return nil, cloudprovider.ErrNotFound + } + storages, err := region.GetStorages("", "", storageId) + if err != nil { + return nil, err + } + if len(storages) == 1 && storages[0].UUID == storageId { + storages[0].region = region + return &storages[0], nil + } + if len(storages) == 0 { + return nil, cloudprovider.ErrNotFound + } + return nil, cloudprovider.ErrDuplicateId +} + +func (region *SRegion) GetStorages(zoneId, clusterId, storageId string) ([]SStorage, error) { + storages := []SStorage{} + params := []string{} + if len(zoneId) > 0 { + params = append(params, "q=zone.uuid="+zoneId) + } + if len(clusterId) > 0 { + params = append(params, "q=cluster.uuid="+clusterId) + } + if SkipEsxi { + params = append(params, "q=type!=VCenter") + } + if len(storageId) > 0 { + params = append(params, "q=uuid="+storageId) + } + return storages, region.client.listAll("primary-storage", params, &storages) +} + +func (storage *SStorage) GetStatus() string { + if storage.Status == "Connected" { + return api.STORAGE_ONLINE + } + return api.STORAGE_OFFLINE +} + +func (storage *SStorage) GetMetadata() *jsonutils.JSONDict { + return nil +} + +func (storage *SStorage) GetId() string { + return storage.UUID +} + +func (storage *SStorage) GetName() string { + return storage.Name +} + +func (storage *SStorage) GetGlobalId() string { + return storage.GetId() +} + +func (storage *SStorage) IsEmulated() bool { + return false +} + +func (storage *SStorage) GetIZone() cloudprovider.ICloudZone { + zone, err := storage.region.GetZone(storage.ZoneUUID) + if err != nil { + log.Errorf("failed to find zone for storage %s(%s)", storage.Name, storage.UUID) + return nil + } + return zone +} + +func (storage *SStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) { + disks, err := storage.region.GetDisks(storage.UUID, []string{}, "") + if err != nil { + return nil, err + } + idisks := []cloudprovider.ICloudDisk{} + for i := 0; i < len(disks); i++ { + disks[i].storage = storage + disks[i].region = storage.region + idisks = append(idisks, &disks[i]) + } + return idisks, nil +} + +func (storage *SStorage) GetStorageType() string { + return strings.ToLower(string(storage.Type)) +} + +func (storage *SStorage) GetMediumType() string { + return api.DISK_TYPE_ROTATE +} + +func (storage *SStorage) GetCapacityMB() int64 { + return storage.TotalCapacity / 1024 / 1024 +} + +func (storage *SStorage) GetStorageConf() jsonutils.JSONObject { + conf := jsonutils.NewDict() + return conf +} + +func (storage *SStorage) Refresh() error { + // do nothing + return nil +} + +func (storage *SStorage) GetEnabled() bool { + return true +} + +func (storage *SStorage) GetIStoragecache() cloudprovider.ICloudStoragecache { + storage.region.GetIStoragecaches() + return storage.region.storageCache +} + +func (storage *SStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) { + poolName, err := storage.GetDataPoolName() + if err != nil { + return nil, err + } + disk, err := storage.region.CreateDisk(name, storage.UUID, "", poolName, sizeGb, desc) + if err != nil { + return nil, err + } + disk.storage = storage + return disk, nil +} + +func (storage *SStorage) GetIDiskById(diskId string) (cloudprovider.ICloudDisk, error) { + disk, err := storage.region.GetDisk(diskId) + if err != nil { + return nil, err + } + if disk.PrimaryStorageUUID != storage.UUID { + return nil, cloudprovider.ErrNotFound + } + disk.region = storage.region + disk.storage = storage + return disk, nil +} + +func (storage *SStorage) GetMountPoint() string { + poolName, _ := storage.GetDataPoolName() + if len(poolName) > 0 { + return fmt.Sprintf("ceph://%s", poolName) + } + return "" +} + +func (storage *SStorage) IsSysDiskStore() bool { + return true +} + +func (storage *SStorage) GetDataPoolName() (string, error) { + for _, pool := range storage.Pools { + if pool.Type == CephPoolTypeData { + return pool.PoolName, nil + } + } + return "", fmt.Errorf("failed to found storage %s(%s) data pool name", storage.Name, storage.UUID) +} diff --git a/pkg/util/zstack/storage_ceph.go b/pkg/util/zstack/storage_ceph.go deleted file mode 100644 index d499445b83..0000000000 --- a/pkg/util/zstack/storage_ceph.go +++ /dev/null @@ -1,172 +0,0 @@ -package zstack - -import ( - "fmt" - "strings" - - "yunion.io/x/jsonutils" - api "yunion.io/x/onecloud/pkg/apis/compute" - "yunion.io/x/onecloud/pkg/cloudprovider" -) - -type TCephPoolType string - -const ( - CephPoolTypeData = TCephPoolType("Data") - CephPoolTypeRoot = TCephPoolType("Root") - CephPoolTypeImageCache = TCephPoolType("ImageCache") -) - -type SCephStorage struct { - zone *SZone - - UUID string `json:"uuid"` - PrimaryStorageUUID string `json:"primaryStorageUuid"` - PoolName string `json:"poolName"` - Type TCephPoolType `json:"type"` - AvailableCapacity int64 `json:"availableCapacity"` - UsedCapacity int64 `json:"usedCapacity"` - ReplicatedSize int64 `json:"replicatedSize"` - TotalCapacity int64 `json:"totalCapacity"` - ZStackTime -} - -func (region *SRegion) getICephStorages(zone *SZone, storageId string) ([]cloudprovider.ICloudStorage, error) { - storage, err := region.GetPrimaryStorage(storageId) - if err != nil { - return nil, err - } - istorages := []cloudprovider.ICloudStorage{} - for i := 0; i < len(storage.Pools); i++ { - storage.Pools[i].zone = zone - istorages = append(istorages, &storage.Pools[i]) - } - return istorages, nil -} - -func (storage *SCephStorage) GetMetadata() *jsonutils.JSONDict { - return nil -} - -func (storage *SCephStorage) GetId() string { - return fmt.Sprintf("%s/%s", storage.PrimaryStorageUUID, storage.UUID) -} - -func (storage *SCephStorage) GetName() string { - primaryStorage, err := storage.zone.region.GetPrimaryStorage(storage.PrimaryStorageUUID) - if err != nil { - return "Unknown" - } - return fmt.Sprintf("%s/%s", primaryStorage.Name, storage.PoolName) -} - -func (storage *SCephStorage) GetGlobalId() string { - return storage.GetId() -} - -func (storage *SCephStorage) IsEmulated() bool { - return false -} - -func (storage *SCephStorage) GetIZone() cloudprovider.ICloudZone { - return storage.zone -} - -func (storage *SCephStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) { - if storage.Type == CephPoolTypeImageCache { - return []cloudprovider.ICloudDisk{}, nil - } - disks, err := storage.zone.region.GetDisks(storage.PrimaryStorageUUID, []string{}, string(storage.Type)) - if err != nil { - return nil, err - } - idisks := []cloudprovider.ICloudDisk{} - for i := 0; i < len(disks); i++ { - disks[i].cephStorage = storage - disks[i].region = storage.zone.region - idisks = append(idisks, &disks[i]) - } - return idisks, nil -} - -func (storage *SCephStorage) GetStorageType() string { - return strings.ToLower(string(storage.Type)) -} - -func (storage *SCephStorage) GetMediumType() string { - return api.DISK_TYPE_ROTATE -} - -func (storage *SCephStorage) GetCapacityMB() int64 { - return storage.TotalCapacity / 1024 / 1024 -} - -func (storage *SCephStorage) GetStorageConf() jsonutils.JSONObject { - conf := jsonutils.NewDict() - return conf -} - -func (storage *SCephStorage) GetManagerId() string { - return storage.zone.region.client.providerID -} - -func (storage *SCephStorage) GetStatus() string { - primaryStorage, err := storage.zone.region.GetPrimaryStorage(storage.PrimaryStorageUUID) - if err != nil { - return api.STORAGE_OFFLINE - } - return primaryStorage.GetStatus() -} - -func (storage *SCephStorage) Refresh() error { - // do nothing - return nil -} - -func (storage *SCephStorage) GetEnabled() bool { - return true -} - -func (storage *SCephStorage) GetIStoragecache() cloudprovider.ICloudStoragecache { - storage.zone.region.GetIStoragecaches() - return storage.zone.region.storageCache -} - -func (storage *SCephStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) { - if storage.Type == CephPoolTypeData { - disk, err := storage.zone.region.CreateDisk(name, storage.PrimaryStorageUUID, "", storage.PoolName, sizeGb, desc) - if err != nil { - return nil, err - } - disk.cephStorage = storage - return disk, nil - } - return nil, cloudprovider.ErrNotSupported -} - -func (storage *SCephStorage) GetIDiskById(diskId string) (cloudprovider.ICloudDisk, error) { - disks, err := storage.zone.region.GetDisks(storage.PrimaryStorageUUID, []string{diskId}, storage.PoolName) - if err != nil { - return nil, err - } - if len(disks) == 1 { - if disks[0].UUID == diskId { - disks[0].region = storage.zone.region - disks[0].cephStorage = storage - return &disks[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(disks) == 0 || len(diskId) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId -} - -func (storage *SCephStorage) GetMountPoint() string { - return "ceph://" + storage.PoolName -} - -func (storage *SCephStorage) IsSysDiskStore() bool { - return storage.Type == CephPoolTypeRoot -} diff --git a/pkg/util/zstack/storage_local.go b/pkg/util/zstack/storage_local.go index 42300865e8..32588d32cd 100644 --- a/pkg/util/zstack/storage_local.go +++ b/pkg/util/zstack/storage_local.go @@ -5,12 +5,13 @@ import ( "strings" "yunion.io/x/jsonutils" + "yunion.io/x/log" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" ) type SLocalStorage struct { - zone *SZone + region *SRegion primaryStorageID string HostUUID string `json:"hostUuid"` @@ -48,19 +49,19 @@ func (region *SRegion) GetLocalStorages(storageId string, hostId string) ([]SLoc return nil, err } for i := 0; i < len(localStorage); i++ { + localStorage[i].region = region localStorage[i].primaryStorageID = storageId } return localStorage, nil } -func (region *SRegion) getILocalStorages(zone *SZone, storageId, hostId string) ([]cloudprovider.ICloudStorage, error) { +func (region *SRegion) getILocalStorages(storageId, hostId string) ([]cloudprovider.ICloudStorage, error) { storages, err := region.GetLocalStorages(storageId, hostId) if err != nil { return nil, err } istorage := []cloudprovider.ICloudStorage{} for i := 0; i < len(storages); i++ { - storages[i].zone = zone istorage = append(istorage, &storages[i]) } return istorage, nil @@ -71,15 +72,15 @@ func (storage *SLocalStorage) GetMetadata() *jsonutils.JSONDict { } func (storage *SLocalStorage) GetId() string { - return fmt.Sprintf("%s/%s", storage.primaryStorageID, storage.HostUUID) + return storage.primaryStorageID } func (storage *SLocalStorage) GetName() string { - primaryStorage, err := storage.zone.region.GetPrimaryStorage(storage.primaryStorageID) + primaryStorage, err := storage.region.GetStorage(storage.primaryStorageID) if err != nil { return "Unknown" } - host, err := storage.zone.region.GetHost(storage.zone.UUID, storage.HostUUID) + host, err := storage.region.GetHost(storage.HostUUID) if err != nil { return "Unknown" } @@ -87,7 +88,7 @@ func (storage *SLocalStorage) GetName() string { } func (storage *SLocalStorage) GetGlobalId() string { - return storage.GetId() + return fmt.Sprintf("%s/%s", storage.primaryStorageID, storage.HostUUID) } func (storage *SLocalStorage) IsEmulated() bool { @@ -95,11 +96,20 @@ func (storage *SLocalStorage) IsEmulated() bool { } func (storage *SLocalStorage) GetIZone() cloudprovider.ICloudZone { - return storage.zone + host, err := storage.region.GetHost(storage.HostUUID) + if err != nil { + log.Errorf("failed get host info %s error: %v", storage.HostUUID, err) + return nil + } + zone, err := storage.region.GetZone(host.ZoneUUID) + if err != nil { + log.Errorf("failed get zone info %s error: %v", host.ZoneUUID, err) + } + return zone } func (storage *SLocalStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) { - tags, err := storage.zone.region.GetSysTags("", "VolumeVO", "", "localStorage::hostUuid::"+storage.HostUUID) + tags, err := storage.region.GetSysTags("", "VolumeVO", "", "localStorage::hostUuid::"+storage.HostUUID) if err != nil { return nil, err } @@ -107,14 +117,17 @@ func (storage *SLocalStorage) GetIDisks() ([]cloudprovider.ICloudDisk, error) { for i := 0; i < len(tags); i++ { diskIds = append(diskIds, tags[i].ResourceUUID) } - disks, err := storage.zone.region.GetDisks(storage.primaryStorageID, diskIds, "") + idisks := []cloudprovider.ICloudDisk{} + if len(diskIds) == 0 { + return idisks, nil + } + disks, err := storage.region.GetDisks(storage.primaryStorageID, diskIds, "") if err != nil { return nil, err } - idisks := []cloudprovider.ICloudDisk{} for i := 0; i < len(disks); i++ { disks[i].localStorage = storage - disks[i].region = storage.zone.region + disks[i].region = storage.region idisks = append(idisks, &disks[i]) } return idisks, nil @@ -137,12 +150,8 @@ func (storage *SLocalStorage) GetStorageConf() jsonutils.JSONObject { return conf } -func (storage *SLocalStorage) GetManagerId() string { - return storage.zone.region.client.providerID -} - func (storage *SLocalStorage) GetStatus() string { - primaryStorage, err := storage.zone.region.GetPrimaryStorage(storage.primaryStorageID) + primaryStorage, err := storage.region.GetStorage(storage.primaryStorageID) if err != nil { return api.STORAGE_OFFLINE } @@ -159,12 +168,12 @@ func (storage *SLocalStorage) GetEnabled() bool { } func (storage *SLocalStorage) GetIStoragecache() cloudprovider.ICloudStoragecache { - storage.zone.region.GetIStoragecaches() - return storage.zone.region.storageCache + storage.region.GetIStoragecaches() + return storage.region.storageCache } func (storage *SLocalStorage) CreateIDisk(name string, sizeGb int, desc string) (cloudprovider.ICloudDisk, error) { - disk, err := storage.zone.region.CreateDisk(name, storage.primaryStorageID, storage.HostUUID, "", sizeGb, desc) + disk, err := storage.region.CreateDisk(name, storage.primaryStorageID, storage.HostUUID, "", sizeGb, desc) if err != nil { return nil, err } @@ -173,23 +182,16 @@ func (storage *SLocalStorage) CreateIDisk(name string, sizeGb int, desc string) } func (storage *SLocalStorage) GetIDiskById(diskId string) (cloudprovider.ICloudDisk, error) { - tags, err := storage.zone.region.GetSysTags("", "VolumeVO", diskId, "localStorage::hostUuid::"+storage.HostUUID) + disk, err := storage.region.GetDisk(diskId) if err != nil { return nil, err } - if len(tags) == 1 { - disk, err := storage.zone.region.GetDisk(diskId) - if err != nil { - return nil, err - } - disk.localStorage = storage - disk.region = storage.zone.region - return disk, nil - } - if len(tags) == 0 || len(diskId) == 0 { + if disk.PrimaryStorageUUID != storage.primaryStorageID { return nil, cloudprovider.ErrNotFound } - return nil, cloudprovider.ErrDuplicateId + disk.localStorage = storage + disk.region = storage.region + return disk, nil } func (storage *SLocalStorage) GetMountPoint() string { diff --git a/pkg/util/zstack/storage_primary.go b/pkg/util/zstack/storage_primary.go deleted file mode 100644 index 15144f7ffa..0000000000 --- a/pkg/util/zstack/storage_primary.go +++ /dev/null @@ -1,79 +0,0 @@ -package zstack - -import ( - api "yunion.io/x/onecloud/pkg/apis/compute" - "yunion.io/x/onecloud/pkg/cloudprovider" -) - -type TStorageType string - -const ( - StorageTypeCeph = TStorageType("Ceph") - StorageTypeLocal = TStorageType("LocalStorage") - StorageTypeVCenter = TStorageType("VCenter") -) - -type SPrimaryStorage struct { - zone *SZone - - ZStackBasic - VCenterUUID string `json:"VCenterUuid"` - Datastore string `json:"datastore"` - ZoneUUID string `json:"zoneUuid"` - URL string `json:"url"` - TotalCapacity int64 `json:"totalCapacity"` - AvailableCapacity int `json:"availableCapacity"` - TotalPhysicalCapacity int `json:"totalPhysicalCapacity"` - AvailablePhysicalCapacity int `json:"availablePhysicalCapacity"` - Type TStorageType `json:"type"` - State string `json:"state"` - Status string `json:"status"` - MountPath string `json:"mountPath"` - AttachedClusterUUIDs []string `json:"attachedClusterUuids"` - - Pools []SCephStorage `json:"pools"` - - ZStackTime -} - -func (region *SRegion) GetPrimaryStorage(storageId string) (*SPrimaryStorage, error) { - storages, err := region.GetPrimaryStorages("", "", storageId) - if err != nil { - return nil, err - } - if len(storages) == 1 { - if storages[0].UUID == storageId { - return &storages[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(storages) == 0 || len(storageId) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId -} - -func (region *SRegion) GetPrimaryStorages(zoneId, clusterId, storageId string) ([]SPrimaryStorage, error) { - storages := []SPrimaryStorage{} - params := []string{} - if len(zoneId) > 0 { - params = append(params, "q=zone.uuid="+zoneId) - } - if len(clusterId) > 0 { - params = append(params, "q=cluster.uuid="+clusterId) - } - if SkipEsxi { - params = append(params, "q=type!=VCenter") - } - if len(storageId) > 0 { - params = append(params, "q=uuid="+storageId) - } - return storages, region.client.listAll("primary-storage", params, &storages) -} - -func (storage *SPrimaryStorage) GetStatus() string { - if storage.Status == "Connected" { - return api.STORAGE_ONLINE - } - return api.STORAGE_OFFLINE -} diff --git a/pkg/util/zstack/storagecache.go b/pkg/util/zstack/storagecache.go index 64a07ddfaf..a605b4797f 100644 --- a/pkg/util/zstack/storagecache.go +++ b/pkg/util/zstack/storagecache.go @@ -3,10 +3,17 @@ package zstack import ( "context" "fmt" + "time" "yunion.io/x/jsonutils" + "yunion.io/x/log" + api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudprovider" + "yunion.io/x/onecloud/pkg/image/options" "yunion.io/x/onecloud/pkg/mcclient" + "yunion.io/x/onecloud/pkg/mcclient/auth" + "yunion.io/x/onecloud/pkg/mcclient/modules" + "yunion.io/x/onecloud/pkg/util/qemuimg" ) type SStoragecache struct { @@ -41,10 +48,6 @@ func (scache *SStoragecache) IsEmulated() bool { return false } -func (scache *SStoragecache) GetManagerId() string { - return scache.region.client.providerID -} - func (scache *SStoragecache) GetIImages() ([]cloudprovider.ICloudImage, error) { images, err := scache.region.GetImages("") if err != nil { @@ -71,8 +74,45 @@ func (scache *SStoragecache) GetPath() string { return "" } -func (scache *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.TokenCredential, imageId string, osArch, osType, osDist, osVersion string, extId string, isForce bool) (string, error) { - return "", cloudprovider.ErrNotImplemented +func (scache *SStoragecache) UploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, isForce bool) (string, error) { + if len(image.ExternalId) > 0 { + log.Debugf("UploadImage: Image external ID exists %s", image.ExternalId) + + img, err := scache.region.GetImage(image.ExternalId) + if err != nil { + log.Errorf("GetImageStatus error %s", err) + } + status := img.GetStatus() + if api.CACHED_IMAGE_STATUS_READY == status && !isForce { + return image.ExternalId, nil + } + log.Debugf("image %s status %s", image.ExternalId, status) + } else { + log.Debugf("UploadImage: no external ID") + } + + return scache.uploadImage(ctx, userCred, image, isForce) +} + +func (self *SStoragecache) uploadImage(ctx context.Context, userCred mcclient.TokenCredential, image *cloudprovider.SImageCreateOption, isForce bool) (string, error) { + s := auth.GetAdminSession(ctx, options.Options.Region, "") + + meta, reader, err := modules.Images.Download(s, image.ImageId, string(qemuimg.QCOW2), false) + if err != nil { + return "", err + } + log.Infof("meta data %s", meta) + + size, _ := meta.Int("size") + img, err := self.region.CreateImage(image.ImageName, string(qemuimg.QCOW2), image.OsType, "", reader, size) + if err != nil { + return "", err + } + err = cloudprovider.WaitStatus(img, api.CACHED_IMAGE_STATUS_READY, time.Second*5, time.Minute*5) + if err != nil { + log.Errorf("waitting for image %s(%s) status ready timeout", img.Name, img.UUID) + } + return img.UUID, err } func (scache *SStoragecache) CreateIImage(snapshoutId, imageName, osType, imageDesc string) (cloudprovider.ICloudImage, error) { diff --git a/pkg/util/zstack/vip.go b/pkg/util/zstack/vip.go new file mode 100644 index 0000000000..60fb6bdcf1 --- /dev/null +++ b/pkg/util/zstack/vip.go @@ -0,0 +1,70 @@ +package zstack + +import ( + "fmt" + + "yunion.io/x/jsonutils" +) + +type SVirtualIP struct { + ZStackBasic + IPRangeUUID string `json:"ipRangeUuid"` + L3NetworkUUID string `json:"l3NetworkUuid"` + IP string `json:"ip"` + State string `json:"state"` + Gateway string `json:"gateway"` + Netmask string `json:"netmask"` + PrefixLen int `json:"prefixLen"` + ServiceProvider string `json:"serviceProvider"` + PeerL3NetworkUuids []string `json:"peerL3NetworkUuids"` + UseFor string `json:"useFor"` + UsedIPUUID string `json:"usedIpUuid"` + ZStackTime +} + +func (region *SRegion) GetVirtualIP(vipId string) (*SVirtualIP, error) { + vip := &SVirtualIP{} + return vip, region.client.getResource("vips", vipId, vip) +} + +func (region *SRegion) GetNetworkId(vip *SVirtualIP) string { + networks, err := region.GetNetworks("", "", vip.L3NetworkUUID, "") + if err == nil { + for _, network := range networks { + if network.Contains(vip.IP) { + return fmt.Sprintf("%s/%s", vip.L3NetworkUUID, network.UUID) + } + } + } + return "" +} + +func (region *SRegion) GetVirtualIPs(vipId string) ([]SVirtualIP, error) { + vips := []SVirtualIP{} + params := []string{} + if len(vipId) > 0 { + params = append(params, "q=uuid="+vipId) + } + return vips, region.client.listAll("vips", params, &vips) +} + +func (region *SRegion) CreateVirtualIP(name, desc, ip string, l3Id string) (*SVirtualIP, error) { + vip := SVirtualIP{} + params := map[string]map[string]string{ + "params": { + "name": name, + "description": desc, + "requiredIp": ip, + "l3NetworkUuid": l3Id, + }, + } + resp, err := region.client.post("vips", jsonutils.Marshal(params)) + if err != nil { + return nil, err + } + return &vip, resp.Unmarshal(&vip, "inventory") +} + +func (region *SRegion) DeleteVirtualIP(vipId string) error { + return region.client.delete("vips", vipId, "") +} diff --git a/pkg/util/zstack/vpc.go b/pkg/util/zstack/vpc.go index 0b74e206a6..5b78d4009f 100644 --- a/pkg/util/zstack/vpc.go +++ b/pkg/util/zstack/vpc.go @@ -24,7 +24,7 @@ func (vpc *SVpc) GetId() string { } func (vpc *SVpc) GetName() string { - return DEFAULT_VPC_NAME + return fmt.Sprintf("%s-VPC", vpc.region.client.providerName) } func (vpc *SVpc) GetGlobalId() string { @@ -95,10 +95,6 @@ func (vpc *SVpc) GetIRouteTables() ([]cloudprovider.ICloudRouteTable, error) { return nil, cloudprovider.ErrNotSupported } -func (vpc *SVpc) GetManagerId() string { - return vpc.region.client.providerID -} - func (vpc *SVpc) Delete() error { return nil } diff --git a/pkg/util/zstack/wire.go b/pkg/util/zstack/wire.go index 4d55fe7902..0855fae2c0 100644 --- a/pkg/util/zstack/wire.go +++ b/pkg/util/zstack/wire.go @@ -1,6 +1,7 @@ package zstack import ( + "fmt" "strings" "yunion.io/x/jsonutils" @@ -22,20 +23,8 @@ type SWire struct { } func (region *SRegion) GetWire(wireId string) (*SWire, error) { - wires, err := region.GetWires("", wireId, "") - if err != nil { - return nil, err - } - if len(wires) == 1 { - if wires[0].UUID == wireId { - return &wires[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(wires) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId + wire := &SWire{vpc: region.GetVpc()} + return wire, region.client.getResource("l2-networks", wireId, wire) } func (region *SRegion) GetWires(zoneId string, wireId string, clusterId string) ([]SWire, error) { @@ -121,9 +110,23 @@ func (wire *SWire) GetBandwidth() int { } func (wire *SWire) CreateINetwork(name string, cidr string, desc string) (cloudprovider.ICloudNetwork, error) { - return nil, cloudprovider.ErrNotImplemented + network, err := wire.vpc.region.CreateNetwork(name, cidr, wire.UUID, desc) + if err != nil { + return nil, err + } + network.wire = wire + return network, nil } func (wire *SWire) GetINetworkById(netid string) (cloudprovider.ICloudNetwork, error) { - return nil, cloudprovider.ErrNotImplemented + idInfo := strings.Split(netid, "/") + if len(idInfo) == 2 { + network, err := wire.vpc.region.GetNetwork(wire.ZoneUUID, wire.UUID, idInfo[0], idInfo[1]) + if err != nil { + return nil, err + } + network.wire = wire + return network, nil + } + return nil, fmt.Errorf("invalid netid %s", netid) } diff --git a/pkg/util/zstack/zone.go b/pkg/util/zstack/zone.go index 9931ba9aac..9d494888be 100644 --- a/pkg/util/zstack/zone.go +++ b/pkg/util/zstack/zone.go @@ -85,43 +85,14 @@ func (zone *SZone) GetIStorageById(storageId string) (cloudprovider.ICloudStorag return nil, cloudprovider.ErrNotFound } -func (region *SRegion) GetHosts(zoneId string, hostId string) ([]SHost, error) { - hosts := []SHost{} - params := []string{} - if len(zoneId) > 0 { - params = append(params, "q=zone.uuid="+zoneId) - } - if len(hostId) > 0 { - params = append(params, "q=uuid="+hostId) - } - if SkipEsxi { - params = append(params, "q=hypervisorType!=ESX") - } - return hosts, region.client.listAll("hosts", params, &hosts) -} - -func (region *SRegion) GetHost(zoneId string, hostId string) (*SHost, error) { - hosts, err := region.GetHosts(zoneId, hostId) - if err != nil { - return nil, err - } - if len(hosts) == 1 { - if hosts[0].UUID == hostId { - return &hosts[0], nil - } - return nil, cloudprovider.ErrNotFound - } - if len(hosts) == 0 || len(hostId) == 0 { - return nil, cloudprovider.ErrNotFound - } - return nil, cloudprovider.ErrDuplicateId -} - func (zone *SZone) GetIHostById(hostId string) (cloudprovider.ICloudHost, error) { - host, err := zone.region.GetHost(zone.UUID, hostId) + host, err := zone.region.GetHost(hostId) if err != nil { return nil, err } + if host.ZoneUUID != zone.UUID { + return nil, cloudprovider.ErrNotFound + } host.zone = zone return host, nil } diff --git a/pkg/util/zstack/zstack.go b/pkg/util/zstack/zstack.go index 89ba1bfcae..878abac463 100644 --- a/pkg/util/zstack/zstack.go +++ b/pkg/util/zstack/zstack.go @@ -141,6 +141,9 @@ func (cli *SZStackClient) _list(resource string, start int, limit int, params [] } func (cli *SZStackClient) getDeleteURL(resource, resourceId, deleteMode string) string { + if len(resourceId) == 0 { + return cli.authURL + fmt.Sprintf("/zstack/%s/%s", ZSTACK_API_VERSION, resource) + } url := cli.authURL + fmt.Sprintf("/zstack/%s/%s/%s", ZSTACK_API_VERSION, resource, resourceId) if len(deleteMode) > 0 { url += "?deleteMode=" + deleteMode @@ -171,6 +174,12 @@ func (cli *SZStackClient) _delete(resource, resourceId, deleteMode string) (json } func (cli *SZStackClient) getURL(resource, resourceId, spec string) string { + if len(resourceId) == 0 { + return cli.authURL + fmt.Sprintf("/zstack/%s/%s", ZSTACK_API_VERSION, resource) + } + if len(spec) == 0 { + return cli.authURL + fmt.Sprintf("/zstack/%s/%s/%s", ZSTACK_API_VERSION, resource, resourceId) + } return cli.authURL + fmt.Sprintf("/zstack/%s/%s/%s/%s", ZSTACK_API_VERSION, resource, resourceId, spec) } @@ -199,6 +208,27 @@ func (cli *SZStackClient) _put(resource, resourceId string, params jsonutils.JSO return resp, nil } +func (cli *SZStackClient) getResource(resource, resourceId string, retval interface{}) error { + if len(resourceId) == 0 { + return cloudprovider.ErrNotFound + } + resp, err := cli._get(resource, resourceId, "") + if err != nil { + return err + } + inventories, err := resp.GetArray("inventories") + if err != nil { + return err + } + if len(inventories) == 1 { + return inventories[0].Unmarshal(retval) + } + if len(inventories) == 0 { + return cloudprovider.ErrNotFound + } + return cloudprovider.ErrDuplicateId +} + func (cli *SZStackClient) get(resource, resourceId string, spec string) (jsonutils.JSONObject, error) { return cli._get(resource, resourceId, spec) } @@ -210,7 +240,25 @@ func (cli *SZStackClient) _get(resource, resourceId string, spec string) (jsonut header.Add("Authorization", "OAuth "+cli.sessionID) requestURL := cli.getURL(resource, resourceId, spec) _, resp, err := httputils.JSONRequest(client, context.Background(), "GET", requestURL, header, nil, cli.debug) - return resp, err + if err != nil { + return nil, err + } + if resp.Contains("location") { + location, _ := resp.GetString("location") + return cli.wait(client, header, "get", requestURL, jsonutils.NewDict(), location) + } + return resp, nil +} + +func (cli *SZStackClient) create(resource string, params jsonutils.JSONObject, retval interface{}) error { + resp, err := cli._post(resource, params) + if err != nil { + return err + } + if retval == nil { + return nil + } + return resp.Unmarshal(retval, "inventory") } func (cli *SZStackClient) post(resource string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) { diff --git a/pkg/util/zstack/zstack_const.go b/pkg/util/zstack/zstack_const.go index 9734b52e88..27dbec2f53 100644 --- a/pkg/util/zstack/zstack_const.go +++ b/pkg/util/zstack/zstack_const.go @@ -12,7 +12,3 @@ type ZStackBasic struct { Name string `json:"name"` Description string `json:"description"` } - -const ( - DEFAULT_VPC_NAME = "ZStackVPC" -) diff --git a/pkg/webconsole/session/remote_console.go b/pkg/webconsole/session/remote_console.go index 6f00d7d6b9..84295bc0b3 100644 --- a/pkg/webconsole/session/remote_console.go +++ b/pkg/webconsole/session/remote_console.go @@ -31,6 +31,7 @@ const ( SPICE = "spice" WMKS = "wmks" VMRC = "vmrc" + ZSTACK = "zstack" ) type RemoteConsoleInfo struct { @@ -108,7 +109,7 @@ func (info *RemoteConsoleInfo) GetConnectParams() (string, error) { return info.getAliyunURL() case QCLOUD: return info.getQcloudURL() - case OPENSTACK, VMRC: + case OPENSTACK, VMRC, ZSTACK: return info.Url, nil default: return "", fmt.Errorf("Can't convert protocol %s to connect params", info.Protocol)