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

Gcloud2 11662 add ai support #83

Merged
merged 1 commit into from
Oct 3, 2023
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
1,221 changes: 1,221 additions & 0 deletions client/ais/v1/ais/ias.go

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions client/ais/v1/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package client

import (
gcorecloud "github.com/G-Core/gcorelabscloud-go"
"github.com/G-Core/gcorelabscloud-go/client/common"

"github.com/urfave/cli/v2"
)

func NewAIClusterClientV1(c *cli.Context) (*gcorecloud.ServiceClient, error) {
return common.BuildClient(c, "ai/clusters", "v1")
}

func NewAIImageClientV1(c *cli.Context) (*gcorecloud.ServiceClient, error) {
return common.BuildClient(c, "ai/images", "v1")
}
func NewAIFlavorClientV1(c *cli.Context) (*gcorecloud.ServiceClient, error) {
return common.BuildClient(c, "ai/flavors", "v1")
}




12 changes: 12 additions & 0 deletions client/ais/v2/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package client

import (
gcorecloud "github.com/G-Core/gcorelabscloud-go"
"github.com/G-Core/gcorelabscloud-go/client/common"

"github.com/urfave/cli/v2"
)

func NewAIClusterClientV2(c *cli.Context) (*gcorecloud.ServiceClient, error) {
return common.BuildClient(c, "ai/clusters", "v2")
}
18 changes: 9 additions & 9 deletions client/instances/v1/instances/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func StringSliceToAppConfigSetOpts(slice []string) (map[string]interface{}, erro
return m, nil
}

