github.com/vmware/govmomi@v0.51.0/pbm/client_test.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package pbm_test
     6  
     7  import (
     8  	"context"
     9  	"os"
    10  	"reflect"
    11  	"sort"
    12  	"testing"
    13  
    14  	"github.com/stretchr/testify/assert"
    15  
    16  	"github.com/vmware/govmomi"
    17  	"github.com/vmware/govmomi/fault"
    18  	"github.com/vmware/govmomi/pbm"
    19  	pbmsim "github.com/vmware/govmomi/pbm/simulator"
    20  	"github.com/vmware/govmomi/pbm/types"
    21  	"github.com/vmware/govmomi/property"
    22  	"github.com/vmware/govmomi/simulator"
    23  	"github.com/vmware/govmomi/view"
    24  	"github.com/vmware/govmomi/vim25"
    25  	"github.com/vmware/govmomi/vim25/mo"
    26  	"github.com/vmware/govmomi/vim25/soap"
    27  	vim "github.com/vmware/govmomi/vim25/types"
    28  )
    29  
    30  func TestClient(t *testing.T) {
    31  	url := os.Getenv("GOVMOMI_PBM_URL")
    32  	if url == "" {
    33  		t.SkipNow()
    34  	}
    35  
    36  	clusterName := os.Getenv("GOVMOMI_PBM_CLUSTER")
    37  
    38  	u, err := soap.ParseURL(url)
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  
    43  	ctx := context.Background()
    44  
    45  	c, err := govmomi.NewClient(ctx, u, true)
    46  	if err != nil {
    47  		t.Fatal(err)
    48  	}
    49  
    50  	pc, err := pbm.NewClient(ctx, c.Client)
    51  	if err != nil {
    52  		t.Fatal(err)
    53  	}
    54  
    55  	t.Logf("PBM version=%s", pc.ServiceContent.AboutInfo.Version)
    56  
    57  	rtype := types.PbmProfileResourceType{
    58  		ResourceType: string(types.PbmProfileResourceTypeEnumSTORAGE),
    59  	}
    60  
    61  	category := types.PbmProfileCategoryEnumREQUIREMENT
    62  
    63  	// 1. Query all the profiles on the vCenter.
    64  	ids, err := pc.QueryProfile(ctx, rtype, string(category))
    65  	if err != nil {
    66  		t.Fatal(err)
    67  	}
    68  
    69  	var qids []string
    70  
    71  	for _, id := range ids {
    72  		qids = append(qids, id.UniqueId)
    73  	}
    74  
    75  	var cids []string
    76  
    77  	// 2. Retrieve the content of all profiles.
    78  	policies, err := pc.RetrieveContent(ctx, ids)
    79  	if err != nil {
    80  		t.Fatal(err)
    81  	}
    82  
    83  	for i := range policies {
    84  		profile := policies[i].GetPbmProfile()
    85  		cids = append(cids, profile.ProfileId.UniqueId)
    86  	}
    87  
    88  	sort.Strings(qids)
    89  	sort.Strings(cids)
    90  
    91  	// Check whether ids retrieved from QueryProfile and RetrieveContent
    92  	// are identical.
    93  	if !reflect.DeepEqual(qids, cids) {
    94  		t.Error("ids mismatch")
    95  	}
    96  
    97  	// 3. Get list of datastores in a cluster if cluster name is specified.
    98  	root := c.ServiceContent.RootFolder
    99  	var datastores []vim.ManagedObjectReference
   100  	var kind []string
   101  
   102  	if clusterName == "" {
   103  		kind = []string{"Datastore"}
   104  	} else {
   105  		kind = []string{"ClusterComputeResource"}
   106  	}
   107  
   108  	m := view.NewManager(c.Client)
   109  
   110  	v, err := m.CreateContainerView(ctx, root, kind, true)
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  
   115  	if clusterName == "" {
   116  		datastores, err = v.Find(ctx, kind, nil)
   117  		if err != nil {
   118  			t.Fatal(err)
   119  		}
   120  	} else {
   121  		var cluster mo.ClusterComputeResource
   122  
   123  		err = v.RetrieveWithFilter(ctx, kind, []string{"datastore"}, &cluster, property.Match{"name": clusterName})
   124  		if err != nil {
   125  			t.Fatal(err)
   126  		}
   127  
   128  		datastores = cluster.Datastore
   129  	}
   130  
   131  	_ = v.Destroy(ctx)
   132  
   133  	t.Logf("checking %d datatores for compatibility results", len(datastores))
   134  
   135  	var hubs []types.PbmPlacementHub
   136  
   137  	for _, ds := range datastores {
   138  		hubs = append(hubs, types.PbmPlacementHub{
   139  			HubType: ds.Type,
   140  			HubId:   ds.Value,
   141  		})
   142  	}
   143  
   144  	var req []types.BasePbmPlacementRequirement
   145  
   146  	for _, id := range ids {
   147  		req = append(req, &types.PbmPlacementCapabilityProfileRequirement{
   148  			ProfileId: id,
   149  		})
   150  	}
   151  
   152  	// 4. Get the compatibility results for all the profiles on the vCenter.
   153  	res, err := pc.CheckRequirements(ctx, hubs, nil, req)
   154  	if err != nil {
   155  		t.Fatal(err)
   156  	}
   157  
   158  	t.Logf("CheckRequirements results: %d", len(res))
   159  
   160  	// user spec for the profile.
   161  	// VSAN profile with 2 capability instances - hostFailuresToTolerate = 2, stripeWidth = 1
   162  	pbmCreateSpecForVSAN := pbm.CapabilityProfileCreateSpec{
   163  		Name:        "Kubernetes-VSAN-TestPolicy",
   164  		Description: "VSAN Test policy create",
   165  		Category:    string(types.PbmProfileCategoryEnumREQUIREMENT),
   166  		CapabilityList: []pbm.Capability{
   167  			{
   168  				ID:        "hostFailuresToTolerate",
   169  				Namespace: "VSAN",
   170  				PropertyList: []pbm.Property{
   171  					{
   172  						ID:       "hostFailuresToTolerate",
   173  						Value:    "2",
   174  						DataType: "int",
   175  					},
   176  				},
   177  			},
   178  			{
   179  				ID:        "stripeWidth",
   180  				Namespace: "VSAN",
   181  				PropertyList: []pbm.Property{
   182  					{
   183  						ID:       "stripeWidth",
   184  						Value:    "1",
   185  						DataType: "int",
   186  					},
   187  				},
   188  			},
   189  		},
   190  	}
   191  
   192  	// Create PBM capability spec for the above defined user spec.
   193  	createSpecVSAN, err := pbm.CreateCapabilityProfileSpec(pbmCreateSpecForVSAN)
   194  	if err != nil {
   195  		t.Fatal(err)
   196  	}
   197  
   198  	// 5. Create SPBM VSAN profile.
   199  	vsanProfileID, err := pc.CreateProfile(ctx, *createSpecVSAN)
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	t.Logf("VSAN Profile: %q successfully created", vsanProfileID.UniqueId)
   204  
   205  	// 6. Verify if profile created exists by issuing a RetrieveContent request.
   206  	_, err = pc.RetrieveContent(ctx, []types.PbmProfileId{*vsanProfileID})
   207  	if err != nil {
   208  		t.Fatal(err)
   209  	}
   210  	t.Logf("Profile: %q exists on vCenter", vsanProfileID.UniqueId)
   211  
   212  	// 7. Get compatible datastores for the VSAN profile.
   213  	compatibleDatastores := res.CompatibleDatastores()
   214  	t.Logf("Found %d compatible-datastores for profile: %q", len(compatibleDatastores), vsanProfileID.UniqueId)
   215  
   216  	// 8. Get non-compatible datastores for the VSAN profile.
   217  	nonCompatibleDatastores := res.NonCompatibleDatastores()
   218  	t.Logf("Found %d non-compatible datastores for profile: %q", len(nonCompatibleDatastores), vsanProfileID.UniqueId)
   219  
   220  	// Check whether count of compatible and non-compatible datastores match the total number of datastores.
   221  	if (len(nonCompatibleDatastores) + len(compatibleDatastores)) != len(datastores) {
   222  		t.Error("datastore count mismatch")
   223  	}
   224  
   225  	// user spec for the profile.
   226  	// VSAN profile with 2 capability instances - stripeWidth = 1 and an SIOC profile.
   227  	pbmCreateSpecVSANandSIOC := pbm.CapabilityProfileCreateSpec{
   228  		Name:        "Kubernetes-VSAN-SIOC-TestPolicy",
   229  		Description: "VSAN-SIOC-Test policy create",
   230  		Category:    string(types.PbmProfileCategoryEnumREQUIREMENT),
   231  		CapabilityList: []pbm.Capability{
   232  			{
   233  				ID:        "stripeWidth",
   234  				Namespace: "VSAN",
   235  				PropertyList: []pbm.Property{
   236  					{
   237  						ID:       "stripeWidth",
   238  						Value:    "1",
   239  						DataType: "int",
   240  					},
   241  				},
   242  			},
   243  			{
   244  				ID:        "spm@DATASTOREIOCONTROL",
   245  				Namespace: "spm",
   246  				PropertyList: []pbm.Property{
   247  					{
   248  						ID:       "limit",
   249  						Value:    "200",
   250  						DataType: "int",
   251  					},
   252  					{
   253  						ID:       "reservation",
   254  						Value:    "1000",
   255  						DataType: "int",
   256  					},
   257  					{
   258  						ID:       "shares",
   259  						Value:    "2000",
   260  						DataType: "int",
   261  					},
   262  				},
   263  			},
   264  		},
   265  	}
   266  
   267  	// Create PBM capability spec for the above defined user spec.
   268  	createSpecVSANandSIOC, err := pbm.CreateCapabilityProfileSpec(pbmCreateSpecVSANandSIOC)
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  
   273  	// 9. Create SPBM VSAN profile.
   274  	vsansiocProfileID, err := pc.CreateProfile(ctx, *createSpecVSANandSIOC)
   275  	if err != nil {
   276  		t.Fatal(err)
   277  	}
   278  	t.Logf("VSAN-SIOC Profile: %q successfully created", vsansiocProfileID.UniqueId)
   279  
   280  	// 9. Get ProfileID by Name
   281  	profileID, err := pc.ProfileIDByName(ctx, pbmCreateSpecVSANandSIOC.Name)
   282  	if err != nil {
   283  		t.Fatal(err)
   284  	}
   285  
   286  	if vsansiocProfileID.UniqueId != profileID {
   287  		t.Errorf("vsan-sioc profile: %q and retrieved profileID: %q did not match", vsansiocProfileID.UniqueId, profileID)
   288  	}
   289  
   290  	t.Logf("VSAN-SIOC profile: %q and retrieved profileID: %q successfully matched", vsansiocProfileID.UniqueId, profileID)
   291  
   292  	// 10. Get ProfileName by ID
   293  	profileName, err := pc.GetProfileNameByID(ctx, profileID)
   294  	if err != nil {
   295  		t.Fatal(err)
   296  	}
   297  
   298  	if profileName != pbmCreateSpecVSANandSIOC.Name {
   299  		t.Errorf("vsan-sioc profile: %q and retrieved profileName: %q did not match", pbmCreateSpecVSANandSIOC.Name, profileName)
   300  	}
   301  
   302  	t.Logf("vsan-sioc profile: %q and retrieved profileName: %q successfully matched", pbmCreateSpecVSANandSIOC.Name, profileName)
   303  
   304  	// 11. Delete VSAN and VSAN-SIOC profile.
   305  	_, err = pc.DeleteProfile(ctx, []types.PbmProfileId{*vsanProfileID, *vsansiocProfileID})
   306  	if err != nil {
   307  		t.Fatal(err)
   308  	}
   309  	t.Logf("Profile: %+v successfully deleted", []types.PbmProfileId{*vsanProfileID, *vsansiocProfileID})
   310  }
   311  
   312  func TestSupportsEncryption(t *testing.T) {
   313  	t.Run("valid profile id", func(t *testing.T) {
   314  		simulator.Test(func(ctx context.Context, c *vim25.Client) {
   315  			pbmc, err := pbm.NewClient(ctx, c)
   316  			assert.NoError(t, err)
   317  			assert.NotNil(t, pbmc)
   318  
   319  			ok, err := pbmc.SupportsEncryption(ctx, pbmsim.DefaultEncryptionProfileID)
   320  			assert.NoError(t, err)
   321  			assert.True(t, ok)
   322  		})
   323  	})
   324  	t.Run("invalid profile id", func(t *testing.T) {
   325  		simulator.Test(func(ctx context.Context, c *vim25.Client) {
   326  			pbmc, err := pbm.NewClient(ctx, c)
   327  			assert.NoError(t, err)
   328  			assert.NotNil(t, pbmc)
   329  
   330  			ok, err := pbmc.SupportsEncryption(ctx, "invalid")
   331  			assert.EqualError(t, err, "ServerFaultCode: Invalid profile ID")
   332  			assert.True(t, fault.Is(err, &vim.RuntimeFault{}))
   333  			assert.False(t, ok)
   334  		})
   335  	})
   336  }
   337  
   338  func TestClientCookie(t *testing.T) {
   339  	simulator.Test(func(ctx context.Context, c *vim25.Client) {
   340  		pbmc, err := pbm.NewClient(ctx, c)
   341  		assert.NoError(t, err)
   342  		assert.NotNil(t, pbmc)
   343  
   344  		// Using default / expected Header.Cookie.XMLName = vcSessionCookie
   345  		ok, err := pbmc.SupportsEncryption(ctx, pbmsim.DefaultEncryptionProfileID)
   346  		assert.NoError(t, err)
   347  		assert.True(t, ok)
   348  
   349  		// Using invalid Header.Cookie.XMLName = myCookie
   350  		myCookie := c.SessionCookie()
   351  		myCookie.XMLName.Local = "myCookie"
   352  
   353  		pbmc.Client.Cookie = func() *soap.HeaderElement {
   354  			return myCookie
   355  		}
   356  
   357  		ok, err = pbmc.SupportsEncryption(ctx, pbmsim.DefaultEncryptionProfileID)
   358  		assert.EqualError(t, err, "ServerFaultCode: NotAuthenticated")
   359  		assert.False(t, ok)
   360  	})
   361  }