feature: 1. baremetal server reuse host ip 2. server name use expression

This commit is contained in:
Qiu Jian
2019-12-24 19:04:41 +08:00
parent 9186fe44d6
commit b31bc7fa3d
18 changed files with 314 additions and 69 deletions
+1 -1
View File
@@ -2199,7 +2199,7 @@ func (s *SBaremetalServer) DoPartitionDisk(term *ssh.Client) ([]*disktool.Partit
rootSize, _ := rootDisk.Int("size")
err = s.doCreateRoot(term, tool.GetRootDisk().GetDevName())
if err != nil {
return nil, errors.Wrapf(err, "Failed to create root")
return nil, errors.Wrap(err, "Failed to create root")
}
tool.RetrievePartitionInfo()
parts := tool.GetPartitions()
+6
View File
@@ -1058,6 +1058,12 @@ func _doCreateItem(
if err != nil {
return nil, httperrors.NewGeneralError(err)
}
// save generateName
if len(generateName) > 0 {
if standaloneMode, ok := model.(IStandaloneModel); ok {
standaloneMode.SetMetadata(ctx, "generate_name", generateName, userCred)
}
}
// HACK: set data same as dataDict
data.(*jsonutils.JSONDict).Update(dataDict)
return model, nil
+12 -1
View File
@@ -256,12 +256,23 @@ type IStandaloneModel interface {
GetIStandaloneModel() IStandaloneModel
ClearSchedDescCache() error
GetMetadata(key string, userCred mcclient.TokenCredential) string
GetMetadataJson(key string, userCred mcclient.TokenCredential) jsonutils.JSONObject
SetMetadata(ctx context.Context, key string, value interface{}, userCred mcclient.TokenCredential) error
SetAllMetadata(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error
SetUserMetadataValues(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error
SetUserMetadataAll(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error
SetCloudMetadataAll(ctx context.Context, dictstore map[string]interface{}, userCred mcclient.TokenCredential) error
RemoveMetadata(ctx context.Context, key string, userCred mcclient.TokenCredential) error
RemoveAllMetadata(ctx context.Context, userCred mcclient.TokenCredential) error
GetAllMetadata(userCred mcclient.TokenCredential) (map[string]string, error)
}
type IMetadataModel interface {
IStandaloneModel
GetAllMetadata(userCred mcclient.TokenCredential) (map[string]string, error)
// GetAllMetadata(userCred mcclient.TokenCredential) (map[string]string, error)
GetMetadataHideKeys() []string
}
+1
View File
@@ -91,6 +91,7 @@ func CheckSync(autoSync bool) bool {
}
}
for modelName, modelMan := range globalTables {
log.Infof("check model %s SQL", modelName)
tableSpec := modelMan.TableSpec()
sqls := tableSpec.SyncSQL()
if len(sqls) > 0 {
+26 -9
View File
@@ -127,7 +127,7 @@ func (self *SBaremetalGuestDriver) ValidateResizeDisk(guest *models.SGuest, disk
return httperrors.NewUnsupportOperationError("Cannot resize disk for baremtal")
}
func (self *SBaremetalGuestDriver) GetNamedNetworkConfiguration(guest *models.SGuest, userCred mcclient.TokenCredential, host *models.SHost, netConfig *api.NetworkConfig) (*models.SNetwork, []models.SNicConfig, api.IPAllocationDirection) {
func (self *SBaremetalGuestDriver) GetNamedNetworkConfiguration(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, netConfig *api.NetworkConfig) (*models.SNetwork, []models.SNicConfig, api.IPAllocationDirection, bool) {
netifs, net := host.GetNetinterfacesWithIdAndCredential(netConfig.Network, userCred, netConfig.Reserved)
if netifs != nil {
nicCnt := 1
@@ -136,7 +136,8 @@ func (self *SBaremetalGuestDriver) GetNamedNetworkConfiguration(guest *models.SG
}
if len(netifs) < nicCnt {
if netConfig.RequireTeaming {
return net, nil, ""
log.Errorf("not enough network interfaces, want %d got %d", nicCnt, len(netifs))
return net, nil, "", false
}
nicCnt = len(netifs)
}
@@ -149,9 +150,17 @@ func (self *SBaremetalGuestDriver) GetNamedNetworkConfiguration(guest *models.SG
}
nicConfs = append(nicConfs, nicConf)
}
return net, nicConfs, api.IPAllocationStepup
reuseAddr := false
hn := host.GetAttach2Network(netConfig.Network)
if hn != nil && netConfig.Address == "" && options.Options.BaremetalServerReuseHostIp {
// try to reuse host network IP address
netConfig.Address = hn.IpAddr
reuseAddr = true
}
return net, nicConfs, api.IPAllocationStepup, reuseAddr
}
return net, nil, ""
return net, nil, "", false
}
func (self *SBaremetalGuestDriver) GetRandomNetworkTypes() []string {
@@ -190,10 +199,10 @@ func (self *SBaremetalGuestDriver) Attach2RandomNetwork(guest *models.SGuest, ct
}
if net != nil {
netsAvaiable = append(netsAvaiable, *net)
if _, exist := netifIndexs[net.Id]; !exist {
netifIndexs[net.Id] = make([]models.SNetInterface, 0)
if _, exist := netifIndexs[net.WireId]; !exist {
netifIndexs[net.WireId] = make([]models.SNetInterface, 0)
}
netifIndexs[net.Id] = append(netifIndexs[net.Id], netifs[idx])
netifIndexs[net.WireId] = append(netifIndexs[net.WireId], netifs[idx])
}
}
if len(netsAvaiable) == 0 {
@@ -201,7 +210,7 @@ func (self *SBaremetalGuestDriver) Attach2RandomNetwork(guest *models.SGuest, ct
}
net := models.ChooseCandidateNetworks(netsAvaiable, netConfig.Exit, netTypes)
if net != nil {
netifs := netifIndexs[net.Id]
netifs := netifIndexs[net.WireId]
nicConfs := make([]models.SNicConfig, 0)
nicCnt := 1
if netConfig.RequireTeaming || netConfig.TryTeaming {
@@ -221,7 +230,15 @@ func (self *SBaremetalGuestDriver) Attach2RandomNetwork(guest *models.SGuest, ct
}
nicConfs = append(nicConfs, nicConf)
}
return guest.Attach2Network(ctx, userCred, net, pendingUsage, "", netConfig.Driver, netConfig.BwLimit, netConfig.Vip, false, api.IPAllocationStepup, false, nicConfs)
address := ""
reuseAddr := false
hn := host.GetAttach2Network(net.Id)
if hn != nil && options.Options.BaremetalServerReuseHostIp {
// try to reuse host network IP address
address = hn.IpAddr
reuseAddr = true
}
return guest.Attach2Network(ctx, userCred, net, pendingUsage, address, netConfig.Driver, netConfig.BwLimit, netConfig.Vip, false, api.IPAllocationStepup, false, reuseAddr, nicConfs)
}
return nil, fmt.Errorf("No appropriate host virtual network...")
}
+3 -3
View File
@@ -52,7 +52,7 @@ func (self *SVirtualizedGuestDriver) PrepareDiskRaidConfig(userCred mcclient.Tok
return nil, nil
}
func (self *SVirtualizedGuestDriver) GetNamedNetworkConfiguration(guest *models.SGuest, userCred mcclient.TokenCredential, host *models.SHost, netConfig *api.NetworkConfig) (*models.SNetwork, []models.SNicConfig, api.IPAllocationDirection) {
func (self *SVirtualizedGuestDriver) GetNamedNetworkConfiguration(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, host *models.SHost, netConfig *api.NetworkConfig) (*models.SNetwork, []models.SNicConfig, api.IPAllocationDirection, bool) {
net, _ := host.GetNetworkWithIdAndCredential(netConfig.Network, userCred, netConfig.Reserved)
nicConfs := []models.SNicConfig{
{
@@ -68,7 +68,7 @@ func (self *SVirtualizedGuestDriver) GetNamedNetworkConfiguration(guest *models.
Ifname: "",
})
}
return net, nicConfs, api.IPAllocationStepdown
return net, nicConfs, api.IPAllocationStepdown, false
}
func (self *SVirtualizedGuestDriver) GetRandomNetworkTypes() []string {
@@ -147,7 +147,7 @@ func (self *SVirtualizedGuestDriver) Attach2RandomNetwork(guest *models.SGuest,
}
nicConfs = append(nicConfs, nicConf)
}
gn, err := guest.Attach2Network(ctx, userCred, selNet, pendingUsage, netConfig.Address, netConfig.Driver, netConfig.BwLimit, netConfig.Vip, netConfig.Reserved, api.IPAllocationDefault, netConfig.RequireDesignatedIP, nicConfs)
gn, err := guest.Attach2Network(ctx, userCred, selNet, pendingUsage, netConfig.Address, netConfig.Driver, netConfig.BwLimit, netConfig.Vip, netConfig.Reserved, api.IPAllocationDefault, netConfig.RequireDesignatedIP, false, nicConfs)
return gn, err
}
+1 -1
View File
@@ -271,7 +271,7 @@ func (self *SDynamicschedtag) PerformEvaluate(ctx context.Context, userCred mccl
log.V(10).Debugf("Dynamicschedtag evaluate input: %s", params.PrettyString())
meet, err := conditionparser.Eval(self.Condition, params)
meet, err := conditionparser.EvalBool(self.Condition, params)
if err != nil {
return nil, err
}
+1 -1
View File
@@ -66,7 +66,7 @@ type IGuestDriver interface {
PrepareDiskRaidConfig(userCred mcclient.TokenCredential, host *SHost, params []*api.BaremetalDiskConfig, disks []*api.DiskConfig) ([]*api.DiskConfig, error)
GetNamedNetworkConfiguration(guest *SGuest, userCred mcclient.TokenCredential, host *SHost, netConfig *api.NetworkConfig) (*SNetwork, []SNicConfig, api.IPAllocationDirection)
GetNamedNetworkConfiguration(guest *SGuest, ctx context.Context, userCred mcclient.TokenCredential, host *SHost, netConfig *api.NetworkConfig) (*SNetwork, []SNicConfig, api.IPAllocationDirection, bool)
Attach2RandomNetwork(guest *SGuest, ctx context.Context, userCred mcclient.TokenCredential, host *SHost, netConfig *api.NetworkConfig, pendingUsage quotas.IQuota) ([]SGuestnetwork, error)
GetRandomNetworkTypes() []string
+29 -12
View File
@@ -151,9 +151,13 @@ func (manager *SGuestnetworkManager) GenerateMac(netId string, suggestion string
}
func (manager *SGuestnetworkManager) newGuestNetwork(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, network *SNetwork,
index int8, address string, mac string, driver string, bwLimit int, virtual bool, reserved bool,
allocDir api.IPAllocationDirection, requiredDesignatedIp bool, ifname string, teamWithMac string) (*SGuestnetwork, error) {
index int8, address string, mac string, driver string, bwLimit int,
virtual bool, reserved bool,
allocDir api.IPAllocationDirection,
requiredDesignatedIp bool,
reUseAddr bool,
ifname string, teamWithMac string,
) (*SGuestnetwork, error) {
gn := SGuestnetwork{}
gn.SetModelManager(GuestnetworkManager, &gn)
@@ -182,16 +186,29 @@ func (manager *SGuestnetworkManager) newGuestNetwork(ctx context.Context, userCr
}
gn.MacAddr = macAddr
if !virtual {
addrTable := network.GetUsedAddresses()
recentAddrTable := manager.getRecentlyReleasedIPAddresses(network.Id, network.getAllocTimoutDuration())
ipAddr, err := network.GetFreeIP(ctx, userCred, addrTable, recentAddrTable, address, allocDir, reserved)
if err != nil {
return nil, err
if len(address) > 0 && reUseAddr {
ipAddr, err := netutils.NewIPV4Addr(address)
if err != nil {
return nil, errors.Wrapf(err, "Reuse invalid address %s", address)
}
if !network.IsAddressInRange(ipAddr) {
return nil, errors.Wrapf(httperrors.ErrOutOfRange, "%s not in network address range", address)
}
// if reuse Ip address, no need to check address availability
// assign it anyway
gn.IpAddr = address
} else {
addrTable := network.GetUsedAddresses()
recentAddrTable := manager.getRecentlyReleasedIPAddresses(network.Id, network.getAllocTimoutDuration())
ipAddr, err := network.GetFreeIP(ctx, userCred, addrTable, recentAddrTable, address, allocDir, reserved)
if err != nil {
return nil, err
}
if len(address) > 0 && ipAddr != address && requiredDesignatedIp {
return nil, fmt.Errorf("candidate ip %s is occupied!", address)
}
gn.IpAddr = ipAddr
}
if len(address) > 0 && ipAddr != address && requiredDesignatedIp {
return nil, fmt.Errorf("candidate ip %s is occupied!", address)
}
gn.IpAddr = ipAddr
}
ifname, err = gn.checkOrAllocateIfname(network, ifname)
if err != nil {
+38 -19
View File
@@ -2504,15 +2504,23 @@ func (self *SGuest) GetOSProfile() osprofile.SOSProfile {
return osProf
}
func (self *SGuest) Attach2Network(ctx context.Context, userCred mcclient.TokenCredential, network *SNetwork,
func (self *SGuest) Attach2Network(
ctx context.Context,
userCred mcclient.TokenCredential,
network *SNetwork,
pendingUsage quotas.IQuota,
address string,
driver string, bwLimit int, virtual bool,
reserved bool, allocDir api.IPAllocationDirection, requireDesignatedIP bool,
nicConfs []SNicConfig) ([]SGuestnetwork, error) {
reserved bool,
allocDir api.IPAllocationDirection,
requireDesignatedIP bool,
reUseAddr bool,
nicConfs []SNicConfig,
) ([]SGuestnetwork, error) {
firstNic, err := self.attach2NetworkOnce(ctx, userCred, network, pendingUsage, address, driver, bwLimit, virtual,
reserved, allocDir, requireDesignatedIP, nicConfs[0], "")
firstNic, err := self.attach2NetworkOnce(ctx, userCred, network, pendingUsage,
address, driver, bwLimit, virtual,
reserved, allocDir, requireDesignatedIP, reUseAddr, nicConfs[0], "")
if err != nil {
return nil, errors.Wrap(err, "self.attach2NetworkOnce")
}
@@ -2524,7 +2532,7 @@ func (self *SGuest) Attach2Network(ctx context.Context, userCred mcclient.TokenC
nicConfs[i].Mac = firstMac.Add(i).String()
}
gn, err := self.attach2NetworkOnce(ctx, userCred, network, pendingUsage, "", firstNic.Driver, 0, true,
false, allocDir, false, nicConfs[i], firstNic.MacAddr)
false, allocDir, false, false, nicConfs[i], firstNic.MacAddr)
if err != nil {
return retNics, errors.Wrap(err, "self.attach2NetworkOnce")
}
@@ -2534,12 +2542,19 @@ func (self *SGuest) Attach2Network(ctx context.Context, userCred mcclient.TokenC
return retNics, nil
}
func (self *SGuest) attach2NetworkOnce(ctx context.Context, userCred mcclient.TokenCredential, network *SNetwork,
func (self *SGuest) attach2NetworkOnce(
ctx context.Context,
userCred mcclient.TokenCredential,
network *SNetwork,
pendingUsage quotas.IQuota,
address string,
driver string, bwLimit int, virtual bool,
reserved bool, allocDir api.IPAllocationDirection, requireDesignatedIP bool,
nicConf SNicConfig, teamWithMac string) (*SGuestnetwork, error) {
reserved bool,
allocDir api.IPAllocationDirection,
requireDesignatedIP bool,
reUseAddr bool,
nicConf SNicConfig, teamWithMac string,
) (*SGuestnetwork, error) {
/*
allow a guest attach to a network 2 times
*/
@@ -2556,9 +2571,11 @@ func (self *SGuest) attach2NetworkOnce(ctx context.Context, userCred mcclient.To
lockman.LockClass(ctx, QuotaManager, self.ProjectId)
defer lockman.ReleaseClass(ctx, QuotaManager, self.ProjectId)
guestnic, err := GuestnetworkManager.newGuestNetwork(ctx, userCred, self, network,
guestnic, err := GuestnetworkManager.newGuestNetwork(ctx, userCred,
self, network,
nicConf.Index, address, nicConf.Mac, driver, bwLimit, virtual, reserved,
allocDir, requireDesignatedIP, nicConf.Ifname, teamWithMac)
allocDir, requireDesignatedIP, reUseAddr,
nicConf.Ifname, teamWithMac)
if err != nil {
return nil, errors.Wrap(err, "GuestnetworkManager.newGuestNetwork")
}
@@ -2723,7 +2740,7 @@ func (self *SGuest) SyncVMNics(ctx context.Context, userCred mcclient.TokenCrede
}
// always try allocate from reserved pool
_, err = self.Attach2Network(ctx, userCred, add.net, nil, ipStr,
add.nic.GetDriver(), 0, false, true, api.IPAllocationDefault, true, []SNicConfig{nicConf})
add.nic.GetDriver(), 0, false, true, api.IPAllocationDefault, true, false, []SNicConfig{nicConf})
if err != nil {
result.AddError(err)
} else {
@@ -3025,12 +3042,12 @@ func (self *SGuest) CreateNetworksOnHost(
if len(netArray) == 0 {
netConfig := self.getDefaultNetworkConfig()
_, err := self.attach2RandomNetwork(ctx, userCred, host, netConfig, pendingUsage)
return err
return errors.Wrap(err, "self.attach2RandomNetwork")
}
for idx, netConfig := range netArray {
netConfig, err := parseNetworkInfo(userCred, netConfig)
for idx := range netArray {
netConfig, err := parseNetworkInfo(userCred, netArray[idx])
if err != nil {
return err
return errors.Wrapf(err, "parseNetworkInfo at %d", idx)
}
var candidateNet *schedapi.CandidateNet
if len(candidateNets) > idx {
@@ -3042,7 +3059,7 @@ func (self *SGuest) CreateNetworksOnHost(
}
_, err = self.attach2NetworkDesc(ctx, userCred, host, netConfig, pendingUsage, networkIds)
if err != nil {
return err
return errors.Wrap(err, "self.attach2NetworkDesc")
}
}
return nil
@@ -3087,12 +3104,12 @@ func (self *SGuest) attach2NetworkDesc(
func (self *SGuest) attach2NamedNetworkDesc(ctx context.Context, userCred mcclient.TokenCredential, host *SHost, netConfig *api.NetworkConfig, pendingUsage quotas.IQuota) ([]SGuestnetwork, error) {
driver := self.GetDriver()
net, nicConfs, allocDir := driver.GetNamedNetworkConfiguration(self, userCred, host, netConfig)
net, nicConfs, allocDir, reuseAddr := driver.GetNamedNetworkConfiguration(self, ctx, userCred, host, netConfig)
if net != nil {
if len(nicConfs) == 0 {
return nil, fmt.Errorf("no avaialble network interface?")
}
gn, err := self.Attach2Network(ctx, userCred, net, pendingUsage, netConfig.Address, netConfig.Driver, netConfig.BwLimit, netConfig.Vip, netConfig.Reserved, allocDir, netConfig.RequireDesignatedIP, nicConfs)
gn, err := self.Attach2Network(ctx, userCred, net, pendingUsage, netConfig.Address, netConfig.Driver, netConfig.BwLimit, netConfig.Vip, netConfig.Reserved, allocDir, netConfig.RequireDesignatedIP, reuseAddr, nicConfs)
if err != nil {
log.Errorf("Attach2Network fail %s", err)
return nil, err
@@ -4009,6 +4026,8 @@ func (self *SGuest) GetShortDesc(ctx context.Context) *jsonutils.JSONDict {
var billingInfo SCloudBillingInfo
if host != nil {
desc.Set("host", jsonutils.NewString(host.Name))
desc.Set("host_id", jsonutils.NewString(host.Id))
billingInfo.SCloudProviderInfo = host.getCloudProviderInfo()
}
+3 -3
View File
@@ -1274,9 +1274,9 @@ func (self *SHost) GetBaremetalnetworks() []SHostnetwork {
return hns
}
func (self *SHost) GetAttach2Network(network *SNetwork) *SHostnetwork {
func (self *SHost) GetAttach2Network(netId string) *SHostnetwork {
q := self.GetBaremetalnetworksQuery()
q = q.Equals("network_id", network.Id)
q = q.Equals("network_id", netId)
hn := SHostnetwork{}
hn.SetModelManager(HostnetworkManager, &hn)
@@ -1289,7 +1289,7 @@ func (self *SHost) GetAttach2Network(network *SNetwork) *SHostnetwork {
}
func (self *SHost) isAttach2Network(network *SNetwork) bool {
hn := self.GetAttach2Network(network)
hn := self.GetAttach2Network(network.Id)
return hn != nil
}
+2 -2
View File
@@ -208,7 +208,7 @@ func (self *SSchedpolicy) PerformEvaluate(ctx context.Context, userCred mcclient
log.V(10).Debugf("Schedpolicy evaluate input: %s", params.PrettyString())
meet, err := conditionparser.Eval(self.Condition, params)
meet, err := conditionparser.EvalBool(self.Condition, params)
if err != nil {
return nil, err
}
@@ -226,7 +226,7 @@ func matchResourceSchedPolicy(
policy SSchedpolicy,
input *jsonutils.JSONDict,
) bool {
meet, err := conditionparser.Eval(policy.Condition, input)
meet, err := conditionparser.EvalBool(policy.Condition, input)
if err != nil {
log.Errorf("Eval Condition %s error: %v", policy.Condition, err)
return false
+2
View File
@@ -117,6 +117,8 @@ type ComputeOptions struct {
DisconnectedCloudAccountRetryProbeIntervalHours int `help:"interval to wait to probe status of a disconnected cloud account" default:"24"`
BaremetalServerReuseHostIp bool `help:"baremetal server reuse host IP address, default true" default:"true"`
SCapabilityOptions
common_options.CommonOptions
common_options.DBOptions
@@ -27,6 +27,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
"yunion.io/x/onecloud/pkg/compute/models"
"yunion.io/x/onecloud/pkg/util/conditionparser"
"yunion.io/x/onecloud/pkg/util/logclient"
)
@@ -75,6 +76,32 @@ func (self *GuestBatchCreateTask) allocateGuestOnHost(ctx context.Context, guest
log.Errorf("GetPendingUsage fail %s", err)
}
if conditionparser.IsTemplate(guest.Name) {
guestInfo := guest.GetShortDesc(ctx)
generateName := guest.GetMetadata("generate_name", self.UserCred)
if len(generateName) == 0 {
generateName = guest.Name
}
newGenName, err := conditionparser.EvalTemplate(generateName, guestInfo)
if err == nil {
newName, err := db.GenerateName2(models.GuestManager,
guest.GetOwnerId(), newGenName, guest, 1)
if err == nil {
_, err = db.Update(guest, func() error {
guest.Name = newName
return nil
})
if err != nil {
log.Errorf("guest update name fail %s", err)
}
} else {
log.Errorf("db.GenerateName2 fail %s", err)
}
} else {
log.Errorf("conditionparser.EvalTemplate fail %s", err)
}
}
host := guest.GetHost()
quotaCpuMem := models.SQuota{Count: 1, Cpu: int(guest.VcpuCount), Memory: guest.VmemSize}
+37 -14
View File
@@ -15,7 +15,7 @@
package conditionparser
import (
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
@@ -24,16 +24,16 @@ import (
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/utils"
)
var (
ErrInvalidOp = errors.New("invalid operation")
ErrFieldNotFound = errors.New("field not found")
ErrOutOfIndex = errors.New("out of index")
ErrFuncArgument = errors.New("invalid function arguments")
ErrMethodNotFound = errors.New("method not found")
ErrInvalidOp = errors.Error("invalid operation")
ErrFieldNotFound = errors.Error("field not found")
ErrOutOfIndex = errors.Error("out of index")
ErrFuncArgument = errors.Error("invalid function arguments")
ErrMethodNotFound = errors.Error("method not found")
)
func IsValid(exprStr string) bool {
@@ -44,22 +44,45 @@ func IsValid(exprStr string) bool {
return true
}
func Eval(exprStr string, input interface{}) (bool, error) {
func EvalString(exprStr string, input interface{}) (string, error) {
if len(exprStr) == 0 {
return "", nil
}
expr, err := parser.ParseExpr(exprStr)
if err != nil {
return "", errors.Wrapf(err, "parse expr %s error", exprStr)
}
result, err := eval(expr, input)
if err != nil {
return "", errors.Wrap(err, "eval")
}
switch strVal := result.(type) {
case string:
return strVal, nil
case *jsonutils.JSONString:
return strVal.GetString()
default:
return fmt.Sprintf("%s", strVal), nil
}
}
func EvalBool(exprStr string, input interface{}) (bool, error) {
if len(exprStr) == 0 {
return true, nil
}
expr, err := parser.ParseExpr(exprStr)
if err != nil {
log.Errorf("parse expr %s error %s", exprStr, err)
return false, err
return false, errors.Wrapf(err, "parse expr %s", exprStr)
}
result, err := eval(expr, input)
if err != nil {
return false, err
return false, errors.Wrap(err, "eval")
}
switch result.(type) {
case bool, *jsonutils.JSONBool:
return getBool(result), nil
switch bVal := result.(type) {
case bool:
return bVal, nil
case *jsonutils.JSONBool:
return bVal.Bool()
case []interface{}, *jsonutils.JSONArray:
arrX := getArray(result)
for i := 0; i < len(arrX); i += 1 {
+45 -3
View File
@@ -49,7 +49,7 @@ func TestAst(t *testing.T) {
}
for _, c := range cases {
result, err := Eval(c.in, input)
result, err := EvalBool(c.in, input)
if err != nil {
t.Errorf("eval expr %s error %s", c.in, err)
return
@@ -108,7 +108,7 @@ func TestEval2(t *testing.T) {
}
for _, c := range cases {
result, err := Eval(c.in, input)
result, err := EvalBool(c.in, input)
if err != nil {
t.Errorf("eval expr %s error %s", c.in, err)
return
@@ -165,7 +165,7 @@ func TestEval3(t *testing.T) {
}
for _, c := range cases {
result, err := Eval(c.in, input)
result, err := EvalBool(c.in, input)
if err != nil {
t.Errorf("eval expr %s error %s", c.in, err)
return
@@ -177,3 +177,45 @@ func TestEval3(t *testing.T) {
}
}
}
func TestEvalString(t *testing.T) {
input := jsonutils.NewDict()
input.Add(jsonutils.NewString("myname"), "name")
input.Add(jsonutils.NewString("myhostname"), "host", "name")
input.Add(jsonutils.NewString("myproject"), "project", "name")
input.Add(jsonutils.NewString("myprojectId"), "project", "id")
cases := []struct {
input string
want string
}{
{
input: "host.name",
want: "myhostname",
},
{
input: "project.id",
want: "myprojectId",
},
{
input: "project.name",
want: "myproject",
},
{
input: "name",
want: "myname",
},
{
input: `name+"/"+project.name`,
want: "myname/myproject",
},
}
for _, c := range cases {
got, err := EvalString(c.input, input)
if err != nil {
t.Errorf("Eval %s fail: %s", c.input, err)
} else if got != c.want {
t.Errorf("Eval %s want %s got %s", c.input, c.want, got)
}
}
}
+35
View File
@@ -0,0 +1,35 @@
package conditionparser
import (
"regexp"
"strings"
"yunion.io/x/pkg/errors"
)
var (
tempPattern = regexp.MustCompile(`\$\{[\w\d._-]*\}`)
)
func IsTemplate(template string) bool {
return tempPattern.MatchString(template)
}
func EvalTemplate(template string, input interface{}) (string, error) {
var output strings.Builder
matches := tempPattern.FindAllStringSubmatchIndex(template, -1)
offset := 0
for _, match := range matches {
output.WriteString(template[offset:match[0]])
o, err := EvalString(template[match[0]+2:match[1]-1], input)
if err != nil {
return "", errors.Wrap(err, template[match[0]+2:match[1]])
}
output.WriteString(o)
offset = match[1]
}
if offset < len(template) {
output.WriteString(template[offset:])
}
return output.String(), nil
}
+45
View File
@@ -0,0 +1,45 @@
package conditionparser
import (
"testing"
"yunion.io/x/jsonutils"
)
func TestEvalTemplate(t *testing.T) {
input := jsonutils.NewDict()
input.Add(jsonutils.NewString("myhost"), "host")
input.Add(jsonutils.NewString("jxq"), "zone")
input.Add(jsonutils.NewString("myprojId"), "project", "id")
input.Add(jsonutils.NewString("my"), "project", "name")
input.Add(jsonutils.NewString("my"), "proj_name")
cases := []struct {
input string
want string
}{
{
input: "${host}-1",
want: "myhost-1",
},
{
input: "${zone}-${host}-1",
want: "jxq-myhost-1",
},
{
input: "${zone}-${host}-${project.name}-1",
want: "jxq-myhost-my-1",
},
{
input: "${zone}-${host}-${proj_name}-1",
want: "jxq-myhost-my-1",
},
}
for _, c := range cases {
got, err := EvalTemplate(c.input, input)
if err != nil {
t.Errorf("EvalTemplate %s error %s", c.input, err)
} else if got != c.want {
t.Errorf("EvalTemplate %s got %s want %s", c.input, got, c.want)
}
}
}