mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-28 01:12:35 +08:00
488 lines
12 KiB
Go
488 lines
12 KiB
Go
// 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 ecloud
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/util/sets"
|
|
|
|
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
|
|
api "yunion.io/x/onecloud/pkg/apis/compute"
|
|
"yunion.io/x/onecloud/pkg/cloudprovider"
|
|
"yunion.io/x/onecloud/pkg/multicloud"
|
|
"yunion.io/x/onecloud/pkg/util/billing"
|
|
)
|
|
|
|
type SNovaRequest struct {
|
|
SApiRequest
|
|
}
|
|
|
|
func NewNovaRequest(ar *SApiRequest) *SNovaRequest {
|
|
return &SNovaRequest{
|
|
SApiRequest: *ar,
|
|
}
|
|
}
|
|
|
|
func (nr *SNovaRequest) GetPort() string {
|
|
if nr.RegionId == "guangzhou-2" {
|
|
return ""
|
|
}
|
|
return nr.SApiRequest.GetPort()
|
|
}
|
|
|
|
type SInstance struct {
|
|
multicloud.SInstanceBase
|
|
multicloud.EcloudTags
|
|
multicloud.SBillingBase
|
|
SZoneRegionBase
|
|
SCreateTime
|
|
|
|
nicComplete bool
|
|
host *SHost
|
|
image *SImage
|
|
|
|
sysDisk cloudprovider.ICloudDisk
|
|
dataDisks []cloudprovider.ICloudDisk
|
|
|
|
Id string
|
|
Name string
|
|
Vcpu int
|
|
Vmemory int
|
|
KeyName string
|
|
ImageRef string
|
|
ImageName string
|
|
ImageOsType string
|
|
FlavorRef string
|
|
SystemDiskSizeGB int `json:"vdisk"`
|
|
SystemDiskId string
|
|
ServerType string
|
|
ServerVmType string
|
|
EcStatus string
|
|
BootVolumeType string
|
|
Deleted int
|
|
Visible bool
|
|
Region string
|
|
PortDetail []SInstanceNic
|
|
}
|
|
|
|
func (i *SInstance) GetBillingType() string {
|
|
return billing_api.BILLING_TYPE_POSTPAID
|
|
}
|
|
|
|
func (i *SInstance) GetExpiredAt() time.Time {
|
|
return time.Time{}
|
|
}
|
|
|
|
func (i *SInstance) GetId() string {
|
|
return i.Id
|
|
}
|
|
|
|
func (i *SInstance) GetName() string {
|
|
return i.Name
|
|
}
|
|
|
|
func (i *SInstance) GetGlobalId() string {
|
|
return i.GetId()
|
|
}
|
|
|
|
func (i *SInstance) GetStatus() string {
|
|
switch i.EcStatus {
|
|
case "active":
|
|
return api.VM_RUNNING
|
|
case "suspended", "paused":
|
|
return api.VM_SUSPEND
|
|
case "build", "rebuild", "resize", "verify_resize", "revert_resize", "password":
|
|
return api.VM_STARTING
|
|
case "reboot", "hard_reboot":
|
|
return api.VM_STOPPING
|
|
case "stopped", "shutoff":
|
|
return api.VM_READY
|
|
case "migrating":
|
|
return api.VM_MIGRATING
|
|
case "backuping":
|
|
return api.VM_BACKUP_CREATING
|
|
default:
|
|
return api.VM_UNKNOWN
|
|
}
|
|
}
|
|
|
|
func (i *SInstance) Refresh() error {
|
|
// TODO
|
|
return nil
|
|
}
|
|
|
|
func (i *SInstance) IsEmulated() bool {
|
|
return false
|
|
}
|
|
|
|
func (self *SInstance) GetBootOrder() string {
|
|
return "dcn"
|
|
}
|
|
|
|
func (self *SInstance) GetVga() string {
|
|
return "std"
|
|
}
|
|
|
|
func (self *SInstance) GetVdi() string {
|
|
return "vnc"
|
|
}
|
|
|
|
func (i *SInstance) GetImage() (*SImage, error) {
|
|
if i.image != nil {
|
|
return i.image, nil
|
|
}
|
|
image, err := i.host.zone.region.GetImage(i.ImageRef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
i.image = image
|
|
return i.image, nil
|
|
}
|
|
|
|
func (i *SInstance) GetOsType() cloudprovider.TOsType {
|
|
return cloudprovider.TOsType(i.ImageOsType)
|
|
}
|
|
|
|
func (i *SInstance) GetOSName() string {
|
|
image, err := i.GetImage()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return image.OsName
|
|
}
|
|
|
|
func (i *SInstance) GetBios() string {
|
|
return "BIOS"
|
|
}
|
|
|
|
func (i *SInstance) GetMachine() string {
|
|
return "pc"
|
|
}
|
|
|
|
func (i *SInstance) GetInstanceType() string {
|
|
return i.FlavorRef
|
|
}
|
|
|
|
func (in *SInstance) GetProjectId() string {
|
|
return ""
|
|
}
|
|
|
|
func (in *SInstance) GetIHost() cloudprovider.ICloudHost {
|
|
return in.host
|
|
}
|
|
|
|
func (in *SInstance) GetIDisks() ([]cloudprovider.ICloudDisk, error) {
|
|
if in.sysDisk == nil {
|
|
in.fetchSysDisk()
|
|
}
|
|
if in.dataDisks == nil {
|
|
err := in.fetchDataDisks()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return append([]cloudprovider.ICloudDisk{in.sysDisk}, in.dataDisks...), nil
|
|
}
|
|
|
|
func (in *SInstance) GetINics() ([]cloudprovider.ICloudNic, error) {
|
|
if !in.nicComplete {
|
|
err := in.makeNicComplete()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "unable to make nics complete")
|
|
}
|
|
in.nicComplete = true
|
|
}
|
|
inics := make([]cloudprovider.ICloudNic, len(in.PortDetail))
|
|
for i := range in.PortDetail {
|
|
in.PortDetail[i].instance = in
|
|
inics[i] = &in.PortDetail[i]
|
|
}
|
|
return inics, nil
|
|
}
|
|
|
|
func (in *SInstance) GetIEIP() (cloudprovider.ICloudEIP, error) {
|
|
if !in.nicComplete {
|
|
err := in.makeNicComplete()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "unable to make nics complete")
|
|
}
|
|
in.nicComplete = true
|
|
}
|
|
var eipId string
|
|
for i := range in.PortDetail {
|
|
if len(in.PortDetail[i].IpId) > 0 {
|
|
eipId = in.PortDetail[i].IpId
|
|
break
|
|
}
|
|
}
|
|
if len(eipId) == 0 {
|
|
return nil, nil
|
|
}
|
|
return in.host.zone.region.GetEipById(eipId)
|
|
}
|
|
|
|
func (in *SInstance) GetSecurityGroupIds() ([]string, error) {
|
|
if !in.nicComplete {
|
|
err := in.makeNicComplete()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "unable to make nics complete")
|
|
}
|
|
in.nicComplete = true
|
|
}
|
|
ret := sets.NewString()
|
|
for i := range in.PortDetail {
|
|
for _, group := range in.PortDetail[i].SecurityGroups {
|
|
ret.Insert(group.Id)
|
|
}
|
|
}
|
|
return ret.UnsortedList(), nil
|
|
}
|
|
|
|
func (in *SInstance) GetVcpuCount() int {
|
|
return in.Vcpu
|
|
}
|
|
|
|
func (in *SInstance) GetVmemSizeMB() int {
|
|
return in.Vmemory
|
|
}
|
|
|
|
func (in *SInstance) AssignSecurityGroup(id string) error {
|
|
return cloudprovider.ErrNotImplemented
|
|
}
|
|
|
|
func (in *SInstance) SetSecurityGroups(ids []string) error {
|
|
return cloudprovider.ErrNotImplemented
|
|
}
|
|
|
|
func (in *SInstance) GetHypervisor() string {
|
|
return api.HYPERVISOR_ECLOUD
|
|
}
|
|
|
|
func (in *SInstance) StartVM(ctx context.Context) error {
|
|
return cloudprovider.ErrNotImplemented
|
|
}
|
|
|
|
func (self *SInstance) StopVM(ctx context.Context, opts *cloudprovider.ServerStopOptions) error {
|
|
return cloudprovider.ErrNotImplemented
|
|
}
|
|
|
|
func (self *SInstance) DeleteVM(ctx context.Context) error {
|
|
return cloudprovider.ErrNotImplemented
|
|
}
|
|
|
|
func (self *SInstance) UpdateVM(ctx context.Context, name string) error {
|
|
return cloudprovider.ErrNotSupported
|
|
}
|
|
|
|
func (self *SInstance) UpdateUserData(userData string) error {
|
|
return cloudprovider.ErrNotSupported
|
|
}
|
|
|
|
func (self *SInstance) RebuildRoot(ctx context.Context, config *cloudprovider.SManagedVMRebuildRootConfig) (string, error) {
|
|
return "", cloudprovider.ErrNotImplemented
|
|
}
|
|
|
|
func (self *SInstance) DeployVM(ctx context.Context, name string, username string, password string, publicKey string, deleteKeypair bool, description string) error {
|
|
return cloudprovider.ErrNotImplemented
|
|
}
|
|
|
|
func (in *SInstance) ChangeConfig(ctx context.Context, config *cloudprovider.SManagedVMChangeConfig) error {
|
|
return errors.ErrNotImplemented
|
|
}
|
|
|
|
func (in *SInstance) GetVNCInfo() (jsonutils.JSONObject, error) {
|
|
url, err := in.host.zone.region.GetInstanceVNCUrl(in.GetId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ret := jsonutils.NewDict()
|
|
ret.Add(jsonutils.NewString(url), "url")
|
|
ret.Add(jsonutils.NewString("ecloud"), "protocol")
|
|
ret.Add(jsonutils.NewString(in.GetId()), "instance_id")
|
|
return ret, nil
|
|
}
|
|
|
|
func (in *SInstance) AttachDisk(ctx context.Context, diskId string) error {
|
|
return cloudprovider.ErrNotImplemented
|
|
}
|
|
|
|
func (in *SInstance) DetachDisk(ctx context.Context, diskId string) error {
|
|
return cloudprovider.ErrNotImplemented
|
|
}
|
|
|
|
func (self *SInstance) Renew(bc billing.SBillingCycle) error {
|
|
return cloudprovider.ErrNotImplemented
|
|
}
|
|
|
|
func (self *SInstance) GetError() error {
|
|
return nil
|
|
}
|
|
|
|
func (in *SInstance) fetchSysDisk() {
|
|
storage, _ := in.host.zone.getStorageByType(api.STORAGE_ECLOUD_SYSTEM)
|
|
disk := SDisk{
|
|
storage: storage,
|
|
ManualAttr: SDiskManualAttr{
|
|
IsVirtual: true,
|
|
TempalteId: in.ImageRef,
|
|
ServerId: in.Id,
|
|
},
|
|
SCreateTime: in.SCreateTime,
|
|
SZoneRegionBase: in.SZoneRegionBase,
|
|
ServerId: []string{in.Id},
|
|
IsShare: false,
|
|
IsDelete: false,
|
|
SizeGB: in.SystemDiskSizeGB,
|
|
ID: in.SystemDiskId,
|
|
Name: fmt.Sprintf("%s-root", in.Name),
|
|
Status: "in-use",
|
|
Type: api.STORAGE_ECLOUD_SYSTEM,
|
|
}
|
|
in.sysDisk = &disk
|
|
return
|
|
}
|
|
|
|
func (in *SInstance) fetchDataDisks() error {
|
|
request := NewNovaRequest(NewApiRequest(in.host.zone.region.ID, "/api/v2/volume/volume/mount/list",
|
|
map[string]string{"serverId": in.Id}, nil))
|
|
disks := make([]SDisk, 0, 5)
|
|
err := in.host.zone.region.client.doList(context.Background(), request, &disks)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
idisks := make([]cloudprovider.ICloudDisk, len(disks))
|
|
for i := range idisks {
|
|
storageType := disks[i].Type
|
|
storage, err := in.host.zone.getStorageByType(storageType)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to fetch storage with stoageType %s", storageType)
|
|
}
|
|
disks[i].storage = storage
|
|
idisks[i] = &disks[i]
|
|
}
|
|
in.dataDisks = idisks
|
|
return nil
|
|
}
|
|
|
|
func (in *SInstance) makeNicComplete() error {
|
|
routerIds := sets.NewString()
|
|
nics := make(map[string]*SInstanceNic, len(in.PortDetail))
|
|
for i := range in.PortDetail {
|
|
nic := &in.PortDetail[i]
|
|
routerIds.Insert(nic.RouterId)
|
|
nics[nic.PortId] = nic
|
|
}
|
|
for _, routerId := range routerIds.UnsortedList() {
|
|
request := NewConsoleRequest(in.host.zone.region.ID, fmt.Sprintf("/api/vpc/%s/nic", routerId),
|
|
map[string]string{
|
|
"resourceId": in.Id,
|
|
}, nil,
|
|
)
|
|
completeNics := make([]SInstanceNic, 0, len(nics)/2)
|
|
err := in.host.zone.region.client.doList(context.Background(), request, &completeNics)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to get nics with instance %s in vpc %s", in.Id, routerId)
|
|
}
|
|
for i := range completeNics {
|
|
id := completeNics[i].Id
|
|
nic, ok := nics[id]
|
|
if !ok {
|
|
continue
|
|
}
|
|
nic.SInstanceNicDetail = completeNics[i].SInstanceNicDetail
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *SRegion) findHost(zoneRegion string) (*SHost, error) {
|
|
zone, err := r.FindZone(zoneRegion)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &SHost{
|
|
zone: zone,
|
|
}, nil
|
|
}
|
|
|
|
func (r *SRegion) GetInstancesWithHost(zoneRegion string) ([]SInstance, error) {
|
|
instances, err := r.GetInstances(zoneRegion)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := range instances {
|
|
host, _ := r.findHost(instances[i].Region)
|
|
instances[i].host = host
|
|
}
|
|
return instances, nil
|
|
}
|
|
|
|
func (r *SRegion) GetInstances(zoneRegion string) ([]SInstance, error) {
|
|
return r.getInstances(zoneRegion, "")
|
|
}
|
|
|
|
func (r *SRegion) getInstances(zoneRegion string, serverId string) ([]SInstance, error) {
|
|
query := map[string]string{
|
|
"serverTypes": "VM",
|
|
"productTypes": "NORMAL,AUTOSCALING,VO,CDN,PAAS_MASTER,PAAS_SLAVE,VCPE,EMR,LOGAUDIT",
|
|
//"productTypes": "NORMAL",
|
|
"visible": "true",
|
|
}
|
|
if len(serverId) > 0 {
|
|
query["serverId"] = serverId
|
|
}
|
|
if len(zoneRegion) > 0 {
|
|
query["region"] = zoneRegion
|
|
}
|
|
request := NewNovaRequest(NewApiRequest(r.ID, "/api/v2/server/web/with/network", query, nil))
|
|
var instances []SInstance
|
|
err := r.client.doList(context.Background(), request, &instances)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return instances, nil
|
|
}
|
|
|
|
func (r *SRegion) GetInstanceById(id string) (*SInstance, error) {
|
|
instances, err := r.getInstances("", id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(instances) == 0 {
|
|
return nil, cloudprovider.ErrNotFound
|
|
}
|
|
instance := &instances[0]
|
|
host, err := r.findHost(instance.Region)
|
|
if err == nil {
|
|
instance.host = host
|
|
}
|
|
return instance, nil
|
|
}
|
|
|
|
func (r *SRegion) GetInstanceVNCUrl(instanceId string) (string, error) {
|
|
request := NewNovaRequest(NewApiRequest(r.ID, fmt.Sprintf("/api/server/%s/vnc", instanceId), nil, nil))
|
|
var url string
|
|
err := r.client.doGet(context.Background(), request, &url)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return url, nil
|
|
}
|