Files
cloudpods/pkg/compute/models/guest_template.go
2025-09-26 14:44:37 +08:00

829 lines
29 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 models
import (
"context"
"fmt"
"time"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/rbacscope"
"yunion.io/x/pkg/util/sets"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/compute"
computeapis "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/cmdline"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/options"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
"yunion.io/x/onecloud/pkg/mcclient/modules/image"
"yunion.io/x/onecloud/pkg/util/logclient"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
const (
IMAGE_TYPE_NORMAL = "normal"
IMAGE_TYPE_GUEST = "guest"
)
type SGuestTemplateManager struct {
db.SSharableVirtualResourceBaseManager
SCloudregionResourceBaseManager
SVpcResourceBaseManager
}
type SGuestTemplate struct {
db.SSharableVirtualResourceBase
SCloudregionResourceBase
SVpcResourceBase
// 虚拟机CPU数量
VcpuCount int `nullable:"false" default:"1" create:"optional" json:"vcpu_count"`
// 虚拟机内存大小MB
VmemSize int `nullable:"false" create:"optional" json:"vmem_size"`
// 虚拟机操作系统类型
// pattern:Linux|Windows|VMWare
OsType string `width:"36" charset:"ascii" nullable:"true" create:"optional" json:"os_type" list:"user" get:"user"`
// 镜像类型
ImageType string `width:"10" charset:"ascii" nullabel:"true" default:"normal" create:"optional" json:"image_type"`
// 镜像ID
ImageId string `width:"128" charset:"ascii" create:"optional" json:"image_id"`
// 虚拟机技术
Hypervisor string `width:"16" charset:"ascii" default:"kvm" create:"optional" json:"hypervisor"`
// 计费方式
BillingType string `width:"16" charset:"ascii" default:"postpaid" create:"optional" list:"user" get:"user" json:"billing_type"`
// 其他配置信息
Content jsonutils.JSONObject `nullable:"false" list:"user" update:"user" create:"optional" json:"content"`
LastCheckTime time.Time
}
var GuestTemplateManager *SGuestTemplateManager
func init() {
GuestTemplateManager = &SGuestTemplateManager{
SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
SGuestTemplate{},
"guesttemplates_tbl",
"servertemplate",
"servertemplates",
),
}
GuestTemplateManager.SetVirtualObject(GuestTemplateManager)
}
func (gtm *SGuestTemplateManager) ValidateCreateData(
ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject,
input computeapis.GuestTemplateCreateInput,
) (computeapis.GuestTemplateCreateInput, error) {
var err error
if input.Content == nil {
return input, httperrors.NewMissingParameterError("content")
}
if !input.Content.Contains("name") && !input.Content.Contains("generate_name") {
input.Content.Set("generate_name", jsonutils.NewString(input.Name))
}
input.GuestTemplateInput, err = gtm.validateData(ctx, userCred, ownerId, query, input.GuestTemplateInput)
if err != nil {
return input, errors.Wrap(err, "gtm.validateData")
}
input.SharableVirtualResourceCreateInput, err = gtm.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
if err != nil {
return input, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ValidateCreateData")
}
return input, nil
}
func (gt *SGuestTemplate) PostCreate(ctx context.Context, userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
gt.SetStatus(ctx, userCred, computeapis.GT_READY, "")
gt.updateCheckTime()
logclient.AddActionLogWithContext(ctx, gt, logclient.ACT_CREATE, nil, userCred, true)
}
func (gt *SGuestTemplate) updateCheckTime() error {
_, err := db.Update(gt, func() error {
gt.LastCheckTime = time.Now()
return nil
})
return err
}
func (gt *SGuestTemplate) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential,
query jsonutils.JSONObject, data jsonutils.JSONObject) {
logclient.AddActionLogWithContext(ctx, gt, logclient.ACT_UPDATE, nil, userCred, true)
}
var HypervisorBrandMap = map[string]string{
computeapis.HYPERVISOR_KVM: computeapis.CLOUD_PROVIDER_ONECLOUD,
computeapis.HYPERVISOR_ESXI: computeapis.CLOUD_PROVIDER_VMWARE,
computeapis.HYPERVISOR_ALIYUN: computeapis.CLOUD_PROVIDER_ALIYUN,
computeapis.HYPERVISOR_QCLOUD: computeapis.CLOUD_PROVIDER_QCLOUD,
computeapis.HYPERVISOR_AZURE: computeapis.CLOUD_PROVIDER_AZURE,
computeapis.HYPERVISOR_AWS: computeapis.CLOUD_PROVIDER_AWS,
computeapis.HYPERVISOR_HUAWEI: computeapis.CLOUD_PROVIDER_HUAWEI,
computeapis.HYPERVISOR_OPENSTACK: computeapis.CLOUD_PROVIDER_OPENSTACK,
computeapis.HYPERVISOR_UCLOUD: computeapis.CLOUD_PROVIDER_UCLOUD,
computeapis.HYPERVISOR_ZSTACK: computeapis.CLOUD_PROVIDER_ZSTACK,
computeapis.HYPERVISOR_GOOGLE: computeapis.CLOUD_PROVIDER_GOOGLE,
computeapis.HYPERVISOR_CTYUN: computeapis.CLOUD_PROVIDER_CTYUN,
computeapis.HYPERVISOR_CNWARE: computeapis.CLOUD_PROVIDER_CNWARE,
}
var BrandHypervisorMap = map[string]string{
computeapis.CLOUD_PROVIDER_ONECLOUD: computeapis.HYPERVISOR_KVM,
computeapis.CLOUD_PROVIDER_VMWARE: computeapis.HYPERVISOR_ESXI,
computeapis.CLOUD_PROVIDER_ALIYUN: computeapis.HYPERVISOR_ALIYUN,
computeapis.CLOUD_PROVIDER_QCLOUD: computeapis.HYPERVISOR_QCLOUD,
computeapis.CLOUD_PROVIDER_AZURE: computeapis.HYPERVISOR_AZURE,
computeapis.CLOUD_PROVIDER_AWS: computeapis.HYPERVISOR_AWS,
computeapis.CLOUD_PROVIDER_HUAWEI: computeapis.HYPERVISOR_HUAWEI,
computeapis.CLOUD_PROVIDER_OPENSTACK: computeapis.HYPERVISOR_OPENSTACK,
computeapis.CLOUD_PROVIDER_UCLOUD: computeapis.HYPERVISOR_UCLOUD,
computeapis.CLOUD_PROVIDER_ZSTACK: computeapis.HYPERVISOR_ZSTACK,
computeapis.CLOUD_PROVIDER_GOOGLE: computeapis.HYPERVISOR_GOOGLE,
computeapis.CLOUD_PROVIDER_CTYUN: computeapis.HYPERVISOR_CTYUN,
computeapis.CLOUD_PROVIDER_CNWARE: computeapis.HYPERVISOR_CNWARE,
}
func Hypervisor2Brand(hypervisor string) string {
brand, ok := HypervisorBrandMap[hypervisor]
if !ok {
return "unkown"
}
return brand
}
func Brand2Hypervisor(brand string) string {
hypervisor, ok := BrandHypervisorMap[brand]
if !ok {
return "unkown"
}
return hypervisor
}
func (gtm *SGuestTemplateManager) validateContent(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, content *jsonutils.JSONDict) (*computeapis.ServerCreateInput, error) {
// hack
if !content.Contains("name") && !content.Contains("generate_name") {
content.Set("generate_name", jsonutils.NewString("fake_name"))
}
input, err := GuestManager.validateCreateData(ctx, userCred, ownerId, query, content)
if err != nil {
return nil, httperrors.NewInputParameterError("%v", err)
}
// check Image
imageId := input.Disks[0].ImageId
image, err := CachedimageManager.getImageInfo(ctx, userCred, imageId, false)
if err != nil {
return nil, errors.Wrapf(err, "getImageInfo of '%s'", imageId)
}
if image == nil {
return nil, fmt.Errorf("no such image %s", imageId)
}
return input, nil
}
func (gtm *SGuestTemplateManager) validateData(
ctx context.Context,
userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider,
query jsonutils.JSONObject,
cinput computeapis.GuestTemplateInput,
) (computeapis.GuestTemplateInput, error) {
if cinput.Content == nil {
return cinput, nil
}
content := cinput.Content
// data := cinput.JSON(cinput)
// not support guest image and guest snapshot for now
if content.Contains("instance_snapshot_id") {
return cinput, httperrors.NewInputParameterError(
"no support for instance snapshot in guest template for now")
}
// I don't hope cinput.Content same with data["content"] will change in GuestManager.validateCreateData
copy := jsonutils.DeepCopy(content).(*jsonutils.JSONDict)
input, err := gtm.validateContent(ctx, userCred, ownerId, query, copy)
if err != nil {
return cinput, httperrors.NewInputParameterError("%v", err)
}
log.Debugf("data: %#v", input)
// fill field
cinput.VmemSize = input.VmemSize
// data.Add(jsonutils.NewInt(int64(input.VmemSize)), "vmem_size")
cinput.VcpuCount = input.VcpuCount
// data.Add(jsonutils.NewInt(int64(input.VcpuCount)), "vcpu_count")
cinput.OsType = input.OsType
// data.Add(jsonutils.NewString(input.OsType), "os_type")
cinput.Hypervisor = input.Hypervisor
// data.Add(jsonutils.NewString(input.Hypervisor), "hypervisor")
cinput.InstanceType = input.InstanceType
cinput.CloudregionId = input.PreferRegion
cinput.BillingType = input.BillingType
// fill vpc
if len(input.Networks) != 0 && len(input.Networks[0].Network) != 0 {
model, err := NetworkManager.FetchById(input.Networks[0].Network)
if err != nil {
return cinput, errors.Wrap(err, "NetworkManager.FetchById")
}
net := model.(*SNetwork)
vpc, _ := net.GetVpc()
if vpc != nil {
cinput.VpcId = vpc.Id
}
}
if len(input.GuestImageID) > 0 {
cinput.ImageType = IMAGE_TYPE_GUEST
cinput.ImageId = input.GuestImageID
// data.Add(jsonutils.NewString(IMAGE_TYPE_GUEST), "image_type")
// data.Add(jsonutils.NewString(input.GuestImageID), "image_id")
} else {
cinput.ImageType = input.GuestImageID
cinput.ImageId = input.Disks[0].ImageId // if input.Didks is empty???
// data.Add(jsonutils.NewString(input.Disks[0].ImageId), "image_id")
// data.Add(jsonutils.NewString(IMAGE_TYPE_NORMAL), "image_type")
}
// hide some properties
content.Remove("name")
content.Remove("generate_name")
// "__count__" was converted to "count" by apigateway
content.Remove("count")
content.Remove("project_id")
content.Remove("__count__")
cinput.Content = content
// data.Add(contentDict, "content")
return cinput, nil
}
func (gt *SGuestTemplate) ValidateUpdateData(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
input computeapis.GuestTemplateUpdateInput,
) (computeapis.GuestTemplateUpdateInput, error) {
var err error
input.GuestTemplateInput, err = GuestTemplateManager.validateData(ctx, userCred, gt.GetOwnerId(), query, input.GuestTemplateInput)
if err != nil {
return input, errors.Wrap(err, "GuestTemplateManager.validateData")
}
input.SharableVirtualResourceBaseUpdateInput, err = gt.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.SharableVirtualResourceBaseUpdateInput)
if err != nil {
return input, errors.Wrap(err, "SSharableVirtualResourceBase.ValidateUpdateData")
}
return input, nil
}
func (manager *SGuestTemplateManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
objs []interface{},
fields stringutils2.SSortedStrings,
isList bool,
) []computeapis.GuestTemplateDetails {
rows := make([]computeapis.GuestTemplateDetails, len(objs))
virtRows := manager.SSharableVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
crRows := manager.SCloudregionResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
vpcRows := manager.SVpcResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
for i := range rows {
rows[i] = computeapis.GuestTemplateDetails{
SharableVirtualResourceDetails: virtRows[i],
CloudregionResourceInfo: crRows[i],
VpcResourceInfo: vpcRows[i],
}
rows[i], _ = objs[i].(*SGuestTemplate).getMoreDetails(ctx, userCred, rows[i])
}
return rows
}
func (gt *SGuestTemplate) getMoreDetails(ctx context.Context, userCred mcclient.TokenCredential,
out computeapis.GuestTemplateDetails) (computeapis.GuestTemplateDetails, error) {
input, err := cmdline.FetchServerCreateInputByJSON(gt.Content)
if err != nil {
return out, err
}
configInfo := computeapis.GuestTemplateConfigInfo{}
if len(input.PreferZone) != 0 {
zone := ZoneManager.FetchZoneById(input.PreferZone)
if zone != nil {
input.PreferZone = zone.GetName()
out.ZoneId = zone.GetId()
}
out.Zone = input.PreferZone
}
out.Brand = Hypervisor2Brand(gt.Hypervisor)
// metadata
configInfo.Metadata = input.Metadata
// sku deal
if len(input.InstanceType) > 0 {
skuOutput := computeapis.GuestTemplateSku{}
sku, err := ServerSkuManager.FetchSkuByNameAndProvider(input.InstanceType, out.Provider, true)
if err != nil {
skuOutput.Name = input.InstanceType
skuOutput.MemorySizeMb = gt.VmemSize
skuOutput.CpuCoreCount = gt.VcpuCount
} else {
skuOutput.Name = sku.Name
skuOutput.MemorySizeMb = sku.MemorySizeMB
skuOutput.CpuCoreCount = sku.CpuCoreCount
skuOutput.InstanceTypeCategory = sku.InstanceTypeCategory
skuOutput.InstanceTypeFamily = sku.InstanceTypeFamily
}
configInfo.Sku = skuOutput
}
// disk deal
disks := make([]computeapis.GuestTemplateDisk, len(input.Disks))
for i := range input.Disks {
disks[i] = computeapis.GuestTemplateDisk{
Backend: input.Disks[i].Backend,
DiskType: input.Disks[i].DiskType,
Index: input.Disks[i].Index,
SizeMb: input.Disks[i].SizeMb,
}
}
configInfo.Disks = disks
// keypair
if len(input.KeypairId) > 0 {
model, err := KeypairManager.FetchByIdOrName(ctx, userCred, input.KeypairId)
if err == nil {
keypair := model.(*SKeypair)
configInfo.Keypair = keypair.GetName()
}
}
// network
if len(input.Networks) > 0 {
networkList := make([]computeapis.GuestTemplateNetwork, 0, len(input.Networks))
networkIdList := make([]string, len(input.Networks))
for i := range input.Networks {
networkIdList[i] = input.Networks[i].Network
}
networkSet := sets.NewString(networkIdList...)
wireQuery := WireManager.Query("id", "vpc_id").SubQuery()
vpcQuery := VpcManager.Query("id", "name").SubQuery()
q := NetworkManager.Query("id", "name", "wire_id", "guest_ip_start", "guest_ip_end", "vlan_id")
if len(networkIdList) == 1 {
q = q.Equals("id", networkIdList[0])
} else {
q = q.In("id", networkIdList)
}
q = q.LeftJoin(wireQuery, sqlchemy.Equals(q.Field("wire_id"), wireQuery.Field("id")))
q = q.LeftJoin(vpcQuery, sqlchemy.Equals(wireQuery.Field("vpc_id"), vpcQuery.Field("id")))
q = q.AppendField(vpcQuery.Field("id", "vpc_id"), vpcQuery.Field("name", "vpc_name"))
q.All(&networkList)
for _, p := range networkList {
if networkSet.Has(p.ID) {
networkSet.Delete(p.ID)
}
}
// some specified network
for _, id := range networkSet.UnsortedList() {
networkList = append(networkList, computeapis.GuestTemplateNetwork{ID: id})
}
configInfo.Nets = networkList
}
if len(input.Secgroups) > 0 {
q := SecurityGroupManager.Query("id", "name").In("id", input.Secgroups)
rows, err := q.Rows()
if err != nil {
return out, errors.Wrap(err, "SQuery.Rows")
}
names := make([]string, 0, len(input.Secgroups))
for rows.Next() {
var id, name string
rows.Scan(&id, &name)
names = append(names, name)
}
rows.Close()
out.Secgroups = names
}
// isolatedDevices
if input.IsolatedDevices != nil && len(input.IsolatedDevices) != 0 {
configInfo.IsolatedDeviceConfig = make([]computeapis.IsolatedDeviceConfig, len(input.IsolatedDevices))
for i := range configInfo.IsolatedDeviceConfig {
configInfo.IsolatedDeviceConfig[i] = *input.IsolatedDevices[i]
}
}
// fill image info
switch gt.ImageType {
case IMAGE_TYPE_NORMAL:
image, err := CachedimageManager.getImageInfo(ctx, userCred, gt.ImageId, false)
if err == nil {
configInfo.Image = image.Name
} else {
configInfo.Image = gt.ImageId
}
case IMAGE_TYPE_GUEST:
s := auth.GetSession(ctx, userCred, options.Options.Region)
ret, err := image.GuestImages.Get(s, gt.ImageId, jsonutils.JSONNull)
if err != nil || !ret.Contains("id") {
configInfo.Image = gt.ImageId
} else {
name, _ := ret.GetString("id")
configInfo.Image = name
}
default:
// no arrivals
}
// reset_password
if input.ResetPassword == nil {
configInfo.ResetPassword = true
} else {
configInfo.ResetPassword = *input.ResetPassword
}
out.ConfigInfo = configInfo
return out, nil
}
func (gt *SGuestTemplate) PerformPublic(
ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
data apis.PerformPublicProjectInput,
) (jsonutils.JSONObject, error) {
// image, network, secgroup, instancegroup
input, err := cmdline.FetchServerCreateInputByJSON(gt.Content)
if err != nil {
return nil, errors.Wrap(err, "fail to convert content of guest template to ServerCreateInput")
}
// check for below private resource in the guest template
privateResource := map[string]int{
"keypair": len(input.KeypairId),
"instance group": len(input.InstanceGroupIds),
"instance snapshot": len(input.InstanceSnapshotId),
}
for k, v := range privateResource {
if v > 0 {
return nil, gt.genForbiddenError(k, "", "")
}
}
targetScopeStr := data.Scope
targetScope := rbacscope.String2ScopeDefault(targetScopeStr, rbacscope.ScopeSystem)
// check if secgroup is public
if len(input.SecgroupId) > 0 {
model, err := SecurityGroupManager.FetchByIdOrName(ctx, userCred, input.SecgroupId)
if err != nil {
return nil, httperrors.NewResourceNotFoundError("there is no such secgroup %s descripted by guest template",
input.SecgroupId)
}
secgroup := model.(*SSecurityGroup)
sgScope := rbacscope.String2Scope(secgroup.PublicScope)
if !secgroup.IsPublic || !sgScope.HigherEqual(targetScope) {
return nil, gt.genForbiddenError("security group", input.SecgroupId, string(targetScope))
}
}
// check if networks is public
if len(input.Networks) > 0 {
for i := range input.Networks {
str := input.Networks[i].Network
model, err := NetworkManager.FetchByIdOrName(ctx, userCred, str)
if err != nil {
return nil, httperrors.NewResourceNotFoundError(
"there is no such secgroup %s descripted by guest template", str)
}
network := model.(*SNetwork)
netScope := rbacscope.String2Scope(network.PublicScope)
if !network.IsPublic || !netScope.HigherEqual(targetScope) {
return nil, gt.genForbiddenError("network", str, string(targetScope))
}
}
}
// check if image is public
var (
isPublic bool
publicScope string
)
switch gt.ImageType {
case IMAGE_TYPE_NORMAL:
image, err := CachedimageManager.GetImageById(ctx, userCred, gt.ImageId, false)
if err != nil {
return nil, errors.Wrapf(err, "fail to fetch image %s descripted by guest template", gt.ImageId)
}
isPublic, publicScope = image.IsPublic, image.PublicScope
case IMAGE_TYPE_GUEST:
s := auth.GetSession(ctx, userCred, options.Options.Region)
ret, err := image.GuestImages.Get(s, gt.ImageId, jsonutils.JSONNull)
if err != nil {
return nil, errors.Wrapf(err, "fail to fetch guest image %s descripted by guest template", gt.ImageId)
}
isPublic = jsonutils.QueryBoolean(ret, "is_public", false)
publicScope, _ = ret.GetString("public_scope")
default:
//no arrivals
}
igScope := rbacscope.String2Scope(publicScope)
if !isPublic || !igScope.HigherEqual(targetScope) {
return nil, gt.genForbiddenError("image", "", string(targetScope))
}
return gt.SSharableVirtualResourceBase.PerformPublic(ctx, userCred, query, data)
}
func (gt *SGuestTemplate) genForbiddenError(resourceName, resourceStr, scope string) error {
var (
msgFmt string
msgArgs []interface{}
)
if resourceStr == "" {
msgFmt = "the %s in guest template is not a public resource"
msgArgs = []interface{}{resourceName}
} else {
msgFmt = "the %s %q in guest template is not a public resource"
msgArgs = []interface{}{resourceName, resourceStr}
}
if scope != "" {
msgFmt += " in %s scope"
msgArgs = append(msgArgs, scope)
}
return httperrors.NewForbiddenError(msgFmt, msgArgs...)
}
func (gt *SGuestTemplate) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
// check service catelog
q := ServiceCatalogManager.Query("name").Equals("guest_template_id", gt.Id)
names := make([]struct{ Name string }, 0, 1)
err := q.All(&names)
if err != nil {
return errors.Wrap(err, "SQuery.All")
}
if len(names) > 0 {
return httperrors.NewForbiddenError("guest template %s used by service catalog %s", gt.Id, names[0].Name)
}
// check scaling group
q = ScalingGroupManager.Query("name").Equals("guest_template_id", gt.Id)
names = make([]struct{ Name string }, 0, 1)
err = q.All(&names)
if err != nil {
return errors.Wrap(err, "SQuery.All")
}
if len(names) > 0 {
return httperrors.NewForbiddenError("guest template %s used by scalig group %s", gt.Id, names[0].Name)
}
return nil
}
// 主机模板列表
func (manager *SGuestTemplateManager) ListItemFilter(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
input computeapis.GuestTemplateListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SSharableVirtualResourceBaseManager.ListItemFilter(ctx, q, userCred, input.SharableVirtualResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ListItemFilter")
}
q, err = manager.SCloudregionResourceBaseManager.ListItemFilter(ctx, q, userCred, input.RegionalFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemFilter")
}
if len(input.VpcId) > 0 {
q, err = manager.SVpcResourceBaseManager.ListItemFilter(ctx, q, userCred, input.VpcFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SVpcResourceBaseManager.ListItemFilter")
}
}
if len(input.CloudEnv) > 0 {
cloudregions := CloudregionManager.Query().SubQuery()
q = q.Join(cloudregions, sqlchemy.Equals(q.Field("cloudregion_id"), cloudregions.Field("id")))
switch input.CloudEnv {
case api.CLOUD_ENV_PUBLIC_CLOUD:
q = q.Filter(sqlchemy.In(cloudregions.Field("provider"), CloudproviderManager.GetPublicProviderProvidersQuery()))
case api.CLOUD_ENV_PRIVATE_CLOUD:
q = q.Filter(sqlchemy.In(cloudregions.Field("provider"), CloudproviderManager.GetPrivateProviderProvidersQuery()))
case api.CLOUD_ENV_ON_PREMISE:
q = q.Filter(sqlchemy.Equals(cloudregions.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD))
case api.CLOUD_ENV_PRIVATE_ON_PREMISE:
q = q.Filter(sqlchemy.OR(
sqlchemy.Equals(cloudregions.Field("provider"), api.CLOUD_PROVIDER_ONECLOUD),
sqlchemy.In(cloudregions.Field("provider"), CloudproviderManager.GetPrivateProviderProvidersQuery()),
))
}
}
if len(input.BillingType) > 0 {
q = q.Equals("billing_type", input.BillingType)
}
if len(input.Brand) > 0 {
q = q.Equals("hypervisor", Brand2Hypervisor(input.Brand))
}
return q, nil
}
func (manager *SGuestTemplateManager) OrderByExtraFields(
ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
input computeapis.GuestTemplateListInput,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SSharableVirtualResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.SharableVirtualResourceListInput)
if err != nil {
return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.OrderByExtraFields")
}
q, err = manager.SCloudregionResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.RegionalFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.OrderByExtraFields")
}
q, err = manager.SVpcResourceBaseManager.OrderByExtraFields(ctx, q, userCred, input.VpcFilterListInput)
if err != nil {
return nil, errors.Wrap(err, "SVpcResourceBaseManager.OrderByExtraFields")
}
return q, nil
}
func (manager *SGuestTemplateManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SSharableVirtualResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
q, err = manager.SCloudregionResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
q, err = manager.SVpcResourceBaseManager.QueryDistinctExtraField(q, field)
if err == nil {
return q, nil
}
return q, httperrors.ErrNotFound
}
type SGuestTemplateValidate struct {
Hypervisor string
CloudregionId string
VpcId string
NetworkIds []string
}
func (gt *SGuestTemplate) Validate(ctx context.Context, userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider, stv SGuestTemplateValidate) (bool, string) {
if stv.Hypervisor != "" && gt.Hypervisor != stv.Hypervisor {
return false, fmt.Sprintf("GuestTemplate has mismatched hypervisor, need %s but %s", stv.Hypervisor, gt.Hypervisor)
}
if stv.CloudregionId != "" && gt.CloudregionId != stv.CloudregionId {
return false, fmt.Sprintf("GuestTemplate has mismatched cloudregion, need %s but %s", stv.CloudregionId, gt.CloudregionId)
}
if stv.VpcId != "" && gt.VpcId != "" && stv.VpcId != gt.VpcId {
return false, fmt.Sprintf("GuestTemplate has mismatched vpc, need %s bu %s", stv.VpcId, gt.VpcId)
}
// check networks
input, err := GuestTemplateManager.validateContent(ctx, userCred, ownerId, jsonutils.NewDict(), gt.Content.(*jsonutils.JSONDict))
if err != nil {
return false, err.Error()
}
if len(input.Networks) != 0 && len(input.Networks[0].Network) != 0 {
for i := range input.Networks {
if !utils.IsInStringArray(input.Networks[i].Network, stv.NetworkIds) {
return false, fmt.Sprintf("GuestTemplate's network '%s' not in networks '%s'", input.Networks[i].Network, stv.NetworkIds)
}
}
}
return true, ""
}
func (manager *SGuestTemplateManager) ListItemExportKeys(ctx context.Context,
q *sqlchemy.SQuery,
userCred mcclient.TokenCredential,
keys stringutils2.SSortedStrings,
) (*sqlchemy.SQuery, error) {
var err error
q, err = manager.SSharableVirtualResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ListItemExportKeys")
}
if keys.ContainsAny(manager.SCloudregionResourceBaseManager.GetExportKeys()...) {
q, err = manager.SCloudregionResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SCloudregionResourceBaseManager.ListItemExportKeys")
}
}
if keys.ContainsAny(manager.SVpcResourceBaseManager.GetExportKeys()...) {
q, err = manager.SVpcResourceBaseManager.ListItemExportKeys(ctx, q, userCred, keys)
if err != nil {
return nil, errors.Wrap(err, "SVpcResourceBaseManager.ListItemExportKeys")
}
}
return q, nil
}
func (g *SGuest) PerformSaveTemplate(ctx context.Context, userCred mcclient.TokenCredential,
query jsonutils.JSONObject, input computeapis.GuestSaveToTemplateInput) (jsonutils.JSONObject, error) {
g.SetStatus(ctx, userCred, computeapis.VM_TEMPLATE_SAVING, "save to template")
if len(input.Name) == 0 && len(input.GenerateName) == 0 {
input.GenerateName = fmt.Sprintf("%s-template", g.Name)
}
data := jsonutils.Marshal(input).(*jsonutils.JSONDict)
if task, err := taskman.TaskManager.NewTask(ctx, "GuestSaveTemplateTask", g, userCred, data, "", "", nil); err != nil {
return nil, errors.Wrap(err, "Unbale to init 'GuestSaveTemplateTask'")
} else {
task.ScheduleRun(nil)
}
return nil, nil
}
func (gt *SGuestTemplate) PerformInspect(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
return nil, gt.inspect(ctx, userCred)
}
func (gt *SGuestTemplate) inspect(ctx context.Context, userCred mcclient.TokenCredential) error {
_, err := GuestTemplateManager.validateContent(ctx, userCred, gt.GetOwnerId(), jsonutils.NewDict(), gt.Content.(*jsonutils.JSONDict))
if err == nil {
gt.updateCheckTime()
gt.SetStatus(ctx, userCred, computeapis.GT_READY, "inspect successfully")
logclient.AddSimpleActionLog(gt, logclient.ACT_HEALTH_CHECK, "", userCred, true)
return nil
}
// invalid
gt.updateCheckTime()
reason := fmt.Sprintf("During the inspection, the guest template is not available: %s", err.Error())
gt.SetStatus(ctx, userCred, computeapis.GT_INVALID, reason)
logclient.AddSimpleActionLog(gt, logclient.ACT_HEALTH_CHECK, reason, userCred, false)
return nil
}
func (gm *SGuestTemplateManager) InspectAllTemplate(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
lastCheckTime := time.Now().Add(time.Duration(-options.Options.GuestTemplateCheckInterval) * time.Hour)
q := gm.Query()
q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("last_check_time")), sqlchemy.LE(q.Field("last_check_time"),
lastCheckTime)))
gts := make([]SGuestTemplate, 0, 10)
err := db.FetchModelObjects(gm, q, &gts)
if err != nil {
log.Errorf("Unable to fetch all guest templates that need to check: %s", err.Error())
return
}
for i := range gts {
gts[i].inspect(ctx, userCred)
}
}