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 }