Skip to main content

Overview

The CloudTower SDK in the Golang environment is applicable to golang 1.16 or above.

Installation

go get github.com/smartxworks/cloudtower-go-sdk

Use

The sample uses two tool libraries: pointy and go-funk. Pointy is used to quickly create a pointer of a primitive type, and go-funk provides some tool methods, such as Map, Filter, and Reduce.

Create Instance

Create ApiClient Instance

import (
apiclient "github.com/smartxworks/cloudtower-go-sdk/client"
httptransport "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
)
transport := httptransport.New("192.168.36.133", "/v2/api", []string{"http"})
client := apiclient.New(transport, strfmt.Default)

if https connection is required,cert should be installed,or skip verify cert

import (
apiclient "github.com/smartxworks/cloudtower-go-sdk/v2/client"
httptransport "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
)
tlsClient, err := httptransport.TLSClient(httptransport.TLSClientOptions{
InsecureSkipVerify: true,
})
if err != nil {
fmt.Print(err)
return
}
transport := httptransport.NewWithClient("192.168.29.157", "/v2/api", []string{"https"}, tlsClient)
client := apiclient.New(transport, strfmt.Default)

Send Request

Import Corresponding client Package

Import or create the client package corresponding to a specific operation.

import (
vm "github.com/smartxworks/cloudtower-go-sdk/client/vm"
)

Verify Authentication

import (
User "github.com/smartxworks/cloudtower-go-sdk/client/user"
)
loginParams := User.NewLoginParams()
loginParams.RequestBody = &models.LoginInput{
Username: pointy.String("username"),
Password: pointy.String("password"),
Source: models.NewUserSource(models.UserSourceLOCAL),
}
logRes, err := client.User.Login(loginParams)
if err != nil {
return err
}
transport.DefaultAuthentication = httptransport.APIKeyAuth("Authorization", "header", *logRes.Payload.Data.Token)

Obtain Resources

getVmParams := vm.NewGetVmsParams();
getVmParams.RequestBody = &models.GetVmsRequestBody{
Where: &models.VMWhereInput{
ID: pointy.String("vm_id"),
},
}
vmsRes, err := client.VM.GetVms(getVmParams)
if err != nil {
return err
}
vms := vmsRes.Payload

Update Resources

Updating resources will have the asynchronous task. Resources will be updated when the asynchronous task is done.

target_vm := vmsRes.Payload[0]
vmStartParams := vm.NewStartVMParams()
vmStartParams.RequestBody = &models.VMStartParams{
Where: &models.VMWhereInput{
ID: target_vm.ID,
},
}
startRes, err := client.VM.StartVM(vmStartParams)
if err != nil {
return err
}

You can use the provided tool WaitTask to synchronously wait for the asynchronous task to end. If the task fails or times out, an error will be returned. The polling interval is 5 seconds, and the timeout period is 300 seconds.

  • Tool Parameter Description
ParameterTypeMust HaveDescription
client*client.CloudtowerYesCheck the client instance.
idstringYesThe ID of the task you want to check.
task := *startRes.Payload[0].TaskID
err = utils.WaitTask(client, task)
if err != nil {
return err
}

In the case of multiple tasks, multiple task ids can be accepted via WaitTasks. The rest is the same as WaitTask.

  • Tool Parameter Description
ParameterTypeMust HaveDescription
client*client.CloudtowerYesCheck the client instance.
ids[]stringYesThe ID list of the task you want to check.
tasks := funk.Map(startRes.Payload, func(tvm *models.WithTaskVM) string {
return *tvm.TaskID
}).([]string)
err = utils.WaitTasks(client, tasks)
if err != nil {
return err
}

Others

Set Language of Returned Information

You can set the ContentLanguage item in the request params to set the language of the return value. The optional value is ["en-US", "zh-CN"], and the default value is en-US. Languages not in the range of optional values will return an HTTP 400 error.

getTaskDefaultParams := task.NewGetTasksParams()
getTaskDefaultParams.RequestBody = &models.GetTasksRequestBody{
First: pointy.Int32(10),
}
taskDefaultRes, err := client.Task.GetTasks(getTaskDefaultParams)

getTaskZhParams := task.NewGetTasksParams()
getTaskZhParams.RequestBody = &models.GetTasksRequestBody{
First: pointy.Int32(10),
}
getTaskZhParams.ContentLanguage = pointy.String("zh-CN")
taskZhRes, err := client.Task.GetTasks(getTaskZhParams)

Scenario Examples

Virtual Machine Backup

package main

