Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial code for mcis and vm plan with location-based algo #511

Merged
merged 2 commits into from
May 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/api/rest/server/mcis/plan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package mcis

import (
"net/http"

"github.com/cloud-barista/cb-tumblebug/src/core/common"
"github.com/cloud-barista/cb-tumblebug/src/core/mcis"
"github.com/labstack/echo/v4"
)

// RestRecommendVm godoc
// @Summary RestRecommendVm specs by range
// @Description RestRecommendVm specs by range
// @Tags [MCIS] Provisioning management
// @Accept json
// @Produce json
// @Param nsId path string true "Namespace ID"
// @Param deploymentPlan body mcis.DeploymentPlan false "RestRecommendVm for range-filtering specs"
// @Success 200 {object} []mcir.TbSpecInfo
// @Failure 404 {object} common.SimpleMsg
// @Failure 500 {object} common.SimpleMsg
// @Router /ns/{nsId}/testRecommendVm [post]
func RestRecommendVm(c echo.Context) error {

nsId := c.Param("nsId")

u := &mcis.DeploymentPlan{}
if err := c.Bind(u); err != nil {
return err
}

content, err := mcis.RecommendVm(nsId, *u)

if err != nil {
common.CBLog.Error(err)
return c.JSONBlob(http.StatusNotFound, []byte(err.Error()))
}

// result := RestFilterSpecsResponse{}
// result.Spec = content
return c.JSON(http.StatusOK, &content)
}
3 changes: 3 additions & 0 deletions src/api/rest/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ func ApiServer() {
//g.DELETE("/:nsId/mcis/:mcisId/vm", rest_mcis.RestDelAllMcisVm)

g.POST("/:nsId/mcis/recommend", rest_mcis.RestPostMcisRecommand)

g.POST("/:nsId/testRecommendVm", rest_mcis.RestRecommendVm)

g.POST("/:nsId/cmd/mcis/:mcisId", rest_mcis.RestPostCmdMcis)
g.POST("/:nsId/cmd/mcis/:mcisId/vm/:vmId", rest_mcis.RestPostCmdMcisVm)
g.POST("/:nsId/install/mcis/:mcisId", rest_mcis.RestPostInstallAgentToMcis)
Expand Down
23 changes: 18 additions & 5 deletions src/core/mcis/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -4103,10 +4103,23 @@ func GetVmTemplate(nsId string, mcisId string, algo string) (TbVmInfo, error) {

}

// GetCloudLocation. (need error handling)
func GetCloudLocation(cloudType string, nativeRegion string) GeoLocation {

location := GeoLocation{}

if cloudType == "" || nativeRegion == "" {

// need error handling instead of assigning default value
location.CloudType = "ufc"
location.NativeRegion = "ufc"
location.BriefAddr = "South Korea (Seoul)"
location.Latitude = "37.4767"
location.Longitude = "126.8841"

return location
}

key := "/cloudtype/" + cloudType + "/region/" + nativeRegion

fmt.Printf("[GetCloudLocation] KEY: %+v\n", key)
Expand Down Expand Up @@ -4146,11 +4159,11 @@ func GetCloudLocation(cloudType string, nativeRegion string) GeoLocation {
}
fmt.Println()
}
}
keyValue, err = common.CBStore.Get(key)
if err != nil {
common.CBLog.Error(err)
return location
keyValue, err = common.CBStore.Get(key)
if err != nil {
common.CBLog.Error(err)
return location
}
}

