Files
cloudpods/pkg/multicloud/ecloud/instance.go
2022-01-21 14:25:49 +08:00

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
}