import (
"fmt"

apiclient "github.com/smartxworks/cloudtower-go-sdk/client"
"github.com/smartxworks/cloudtower-go-sdk/client/iscsi_lun_snapshot"
"github.com/smartxworks/cloudtower-go-sdk/client/user"
"github.com/smartxworks/cloudtower-go-sdk/client/vm"
"github.com/smartxworks/cloudtower-go-sdk/client/vm_snapshot"
"github.com/smartxworks/cloudtower-go-sdk/models"
"github.com/smartxworks/cloudtower-go-sdk/utils"
"github.com/openlyinc/pointy"
"github.com/thoas/go-funk"

httptransport "github.com/go-openapi/runtime/client"

"github.com/go-openapi/strfmt"
)

func create_vm_snapshot(
client *apiclient.Cloudtower,
targetVmName string,
targetSnapshotName string,
consistentType models.ConsistentType) (*models.VMSnapshot, []*models.IscsiLunSnapshot, error) {
getVmParams := vm.NewGetVmsParams()
getVmParams.RequestBody = &models.GetVmsRequestBody{
Where: &models.VMWhereInput{
Name: &targetVmName,
},
First: pointy.Int32(1),
}
getVmRes, err := client.VM.GetVms(getVmParams)
if err != nil {
return nil, nil, err
}
targetVm := getVmRes.Payload[0]
vmToolStatus := *targetVm.VMToolsStatus
if vmToolStatus != models.VMToolsStatusRUNNING && consistentType == models.ConsistentTypeFILESYSTEMCONSISTENT {
consistentType = models.ConsistentTypeCRASHCONSISTENT
}
createSnapshotParams := vm_snapshot.NewCreateVMSnapshotParams()
createSnapshotParams.RequestBody = &models.VMSnapshotCreationParams{
Data: []*models.VMSnapshotCreationParamsDataItems0{
{
VMID: targetVm.ID,
Name: &targetSnapshotName,
ConsistentType: consistentType.Pointer(),
},
},
}
createRes, err := client.VMSnapshot.CreateVMSnapshot(createSnapshotParams)
if err != nil {
return nil, nil, err
}
withTaskSnapshot := createRes.Payload[0]
err = utils.WaitTask(client, withTaskSnapshot.TaskID)
if err != nil {
return nil, nil, err
}
getSnapshotParams := vm_snapshot.NewGetVMSnapshotsParams()
getSnapshotParams.RequestBody = &models.GetVMSnapshotsRequestBody{
Where: &models.VMSnapshotWhereInput{
ID: withTaskSnapshot.Data.ID,
},
}
getSnapshotRes, err := client.VMSnapshot.GetVMSnapshots(getSnapshotParams)
if err != nil {
return nil, nil, err
}
createdSnapshot := getSnapshotRes.Payload[0]

lunSnapshotIds := funk.Map(funk.Filter(createdSnapshot.VMDisks, func(disk *models.NestedFrozenDisks) bool {
return *disk.Type == models.VMDiskTypeDISK
}), func(disk *models.NestedFrozenDisks) string {
return *disk.SnapshotLocalID
}).([]string)
getLunSnapshotParams := iscsi_lun_snapshot.NewGetIscsiLunSnapshotsParams()
getLunSnapshotParams.RequestBody = &models.GetIscsiLunSnapshotsRequestBody{
Where: &models.IscsiLunSnapshotWhereInput{
NameIn: lunSnapshotIds,
},
}
getLunSnapshotRes, err := client.IscsiLunSnapshot.GetIscsiLunSnapshots(getLunSnapshotParams)
if err != nil {
return nil, nil, err
}
return createdSnapshot, getLunSnapshotRes.Payload, nil
}

Build Dashboard

Define Tool Library

import (
"fmt"

apiclient "github.com/smartxworks/cloudtower-go-sdk/client"
"github.com/smartxworks/cloudtower-go-sdk/client/alert"
"github.com/smartxworks/cloudtower-go-sdk/client/cluster"
"github.com/smartxworks/cloudtower-go-sdk/client/disk"
"github.com/smartxworks/cloudtower-go-sdk/client/host"
"github.com/smartxworks/cloudtower-go-sdk/client/user"
"github.com/smartxworks/cloudtower-go-sdk/models"
"github.com/openlyinc/pointy"
"github.com/thoas/go-funk"

httptransport "github.com/go-openapi/runtime/client"

"github.com/go-openapi/strfmt"
)

var ByteUnits []string = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB"}
var HzUnits []string = []string{"Hz", "KHz", "MHz", "GHz", "THz"}

func formatUnit(base float64, units []string, step int32) string {
length := len(units)
if length == 0 {
panic("No unit provided")
}
if base <= 0 {
return fmt.Sprintf("0%s", units[0])
}
for i, unit := range units {
if base < float64(step) || i == length-1 {
return fmt.Sprintf("%0.2f%s", base, unit)
}
base = base / float64(step)
}
return fmt.Sprintf("%0.2f%s", base, units[length-1])
}