if keyValue != nil {
Expand Down
286 changes: 286 additions & 0 deletions src/core/mcis/plan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
package mcis

import (
"fmt"
"math"
"sort"
"strconv"
"strings"

"github.com/cloud-barista/cb-tumblebug/src/core/common"
"github.com/cloud-barista/cb-tumblebug/src/core/mcir"
)

// DeploymentPlan is struct for .
type DeploymentPlan struct {
Filter FilterInfo `json:"filter"`
Priority PriorityInfo `json:"priority"`
Limit string `json:"limit"`
}

// FilterInfo is struct for .
type FilterInfo struct {
Policy []FilterCondition `json:"policy"`
}

// FilterCondition is struct for .
type FilterCondition struct {
Metric string `json:"metric"`
Condition []Operation `json:"condition"`
}

// Operation is struct for .
type Operation struct {
Operator string `json:"operator" ` // <, <=, >, >=, ...
Operand string `json:"operand"` // 10, 70, 80, 98, ...
}

// PriorityInfo is struct for .
type PriorityInfo struct {
Policy []PriorityCondition `json:"policy"`
}

// FilterCondition is struct for .
type PriorityCondition struct {
Metric string `json:"metric"` // location
Weight string `json:"weight"` // 0.3
Parameter []ParameterKeyVal `json:"parameter"`
}

// Operation is struct for .
type ParameterKeyVal struct {
Key string `json:"key" ` // coordinate
Val []string `json:"val"` // [{Latitude,Longitude},{12,543},{66,33},{31,433}]
}

///

//// Info manage for MCIS recommendation
func RecommendVm(nsId string, plan DeploymentPlan) ([]mcir.TbSpecInfo, error) {

fmt.Println("RecommendVm")

// Filtering first

u := &mcir.FilterSpecsByRangeRequest{}

// veryLargeValue := float32(math.MaxFloat32)
// verySmallValue := float32(0)

// Filtering
fmt.Println("[Filtering specs]")

for _, v := range plan.Filter.Policy {
metric := v.Metric
conditions := v.Condition
for _, condition := range conditions {

operand64, err := strconv.ParseFloat(condition.Operand, 32)
operand := float32(operand64)
if err != nil {
common.CBLog.Error(err)
return []mcir.TbSpecInfo{}, err
}

switch condition.Operator {
case "<=":

switch metric {
case "num_vCPU":
u.Num_vCPU.Max = operand
case "mem_GiB":
u.Mem_GiB.Max = operand
case "Cost_per_hour":
u.Cost_per_hour.Max = operand
default:
fmt.Println("[Checking] Not available metric " + metric)
}

case ">=":

switch metric {
case "num_vCPU":
u.Num_vCPU.Min = operand
case "mem_GiB":
u.Mem_GiB.Min = operand
case "Cost_per_hour":
u.Cost_per_hour.Min = operand
default:
fmt.Println("[Checking] Not available metric " + metric)
}

case "==":

switch metric {
case "num_vCPU":
u.Num_vCPU.Max = operand
u.Num_vCPU.Min = operand
case "mem_GiB":
u.Mem_GiB.Max = operand
u.Mem_GiB.Min = operand
case "Cost_per_hour":
u.Cost_per_hour.Max = operand
u.Cost_per_hour.Min = operand
default:
fmt.Println("[Checking] Not available metric " + metric)
}
}
}
}

filteredSpecs, err := mcir.FilterSpecsByRange(nsId, *u)

if err != nil {
common.CBLog.Error(err)
return []mcir.TbSpecInfo{}, err
}

// Prioritizing
fmt.Println("[Prioritizing specs]")
prioritySpecs := []mcir.TbSpecInfo{}

for _, v := range plan.Priority.Policy {
metric := v.Metric

switch metric {
case "location":
prioritySpecs, err = RecommendVmLocation(nsId, &filteredSpecs, &v.Parameter)
case "latency":
//
case "cost":
//
default:
// fmt.Println("[Checking] Not available metric " + metric)
}

}

return prioritySpecs, nil

}

// RecommendVmLocation func prioritize specs based on given location
func RecommendVmLocation(nsId string, specList *[]mcir.TbSpecInfo, param *[]ParameterKeyVal) ([]mcir.TbSpecInfo, error) {

result := []mcir.TbSpecInfo{}

for _, v := range *param {

switch v.Key {
case "coordinateClose":
//
coordinateStr := v.Val[0]

slice := strings.Split(coordinateStr, ",")
latitude, err := strconv.ParseFloat(slice[0], 32)
if err != nil {
common.CBLog.Error(err)
return []mcir.TbSpecInfo{}, err
}
longitude, err := strconv.ParseFloat(slice[1], 32)
if err != nil {
common.CBLog.Error(err)
return []mcir.TbSpecInfo{}, err
}

type distanceType struct {
distance float64
index int
priorityIndex int
}
distances := []distanceType{}

for i := range *specList {
distances = append(distances, distanceType{})
distances[i].distance, err = getDistance(latitude, longitude, (*specList)[i].ConnectionName)
if err != nil {
common.CBLog.Error(err)
return []mcir.TbSpecInfo{}, err
}
distances[i].index = i
}

sort.Slice(distances, func(i, j int) bool {
return distances[i].distance < distances[j].distance
})
fmt.Printf("\n distances : %v \n", distances)

priorityCnt := 1
for i := range distances {

// priorityIndex++ if two distances are not equal (give the same priorityIndex if two variables are same)
if i != 0 {
if distances[i].distance > distances[i-1].distance {
priorityCnt++
}
}
distances[i].priorityIndex = priorityCnt

}

for i := range *specList {
// update OrderInFilteredResult based on calculated priorityIndex
(*specList)[distances[i].index].OrderInFilteredResult = uint16(distances[i].priorityIndex)
// assign nomalized priorityIdex value to EvaluationScore_01
(*specList)[distances[i].index].EvaluationScore_01 = float32(1 - (float32(distances[i].priorityIndex) / float32(len(*specList))))
(*specList)[distances[i].index].EvaluationScore_02 = float32(distances[i].distance)
}
fmt.Printf("\n distances : %v \n", distances)

//fmt.Printf("\n distances : %v \n", *specList)

case "coordinateWithin":
//
case "coordinateFair":
//
default:
// fmt.Println("[Checking] Not available metric " + metric)
}

}

for i := range *specList {
result = append(result, (*specList)[i])
//result[i].OrderInFilteredResult = uint16(i + 1)
}

sort.Slice(result, func(i, j int) bool {
return result[i].OrderInFilteredResult < result[j].OrderInFilteredResult
})
fmt.Printf("\n result : %v \n", result)

// updatedSpec, err := mcir.UpdateSpec(nsId, *result)
// content, err = mcir.SortSpecs(*specList, "mem_GiB", "descending")
return result, nil
}

// getDistance func get geographical distance between given coordinate and connectionConfig
func getDistance(latitude float64, longitude float64, ConnectionName string) (float64, error) {
configTmp, _ := common.GetConnConfig(ConnectionName)
regionTmp, _ := common.GetRegionInfo(configTmp.RegionName)

nativeRegion := ""
for _, v := range regionTmp.KeyValueInfoList {
if strings.ToLower(v.Key) == "region" || strings.ToLower(v.Key) == "location" {
nativeRegion = v.Value
break
}
}
Location := GetCloudLocation(strings.ToLower(configTmp.ProviderName), strings.ToLower(nativeRegion))

cloudLatitude, err := strconv.ParseFloat(Location.Latitude, 32)
if err != nil {
common.CBLog.Error(err)
return 0, err
}
cloudLongitude, err := strconv.ParseFloat(Location.Longitude, 32)
if err != nil {
common.CBLog.Error(err)
return 0, err
}

first := math.Pow(float64(cloudLatitude-latitude), 2)
second := math.Pow(float64(cloudLongitude-longitude), 2)
return math.Sqrt(first + second), nil

}
Loading