func getUserData(c *cli.Context) (string, error) {
func GetUserData(c *cli.Context) (string, error) {
userData := ""
userDataFile := c.String("user-data-file")
userDataContent := c.String("user-data")
Expand All @@ -126,7 +126,7 @@ func getUserData(c *cli.Context) (string, error) {
return userData, nil
}

func getInstanceVolumes(c *cli.Context) ([]instances.CreateVolumeOpts, error) {
func GetInstanceVolumes(c *cli.Context) ([]instances.CreateVolumeOpts, error) {
volumeSources := utils.GetEnumStringSliceValue(c, "volume-source")
volumeTypes := utils.GetEnumStringSliceValue(c, "volume-type")
volumeBootIndexes := c.IntSlice("volume-boot-index")
Expand Down Expand Up @@ -198,7 +198,7 @@ func getInstanceVolumes(c *cli.Context) ([]instances.CreateVolumeOpts, error) {

}

func getInterfaces(c *cli.Context) ([]instances.InterfaceInstanceCreateOpts, error) {
func GetInterfaces(c *cli.Context) ([]instances.InterfaceInstanceCreateOpts, error) {
interfaceTypes := utils.GetEnumStringSliceValue(c, "interface-type")
interfaceNetworkIDs := c.StringSlice("interface-network-id")
interfaceSubnetIDs := c.StringSlice("interface-subnet-id")
Expand Down Expand Up @@ -284,7 +284,7 @@ func getBaremetalInterfaces(c *cli.Context) ([]bminstances.InterfaceOpts, error)

}

func getSecurityGroups(c *cli.Context) []gcorecloud.ItemID {
func GetSecurityGroups(c *cli.Context) []gcorecloud.ItemID {
securityGroups := c.StringSlice("security-group")
res := make([]gcorecloud.ItemID, len(securityGroups))
for i, s := range securityGroups {
Expand Down Expand Up @@ -681,26 +681,26 @@ var instanceCreateCommandV2 = cli.Command{
return cli.NewExitError(err, 1)
}

userData, err := getUserData(c)
userData, err := GetUserData(c)
if err != nil {
_ = cli.ShowCommandHelp(c, "create")
return cli.NewExitError(err, 1)
}

instanceVolumes, err := getInstanceVolumes(c)
instanceVolumes, err := GetInstanceVolumes(c)
if err != nil {
_ = cli.ShowCommandHelp(c, "create")
return cli.NewExitError(err, 1)
}

// todo add security group mapping
instanceInterfaces, err := getInterfaces(c)
instanceInterfaces, err := GetInterfaces(c)
if err != nil {
_ = cli.ShowCommandHelp(c, "create")
return cli.NewExitError(err, 1)
}

securityGroups := getSecurityGroups(c)
securityGroups := GetSecurityGroups(c)

metadata, err := StringSliceToMetadataSetOpts(c.StringSlice("metadata"))
if err != nil {
Expand Down Expand Up @@ -1290,7 +1290,7 @@ var instanceCreateBaremetalCommand = cli.Command{
return cli.NewExitError(err, 1)
}

userData, err := getUserData(c)
userData, err := GetUserData(c)
if err != nil {
_ = cli.ShowCommandHelp(c, "create_baremetal")
return cli.NewExitError(err, 1)
Expand Down
1 change: 0 additions & 1 deletion client/loadbalancers/v1/listeners/lsiteners.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ var listenerCreateSubCommand = cli.Command{
AllowedCIDRS: c.StringSlice("allowed-cidrs") ,
}


results, err := listeners.Create(client, opts).Extract()
if err != nil {
return cli.NewExitError(err, 1)
Expand Down
2 changes: 2 additions & 0 deletions cmd/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/G-Core/gcorelabscloud-go/client/apitokens/v1/apitokens"
"github.com/G-Core/gcorelabscloud-go/client/apptemplates/v1/apptemplates"
"github.com/G-Core/gcorelabscloud-go/client/file_shares/v1/file_shares"
"github.com/G-Core/gcorelabscloud-go/client/ais/v1/ais"
"github.com/G-Core/gcorelabscloud-go/client/flags"
"github.com/G-Core/gcorelabscloud-go/client/flavors/v1/flavors"
"github.com/G-Core/gcorelabscloud-go/client/floatingips/v1/floatingips"
Expand Down Expand Up @@ -75,6 +76,7 @@ var commands = []*cli.Command{
&apptemplates.Commands,
&apitokens.Commands,
&file_shares.Commands,
&ais.Commands,
}

type clientCommands struct {
Expand Down
51 changes: 51 additions & 0 deletions gcore/ai/v1/aiflavors/requests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package aiflavors

import (
gcorecloud "github.com/G-Core/gcorelabscloud-go"
"github.com/G-Core/gcorelabscloud-go/pagination"
)


type ListOptsBuilder interface {
ToAIFlavorListQuery() (string, error)
}

type AIFlavorListOpts struct {
Disabled bool `q:"disabled"`
IncludeCapacity bool `q:"include_capacity"`
IncludePrices bool `q:"include_prices"`
}

// ToAIFlavorListQuery formats a AIFlavorListOpts into a query string.
func (opts AIFlavorListOpts) ToAIFlavorListQuery() (string, error) {
q, err := gcorecloud.BuildQueryString(opts)
if err != nil {
return "", err
}
return q.String(), err
}


// List retrieves list of AI flavors
func List(c *gcorecloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
url := listAIFlavorsURL(c)
if opts != nil {
query, err := opts.ToAIFlavorListQuery()
if err != nil {
return pagination.Pager{Err: err}
}
url += query
}
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
return AIFlavorPage{pagination.LinkedPageBase{PageResult: r}}
})
}

// ListAll retrieves list of all AI flavors
func ListAll(c *gcorecloud.ServiceClient, opts ListOptsBuilder) ([]AIFlavor, error) {
results, err := List(c, opts).AllPages()
if err != nil {
return nil, err
}
return ExtractAIFlavors(results)
}
91 changes: 91 additions & 0 deletions gcore/ai/v1/aiflavors/results.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package aiflavors

import (
gcorecloud "github.com/G-Core/gcorelabscloud-go"
"github.com/G-Core/gcorelabscloud-go/pagination"
"github.com/shopspring/decimal"
)

type commonResult struct {
gcorecloud.Result
}

// Extract is a function that accepts a result and extracts a instance resource.
func (r commonResult) Extract() (*AIFlavor, error) {
var s AIFlavor
err := r.ExtractInto(&s)
return &s, err
}

func (r commonResult) ExtractInto(v interface{}) error {
return r.Result.ExtractIntoStructPtr(v, "")
}

// AIFlavorPage is the page returned by a pager when traversing over a
// collection of instances.
type AIFlavorPage struct {
pagination.LinkedPageBase
}

// NextPageURL is invoked when a paginated collection of flavors has reached
// the end of a page and the pager seeks to traverse over a new one. In order
// to do this, it needs to construct the next page's URL.
func (r AIFlavorPage) NextPageURL() (string, error) {
var s struct {
Links []gcorecloud.Link `json:"links"`
}
err := r.ExtractInto(&s)
if err != nil {
return "", err
}
return gcorecloud.ExtractNextURL(s.Links)
}

// IsEmpty checks whether a FlavorPage struct is empty.
func (r AIFlavorPage) IsEmpty() (bool, error) {
is, err := ExtractAIFlavors(r)
return len(is) == 0, err
}

// ExtractFlavor accepts a Page struct, specifically a FlavorPage struct,
// and extracts the elements into a slice of Flavor structs. In other words,
// a generic collection is mapped into a relevant slice.
func ExtractAIFlavors(r pagination.Page) ([]AIFlavor, error) {
var s []AIFlavor
err := ExtractAIFlavorsInto(r, &s)
return s, err
}

func ExtractAIFlavorsInto(r pagination.Page, v interface{}) error {
return r.(AIFlavorPage).Result.ExtractIntoSlicePtr(v, "results")
}


type HardwareDescription struct {
CPU string `json:"cpu,omitempty"`
Disk string `json:"disk,omitempty"`
Network string `json:"network,omitempty"`
RAM string `json:"ram,omitempty"`
Ephemeral string `json:"ephemeral,omitempty"`
GPU string `json:"gpu,omitempty"`
IPU string `json:"ipu,omitempty"`
PoplarCount string `json:"poplar_count,omitempty"`
SGXEPCSize string `json:"sgx_epc_size,omitempty"`
}

// Flavor represents a flavor structure.
type AIFlavor struct {
FlavorID string `json:"flavor_id"`
FlavorName string `json:"flavor_name"`
Disabled bool `json:"disabled"`
ResourceClass string `json:"resource_class"`
PriceStatus *string `json:"price_status,omitempty"`
CurrencyCode *gcorecloud.Currency `json:"currency_code,omitempty"`
PricePerHour *decimal.Decimal `json:"price_per_hour,omitempty"`
PricePerMonth *decimal.Decimal `json:"price_per_month,omitempty"`
HardwareDescription *HardwareDescription `json:"hardware_description,omitempty"`
RAM *int `json:"ram,omitempty"`
VCPUS *int `json:"vcpus,omitempty"`
Capacity *int `json:"capacity,omitempty"`
}

1 change: 1 addition & 0 deletions gcore/ai/v1/aiflavors/testing/docs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package testing
39 changes: 39 additions & 0 deletions gcore/ai/v1/aiflavors/testing/fixtures.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package testing

import (
"github.com/G-Core/gcorelabscloud-go/gcore/ai/v1/aiflavors"
)

const ListResponse = `
{
"count": 1,
"results": [
{
"resource_class": "bm1-ai-small",
"hardware_description": {
"network": "2x100G",
"ipu": "vPOD-16 (Classic)",
"poplar_count": "2"
},
"disabled": false,
"flavor_name": "bm1-ai-2xsmall-v1pod-16",
"flavor_id": "bm1-ai-2xsmall-v1pod-16"
}
]
}
`

var (
AIFlavor1 = aiflavors.AIFlavor{
FlavorID: "bm1-ai-2xsmall-v1pod-16",
FlavorName: "bm1-ai-2xsmall-v1pod-16",
Disabled: false,
ResourceClass: "bm1-ai-small",
HardwareDescription: &aiflavors.HardwareDescription{
Network: "2x100G",
IPU: "vPOD-16 (Classic)",
PoplarCount: "2",
},
}
ExpectedAIFlavorSlice = []aiflavors.AIFlavor{AIFlavor1}
)
59 changes: 59 additions & 0 deletions gcore/ai/v1/aiflavors/testing/requests_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package testing

import (
"fmt"
"net/http"
"testing"

"github.com/G-Core/gcorelabscloud-go/gcore/ai/v1/aiflavors"
"github.com/G-Core/gcorelabscloud-go/pagination"
th "github.com/G-Core/gcorelabscloud-go/testhelper"
fake "github.com/G-Core/gcorelabscloud-go/testhelper/client"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
)

func prepareListTestURLParams(version string, projectID int, regionID int) string {
return fmt.Sprintf("/%s/ai/flavors/%d/%d", version, projectID, regionID)
}

func prepareListTestURL() string {
return prepareListTestURLParams("v1", fake.ProjectID, fake.RegionID)
}

func TestList(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()

th.Mux.HandleFunc(prepareListTestURL(), func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "Authorization", fmt.Sprintf("Bearer %s", fake.AccessToken))
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, err := fmt.Fprint(w, ListResponse)
if err != nil {
log.Error(err)
}
})

client := fake.ServiceTokenClient("ai/flavors", "v1")
count := 0

opts := aiflavors.AIFlavorListOpts{}

err := aiflavors.List(client, opts).EachPage(func(page pagination.Page) (bool, error) {
count++
actual, err := aiflavors.ExtractAIFlavors(page)
require.NoError(t, err)
ct := actual[0]
require.Equal(t, AIFlavor1, ct)
require.Equal(t, ExpectedAIFlavorSlice, actual)
return true, nil
})

th.AssertNoErr(t, err)

if count != 1 {
t.Errorf("Expected 1 page, got %d", count)
}
}
9 changes: 9 additions & 0 deletions gcore/ai/v1/aiflavors/urls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package aiflavors

import (
gcorecloud "github.com/G-Core/gcorelabscloud-go"
)

func listAIFlavorsURL(c *gcorecloud.ServiceClient) string {
return c.ServiceURL()
}
Loading
Loading