Build Alert Information

type AlertInfo struct {
Critical []*models.Alert
Notice []*models.Alert
Info []*models.Alert
}

func NewAlertInfo(critial []*models.Alert, notice []*models.Alert, info []*models.Alert) *AlertInfo {
return &AlertInfo{
Critical: critial,
Notice: notice,
Info: info,
}
}

func buildAlertInfo(client *apiclient.Cloudtower, clusterIds []string) (*AlertInfo, error) {
getAlertParams := alert.NewGetAlertsParams()
if len(clusterIds) == 0 {
getAlertParams.RequestBody = &models.GetAlertsRequestBody{
Where: &models.AlertWhereInput{
Ended: pointy.Bool(false),
},
}
} else {
getAlertParams.RequestBody = &models.GetAlertsRequestBody{
Where: &models.AlertWhereInput{
Ended: pointy.Bool(false),
Cluster: &models.ClusterWhereInput{
IDIn: clusterIds,
},
},
}
}
res, err := client.Alert.GetAlerts(getAlertParams)
if err != nil {
return nil, err
}
var critial []*models.Alert = []*models.Alert{}
var notice []*models.Alert = []*models.Alert{}
var info []*models.Alert = []*models.Alert{}
for _, alert := range res.Payload {
switch *alert.Severity {
case "CRITICAL":
critial = append(critial, alert)
case "NOTICE":
notice = append(notice, alert)
case "INFO":
info = append(info, alert)
}
}
return NewAlertInfo(critial, notice, info), nil
}

Build Hard Disk Information

Here is an example of a mechanical hard drive.

type DiskInfo struct {
Healthy int32
Warning int32
Error int32
Total int32
}

func NewDiskInfo() *DiskInfo {
return &DiskInfo{
Healthy: 0,
Warning: 0,
Error: 0,
Total: 0,
}
}

func buildHddInfo(client *apiclient.Cloudtower, clusterIds []string) (*DiskInfo, error) {
getDiskParams := disk.NewGetDisksParams()
if len(clusterIds) == 0 {
getDiskParams.RequestBody = &models.GetDisksRequestBody{}
} else {
getDiskParams.RequestBody = &models.GetDisksRequestBody{
Where: &models.DiskWhereInput{
Host: &models.HostWhereInput{
Cluster: &models.ClusterWhereInput{
IDIn: clusterIds,
},
},
},
}
}
res, err := client.Disk.GetDisks(getDiskParams)
if err != nil {
return nil, err
}
hddInfo := NewDiskInfo()
for _, disk := range res.Payload {
if *disk.Type == models.DiskTypeHDD {
if funk.Contains(
[]models.DiskHealthStatus{
models.DiskHealthStatusHEALTHY,
models.DiskHealthStatusSUBHEALTHY,
models.DiskHealthStatusSMARTFAILED,
},
*disk.HealthStatus,
) {
hddInfo.Error++
} else if funk.Contains(
[]models.DiskUsageStatus{
models.DiskUsageStatusUNMOUNTED,
models.DiskUsageStatusPARTIALMOUNTED,
},
*disk.UsageStatus,
) {
hddInfo.Warning++
} else {
hddInfo.Healthy++
}
hddInfo.Total++
}
}
return hddInfo, nil
}

Build Performance Metrics

Get the number of CPU cores, total CPU frequency, CPU usage, total memory, memory usage, total storage resources, used storage resources, invalid storage resources, and available storage resources of the specified cluster.

type CpuInfo struct {
TotalCore uint32
TotalInHz uint64
Total string
UsedInHz uint64
Used string
Usage string
}

func NewCpuInfo() *CpuInfo {
return &CpuInfo{
TotalCore: 0,
TotalInHz: 0,
UsedInHz: 0,
}
}

func (info *CpuInfo) compute() *CpuInfo {
info.Total = formatUnit(float64(info.TotalInHz), HzUnits, 1000)
info.Used = formatUnit(float64(info.UsedInHz), HzUnits, 1000)
info.Usage = fmt.Sprintf("%0.2f%%", float64(info.UsedInHz)/float64(info.TotalInHz))
return info
}

type MemoryInfo struct {
TotalInByte uint64
Total string
UsedInByte uint64
Used string
Usage string
}

func NewMemoryInfo() *MemoryInfo {
return &MemoryInfo{
TotalInByte: 0,
UsedInByte: 0,
}
}

