tests: upload & test in vCenter. Closes #338
This commit is contained in:
parent
02346faff8
commit
9cce43d384
255 changed files with 127290 additions and 3 deletions
86
vendor/github.com/vmware/govmomi/vapi/internal/internal.go
generated
vendored
Normal file
86
vendor/github.com/vmware/govmomi/vapi/internal/internal.go
generated
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 internal
|
||||
|
||||
import (
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// VAPI REST Paths
|
||||
const (
|
||||
SessionPath = "/com/vmware/cis/session"
|
||||
CategoryPath = "/com/vmware/cis/tagging/category"
|
||||
TagPath = "/com/vmware/cis/tagging/tag"
|
||||
AssociationPath = "/com/vmware/cis/tagging/tag-association"
|
||||
LibraryPath = "/com/vmware/content/library"
|
||||
LibraryItemFileData = "/com/vmware/cis/data"
|
||||
LibraryItemPath = "/com/vmware/content/library/item"
|
||||
LibraryItemFilePath = "/com/vmware/content/library/item/file"
|
||||
LibraryItemUpdateSession = "/com/vmware/content/library/item/update-session"
|
||||
LibraryItemUpdateSessionFile = "/com/vmware/content/library/item/updatesession/file"
|
||||
LibraryItemDownloadSession = "/com/vmware/content/library/item/download-session"
|
||||
LibraryItemDownloadSessionFile = "/com/vmware/content/library/item/downloadsession/file"
|
||||
LocalLibraryPath = "/com/vmware/content/local-library"
|
||||
SubscribedLibraryPath = "/com/vmware/content/subscribed-library"
|
||||
SubscribedLibraryItem = "/com/vmware/content/library/subscribed-item"
|
||||
Subscriptions = "/com/vmware/content/library/subscriptions"
|
||||
VCenterOVFLibraryItem = "/com/vmware/vcenter/ovf/library-item"
|
||||
VCenterVMTXLibraryItem = "/vcenter/vm-template/library-items"
|
||||
VCenterVM = "/vcenter/vm"
|
||||
SessionCookieName = "vmware-api-session-id"
|
||||
UseHeaderAuthn = "vmware-use-header-authn"
|
||||
)
|
||||
|
||||
// AssociatedObject is the same structure as types.ManagedObjectReference,
|
||||
// just with a different field name (ID instead of Value).
|
||||
// In the API we use mo.Reference, this type is only used for wire transfer.
|
||||
type AssociatedObject struct {
|
||||
Type string `json:"type"`
|
||||
Value string `json:"id"`
|
||||
}
|
||||
|
||||
// Reference implements mo.Reference
|
||||
func (o AssociatedObject) Reference() types.ManagedObjectReference {
|
||||
return types.ManagedObjectReference(o)
|
||||
}
|
||||
|
||||
// Association for tag-association requests.
|
||||
type Association struct {
|
||||
ObjectID *AssociatedObject `json:"object_id,omitempty"`
|
||||
}
|
||||
|
||||
// NewAssociation returns an Association, converting ref to an AssociatedObject.
|
||||
func NewAssociation(ref mo.Reference) Association {
|
||||
obj := AssociatedObject(ref.Reference())
|
||||
return Association{
|
||||
ObjectID: &obj,
|
||||
}
|
||||
}
|
||||
|
||||
type SubscriptionDestination struct {
|
||||
ID string `json:"subscription"`
|
||||
}
|
||||
|
||||
type SubscriptionDestinationSpec struct {
|
||||
Subscriptions []SubscriptionDestination `json:"subscriptions,omitempty"`
|
||||
}
|
||||
|
||||
type SubscriptionItemDestinationSpec struct {
|
||||
Force bool `json:"force_sync_content"`
|
||||
Subscriptions []SubscriptionDestination `json:"subscriptions,omitempty"`
|
||||
}
|
||||
185
vendor/github.com/vmware/govmomi/vapi/library/finder/README.md
generated
vendored
Normal file
185
vendor/github.com/vmware/govmomi/vapi/library/finder/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
# The content library finder
|
||||
The govmomi package now includes a finder for the content library, [`github.com/vmware/govmomi/vapi/library.Finder`](https://github.com/akutz/govmomi/blob/feature/content-library/vapi/library/finder/finder.go). This finder supports searching for objects in the content library using an inventory path design similar to the [standard govmomi finder](https://github.com/vmware/govmomi/blob/master/find/finder.go) and includes support for wildcard matching courtesy of golang's [`path.Match`](https://golang.org/pkg/path/#Match). For example:
|
||||
|
||||
| Pattern | Result |
|
||||
|---------|--------|
|
||||
| `*` | Gets all of the content libraries |
|
||||
| `*/` | Gets all of the content library items for all of the content libraries. |
|
||||
| `/Public/` | Gets all of the content library items for the content library named `Public` |
|
||||
| `/*/*/` | Gets all of the content library files for all of the content library items for all of the content libraries. |
|
||||
| `Public/*/photon2*` | Gets all of the files that begin with `photon2` in all of the content library items for the content library named `Public` |
|
||||
|
||||
The use of a wildcard character in the search string results in a full listing of content library objects for that part of the path's server-side analogue. If `Public/Photon2*` is searched, then all of the content library items for the `Public` library are listed in order to perform the wildcard matching. However, if `Public/PhotonOS-2-GA` then the server-side API call [`library:item:find`](https://vdc-repo.vmware.com/vmwb-repository/dcr-public/1cd28284-3b72-4885-9e31-d1c6d9e26686/71ef7304-a6c9-43b3-a3cd-868b2c236c81/doc/operations/com/vmware/content/library/item.find-operation.html) is used to find an item with the name `PhotonOS-2-GA`.
|
||||
|
||||
## Performance
|
||||
Search strings that do not use wildcard characters **should** be **more** efficient as these searches rely on server-side find calls and do not require dumping all of the objects. However, this is only true for systems with a large number of objects in the content libraries. This is due to the number of round-trips required to lookup an object with a direct path. For example:
|
||||
|
||||
| Search path | Roundtrips |
|
||||
|------|------------|
|
||||
| `Public/Photon2` | 4 |
|
||||
| `Public*/Photon2*` | 2 |
|
||||
|
||||
The *absolute* search path takes twice as many roundtrips compared to the search path with wildcards:
|
||||
|
||||
### Absolute path search logic
|
||||
1. Find library ID for library with name using server-side find API
|
||||
2. Get library with library ID
|
||||
3. Find item ID for item with name using server-side find API
|
||||
4. Get item with item ID
|
||||
|
||||
### Wildcard search logic
|
||||
1. Get all of the libraries and filter the pattern on the client-side
|
||||
2. Get all of the items for the library and filter the pattern on the client-=side
|
||||
|
||||
### Searches at scale
|
||||
While a system that has few content library objects benefits from wildcard search logic, the fact is that the above approach regarding absolute paths proves out to be **much** more efficient for systems with large numbers of content library objects.
|
||||
|
||||
|
||||
## `govc library.ls`
|
||||
|
||||
### Listing all the objects in the content library
|
||||
```shell
|
||||
$ govc library.ls '*/*/'
|
||||
/ISOs/CentOS-7-x86_64-Minimal-1804/CentOS-7-x86_64-Minimal-1804.iso
|
||||
/ISOs/CoreOS Production/coreos_production_iso_image.iso
|
||||
/ISOs/VMware-VCSA-all-6.7.0-8217866.iso/VMware-VCSA-all-6.7.0-8217866.iso
|
||||
/ISOs/VMware-VIM-all-6.7.0-8217866.iso/VMware-VIM-all-6.7.0-8217866.iso
|
||||
/ISOs/ubuntu-16.04.5-server-amd64/ubuntu-16.04.5-server-amd64.iso
|
||||
/ISOs/photon-2.0-304b817/photon-2.0-304b817.iso
|
||||
/OVAs/VMware-vCenter-Server-Appliance-6.7.0.10000-8217866_OVF10.ova/VMware-vCenter-Server-Appliance-6.7.0.10000-8217866_OVF10.ova
|
||||
/OVAs/coreos_production_vmware_ova/coreos_production_vmware_ova.ovf
|
||||
/OVAs/coreos_production_vmware_ova/coreos_production_vmware_ova-1.vmdk
|
||||
/OVAs/centos_cloud_template/centos_cloud_template-2.iso
|
||||
/OVAs/centos_cloud_template/centos_cloud_template.ovf
|
||||
/OVAs/centos_cloud_template/centos_cloud_template-1.vmdk
|
||||
/OVAs/centos_cloud_template/centos_cloud_template-3.nvram
|
||||
/OVAs/photon-custom-hw13-2.0-304b817/photon-ova-disk1.vmdk
|
||||
/OVAs/photon-custom-hw13-2.0-304b817/photon-ova.ovf
|
||||
/OVAs/yakity-centos/yakity-centos-2.nvram
|
||||
/OVAs/yakity-centos/yakity-centos-1.vmdk
|
||||
/OVAs/yakity-centos/yakity-centos.ovf
|
||||
/OVAs/yakity-photon/yakity-photon-1.vmdk
|
||||
/OVAs/yakity-photon/yakity-photon.ovf
|
||||
/OVAs/yakity-photon/yakity-photon-2.nvram
|
||||
/OVAs/ubuntu-16.04-server-cloudimg-amd64/ubuntu-16.04-server-cloudimg-amd64.ovf
|
||||
/OVAs/ubuntu-16.04-server-cloudimg-amd64/ubuntu-16.04-server-cloudimg-amd64-1.vmdk
|
||||
/sk8-TestUploadOVA/photon2-cloud-init/photon2-cloud-init.ovf
|
||||
/sk8-TestUploadOVA/photon2-cloud-init/photon2-cloud-init-1.vmdk
|
||||
/Public/photon2-cloud-init/photon2-cloud-init.ovf
|
||||
/Public/photon2-cloud-init/photon2-cloud-init-1.vmdk
|
||||
```
|
||||
|
||||
## `govc library.info`
|
||||
|
||||
### Getting the info for all the objects in the content library
|
||||
```shell
|
||||
$ govc library.info '*/*/'
|
||||
Name: CentOS-7-x86_64-Minimal-1804.iso
|
||||
Path: /ISOs/CentOS-7-x86_64-Minimal-1804/CentOS-7-x86_64-Minimal-1804.iso
|
||||
Size: 824637106200
|
||||
Version: 1
|
||||
Name: coreos_production_iso_image.iso
|
||||
Path: /ISOs/CoreOS Production/coreos_production_iso_image.iso
|
||||
Size: 824637441624
|
||||
Version: 1
|
||||
Name: VMware-VCSA-all-6.7.0-8217866.iso
|
||||
Path: /ISOs/VMware-VCSA-all-6.7.0-8217866.iso/VMware-VCSA-all-6.7.0-8217866.iso
|
||||
Size: 824637106368
|
||||
Version: 1
|
||||
Name: VMware-VIM-all-6.7.0-8217866.iso
|
||||
Path: /ISOs/VMware-VIM-all-6.7.0-8217866.iso/VMware-VIM-all-6.7.0-8217866.iso
|
||||
Size: 824637106504
|
||||
Version: 1
|
||||
Name: ubuntu-16.04.5-server-amd64.iso
|
||||
Path: /ISOs/ubuntu-16.04.5-server-amd64/ubuntu-16.04.5-server-amd64.iso
|
||||
Size: 824637441944
|
||||
Version: 1
|
||||
Name: photon-2.0-304b817.iso
|
||||
Path: /ISOs/photon-2.0-304b817/photon-2.0-304b817.iso
|
||||
Size: 824637106672
|
||||
Version: 1
|
||||
Name: VMware-vCenter-Server-Appliance-6.7.0.10000-8217866_OVF10.ova
|
||||
Path: /OVAs/VMware-vCenter-Server-Appliance-6.7.0.10000-8217866_OVF10.ova/VMware-vCenter-Server-Appliance-6.7.0.10000-8217866_OVF10.ova
|
||||
Size: 824637106880
|
||||
Version: 1
|
||||
Name: coreos_production_vmware_ova.ovf
|
||||
Path: /OVAs/coreos_production_vmware_ova/coreos_production_vmware_ova.ovf
|
||||
Size: 824637107032
|
||||
Version: 1
|
||||
Name: coreos_production_vmware_ova-1.vmdk
|
||||
Path: /OVAs/coreos_production_vmware_ova/coreos_production_vmware_ova-1.vmdk
|
||||
Size: 824637107072
|
||||
Version: 1
|
||||
Name: centos_cloud_template-2.iso
|
||||
Path: /OVAs/centos_cloud_template/centos_cloud_template-2.iso
|
||||
Size: 824636997760
|
||||
Version: 1
|
||||
Name: centos_cloud_template.ovf
|
||||
Path: /OVAs/centos_cloud_template/centos_cloud_template.ovf
|
||||
Size: 824636997792
|
||||
Version: 1
|
||||
Name: centos_cloud_template-1.vmdk
|
||||
Path: /OVAs/centos_cloud_template/centos_cloud_template-1.vmdk
|
||||
Size: 824636997832
|
||||
Version: 1
|
||||
Name: centos_cloud_template-3.nvram
|
||||
Path: /OVAs/centos_cloud_template/centos_cloud_template-3.nvram
|
||||
Size: 824636997856
|
||||
Version: 1
|
||||
Name: photon-ova-disk1.vmdk
|
||||
Path: /OVAs/photon-custom-hw13-2.0-304b817/photon-ova-disk1.vmdk
|
||||
Size: 824637107280
|
||||
Version: 1
|
||||
Name: photon-ova.ovf
|
||||
Path: /OVAs/photon-custom-hw13-2.0-304b817/photon-ova.ovf
|
||||
Size: 824637107328
|
||||
Version: 1
|
||||
Name: yakity-centos-2.nvram
|
||||
Path: /OVAs/yakity-centos/yakity-centos-2.nvram
|
||||
Size: 824637253000
|
||||
Version: 2
|
||||
Name: yakity-centos-1.vmdk
|
||||
Path: /OVAs/yakity-centos/yakity-centos-1.vmdk
|
||||
Size: 824637253024
|
||||
Version: 2
|
||||
Name: yakity-centos.ovf
|
||||
Path: /OVAs/yakity-centos/yakity-centos.ovf
|
||||
Size: 824637253056
|
||||
Version: 2
|
||||
Name: yakity-photon-1.vmdk
|
||||
Path: /OVAs/yakity-photon/yakity-photon-1.vmdk
|
||||
Size: 824637107504
|
||||
Version: 5
|
||||
Name: yakity-photon.ovf
|
||||
Path: /OVAs/yakity-photon/yakity-photon.ovf
|
||||
Size: 824637107536
|
||||
Version: 5
|
||||
Name: yakity-photon-2.nvram
|
||||
Path: /OVAs/yakity-photon/yakity-photon-2.nvram
|
||||
Size: 824637107560
|
||||
Version: 5
|
||||
Name: ubuntu-16.04-server-cloudimg-amd64.ovf
|
||||
Path: /OVAs/ubuntu-16.04-server-cloudimg-amd64/ubuntu-16.04-server-cloudimg-amd64.ovf
|
||||
Size: 824637442112
|
||||
Version: 1
|
||||
Name: ubuntu-16.04-server-cloudimg-amd64-1.vmdk
|
||||
Path: /OVAs/ubuntu-16.04-server-cloudimg-amd64/ubuntu-16.04-server-cloudimg-amd64-1.vmdk
|
||||
Size: 824637442136
|
||||
Version: 1
|
||||
Name: photon2-cloud-init.ovf
|
||||
Path: /sk8-TestUploadOVA/photon2-cloud-init/photon2-cloud-init.ovf
|
||||
Size: 824636903184
|
||||
Version: 1
|
||||
Name: photon2-cloud-init-1.vmdk
|
||||
Path: /sk8-TestUploadOVA/photon2-cloud-init/photon2-cloud-init-1.vmdk
|
||||
Size: 824636903208
|
||||
Version: 1
|
||||
Name: photon2-cloud-init.ovf
|
||||
Path: /Public/photon2-cloud-init/photon2-cloud-init.ovf
|
||||
Size: 824637442304
|
||||
Version: 3
|
||||
Name: photon2-cloud-init-1.vmdk
|
||||
Path: /Public/photon2-cloud-init/photon2-cloud-init-1.vmdk
|
||||
Size: 824637442328
|
||||
Version: 3
|
||||
```
|
||||
328
vendor/github.com/vmware/govmomi/vapi/library/finder/finder.go
generated
vendored
Normal file
328
vendor/github.com/vmware/govmomi/vapi/library/finder/finder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 finder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/library"
|
||||
)
|
||||
|
||||
// Finder is a helper object for finding content library objects by their
|
||||
// inventory paths: /LIBRARY/ITEM/FILE.
|
||||
//
|
||||
// Wildcard characters `*` and `?` are both supported. However, the use
|
||||
// of a wildcard character in the search string results in a full listing of
|
||||
// that part of the path's server-side items.
|
||||
//
|
||||
// Path parts that do not use wildcard characters rely on server-side Find
|
||||
// functions to find the path token by its name. Ironically finding one
|
||||
// item with a direct path takes longer than if a wildcard is used because
|
||||
// of the multiple round-trips. Direct paths will be more performant on
|
||||
// systems that have numerous items.
|
||||
type Finder struct {
|
||||
M *library.Manager
|
||||
}
|
||||
|
||||
// NewFinder returns a new Finder.
|
||||
func NewFinder(m *library.Manager) *Finder {
|
||||
return &Finder{m}
|
||||
}
|
||||
|
||||
// Find finds one or more items that match the provided inventory path(s).
|
||||
func (f *Finder) Find(
|
||||
ctx context.Context, ipath ...string) ([]FindResult, error) {
|
||||
|
||||
if len(ipath) == 0 {
|
||||
ipath = []string{""}
|
||||
}
|
||||
var result []FindResult
|
||||
for _, p := range ipath {
|
||||
results, err := f.find(ctx, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, results...)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *Finder) find(ctx context.Context, ipath string) ([]FindResult, error) {
|
||||
|
||||
if ipath == "" {
|
||||
ipath = "*"
|
||||
}
|
||||
|
||||
// Get the argument and remove any leading separator characters.
|
||||
ipath = strings.TrimPrefix(ipath, "/")
|
||||
|
||||
// Tokenize the path into its distinct parts.
|
||||
parts := strings.Split(ipath, "/")
|
||||
|
||||
// If there are more than three parts then the file name contains
|
||||
// the "/" character. In that case collapse any additional parts
|
||||
// back into the filename.
|
||||
if len(parts) > 3 {
|
||||
parts = []string{
|
||||
parts[0],
|
||||
parts[1],
|
||||
strings.Join(parts[2:], "/"),
|
||||
}
|
||||
}
|
||||
|
||||
libs, err := f.findLibraries(ctx, parts[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the path is a single token then the libraries are requested.
|
||||
if len(parts) < 2 {
|
||||
return libs, nil
|
||||
}
|
||||
|
||||
items, err := f.findLibraryItems(ctx, libs, parts[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the path is two tokens then the library items are requested.
|
||||
if len(parts) < 3 {
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// Get the library item files.
|
||||
return f.findLibraryItemFiles(ctx, items, parts[2])
|
||||
}
|
||||
|
||||
// FindResult is the type of object returned from a Find operation.
|
||||
type FindResult interface {
|
||||
|
||||
// GetParent returns the parent of the find result. If the find result
|
||||
// is a Library then this function will return nil.
|
||||
GetParent() FindResult
|
||||
|
||||
// GetPath returns the inventory path of the find result.
|
||||
GetPath() string
|
||||
|
||||
// GetID returns the ID of the find result.
|
||||
GetID() string
|
||||
|
||||
// GetName returns the name of the find result.
|
||||
GetName() string
|
||||
|
||||
// GetResult gets the underlying library object.
|
||||
GetResult() interface{}
|
||||
}
|
||||
|
||||
type findResult struct {
|
||||
result interface{}
|
||||
parent FindResult
|
||||
}
|
||||
|
||||
func (f findResult) GetResult() interface{} {
|
||||
return f.result
|
||||
}
|
||||
func (f findResult) GetParent() FindResult {
|
||||
return f.parent
|
||||
}
|
||||
func (f findResult) GetPath() string {
|
||||
switch f.result.(type) {
|
||||
case library.Library:
|
||||
return fmt.Sprintf("/%s", f.GetName())
|
||||
case library.Item, library.File:
|
||||
return fmt.Sprintf("%s/%s", f.parent.GetPath(), f.GetName())
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (f findResult) GetID() string {
|
||||
switch t := f.result.(type) {
|
||||
case library.Library:
|
||||
return t.ID
|
||||
case library.Item:
|
||||
return t.ID
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (f findResult) GetName() string {
|
||||
switch t := f.result.(type) {
|
||||
case library.Library:
|
||||
return t.Name
|
||||
case library.Item:
|
||||
return t.Name
|
||||
case library.File:
|
||||
return t.Name
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (f findResult) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(f.GetResult())
|
||||
}
|
||||
|
||||
func (f *Finder) findLibraries(
|
||||
ctx context.Context,
|
||||
token string) ([]FindResult, error) {
|
||||
|
||||
if token == "" {
|
||||
token = "*"
|
||||
}
|
||||
|
||||
var result []FindResult
|
||||
|
||||
// If the token does not contain any wildcard characters then perform
|
||||
// a lookup by name using a server side call.
|
||||
if !strings.ContainsAny(token, "*?") {
|
||||
libIDs, err := f.M.FindLibrary(ctx, library.Find{Name: token})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, id := range libIDs {
|
||||
lib, err := f.M.GetLibraryByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, findResult{result: *lib})
|
||||
}
|
||||
if len(result) == 0 {
|
||||
lib, err := f.M.GetLibraryByID(ctx, token)
|
||||
if err == nil {
|
||||
result = append(result, findResult{result: *lib})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
libs, err := f.M.GetLibraries(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, lib := range libs {
|
||||
match, err := path.Match(token, lib.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if match {
|
||||
result = append(result, findResult{result: lib})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *Finder) findLibraryItems(
|
||||
ctx context.Context,
|
||||
parents []FindResult, token string) ([]FindResult, error) {
|
||||
|
||||
if token == "" {
|
||||
token = "*"
|
||||
}
|
||||
|
||||
var result []FindResult
|
||||
|
||||
for _, parent := range parents {
|
||||
// If the token does not contain any wildcard characters then perform
|
||||
// a lookup by name using a server side call.
|
||||
if !strings.ContainsAny(token, "*?") {
|
||||
childIDs, err := f.M.FindLibraryItems(
|
||||
ctx, library.FindItem{
|
||||
Name: token,
|
||||
LibraryID: parent.GetID(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, id := range childIDs {
|
||||
child, err := f.M.GetLibraryItem(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, findResult{
|
||||
result: *child,
|
||||
parent: parent,
|
||||
})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
children, err := f.M.GetLibraryItems(ctx, parent.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, child := range children {
|
||||
match, err := path.Match(token, child.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if match {
|
||||
result = append(
|
||||
result, findResult{parent: parent, result: child})
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *Finder) findLibraryItemFiles(
|
||||
ctx context.Context,
|
||||
parents []FindResult, token string) ([]FindResult, error) {
|
||||
|
||||
if token == "" {
|
||||
token = "*"
|
||||
}
|
||||
|
||||
var result []FindResult
|
||||
|
||||
for _, parent := range parents {
|
||||
// If the token does not contain any wildcard characters then perform
|
||||
// a lookup by name using a server side call.
|
||||
if !strings.ContainsAny(token, "*?") {
|
||||
child, err := f.M.GetLibraryItemFile(ctx, parent.GetID(), token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, findResult{
|
||||
result: *child,
|
||||
parent: parent,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
children, err := f.M.ListLibraryItemFiles(ctx, parent.GetID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, child := range children {
|
||||
match, err := path.Match(token, child.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if match {
|
||||
result = append(
|
||||
result, findResult{parent: parent, result: child})
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
308
vendor/github.com/vmware/govmomi/vapi/library/library.go
generated
vendored
Normal file
308
vendor/github.com/vmware/govmomi/vapi/library/library.go
generated
vendored
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
)
|
||||
|
||||
// StorageBackings for Content Libraries
|
||||
type StorageBackings struct {
|
||||
DatastoreID string `json:"datastore_id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// Library provides methods to create, read, update, delete, and enumerate libraries.
|
||||
type Library struct {
|
||||
CreationTime *time.Time `json:"creation_time,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
LastModifiedTime *time.Time `json:"last_modified_time,omitempty"`
|
||||
LastSyncTime *time.Time `json:"last_sync_time,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Storage []StorageBackings `json:"storage_backings,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Subscription *Subscription `json:"subscription_info,omitempty"`
|
||||
Publication *Publication `json:"publish_info,omitempty"`
|
||||
}
|
||||
|
||||
// Subscription info
|
||||
type Subscription struct {
|
||||
AuthenticationMethod string `json:"authentication_method"`
|
||||
AutomaticSyncEnabled *bool `json:"automatic_sync_enabled,omitempty"`
|
||||
OnDemand *bool `json:"on_demand,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
SslThumbprint string `json:"ssl_thumbprint,omitempty"`
|
||||
SubscriptionURL string `json:"subscription_url,omitempty"`
|
||||
UserName string `json:"user_name,omitempty"`
|
||||
}
|
||||
|
||||
// Publication info
|
||||
type Publication struct {
|
||||
AuthenticationMethod string `json:"authentication_method"`
|
||||
UserName string `json:"user_name,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
CurrentPassword string `json:"current_password,omitempty"`
|
||||
PersistJSON *bool `json:"persist_json_enabled,omitempty"`
|
||||
Published *bool `json:"published,omitempty"`
|
||||
PublishURL string `json:"publish_url,omitempty"`
|
||||
}
|
||||
|
||||
// SubscriberSummary as returned by ListSubscribers
|
||||
type SubscriberSummary struct {
|
||||
LibraryID string `json:"subscribed_library"`
|
||||
LibraryName string `json:"subscribed_library_name"`
|
||||
SubscriptionID string `json:"subscription"`
|
||||
LibraryVcenterHostname string `json:"subscribed_library_vcenter_hostname,omitempty"`
|
||||
}
|
||||
|
||||
// Placement information used to place a virtual machine template
|
||||
type Placement struct {
|
||||
ResourcePool string `json:"resource_pool,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Folder string `json:"folder,omitempty"`
|
||||
Cluster string `json:"cluster,omitempty"`
|
||||
Network string `json:"network,omitempty"`
|
||||
}
|
||||
|
||||
// Vcenter contains information about the vCenter Server instance where a subscribed library associated with a subscription exists.
|
||||
type Vcenter struct {
|
||||
Hostname string `json:"hostname"`
|
||||
Port int `json:"https_port,omitempty"`
|
||||
ServerGUID string `json:"server_guid"`
|
||||
}
|
||||
|
||||
// Subscriber contains the detailed info for a library subscriber.
|
||||
type Subscriber struct {
|
||||
LibraryID string `json:"subscribed_library"`
|
||||
LibraryName string `json:"subscribed_library_name"`
|
||||
LibraryLocation string `json:"subscribed_library_location"`
|
||||
Placement *Placement `json:"subscribed_library_placement,omitempty"`
|
||||
Vcenter *Vcenter `json:"subscribed_library_vcenter,omitempty"`
|
||||
}
|
||||
|
||||
// SubscriberLibrary is the specification for a subscribed library to be associated with a subscription.
|
||||
type SubscriberLibrary struct {
|
||||
Target string `json:"target"`
|
||||
LibraryID string `json:"subscribed_library,omitempty"`
|
||||
Location string `json:"location"`
|
||||
Vcenter *Vcenter `json:"vcenter,omitempty"`
|
||||
Placement *Placement `json:"placement,omitempty"`
|
||||
}
|
||||
|
||||
// Patch merges updates from the given src.
|
||||
func (l *Library) Patch(src *Library) {
|
||||
if src.Name != "" {
|
||||
l.Name = src.Name
|
||||
}
|
||||
if src.Description != "" {
|
||||
l.Description = src.Description
|
||||
}
|
||||
if src.Version != "" {
|
||||
l.Version = src.Version
|
||||
}
|
||||
}
|
||||
|
||||
// Manager extends rest.Client, adding content library related methods.
|
||||
type Manager struct {
|
||||
*rest.Client
|
||||
}
|
||||
|
||||
// NewManager creates a new Manager instance with the given client.
|
||||
func NewManager(client *rest.Client) *Manager {
|
||||
return &Manager{
|
||||
Client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Find is the search criteria for finding libraries.
|
||||
type Find struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// FindLibrary returns one or more libraries that match the provided search
|
||||
// criteria.
|
||||
//
|
||||
// The provided name is case-insensitive.
|
||||
//
|
||||
// Either the name or type of library may be set to empty values in order
|
||||
// to search for all libraries, all libraries with a specific name, regardless
|
||||
// of type, or all libraries of a specified type.
|
||||
func (c *Manager) FindLibrary(ctx context.Context, search Find) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryPath).WithAction("find")
|
||||
spec := struct {
|
||||
Spec Find `json:"spec"`
|
||||
}{search}
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// CreateLibrary creates a new library with the given Type, Name,
|
||||
// Description, and CategoryID.
|
||||
func (c *Manager) CreateLibrary(ctx context.Context, library Library) (string, error) {
|
||||
spec := struct {
|
||||
Library Library `json:"create_spec"`
|
||||
}{library}
|
||||
path := internal.LocalLibraryPath
|
||||
if library.Type == "SUBSCRIBED" {
|
||||
path = internal.SubscribedLibraryPath
|
||||
sub := library.Subscription
|
||||
u, err := url.Parse(sub.SubscriptionURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if u.Scheme == "https" && sub.SslThumbprint == "" {
|
||||
thumbprint := c.Thumbprint(u.Host)
|
||||
if thumbprint == "" {
|
||||
t := c.DefaultTransport()
|
||||
if t.TLSClientConfig.InsecureSkipVerify {
|
||||
var info object.HostCertificateInfo
|
||||
_ = info.FromURL(u, t.TLSClientConfig)
|
||||
thumbprint = info.ThumbprintSHA1
|
||||
}
|
||||
sub.SslThumbprint = thumbprint
|
||||
}
|
||||
}
|
||||
}
|
||||
url := c.Resource(path)
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// SyncLibrary syncs a subscribed library.
|
||||
func (c *Manager) SyncLibrary(ctx context.Context, library *Library) error {
|
||||
path := internal.SubscribedLibraryPath
|
||||
url := c.Resource(path).WithID(library.ID).WithAction("sync")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// PublishLibrary publishes the library to specified subscriptions.
|
||||
// If no subscriptions are specified, then publishes the library to all subscriptions.
|
||||
func (c *Manager) PublishLibrary(ctx context.Context, library *Library, subscriptions []string) error {
|
||||
path := internal.LocalLibraryPath
|
||||
var spec internal.SubscriptionDestinationSpec
|
||||
for i := range subscriptions {
|
||||
spec.Subscriptions = append(spec.Subscriptions, internal.SubscriptionDestination{ID: subscriptions[i]})
|
||||
}
|
||||
url := c.Resource(path).WithID(library.ID).WithAction("publish")
|
||||
return c.Do(ctx, url.Request(http.MethodPost, spec), nil)
|
||||
}
|
||||
|
||||
// DeleteLibrary deletes an existing library.
|
||||
func (c *Manager) DeleteLibrary(ctx context.Context, library *Library) error {
|
||||
path := internal.LocalLibraryPath
|
||||
if library.Type == "SUBSCRIBED" {
|
||||
path = internal.SubscribedLibraryPath
|
||||
}
|
||||
url := c.Resource(path).WithID(library.ID)
|
||||
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
}
|
||||
|
||||
// ListLibraries returns a list of all content library IDs in the system.
|
||||
func (c *Manager) ListLibraries(ctx context.Context) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryPath)
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryByID returns information on a library for the given ID.
|
||||
func (c *Manager) GetLibraryByID(ctx context.Context, id string) (*Library, error) {
|
||||
url := c.Resource(internal.LibraryPath).WithID(id)
|
||||
var res Library
|
||||
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryByName returns information on a library for the given name.
|
||||
func (c *Manager) GetLibraryByName(ctx context.Context, name string) (*Library, error) {
|
||||
// Lookup by name
|
||||
libraries, err := c.GetLibraries(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range libraries {
|
||||
if libraries[i].Name == name {
|
||||
return &libraries[i], nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("library name (%s) not found", name)
|
||||
}
|
||||
|
||||
// GetLibraries returns a list of all content library details in the system.
|
||||
func (c *Manager) GetLibraries(ctx context.Context) ([]Library, error) {
|
||||
ids, err := c.ListLibraries(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get libraries failed for: %s", err)
|
||||
}
|
||||
|
||||
var libraries []Library
|
||||
for _, id := range ids {
|
||||
library, err := c.GetLibraryByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get library %s failed for %s", id, err)
|
||||
}
|
||||
|
||||
libraries = append(libraries, *library)
|
||||
|
||||
}
|
||||
return libraries, nil
|
||||
}
|
||||
|
||||
// ListSubscribers lists the subscriptions of the published library.
|
||||
func (c *Manager) ListSubscribers(ctx context.Context, library *Library) ([]SubscriberSummary, error) {
|
||||
url := c.Resource(internal.Subscriptions).WithParam("library", library.ID)
|
||||
var res []SubscriberSummary
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// CreateSubscriber creates a subscription of the published library.
|
||||
func (c *Manager) CreateSubscriber(ctx context.Context, library *Library, s SubscriberLibrary) (string, error) {
|
||||
var spec struct {
|
||||
Sub struct {
|
||||
SubscriberLibrary SubscriberLibrary `json:"subscribed_library"`
|
||||
} `json:"spec"`
|
||||
}
|
||||
spec.Sub.SubscriberLibrary = s
|
||||
url := c.Resource(internal.Subscriptions).WithID(library.ID)
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, &spec), &res)
|
||||
}
|
||||
|
||||
// GetSubscriber returns information about the specified subscriber of the published library.
|
||||
func (c *Manager) GetSubscriber(ctx context.Context, library *Library, subscriber string) (*Subscriber, error) {
|
||||
id := internal.SubscriptionDestination{ID: subscriber}
|
||||
url := c.Resource(internal.Subscriptions).WithID(library.ID).WithAction("get")
|
||||
var res Subscriber
|
||||
return &res, c.Do(ctx, url.Request(http.MethodPost, &id), &res)
|
||||
}
|
||||
|
||||
// DeleteSubscriber deletes the specified subscription of the published library.
|
||||
// The subscribed library associated with the subscription will not be deleted.
|
||||
func (c *Manager) DeleteSubscriber(ctx context.Context, library *Library, subscriber string) error {
|
||||
id := internal.SubscriptionDestination{ID: subscriber}
|
||||
url := c.Resource(internal.Subscriptions).WithID(library.ID).WithAction("delete")
|
||||
return c.Do(ctx, url.Request(http.MethodPost, &id), nil)
|
||||
}
|
||||
56
vendor/github.com/vmware/govmomi/vapi/library/library_file.go
generated
vendored
Normal file
56
vendor/github.com/vmware/govmomi/vapi/library/library_file.go
generated
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
)
|
||||
|
||||
// Checksum provides checksum information on library item files.
|
||||
type Checksum struct {
|
||||
Algorithm string `json:"algorithm,omitempty"`
|
||||
Checksum string `json:"checksum"`
|
||||
}
|
||||
|
||||
// File provides methods to get information on library item files.
|
||||
type File struct {
|
||||
Cached *bool `json:"cached,omitempty"`
|
||||
Checksum *Checksum `json:"checksum_info,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Size *int64 `json:"size,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// ListLibraryItemFiles returns a list of all the files for a library item.
|
||||
func (c *Manager) ListLibraryItemFiles(ctx context.Context, id string) ([]File, error) {
|
||||
url := c.Resource(internal.LibraryItemFilePath).WithParam("library_item_id", id)
|
||||
var res []File
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItemFile returns a file with the provided name for a library item.
|
||||
func (c *Manager) GetLibraryItemFile(ctx context.Context, id, fileName string) (*File, error) {
|
||||
url := c.Resource(internal.LibraryItemFilePath).WithID(id).WithAction("get")
|
||||
spec := struct {
|
||||
Name string `json:"name"`
|
||||
}{fileName}
|
||||
var res File
|
||||
return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
180
vendor/github.com/vmware/govmomi/vapi/library/library_item.go
generated
vendored
Normal file
180
vendor/github.com/vmware/govmomi/vapi/library/library_item.go
generated
vendored
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
)
|
||||
|
||||
const (
|
||||
ItemTypeISO = "iso"
|
||||
ItemTypeOVF = "ovf"
|
||||
ItemTypeVMTX = "vm-template"
|
||||
)
|
||||
|
||||
// Item provides methods to create, read, update, delete, and enumerate library items.
|
||||
type Item struct {
|
||||
Cached bool `json:"cached,omitempty"`
|
||||
ContentVersion string `json:"content_version,omitempty"`
|
||||
CreationTime *time.Time `json:"creation_time,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
LastModifiedTime *time.Time `json:"last_modified_time,omitempty"`
|
||||
LastSyncTime *time.Time `json:"last_sync_time,omitempty"`
|
||||
LibraryID string `json:"library_id,omitempty"`
|
||||
MetadataVersion string `json:"metadata_version,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
SourceID string `json:"source_id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// Patch merges updates from the given src.
|
||||
func (i *Item) Patch(src *Item) {
|
||||
if src.Name != "" {
|
||||
i.Name = src.Name
|
||||
}
|
||||
if src.Description != "" {
|
||||
i.Description = src.Description
|
||||
}
|
||||
if src.Type != "" {
|
||||
i.Type = src.Type
|
||||
}
|
||||
if src.Version != "" {
|
||||
i.Version = src.Version
|
||||
}
|
||||
}
|
||||
|
||||
// CreateLibraryItem creates a new library item
|
||||
func (c *Manager) CreateLibraryItem(ctx context.Context, item Item) (string, error) {
|
||||
type createItemSpec struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
LibraryID string `json:"library_id,omitempty"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
spec := struct {
|
||||
Item createItemSpec `json:"create_spec"`
|
||||
}{
|
||||
Item: createItemSpec{
|
||||
Name: item.Name,
|
||||
Description: item.Description,
|
||||
LibraryID: item.LibraryID,
|
||||
Type: item.Type,
|
||||
},
|
||||
}
|
||||
url := c.Resource(internal.LibraryItemPath)
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// CopyLibraryItem copies a library item
|
||||
func (c *Manager) CopyLibraryItem(ctx context.Context, src *Item, dst Item) (string, error) {
|
||||
body := struct {
|
||||
Item `json:"destination_create_spec"`
|
||||
}{dst}
|
||||
url := c.Resource(internal.LibraryItemPath).WithID(src.ID).WithAction("copy")
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, body), &res)
|
||||
}
|
||||
|
||||
// SyncLibraryItem syncs a subscribed library item
|
||||
func (c *Manager) SyncLibraryItem(ctx context.Context, item *Item, force bool) error {
|
||||
body := struct {
|
||||
Force bool `json:"force_sync_content"`
|
||||
}{force}
|
||||
url := c.Resource(internal.SubscribedLibraryItem).WithID(item.ID).WithAction("sync")
|
||||
return c.Do(ctx, url.Request(http.MethodPost, body), nil)
|
||||
}
|
||||
|
||||
// PublishLibraryItem publishes a library item to specified subscriptions.
|
||||
// If no subscriptions are specified, then publishes the library item to all subscriptions.
|
||||
func (c *Manager) PublishLibraryItem(ctx context.Context, item *Item, force bool, subscriptions []string) error {
|
||||
body := internal.SubscriptionItemDestinationSpec{
|
||||
Force: force,
|
||||
}
|
||||
for i := range subscriptions {
|
||||
body.Subscriptions = append(body.Subscriptions, internal.SubscriptionDestination{ID: subscriptions[i]})
|
||||
}
|
||||
url := c.Resource(internal.LibraryItemPath).WithID(item.ID).WithAction("publish")
|
||||
return c.Do(ctx, url.Request(http.MethodPost, body), nil)
|
||||
}
|
||||
|
||||
// DeleteLibraryItem deletes an existing library item.
|
||||
func (c *Manager) DeleteLibraryItem(ctx context.Context, item *Item) error {
|
||||
url := c.Resource(internal.LibraryItemPath).WithID(item.ID)
|
||||
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
}
|
||||
|
||||
// ListLibraryItems returns a list of all items in a content library.
|
||||
func (c *Manager) ListLibraryItems(ctx context.Context, id string) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryItemPath).WithParam("library_id", id)
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItem returns information on a library item for the given ID.
|
||||
func (c *Manager) GetLibraryItem(ctx context.Context, id string) (*Item, error) {
|
||||
url := c.Resource(internal.LibraryItemPath).WithID(id)
|
||||
var res Item
|
||||
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItems returns a list of all the library items for the specified library.
|
||||
func (c *Manager) GetLibraryItems(ctx context.Context, libraryID string) ([]Item, error) {
|
||||
ids, err := c.ListLibraryItems(ctx, libraryID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get library items failed for: %s", err)
|
||||
}
|
||||
var items []Item
|
||||
for _, id := range ids {
|
||||
item, err := c.GetLibraryItem(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get library item for %s failed for %s", id, err)
|
||||
}
|
||||
items = append(items, *item)
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// FindItem is the search criteria for finding library items.
|
||||
type FindItem struct {
|
||||
Cached *bool `json:"cached,omitempty"`
|
||||
LibraryID string `json:"library_id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
SourceID string `json:"source_id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// FindLibraryItems returns the IDs of all the library items that match the
|
||||
// search criteria.
|
||||
func (c *Manager) FindLibraryItems(
|
||||
ctx context.Context, search FindItem) ([]string, error) {
|
||||
|
||||
url := c.Resource(internal.LibraryItemPath).WithAction("find")
|
||||
spec := struct {
|
||||
Spec FindItem `json:"spec"`
|
||||
}{search}
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
71
vendor/github.com/vmware/govmomi/vapi/library/library_item_downloadsession_file.go
generated
vendored
Normal file
71
vendor/github.com/vmware/govmomi/vapi/library/library_item_downloadsession_file.go
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
)
|
||||
|
||||
// DownloadFile is the specification for the downloadsession
|
||||
// operations file:add, file:get, and file:list.
|
||||
type DownloadFile struct {
|
||||
BytesTransferred int64 `json:"bytes_transferred"`
|
||||
Checksum *Checksum `json:"checksum_info,omitempty"`
|
||||
DownloadEndpoint *TransferEndpoint `json:"download_endpoint,omitempty"`
|
||||
ErrorMessage *rest.LocalizableMessage `json:"error_message,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// GetLibraryItemDownloadSessionFile retrieves information about a specific file that is a part of an download session.
|
||||
func (c *Manager) GetLibraryItemDownloadSessionFile(ctx context.Context, sessionID string, name string) (*DownloadFile, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSessionFile).WithID(sessionID).WithAction("get")
|
||||
spec := struct {
|
||||
Name string `json:"file_name"`
|
||||
}{name}
|
||||
var res DownloadFile
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Status == "ERROR" {
|
||||
return nil, res.ErrorMessage
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// ListLibraryItemDownloadSessionFile retrieves information about a specific file that is a part of an download session.
|
||||
func (c *Manager) ListLibraryItemDownloadSessionFile(ctx context.Context, sessionID string) ([]DownloadFile, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSessionFile).WithParam("download_session_id", sessionID)
|
||||
var res []DownloadFile
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// PrepareLibraryItemDownloadSessionFile retrieves information about a specific file that is a part of an download session.
|
||||
func (c *Manager) PrepareLibraryItemDownloadSessionFile(ctx context.Context, sessionID string, name string) (*DownloadFile, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSessionFile).WithID(sessionID).WithAction("prepare")
|
||||
spec := struct {
|
||||
Name string `json:"file_name"`
|
||||
}{name}
|
||||
var res DownloadFile
|
||||
return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
165
vendor/github.com/vmware/govmomi/vapi/library/library_item_updatesession.go
generated
vendored
Normal file
165
vendor/github.com/vmware/govmomi/vapi/library/library_item_updatesession.go
generated
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
)
|
||||
|
||||
// Session is used to create an initial update or download session
|
||||
type Session struct {
|
||||
ClientProgress int64 `json:"client_progress,omitempty"`
|
||||
ErrorMessage *rest.LocalizableMessage `json:"error_message,omitempty"`
|
||||
ExpirationTime *time.Time `json:"expiration_time,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
LibraryItemContentVersion string `json:"library_item_content_version,omitempty"`
|
||||
LibraryItemID string `json:"library_item_id,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
// CreateLibraryItemUpdateSession creates a new library item
|
||||
func (c *Manager) CreateLibraryItemUpdateSession(ctx context.Context, session Session) (string, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession)
|
||||
spec := struct {
|
||||
CreateSpec Session `json:"create_spec"`
|
||||
}{session}
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItemUpdateSession gets the update session information with status
|
||||
func (c *Manager) GetLibraryItemUpdateSession(ctx context.Context, id string) (*Session, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id)
|
||||
var res Session
|
||||
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// ListLibraryItemUpdateSession gets the list of update sessions
|
||||
func (c *Manager) ListLibraryItemUpdateSession(ctx context.Context) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession)
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// CancelLibraryItemUpdateSession cancels an update session
|
||||
func (c *Manager) CancelLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id).WithAction("cancel")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// CompleteLibraryItemUpdateSession completes an update session
|
||||
func (c *Manager) CompleteLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id).WithAction("complete")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// DeleteLibraryItemUpdateSession deletes an update session
|
||||
func (c *Manager) DeleteLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id)
|
||||
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
}
|
||||
|
||||
// FailLibraryItemUpdateSession fails an update session
|
||||
func (c *Manager) FailLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id).WithAction("fail")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// KeepAliveLibraryItemUpdateSession keeps an inactive update session alive.
|
||||
func (c *Manager) KeepAliveLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id).WithAction("keep-alive")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// WaitOnLibraryItemUpdateSession blocks until the update session is no longer
|
||||
// in the ACTIVE state.
|
||||
func (c *Manager) WaitOnLibraryItemUpdateSession(
|
||||
ctx context.Context, sessionID string,
|
||||
interval time.Duration, intervalCallback func()) error {
|
||||
|
||||
// Wait until the upload operation is complete to return.
|
||||
for {
|
||||
session, err := c.GetLibraryItemUpdateSession(ctx, sessionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if session.State != "ACTIVE" {
|
||||
if session.State == "ERROR" {
|
||||
return session.ErrorMessage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
time.Sleep(interval)
|
||||
if intervalCallback != nil {
|
||||
intervalCallback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateLibraryItemDownloadSession creates a new library item
|
||||
func (c *Manager) CreateLibraryItemDownloadSession(ctx context.Context, session Session) (string, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession)
|
||||
spec := struct {
|
||||
CreateSpec Session `json:"create_spec"`
|
||||
}{session}
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItemDownloadSession gets the download session information with status
|
||||
func (c *Manager) GetLibraryItemDownloadSession(ctx context.Context, id string) (*Session, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id)
|
||||
var res Session
|
||||
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// ListLibraryItemDownloadSession gets the list of download sessions
|
||||
func (c *Manager) ListLibraryItemDownloadSession(ctx context.Context) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession)
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// CancelLibraryItemDownloadSession cancels an download session
|
||||
func (c *Manager) CancelLibraryItemDownloadSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id).WithAction("cancel")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// DeleteLibraryItemDownloadSession deletes an download session
|
||||
func (c *Manager) DeleteLibraryItemDownloadSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id)
|
||||
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
}
|
||||
|
||||
// FailLibraryItemDownloadSession fails an download session
|
||||
func (c *Manager) FailLibraryItemDownloadSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id).WithAction("fail")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// KeepAliveLibraryItemDownloadSession keeps an inactive download session alive.
|
||||
func (c *Manager) KeepAliveLibraryItemDownloadSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id).WithAction("keep-alive")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
149
vendor/github.com/vmware/govmomi/vapi/library/library_item_updatesession_file.go
generated
vendored
Normal file
149
vendor/github.com/vmware/govmomi/vapi/library/library_item_updatesession_file.go
generated
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 library
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
)
|
||||
|
||||
// TransferEndpoint provides information on the source of a library item file.
|
||||
type TransferEndpoint struct {
|
||||
URI string `json:"uri,omitempty"`
|
||||
SSLCertificateThumbprint string `json:"ssl_certificate_thumbprint,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateFile is the specification for the updatesession
|
||||
// operations file:add, file:get, and file:list.
|
||||
type UpdateFile struct {
|
||||
BytesTransferred int64 `json:"bytes_transferred,omitempty"`
|
||||
Checksum *Checksum `json:"checksum_info,omitempty"`
|
||||
ErrorMessage *rest.LocalizableMessage `json:"error_message,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
SourceEndpoint *TransferEndpoint `json:"source_endpoint,omitempty"`
|
||||
SourceType string `json:"source_type"`
|
||||
Status string `json:"status,omitempty"`
|
||||
UploadEndpoint *TransferEndpoint `json:"upload_endpoint,omitempty"`
|
||||
}
|
||||
|
||||
// AddLibraryItemFile adds a file
|
||||
func (c *Manager) AddLibraryItemFile(ctx context.Context, sessionID string, updateFile UpdateFile) (*UpdateFile, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("add")
|
||||
spec := struct {
|
||||
FileSpec UpdateFile `json:"file_spec"`
|
||||
}{updateFile}
|
||||
var res UpdateFile
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Status == "ERROR" {
|
||||
return nil, res.ErrorMessage
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// AddLibraryItemFileFromURI adds a file from a remote URI.
|
||||
func (c *Manager) AddLibraryItemFileFromURI(
|
||||
ctx context.Context,
|
||||
sessionID, fileName, uri string) (*UpdateFile, error) {
|
||||
|
||||
n, fingerprint, err := c.getContentLengthAndFingerprint(ctx, uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, err := c.AddLibraryItemFile(ctx, sessionID, UpdateFile{
|
||||
Name: fileName,
|
||||
SourceType: "PULL",
|
||||
Size: n,
|
||||
SourceEndpoint: &TransferEndpoint{
|
||||
URI: uri,
|
||||
SSLCertificateThumbprint: fingerprint,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return info, c.CompleteLibraryItemUpdateSession(ctx, sessionID)
|
||||
}
|
||||
|
||||
// GetLibraryItemUpdateSessionFile retrieves information about a specific file
|
||||
// that is a part of an update session.
|
||||
func (c *Manager) GetLibraryItemUpdateSessionFile(ctx context.Context, sessionID string, fileName string) (*UpdateFile, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("get")
|
||||
spec := struct {
|
||||
Name string `json:"file_name"`
|
||||
}{fileName}
|
||||
var res UpdateFile
|
||||
return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// getContentLengthAndFingerprint gets the number of bytes returned
|
||||
// by the URI as well as the SHA1 fingerprint of the peer certificate
|
||||
// if the URI's scheme is https.
|
||||
func (c *Manager) getContentLengthAndFingerprint(
|
||||
ctx context.Context, uri string) (int64, string, error) {
|
||||
resp, err := c.Head(uri)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
if resp.TLS == nil || len(resp.TLS.PeerCertificates) == 0 {
|
||||
return resp.ContentLength, "", nil
|
||||
}
|
||||
fingerprint := c.Thumbprint(resp.Request.URL.Host)
|
||||
if fingerprint == "" {
|
||||
if c.DefaultTransport().TLSClientConfig.InsecureSkipVerify {
|
||||
fingerprint = soap.ThumbprintSHA1(resp.TLS.PeerCertificates[0])
|
||||
}
|
||||
}
|
||||
return resp.ContentLength, fingerprint, nil
|
||||
}
|
||||
|
||||
// ReadManifest converts an ovf manifest to a map of file name -> Checksum.
|
||||
func ReadManifest(m io.Reader) (map[string]*Checksum, error) {
|
||||
// expected format: openssl sha1 *.{ovf,vmdk}
|
||||
c := make(map[string]*Checksum)
|
||||
|
||||
scanner := bufio.NewScanner(m)
|
||||
for scanner.Scan() {
|
||||
line := strings.SplitN(scanner.Text(), ")=", 2)
|
||||
if len(line) != 2 {
|
||||
continue
|
||||
}
|
||||
name := strings.SplitN(line[0], "(", 2)
|
||||
if len(name) != 2 {
|
||||
continue
|
||||
}
|
||||
sum := &Checksum{
|
||||
Algorithm: strings.TrimSpace(name[0]),
|
||||
Checksum: strings.TrimSpace(line[1]),
|
||||
}
|
||||
c[name[1]] = sum
|
||||
}
|
||||
|
||||
return c, scanner.Err()
|
||||
}
|
||||
289
vendor/github.com/vmware/govmomi/vapi/rest/client.go
generated
vendored
Normal file
289
vendor/github.com/vmware/govmomi/vapi/rest/client.go
generated
vendored
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 rest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
)
|
||||
|
||||
// Client extends soap.Client to support JSON encoding, while inheriting security features, debug tracing and session persistence.
|
||||
type Client struct {
|
||||
mu sync.Mutex
|
||||
|
||||
*soap.Client
|
||||
sessionID string
|
||||
}
|
||||
|
||||
// Session information
|
||||
type Session struct {
|
||||
User string `json:"user"`
|
||||
Created time.Time `json:"created_time"`
|
||||
LastAccessed time.Time `json:"last_accessed_time"`
|
||||
}
|
||||
|
||||
// LocalizableMessage represents a localizable error
|
||||
type LocalizableMessage struct {
|
||||
Args []string `json:"args,omitempty"`
|
||||
DefaultMessage string `json:"default_message,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
func (m *LocalizableMessage) Error() string {
|
||||
return m.DefaultMessage
|
||||
}
|
||||
|
||||
// NewClient creates a new Client instance.
|
||||
func NewClient(c *vim25.Client) *Client {
|
||||
sc := c.Client.NewServiceClient(Path, "")
|
||||
|
||||
return &Client{Client: sc}
|
||||
}
|
||||
|
||||
// SessionID is set by calling Login() or optionally with the given id param
|
||||
func (c *Client) SessionID(id ...string) string {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if len(id) != 0 {
|
||||
c.sessionID = id[0]
|
||||
}
|
||||
return c.sessionID
|
||||
}
|
||||
|
||||
type marshaledClient struct {
|
||||
SoapClient *soap.Client
|
||||
SessionID string
|
||||
}
|
||||
|
||||
func (c *Client) MarshalJSON() ([]byte, error) {
|
||||
m := marshaledClient{
|
||||
SoapClient: c.Client,
|
||||
SessionID: c.sessionID,
|
||||
}
|
||||
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func (c *Client) UnmarshalJSON(b []byte) error {
|
||||
var m marshaledClient
|
||||
|
||||
err := json.Unmarshal(b, &m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*c = Client{
|
||||
Client: m.SoapClient,
|
||||
sessionID: m.SessionID,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Resource helper for the given path.
|
||||
func (c *Client) Resource(path string) *Resource {
|
||||
r := &Resource{u: c.URL()}
|
||||
r.u.Path = Path + path
|
||||
return r
|
||||
}
|
||||
|
||||
type Signer interface {
|
||||
SignRequest(*http.Request) error
|
||||
}
|
||||
|
||||
type signerContext struct{}
|
||||
|
||||
func (c *Client) WithSigner(ctx context.Context, s Signer) context.Context {
|
||||
return context.WithValue(ctx, signerContext{}, s)
|
||||
}
|
||||
|
||||
type statusError struct {
|
||||
res *http.Response
|
||||
}
|
||||
|
||||
func (e *statusError) Error() string {
|
||||
return fmt.Sprintf("%s %s: %s", e.res.Request.Method, e.res.Request.URL, e.res.Status)
|
||||
}
|
||||
|
||||
// Do sends the http.Request, decoding resBody if provided.
|
||||
func (c *Client) Do(ctx context.Context, req *http.Request, resBody interface{}) error {
|
||||
switch req.Method {
|
||||
case http.MethodPost, http.MethodPatch:
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
if id := c.SessionID(); id != "" {
|
||||
req.Header.Set(internal.SessionCookieName, id)
|
||||
}
|
||||
|
||||
if s, ok := ctx.Value(signerContext{}).(Signer); ok {
|
||||
if err := s.SignRequest(req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return c.Client.Do(ctx, req, func(res *http.Response) error {
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
case http.StatusNoContent:
|
||||
case http.StatusBadRequest:
|
||||
// TODO: structured error types
|
||||
detail, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("%s: %s", res.Status, bytes.TrimSpace(detail))
|
||||
default:
|
||||
return &statusError{res}
|
||||
}
|
||||
|
||||
if resBody == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch b := resBody.(type) {
|
||||
case io.Writer:
|
||||
_, err := io.Copy(b, res.Body)
|
||||
return err
|
||||
default:
|
||||
val := struct {
|
||||
Value interface{} `json:"value,omitempty"`
|
||||
}{
|
||||
resBody,
|
||||
}
|
||||
return json.NewDecoder(res.Body).Decode(&val)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// authHeaders ensures the given map contains a REST auth header
|
||||
func (c *Client) authHeaders(h map[string]string) map[string]string {
|
||||
if _, exists := h[internal.SessionCookieName]; exists {
|
||||
return h
|
||||
}
|
||||
if h == nil {
|
||||
h = make(map[string]string)
|
||||
}
|
||||
|
||||
h[internal.SessionCookieName] = c.SessionID()
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// Download wraps soap.Client.Download, adding the REST authentication header
|
||||
func (c *Client) Download(ctx context.Context, u *url.URL, param *soap.Download) (io.ReadCloser, int64, error) {
|
||||
p := *param
|
||||
p.Headers = c.authHeaders(p.Headers)
|
||||
return c.Client.Download(ctx, u, &p)
|
||||
}
|
||||
|
||||
// DownloadFile wraps soap.Client.DownloadFile, adding the REST authentication header
|
||||
func (c *Client) DownloadFile(ctx context.Context, file string, u *url.URL, param *soap.Download) error {
|
||||
p := *param
|
||||
p.Headers = c.authHeaders(p.Headers)
|
||||
return c.Client.DownloadFile(ctx, file, u, &p)
|
||||
}
|
||||
|
||||
// Upload wraps soap.Client.Upload, adding the REST authentication header
|
||||
func (c *Client) Upload(ctx context.Context, f io.Reader, u *url.URL, param *soap.Upload) error {
|
||||
p := *param
|
||||
p.Headers = c.authHeaders(p.Headers)
|
||||
return c.Client.Upload(ctx, f, u, &p)
|
||||
}
|
||||
|
||||
// Login creates a new session via Basic Authentication with the given url.Userinfo.
|
||||
func (c *Client) Login(ctx context.Context, user *url.Userinfo) error {
|
||||
req := c.Resource(internal.SessionPath).Request(http.MethodPost)
|
||||
|
||||
req.Header.Set(internal.UseHeaderAuthn, "true")
|
||||
|
||||
if user != nil {
|
||||
if password, ok := user.Password(); ok {
|
||||
req.SetBasicAuth(user.Username(), password)
|
||||
}
|
||||
}
|
||||
|
||||
var id string
|
||||
err := c.Do(ctx, req, &id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.SessionID(id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) LoginByToken(ctx context.Context) error {
|
||||
return c.Login(ctx, nil)
|
||||
}
|
||||
|
||||
// Session returns the user's current session.
|
||||
// Nil is returned if the session is not authenticated.
|
||||
func (c *Client) Session(ctx context.Context) (*Session, error) {
|
||||
var s Session
|
||||
req := c.Resource(internal.SessionPath).WithAction("get").Request(http.MethodPost)
|
||||
err := c.Do(ctx, req, &s)
|
||||
if err != nil {
|
||||
if e, ok := err.(*statusError); ok {
|
||||
if e.res.StatusCode == http.StatusUnauthorized {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
// Logout deletes the current session.
|
||||
func (c *Client) Logout(ctx context.Context) error {
|
||||
req := c.Resource(internal.SessionPath).Request(http.MethodDelete)
|
||||
return c.Do(ctx, req, nil)
|
||||
}
|
||||
|
||||
// Valid returns whether or not the client is valid and ready for use.
|
||||
// This should be called after unmarshalling the client.
|
||||
func (c *Client) Valid() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if c.Client == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Path returns rest.Path (see cache.Client)
|
||||
func (c *Client) Path() string {
|
||||
return Path
|
||||
}
|
||||
89
vendor/github.com/vmware/govmomi/vapi/rest/resource.go
generated
vendored
Normal file
89
vendor/github.com/vmware/govmomi/vapi/rest/resource.go
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
Copyright (c) 2019 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 rest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const (
|
||||
Path = "/rest"
|
||||
)
|
||||
|
||||
// Resource wraps url.URL with helpers
|
||||
type Resource struct {
|
||||
u *url.URL
|
||||
}
|
||||
|
||||
func (r *Resource) String() string {
|
||||
return r.u.String()
|
||||
}
|
||||
|
||||
// WithID appends id to the URL.Path
|
||||
func (r *Resource) WithID(id string) *Resource {
|
||||
r.u.Path += "/id:" + id
|
||||
return r
|
||||
}
|
||||
|
||||
// WithAction sets adds action to the URL.RawQuery
|
||||
func (r *Resource) WithAction(action string) *Resource {
|
||||
return r.WithParam("~action", action)
|
||||
}
|
||||
|
||||
// WithParam sets adds a parameter to the URL.RawQuery
|
||||
func (r *Resource) WithParam(name string, value string) *Resource {
|
||||
r.u.RawQuery = url.Values{
|
||||
name: []string{value},
|
||||
}.Encode()
|
||||
return r
|
||||
}
|
||||
|
||||
// Request returns a new http.Request for the given method.
|
||||
// An optional body can be provided for POST and PATCH methods.
|
||||
func (r *Resource) Request(method string, body ...interface{}) *http.Request {
|
||||
rdr := io.MultiReader() // empty body by default
|
||||
if len(body) != 0 {
|
||||
rdr = encode(body[0])
|
||||
}
|
||||
req, err := http.NewRequest(method, r.u.String(), rdr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
type errorReader struct {
|
||||
e error
|
||||
}
|
||||
|
||||
func (e errorReader) Read([]byte) (int, error) {
|
||||
return -1, e.e
|
||||
}
|
||||
|
||||
// encode body as JSON, deferring any errors until io.Reader is used.
|
||||
func encode(body interface{}) io.Reader {
|
||||
var b bytes.Buffer
|
||||
err := json.NewEncoder(&b).Encode(body)
|
||||
if err != nil {
|
||||
return errorReader{err}
|
||||
}
|
||||
return &b
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue