251 lines
7 KiB
Go
251 lines
7 KiB
Go
/*
|
|
Copyright (c) 2017-2024 VMware, Inc. All Rights Reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package pbm
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/vmware/govmomi/pbm/types"
|
|
"github.com/vmware/govmomi/property"
|
|
"github.com/vmware/govmomi/view"
|
|
"github.com/vmware/govmomi/vim25"
|
|
"github.com/vmware/govmomi/vim25/mo"
|
|
vim "github.com/vmware/govmomi/vim25/types"
|
|
)
|
|
|
|
// A struct to capture pbm create spec details.
|
|
type CapabilityProfileCreateSpec struct {
|
|
Name string
|
|
SubProfileName string
|
|
Description string
|
|
Category string
|
|
CapabilityList []Capability
|
|
}
|
|
|
|
// A struct to capture pbm capability instance details.
|
|
type Capability struct {
|
|
ID string
|
|
Namespace string
|
|
PropertyList []Property
|
|
}
|
|
|
|
// A struct to capture pbm property instance details.
|
|
type Property struct {
|
|
ID string
|
|
Operator string
|
|
Value string
|
|
DataType string
|
|
}
|
|
|
|
func CreateCapabilityProfileSpec(pbmCreateSpec CapabilityProfileCreateSpec) (*types.PbmCapabilityProfileCreateSpec, error) {
|
|
capabilities, err := createCapabilityInstances(pbmCreateSpec.CapabilityList)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pbmCapabilityProfileSpec := types.PbmCapabilityProfileCreateSpec{
|
|
Name: pbmCreateSpec.Name,
|
|
Description: pbmCreateSpec.Description,
|
|
Category: pbmCreateSpec.Category,
|
|
ResourceType: types.PbmProfileResourceType{
|
|
ResourceType: string(types.PbmProfileResourceTypeEnumSTORAGE),
|
|
},
|
|
Constraints: &types.PbmCapabilitySubProfileConstraints{
|
|
SubProfiles: []types.PbmCapabilitySubProfile{
|
|
types.PbmCapabilitySubProfile{
|
|
Capability: capabilities,
|
|
Name: pbmCreateSpec.SubProfileName,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
return &pbmCapabilityProfileSpec, nil
|
|
}
|
|
|
|
func createCapabilityInstances(rules []Capability) ([]types.PbmCapabilityInstance, error) {
|
|
var capabilityInstances []types.PbmCapabilityInstance
|
|
for _, capabilityRule := range rules {
|
|
capability := types.PbmCapabilityInstance{
|
|
Id: types.PbmCapabilityMetadataUniqueId{
|
|
Namespace: capabilityRule.Namespace,
|
|
Id: capabilityRule.ID,
|
|
},
|
|
}
|
|
|
|
var propertyInstances []types.PbmCapabilityPropertyInstance
|
|
for _, propertyRule := range capabilityRule.PropertyList {
|
|
property := types.PbmCapabilityPropertyInstance{
|
|
Id: propertyRule.ID,
|
|
}
|
|
if propertyRule.Operator != "" {
|
|
property.Operator = propertyRule.Operator
|
|
}
|
|
var err error
|
|
switch strings.ToLower(propertyRule.DataType) {
|
|
case "int":
|
|
// Go int32 is marshalled to xsi:int whereas Go int is marshalled to xsi:long when sending down the wire.
|
|
var val int32
|
|
val, err = verifyPropertyValueIsInt(propertyRule.Value, propertyRule.DataType)
|
|
property.Value = val
|
|
case "bool":
|
|
var val bool
|
|
val, err = verifyPropertyValueIsBoolean(propertyRule.Value, propertyRule.DataType)
|
|
property.Value = val
|
|
case "string":
|
|
property.Value = propertyRule.Value
|
|
case "set":
|
|
set := types.PbmCapabilityDiscreteSet{}
|
|
for _, val := range strings.Split(propertyRule.Value, ",") {
|
|
set.Values = append(set.Values, val)
|
|
}
|
|
property.Value = set
|
|
default:
|
|
return nil, fmt.Errorf("invalid value: %q with datatype: %q", propertyRule.Value, propertyRule.Value)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid value: %q with datatype: %q", propertyRule.Value, propertyRule.Value)
|
|
}
|
|
propertyInstances = append(propertyInstances, property)
|
|
}
|
|
constraintInstances := []types.PbmCapabilityConstraintInstance{
|
|
types.PbmCapabilityConstraintInstance{
|
|
PropertyInstance: propertyInstances,
|
|
},
|
|
}
|
|
capability.Constraint = constraintInstances
|
|
capabilityInstances = append(capabilityInstances, capability)
|
|
}
|
|
return capabilityInstances, nil
|
|
}
|
|
|
|
// Verify if the capability value is of type integer.
|
|
func verifyPropertyValueIsInt(propertyValue string, dataType string) (int32, error) {
|
|
val, err := strconv.ParseInt(propertyValue, 10, 32)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
return int32(val), nil
|
|
}
|
|
|
|
// Verify if the capability value is of type integer.
|
|
func verifyPropertyValueIsBoolean(propertyValue string, dataType string) (bool, error) {
|
|
val, err := strconv.ParseBool(propertyValue)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return val, nil
|
|
}
|
|
|
|
// ProfileMap contains a map of storage profiles by name.
|
|
type ProfileMap struct {
|
|
Name map[string]types.BasePbmProfile
|
|
Profile []types.BasePbmProfile
|
|
}
|
|
|
|
// ProfileMap builds a map of storage profiles by name.
|
|
func (c *Client) ProfileMap(ctx context.Context, uid ...string) (*ProfileMap, error) {
|
|
m := &ProfileMap{Name: make(map[string]types.BasePbmProfile)}
|
|
|
|
rtype := types.PbmProfileResourceType{
|
|
ResourceType: string(types.PbmProfileResourceTypeEnumSTORAGE),
|
|
}
|
|
|
|
category := types.PbmProfileCategoryEnumREQUIREMENT
|
|
|
|
var ids []types.PbmProfileId
|
|
if len(uid) == 0 {
|
|
var err error
|
|
ids, err = c.QueryProfile(ctx, rtype, string(category))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
ids = make([]types.PbmProfileId, len(uid))
|
|
for i, id := range uid {
|
|
ids[i].UniqueId = id
|
|
}
|
|
}
|
|
|
|
profiles, err := c.RetrieveContent(ctx, ids)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m.Profile = profiles
|
|
|
|
for _, p := range profiles {
|
|
base := p.GetPbmProfile()
|
|
m.Name[base.Name] = p
|
|
m.Name[base.ProfileId.UniqueId] = p
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
// DatastoreMap contains a map of Datastore by name.
|
|
type DatastoreMap struct {
|
|
Name map[string]string
|
|
PlacementHub []types.PbmPlacementHub
|
|
}
|
|
|
|
// DatastoreMap returns a map of Datastore by name.
|
|
// The root reference can be a ClusterComputeResource or Folder.
|
|
func (c *Client) DatastoreMap(ctx context.Context, vc *vim25.Client, root vim.ManagedObjectReference) (*DatastoreMap, error) {
|
|
m := &DatastoreMap{Name: make(map[string]string)}
|
|
|
|
prop := []string{"name"}
|
|
var content []vim.ObjectContent
|
|
|
|
if root.Type == "ClusterComputeResource" {
|
|
pc := property.DefaultCollector(vc)
|
|
var cluster mo.ClusterComputeResource
|
|
|
|
if err := pc.RetrieveOne(ctx, root, []string{"datastore"}, &cluster); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := pc.Retrieve(ctx, cluster.Datastore, prop, &content); err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
kind := []string{"Datastore"}
|
|
m := view.NewManager(vc)
|
|
|
|
v, err := m.CreateContainerView(ctx, root, kind, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = v.Retrieve(ctx, kind, prop, &content)
|
|
_ = v.Destroy(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
for _, item := range content {
|
|
m.PlacementHub = append(m.PlacementHub, types.PbmPlacementHub{
|
|
HubType: item.Obj.Type,
|
|
HubId: item.Obj.Value,
|
|
})
|
|
m.Name[item.Obj.Value] = item.PropSet[0].Val.(string)
|
|
}
|
|
|
|
return m, nil
|
|
}
|