func (info *MemoryInfo) compute() *MemoryInfo {
info.Total = formatUnit(float64(info.TotalInByte), ByteUnits, 1024)
info.Used = formatUnit(float64(info.UsedInByte), ByteUnits, 1024)
info.Usage = fmt.Sprintf("%0.2f%%", float64(info.UsedInByte)/float64(info.TotalInByte))
return info
}

type StorageInfo struct {
TotalInByte uint64
Total string
UsedInByte uint64
Used string
InvalidInByte uint64
Invalid string
AvailableInByte uint64
Available string
}

func NewStorageInfo() *StorageInfo {
return &StorageInfo{
TotalInByte: 0,
UsedInByte: 0,
InvalidInByte: 0,
AvailableInByte: 0,
}
}

func (info *StorageInfo) compute() *StorageInfo {
info.AvailableInByte = info.TotalInByte - info.UsedInByte - info.InvalidInByte
info.Total = formatUnit(float64(info.TotalInByte), ByteUnits, 1024)
info.Used = formatUnit(float64(info.UsedInByte), ByteUnits, 1024)
info.Invalid = formatUnit(float64(info.InvalidInByte), ByteUnits, 1024)
info.Available = formatUnit(float64(info.AvailableInByte), ByteUnits, 1024)
return info
}

type MetricInfo struct {
Storage *StorageInfo
Memory *MemoryInfo
Cpu *CpuInfo
}

func buildMetricsInfo(client *apiclient.Cloudtower, clusters []*models.Cluster, clusterIds []string) (*MetricInfo, error) {
memory := NewMemoryInfo()
storage := NewStorageInfo()
cpu := NewCpuInfo()
getHostParams := host.NewGetHostsParams()
if len(clusterIds) == 0 {
getHostParams.RequestBody = &models.GetHostsRequestBody{}
} else {
getHostParams.RequestBody = &models.GetHostsRequestBody{
Where: &models.HostWhereInput{
Cluster: &models.ClusterWhereInput{
IDIn: clusterIds,
},
},
}
}
hosts, err := client.Host.GetHosts(getHostParams)
if err != nil {
return nil, err
}
clusterIdMap := make(map[string]*models.Cluster)
for _, cluster := range clusters {
if _, ok := clusterIdMap[*cluster.ID]; !ok {
clusterIdMap[*cluster.ID] = cluster
}
if *cluster.Type == models.ClusterTypeSMTXOS {
cpu.TotalCore += uint32(*cluster.TotalCPUCores)
cpu.TotalInHz += uint64(*cluster.TotalCPUHz)
cpu.UsedInHz += uint64(*cluster.UsedCPUHz)
if cluster.Hypervisor != nil && *cluster.Hypervisor == models.HypervisorVMWARE {
memory.TotalInByte += uint64(*cluster.TotalMemoryBytes)
memory.UsedInByte += uint64(*cluster.UsedMemoryBytes)
}
}
storage.TotalInByte += uint64(*cluster.TotalDataCapacity)
storage.UsedInByte += uint64(*cluster.UsedDataSpace)
storage.InvalidInByte += uint64(*cluster.FailureDataSpace)
}
for _, host := range hosts.Payload {
cluster, ok := clusterIdMap[*host.Cluster.ID]
if ok {
if *cluster.Hypervisor == models.HypervisorELF {
memory.TotalInByte += uint64(*host.TotalMemoryBytes)
memory.UsedInByte += uint64(*host.UsedMemoryBytes)
}
}
}
storage.compute()
cpu.compute()
memory.compute()
return &MetricInfo{
Memory: memory,
Cpu: cpu,
Storage: storage,
}, nil
}

Build Dashboard

type DashboardInfo struct {
Metric *MetricInfo
Hdd *DiskInfo
Alert *AlertInfo
}

func BuildDashboard(client *apiclient.Cloudtower, clusterIds []string) (*DashboardInfo, error) {
getClusterParams := cluster.NewGetClustersParams()
if len(clusterIds) == 0 {
getClusterParams.RequestBody = &models.GetClustersRequestBody{}
} else {
getClusterParams.RequestBody = &models.GetClustersRequestBody{
Where: &models.ClusterWhereInput{
IDIn: clusterIds,
},
}
}
res, err := client.Cluster.GetClusters(getClusterParams)
if err != nil {
return nil, err
}
metrics, err := buildMetricsInfo(client, res.Payload, clusterIds)
if err != nil {
return nil, err
}
hdd, err := buildHddInfo(client, clusterIds)
if err != nil {
return nil, err
}
alert, err := buildAlertInfo(client, clusterIds)
if err != nil {
return nil, err
}
return &DashboardInfo{
Metric: metrics,
Hdd: hdd,
Alert: alert,
}, nil
}