diff --git a/Gopkg.lock b/Gopkg.lock index cbf2c79e3e..22d40dc867 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -760,6 +760,14 @@ revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" version = "v1.2.2" +[[projects]] + branch = "master" + digest = "1:a38c75e7edd595bbaa03334c1ac26163a5e990d81f05cd9ec5fd0edc9c786078" + name = "github.com/texttheater/golang-levenshtein" + packages = ["levenshtein"] + pruneopts = "UT" + revision = "d188e65d659ef53fcdb0691c12f1bba64928b649" + [[projects]] digest = "1:98e5cda86f67cd1ac95389d98670b66dea8cae480fe6292b83bccccfe60b4106" name = "github.com/ugorji/go" @@ -1116,7 +1124,7 @@ [[projects]] branch = "master" - digest = "1:f640bec6e2c558f83117e88f16753d54685c95b8fb83d5660ed6c43e04adde72" + digest = "1:47660d9bad5ac13797da13236922903a26206c81aa2df28af44b3e7ddd3e3f06" name = "yunion.io/x/pkg" packages = [ "gotypes", @@ -1151,7 +1159,7 @@ "utils", ] pruneopts = "UT" - revision = "3bcf68100d212545f2f16b91bb3962bc8b228da7" + revision = "6a2c5061c44f5e4a813a87553014ef72d1b983ea" [[projects]] branch = "master" @@ -1163,11 +1171,11 @@ [[projects]] branch = "master" - digest = "1:64b263a23f3c35521bb811b2aa9f93f8c7ce1ff06487db12d6aa3fc25705ad2e" + digest = "1:c69a05ea10fc186aaf589c10184d00dbfa3a46fbd7866ed1e52d6bd84f8607d5" name = "yunion.io/x/structarg" packages = ["."] pruneopts = "UT" - revision = "ec02b19c0bccfc991fc9e850f5a2a543ae43254b" + revision = "7dc6c41bf325ce7f2ce2a1cebbd8d396f7f6188e" [solve-meta] analyzer-name = "dep" diff --git a/Gopkg.toml b/Gopkg.toml index 3353f32046..8304c467c1 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -93,3 +93,7 @@ [[constraint]] branch = "master" name = "github.com/kr/pty" + +[[constraint]] + branch = "master" + name = "github.com/texttheater/golang-levenshtein" diff --git a/vendor/github.com/texttheater/golang-levenshtein/LICENSE b/vendor/github.com/texttheater/golang-levenshtein/LICENSE new file mode 100644 index 0000000000..11138952ba --- /dev/null +++ b/vendor/github.com/texttheater/golang-levenshtein/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Kilian Evang and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/texttheater/golang-levenshtein/levenshtein/levenshtein.go b/vendor/github.com/texttheater/golang-levenshtein/levenshtein/levenshtein.go new file mode 100644 index 0000000000..4e57e5c5dd --- /dev/null +++ b/vendor/github.com/texttheater/golang-levenshtein/levenshtein/levenshtein.go @@ -0,0 +1,245 @@ +// This package implements the Levenshtein algorithm for computing the +// similarity between two strings. The central function is MatrixForStrings, +// which computes the Levenshtein matrix. The functions DistanceForMatrix, +// EditScriptForMatrix and RatioForMatrix read various interesting properties +// off the matrix. The package also provides the convenience functions +// DistanceForStrings, EditScriptForStrings and RatioForStrings for going +// directly from two strings to the property of interest. +package levenshtein + +import ( + "fmt" + "io" + "os" +) + +type EditOperation int + +const ( + Ins = iota + Del + Sub + Match +) + +type EditScript []EditOperation + +type MatchFunction func(rune, rune) bool + +type Options struct { + InsCost int + DelCost int + SubCost int + Matches MatchFunction +} + +// DefaultOptions is the default options: insertion cost is 1, deletion cost is +// 1, substitution cost is 2, and two runes match iff they are the same. +var DefaultOptions Options = Options{ + InsCost: 1, + DelCost: 1, + SubCost: 2, + Matches: func(sourceCharacter rune, targetCharacter rune) bool { + return sourceCharacter == targetCharacter + }, +} + +func (operation EditOperation) String() string { + if operation == Match { + return "match" + } else if operation == Ins { + return "ins" + } else if operation == Sub { + return "sub" + } + return "del" +} + +// DistanceForStrings returns the edit distance between source and target. +// +// It has a runtime proportional to len(source) * len(target) and memory use +// proportional to len(target). +func DistanceForStrings(source []rune, target []rune, op Options) int { + // Note: This algorithm is a specialization of MatrixForStrings. + // MatrixForStrings returns the full edit matrix. However, we only need a + // single value (see DistanceForMatrix) and the main loop of the algorithm + // only uses the current and previous row. As such we create a 2D matrix, + // but with height 2 (enough to store current and previous row). + height := len(source) + 1 + width := len(target) + 1 + matrix := make([][]int, 2) + + // Initialize trivial distances (from/to empty string). That is, fill + // the left column and the top row with row/column indices. + for i := 0; i < 2; i++ { + matrix[i] = make([]int, width) + matrix[i][0] = i + } + for j := 1; j < width; j++ { + matrix[0][j] = j + } + + // Fill in the remaining cells: for each prefix pair, choose the + // (edit history, operation) pair with the lowest cost. + for i := 1; i < height; i++ { + cur := matrix[i%2] + prev := matrix[(i-1)%2] + cur[0] = i + for j := 1; j < width; j++ { + delCost := prev[j] + op.DelCost + matchSubCost := prev[j-1] + if !op.Matches(source[i-1], target[j-1]) { + matchSubCost += op.SubCost + } + insCost := cur[j-1] + op.InsCost + cur[j] = min(delCost, min(matchSubCost, insCost)) + } + } + return matrix[(height-1)%2][width-1] +} + +// DistanceForMatrix reads the edit distance off the given Levenshtein matrix. +func DistanceForMatrix(matrix [][]int) int { + return matrix[len(matrix)-1][len(matrix[0])-1] +} + +// RatioForStrings returns the Levenshtein ratio for the given strings. The +// ratio is computed as follows: +// +// (sourceLength + targetLength - distance) / (sourceLength + targetLength) +func RatioForStrings(source []rune, target []rune, op Options) float64 { + matrix := MatrixForStrings(source, target, op) + return RatioForMatrix(matrix) +} + +// RatioForMatrix returns the Levenshtein ratio for the given matrix. The ratio +// is computed as follows: +// +// (sourceLength + targetLength - distance) / (sourceLength + targetLength) +func RatioForMatrix(matrix [][]int) float64 { + sourcelength := len(matrix) - 1 + targetlength := len(matrix[0]) - 1 + sum := sourcelength + targetlength + + if sum == 0 { + return 0 + } + + dist := DistanceForMatrix(matrix) + return float64(sum-dist) / float64(sum) +} + +// MatrixForStrings generates a 2-D array representing the dynamic programming +// table used by the Levenshtein algorithm, as described e.g. here: +// http://www.let.rug.nl/kleiweg/lev/ +// The reason for putting the creation of the table into a separate function is +// that it cannot only be used for reading of the edit distance between two +// strings, but also e.g. to backtrace an edit script that provides an +// alignment between the characters of both strings. +func MatrixForStrings(source []rune, target []rune, op Options) [][]int { + // Make a 2-D matrix. Rows correspond to prefixes of source, columns to + // prefixes of target. Cells will contain edit distances. + // Cf. http://www.let.rug.nl/~kleiweg/lev/levenshtein.html + height := len(source) + 1 + width := len(target) + 1 + matrix := make([][]int, height) + + // Initialize trivial distances (from/to empty string). That is, fill + // the left column and the top row with row/column indices. + for i := 0; i < height; i++ { + matrix[i] = make([]int, width) + matrix[i][0] = i + } + for j := 1; j < width; j++ { + matrix[0][j] = j + } + + // Fill in the remaining cells: for each prefix pair, choose the + // (edit history, operation) pair with the lowest cost. + for i := 1; i < height; i++ { + for j := 1; j < width; j++ { + delCost := matrix[i-1][j] + op.DelCost + matchSubCost := matrix[i-1][j-1] + if !op.Matches(source[i-1], target[j-1]) { + matchSubCost += op.SubCost + } + insCost := matrix[i][j-1] + op.InsCost + matrix[i][j] = min(delCost, min(matchSubCost, + insCost)) + } + } + //LogMatrix(source, target, matrix) + return matrix +} + +// EditScriptForStrings returns an optimal edit script to turn source into +// target. +func EditScriptForStrings(source []rune, target []rune, op Options) EditScript { + return backtrace(len(source), len(target), + MatrixForStrings(source, target, op), op) +} + +// EditScriptForMatrix returns an optimal edit script based on the given +// Levenshtein matrix. +func EditScriptForMatrix(matrix [][]int, op Options) EditScript { + return backtrace(len(matrix)-1, len(matrix[0])-1, matrix, op) +} + +// WriteMatrix writes a visual representation of the given matrix for the given +// strings to the given writer. +func WriteMatrix(source []rune, target []rune, matrix [][]int, writer io.Writer) { + fmt.Fprintf(writer, " ") + for _, targetRune := range target { + fmt.Fprintf(writer, " %c", targetRune) + } + fmt.Fprintf(writer, "\n") + fmt.Fprintf(writer, " %2d", matrix[0][0]) + for j, _ := range target { + fmt.Fprintf(writer, " %2d", matrix[0][j+1]) + } + fmt.Fprintf(writer, "\n") + for i, sourceRune := range source { + fmt.Fprintf(writer, "%c %2d", sourceRune, matrix[i+1][0]) + for j, _ := range target { + fmt.Fprintf(writer, " %2d", matrix[i+1][j+1]) + } + fmt.Fprintf(writer, "\n") + } +} + +// LogMatrix writes a visual representation of the given matrix for the given +// strings to os.Stderr. This function is deprecated, use +// WriteMatrix(source, target, matrix, os.Stderr) instead. +func LogMatrix(source []rune, target []rune, matrix [][]int) { + WriteMatrix(source, target, matrix, os.Stderr) +} + +func backtrace(i int, j int, matrix [][]int, op Options) EditScript { + if i > 0 && matrix[i-1][j]+op.DelCost == matrix[i][j] { + return append(backtrace(i-1, j, matrix, op), Del) + } + if j > 0 && matrix[i][j-1]+op.InsCost == matrix[i][j] { + return append(backtrace(i, j-1, matrix, op), Ins) + } + if i > 0 && j > 0 && matrix[i-1][j-1]+op.SubCost == matrix[i][j] { + return append(backtrace(i-1, j-1, matrix, op), Sub) + } + if i > 0 && j > 0 && matrix[i-1][j-1] == matrix[i][j] { + return append(backtrace(i-1, j-1, matrix, op), Match) + } + return []EditOperation{} +} + +func min(a int, b int) int { + if b < a { + return b + } + return a +} + +func max(a int, b int) int { + if b > a { + return b + } + return a +} diff --git a/vendor/yunion.io/x/pkg/util/filterclause/filterclause.go b/vendor/yunion.io/x/pkg/util/filterclause/filterclause.go index 1bddde5c61..6f60b3343d 100644 --- a/vendor/yunion.io/x/pkg/util/filterclause/filterclause.go +++ b/vendor/yunion.io/x/pkg/util/filterclause/filterclause.go @@ -45,6 +45,14 @@ func (fc *SFilterClause) QueryCondition(q *sqlchemy.SQuery) sqlchemy.ICondition return sqlchemy.NotIn(field, fc.params) case "between": return sqlchemy.Between(field, fc.params[0], fc.params[1]) + case "ge": + return sqlchemy.GE(field, fc.params[0]) + case "gt": + return sqlchemy.GT(field, fc.params[0]) + case "le": + return sqlchemy.LE(field, fc.params[0]) + case "lt": + return sqlchemy.LT(field, fc.params[0]) case "like": return sqlchemy.Like(field, fc.params[0]) case "contains": diff --git a/vendor/yunion.io/x/pkg/util/sysutils/storagetypes.go b/vendor/yunion.io/x/pkg/util/sysutils/storagetypes.go index 16baa403d2..c88c6caa91 100644 --- a/vendor/yunion.io/x/pkg/util/sysutils/storagetypes.go +++ b/vendor/yunion.io/x/pkg/util/sysutils/storagetypes.go @@ -11,16 +11,21 @@ const ( STORAGE_CLOUD = "cloud" STORAGE_CLOUD_SSD = "cloud_ssd" + STORAGE_CLOUD_ESSD = "cloud_essd" //增强型(Enhanced)SSD 云盘 STORAGE_CLOUD_EFFICIENCY = "cloud_efficiency" - STORAGE_STANDARD = "standard" //Azure hdd storage type - STORAGE_PREMIUM = "premium" //Azure ssd storage type + //Azure hdd and ssd storagetype + STORAGE_STANDARD_GRS = "standard_grs" + STORAGE_STANDARD_LRS = "standard_lrs" + STORAGE_STANDARD_RAGRS = "standard_ragrs" + STORAGE_STANDARD_ZRS = "standard_zrs" + STORAGE_PREMIUM_LRS = "premium_lrs" ) var STORAGE_TYPES = []string{STORAGE_LOCAL, STORAGE_BAREMETAL, STORAGE_SHEEPDOG, STORAGE_RBD, STORAGE_DOCKER, STORAGE_NAS, STORAGE_VSAN, - STORAGE_CLOUD, STORAGE_CLOUD_SSD, STORAGE_CLOUD_EFFICIENCY, - STORAGE_STANDARD, STORAGE_PREMIUM} + STORAGE_CLOUD, STORAGE_CLOUD_SSD, STORAGE_CLOUD_ESSD, STORAGE_CLOUD_EFFICIENCY, + STORAGE_STANDARD_GRS, STORAGE_STANDARD_LRS, STORAGE_STANDARD_RAGRS, STORAGE_STANDARD_ZRS, STORAGE_PREMIUM_LRS} var LOCAL_STORAGE_TYPES = []string{STORAGE_LOCAL, STORAGE_BAREMETAL} diff --git a/vendor/yunion.io/x/structarg/stringdist.go b/vendor/yunion.io/x/structarg/stringdist.go new file mode 100644 index 0000000000..4c087923cf --- /dev/null +++ b/vendor/yunion.io/x/structarg/stringdist.go @@ -0,0 +1,92 @@ +package structarg + +import ( + "github.com/texttheater/golang-levenshtein/levenshtein" + "sort" + "strings" + "fmt" +) + +type stringDistance struct { + str string + /* hanming distance */ + dist int + /* similarity rate, 0~1: totally different ~ identical */ + rate float64 +} + +type LevenshteinStrings struct { + target string + candidates []stringDistance +} + +func (strs LevenshteinStrings) Len() int { + return len(strs.candidates) +} + +func (strs LevenshteinStrings) Swap(i, j int) { + strs.candidates[i], strs.candidates[j] = strs.candidates[j], strs.candidates[i] +} + +func (strs LevenshteinStrings) Less(i, j int) bool { + if strs.candidates[i].dist != strs.candidates[j].dist { + if strs.candidates[i].dist < strs.candidates[j].dist { + return true + } else { + return false + } + } + if strs.candidates[i].rate != strs.candidates[j].rate { + if strs.candidates[i].rate > strs.candidates[j].rate { + return true + } else { + return false + } + } + if strs.candidates[i].str < strs.candidates[j].str { + return true + } + return false +} + +/** + * + * minRate: minimal similarity ratio, between 0.0~1.0, 0.0: totally different, 1.0: exactly identitical + */ +func FindSimilar(niddle string, stack []string, maxDist int, minRate float64) []string { + cands := make([]stringDistance, 0) + for i := 0; i < len(stack); i += 1 { + cand := stringDistance{} + dist := levenshtein.DistanceForStrings([]rune(stack[i]), []rune(niddle), levenshtein.DefaultOptions) + rate := 1.0 + if len(stack[i]) + len(niddle) > 0 { + rate = float64(len(stack[i]) + len(niddle) - dist)/float64(len(stack[i]) + len(niddle)) + } + if (maxDist < 0 || dist <= maxDist) && (minRate < 0.0 || minRate > 1.0 || rate >= minRate) { + cand.str = stack[i] + cand.dist = dist + cand.rate = rate + cands = append(cands, cand) + } + } + lstrs := LevenshteinStrings{target: niddle, candidates: cands} + sort.Sort(lstrs) + result := make([]string, len(cands)) + for i := 0; i < len(result); i += 1 { + result[i] = lstrs.candidates[i].str + } + return result +} + +func ChoicesString(choices []string) string { + if len(choices) == 0 { + return "" + } + if len(choices) == 1 { + return choices[0] + } + if len(choices) == 2 { + return strings.Join(choices, " or ") + } + return fmt.Sprintf("%s or %s", strings.Join(choices[:len(choices)-1], ", "), choices[len(choices)-1]) +} \ No newline at end of file diff --git a/vendor/yunion.io/x/structarg/structarg.go b/vendor/yunion.io/x/structarg/structarg.go index 7eb514b3f0..5ff1cd22d4 100644 --- a/vendor/yunion.io/x/structarg/structarg.go +++ b/vendor/yunion.io/x/structarg/structarg.go @@ -459,7 +459,12 @@ func (this *SingleArgument) MetaVar() string { if len(this.metavar) > 0 { return this.metavar } else if len(this.choices) > 0 { - return fmt.Sprintf("{%s}", strings.Join(this.choices, ",")) + choices := this.choices + if len(choices) > 2 { + choices = choices[:2] + choices = append(choices, "...") + } + return fmt.Sprintf("{%s}", strings.Join(choices, ",")) } else { return strings.ToUpper(strings.Replace(this.Token(), "-", "_", -1)) } @@ -547,7 +552,18 @@ func (this *SingleArgument) InChoices(val string) bool { func (this *SingleArgument) SetValue(val string) error { if !this.InChoices(val) { - return fmt.Errorf("Unknown argument %s for %s%s", val, this.token, this.MetaVar()) + cands := FindSimilar(val, this.choices, -1, 0.5) + if len(cands) > 3 { + cands = cands[:3] + } + msg := fmt.Sprintf("Unknown argument '%s' for %s", val, this.token) //, this.MetaVar()) + if len(cands) > 0 { + for i := 0; i < len(cands); i += 1 { + cands[i] = fmt.Sprintf("'%s'", cands[i]) + } + msg = fmt.Sprintf("%s, do you mean %s?", msg, ChoicesString(cands)) + } + return fmt.Errorf(msg) } e := gotypes.SetValue(this.value, val) if e != nil { @@ -874,7 +890,7 @@ func (this *ArgumentParser) ParseArgs(args []string, ignore_unknown bool) error break } if arg.IsSubcommand() { - var subarg *SubcommandArgument = arg.(*SubcommandArgument) + subarg := arg.(*SubcommandArgument) var subparser = subarg.GetSubParser() err = subparser.ParseArgs(args[i+1:], ignore_unknown) break