github.com/vmware/govmomi@v0.43.0/vapi/library/library.go (about)

     1  /*
     2  Copyright (c) 2018-2024 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package library
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net/http"
    23  	"net/url"
    24  	"time"
    25  
    26  	"github.com/vmware/govmomi/object"
    27  	"github.com/vmware/govmomi/vapi/internal"
    28  	"github.com/vmware/govmomi/vapi/rest"
    29  )
    30  
    31  // StorageBacking defines a storage location where content in a library will be stored.
    32  type StorageBacking struct {
    33  	DatastoreID string `json:"datastore_id,omitempty"`
    34  	Type        string `json:"type,omitempty"`
    35  	StorageURI  string `json:"storage_uri,omitempty"`
    36  }
    37  
    38  // Library  provides methods to create, read, update, delete, and enumerate libraries.
    39  type Library struct {
    40  	CreationTime          *time.Time       `json:"creation_time,omitempty"`
    41  	Description           *string          `json:"description,omitempty"`
    42  	ID                    string           `json:"id,omitempty"`
    43  	LastModifiedTime      *time.Time       `json:"last_modified_time,omitempty"`
    44  	LastSyncTime          *time.Time       `json:"last_sync_time,omitempty"`
    45  	Name                  string           `json:"name,omitempty"`
    46  	Storage               []StorageBacking `json:"storage_backings,omitempty"`
    47  	Type                  string           `json:"type,omitempty"`
    48  	Version               string           `json:"version,omitempty"`
    49  	Subscription          *Subscription    `json:"subscription_info,omitempty"`
    50  	Publication           *Publication     `json:"publish_info,omitempty"`
    51  	SecurityPolicyID      string           `json:"security_policy_id,omitempty"`
    52  	UnsetSecurityPolicyID bool             `json:"unset_security_policy_id,omitempty"`
    53  	ServerGUID            string           `json:"server_guid,omitempty"`
    54  	StateInfo             *StateInfo       `json:"state_info,omitempty"`
    55  }
    56  
    57  // StateInfo provides the state info of a content library.
    58  type StateInfo struct {
    59  	State string `json:"state"`
    60  }
    61  
    62  // Subscription info
    63  type Subscription struct {
    64  	AuthenticationMethod string `json:"authentication_method"`
    65  	AutomaticSyncEnabled *bool  `json:"automatic_sync_enabled,omitempty"`
    66  	OnDemand             *bool  `json:"on_demand,omitempty"`
    67  	Password             string `json:"password,omitempty"`
    68  	SslThumbprint        string `json:"ssl_thumbprint,omitempty"`
    69  	SubscriptionURL      string `json:"subscription_url,omitempty"`
    70  	UserName             string `json:"user_name,omitempty"`
    71  }
    72  
    73  // Publication info
    74  type Publication struct {
    75  	AuthenticationMethod string `json:"authentication_method"`
    76  	UserName             string `json:"user_name,omitempty"`
    77  	Password             string `json:"password,omitempty"`
    78  	CurrentPassword      string `json:"current_password,omitempty"`
    79  	PersistJSON          *bool  `json:"persist_json_enabled,omitempty"`
    80  	Published            *bool  `json:"published,omitempty"`
    81  	PublishURL           string `json:"publish_url,omitempty"`
    82  }
    83  
    84  // SubscriberSummary as returned by ListSubscribers
    85  type SubscriberSummary struct {
    86  	LibraryID              string `json:"subscribed_library"`
    87  	LibraryName            string `json:"subscribed_library_name"`
    88  	SubscriptionID         string `json:"subscription"`
    89  	LibraryVcenterHostname string `json:"subscribed_library_vcenter_hostname,omitempty"`
    90  }
    91  
    92  // Placement information used to place a virtual machine template
    93  type Placement struct {
    94  	ResourcePool string `json:"resource_pool,omitempty"`
    95  	Host         string `json:"host,omitempty"`
    96  	Folder       string `json:"folder,omitempty"`
    97  	Cluster      string `json:"cluster,omitempty"`
    98  	Network      string `json:"network,omitempty"`
    99  }
   100  
   101  // Vcenter contains information about the vCenter Server instance where a subscribed library associated with a subscription exists.
   102  type Vcenter struct {
   103  	Hostname   string `json:"hostname"`
   104  	Port       int    `json:"https_port,omitempty"`
   105  	ServerGUID string `json:"server_guid"`
   106  }
   107  
   108  // Subscriber contains the detailed info for a library subscriber.
   109  type Subscriber struct {
   110  	LibraryID       string     `json:"subscribed_library"`
   111  	LibraryName     string     `json:"subscribed_library_name"`
   112  	LibraryLocation string     `json:"subscribed_library_location"`
   113  	Placement       *Placement `json:"subscribed_library_placement,omitempty"`
   114  	Vcenter         *Vcenter   `json:"subscribed_library_vcenter,omitempty"`
   115  }
   116  
   117  // SubscriberLibrary is the specification for a subscribed library to be associated with a subscription.
   118  type SubscriberLibrary struct {
   119  	Target    string     `json:"target"`
   120  	LibraryID string     `json:"subscribed_library,omitempty"`
   121  	Location  string     `json:"location"`
   122  	Vcenter   *Vcenter   `json:"vcenter,omitempty"`
   123  	Placement *Placement `json:"placement,omitempty"`
   124  }
   125  
   126  // Patch merges updates from the given src.
   127  func (l *Library) Patch(src *Library) {
   128  	if src.Name != "" {
   129  		l.Name = src.Name
   130  	}
   131  	if src.Description != nil {
   132  		l.Description = src.Description
   133  	}
   134  	if src.Version != "" {
   135  		l.Version = src.Version
   136  	}
   137  }
   138  
   139  // Manager extends rest.Client, adding content library related methods.
   140  type Manager struct {
   141  	*rest.Client
   142  }
   143  
   144  // NewManager creates a new Manager instance with the given client.
   145  func NewManager(client *rest.Client) *Manager {
   146  	return &Manager{
   147  		Client: client,
   148  	}
   149  }
   150  
   151  // Find is the search criteria for finding libraries.
   152  type Find struct {
   153  	Name string `json:"name,omitempty"`
   154  	Type string `json:"type,omitempty"`
   155  }
   156  
   157  // FindLibrary returns one or more libraries that match the provided search
   158  // criteria.
   159  //
   160  // The provided name is case-insensitive.
   161  //
   162  // Either the name or type of library may be set to empty values in order
   163  // to search for all libraries, all libraries with a specific name, regardless
   164  // of type, or all libraries of a specified type.
   165  func (c *Manager) FindLibrary(ctx context.Context, search Find) ([]string, error) {
   166  	url := c.Resource(internal.LibraryPath).WithAction("find")
   167  	spec := struct {
   168  		Spec Find `json:"spec"`
   169  	}{search}
   170  	var res []string
   171  	return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
   172  }
   173  
   174  // CreateLibrary creates a new library with the given Type, Name,
   175  // Description, and CategoryID.
   176  func (c *Manager) CreateLibrary(ctx context.Context, library Library) (string, error) {
   177  	spec := struct {
   178  		Library Library `json:"create_spec"`
   179  	}{library}
   180  	path := internal.LocalLibraryPath
   181  	if library.Type == "SUBSCRIBED" {
   182  		path = internal.SubscribedLibraryPath
   183  		sub := library.Subscription
   184  		u, err := url.Parse(sub.SubscriptionURL)
   185  		if err != nil {
   186  			return "", err
   187  		}
   188  		if u.Scheme == "https" && sub.SslThumbprint == "" {
   189  			thumbprint := c.Thumbprint(u.Host)
   190  			if thumbprint == "" {
   191  				t := c.DefaultTransport()
   192  				if t.TLSClientConfig.InsecureSkipVerify {
   193  					var info object.HostCertificateInfo
   194  					_ = info.FromURL(u, t.TLSClientConfig)
   195  					thumbprint = info.ThumbprintSHA1
   196  				}
   197  				sub.SslThumbprint = thumbprint
   198  			}
   199  		}
   200  	}
   201  	url := c.Resource(path)
   202  	var res string
   203  	return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
   204  }
   205  
   206  // SyncLibrary syncs a subscribed library.
   207  func (c *Manager) SyncLibrary(ctx context.Context, library *Library) error {
   208  	path := internal.SubscribedLibraryPath
   209  	url := c.Resource(path).WithID(library.ID).WithAction("sync")
   210  	return c.Do(ctx, url.Request(http.MethodPost), nil)
   211  }
   212  
   213  // PublishLibrary publishes the library to specified subscriptions.
   214  // If no subscriptions are specified, then publishes the library to all subscriptions.
   215  func (c *Manager) PublishLibrary(ctx context.Context, library *Library, subscriptions []string) error {
   216  	path := internal.LocalLibraryPath
   217  	var spec internal.SubscriptionDestinationSpec
   218  	for i := range subscriptions {
   219  		spec.Subscriptions = append(spec.Subscriptions, internal.SubscriptionDestination{ID: subscriptions[i]})
   220  	}
   221  	url := c.Resource(path).WithID(library.ID).WithAction("publish")
   222  	return c.Do(ctx, url.Request(http.MethodPost, spec), nil)
   223  }
   224  
   225  // UpdateLibrary can update one or both of the tag Description and Name fields.
   226  func (c *Manager) UpdateLibrary(ctx context.Context, l *Library) error {
   227  	spec := struct {
   228  		Library `json:"update_spec"`
   229  	}{
   230  		Library{
   231  			Name:        l.Name,
   232  			Description: l.Description,
   233  		},
   234  	}
   235  	url := c.Resource(internal.LibraryPath).WithID(l.ID)
   236  	return c.Do(ctx, url.Request(http.MethodPatch, spec), nil)
   237  }
   238  
   239  // DeleteLibrary deletes an existing library.
   240  func (c *Manager) DeleteLibrary(ctx context.Context, library *Library) error {
   241  	path := internal.LocalLibraryPath
   242  	if library.Type == "SUBSCRIBED" {
   243  		path = internal.SubscribedLibraryPath
   244  	}
   245  	url := c.Resource(path).WithID(library.ID)
   246  	return c.Do(ctx, url.Request(http.MethodDelete), nil)
   247  }
   248  
   249  // ListLibraries returns a list of all content library IDs in the system.
   250  func (c *Manager) ListLibraries(ctx context.Context) ([]string, error) {
   251  	url := c.Resource(internal.LibraryPath)
   252  	var res []string
   253  	return res, c.Do(ctx, url.Request(http.MethodGet), &res)
   254  }
   255  
   256  // GetLibraryByID returns information on a library for the given ID.
   257  func (c *Manager) GetLibraryByID(ctx context.Context, id string) (*Library, error) {
   258  	url := c.Resource(internal.LibraryPath).WithID(id)
   259  	var res Library
   260  	return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
   261  }
   262  
   263  // GetLibraryByName returns information on a library for the given name.
   264  func (c *Manager) GetLibraryByName(ctx context.Context, name string) (*Library, error) {
   265  	// Lookup by name
   266  	libraries, err := c.GetLibraries(ctx)
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  	for i := range libraries {
   271  		if libraries[i].Name == name {
   272  			return &libraries[i], nil
   273  		}
   274  	}
   275  	return nil, fmt.Errorf("library name (%s) not found", name)
   276  }
   277  
   278  // GetLibraries returns a list of all content library details in the system.
   279  func (c *Manager) GetLibraries(ctx context.Context) ([]Library, error) {
   280  	ids, err := c.ListLibraries(ctx)
   281  	if err != nil {
   282  		return nil, fmt.Errorf("get libraries failed for: %s", err)
   283  	}
   284  
   285  	var libraries []Library
   286  	for _, id := range ids {
   287  		library, err := c.GetLibraryByID(ctx, id)
   288  		if err != nil {
   289  			return nil, fmt.Errorf("get library %s failed for %s", id, err)
   290  		}
   291  
   292  		libraries = append(libraries, *library)
   293  
   294  	}
   295  	return libraries, nil
   296  }
   297  
   298  // ListSubscribers lists the subscriptions of the published library.
   299  func (c *Manager) ListSubscribers(ctx context.Context, library *Library) ([]SubscriberSummary, error) {
   300  	url := c.Resource(internal.Subscriptions).WithParam("library", library.ID)
   301  	var res []SubscriberSummary
   302  	return res, c.Do(ctx, url.Request(http.MethodGet), &res)
   303  }
   304  
   305  // CreateSubscriber creates a subscription of the published library.
   306  func (c *Manager) CreateSubscriber(ctx context.Context, library *Library, s SubscriberLibrary) (string, error) {
   307  	var spec struct {
   308  		Sub struct {
   309  			SubscriberLibrary SubscriberLibrary `json:"subscribed_library"`
   310  		} `json:"spec"`
   311  	}
   312  	spec.Sub.SubscriberLibrary = s
   313  	url := c.Resource(internal.Subscriptions).WithID(library.ID)
   314  	var res string
   315  	return res, c.Do(ctx, url.Request(http.MethodPost, &spec), &res)
   316  }
   317  
   318  // GetSubscriber returns information about the specified subscriber of the published library.
   319  func (c *Manager) GetSubscriber(ctx context.Context, library *Library, subscriber string) (*Subscriber, error) {
   320  	id := internal.SubscriptionDestination{ID: subscriber}
   321  	url := c.Resource(internal.Subscriptions).WithID(library.ID).WithAction("get")
   322  	var res Subscriber
   323  	return &res, c.Do(ctx, url.Request(http.MethodPost, &id), &res)
   324  }
   325  
   326  // DeleteSubscriber deletes the specified subscription of the published library.
   327  // The subscribed library associated with the subscription will not be deleted.
   328  func (c *Manager) DeleteSubscriber(ctx context.Context, library *Library, subscriber string) error {
   329  	id := internal.SubscriptionDestination{ID: subscriber}
   330  	url := c.Resource(internal.Subscriptions).WithID(library.ID).WithAction("delete")
   331  	return c.Do(ctx, url.Request(http.MethodPost, &id), nil)
   332  }
   333  
   334  // EvictSubscribedLibrary evicts the cached content of an on-demand subscribed library.
   335  // This operation allows the cached content of a subscribed library to be removed to free up storage capacity.
   336  func (c *Manager) EvictSubscribedLibrary(ctx context.Context, library *Library) error {
   337  	path := internal.SubscribedLibraryPath
   338  	url := c.Resource(path).WithID(library.ID).WithAction("evict")
   339  	return c.Do(ctx, url.Request(http.MethodPost), nil)
   340  }