github.com/vmware/govmomi@v0.37.2/vapi/library/library_item_updatesession_file.go (about) 1 /* 2 Copyright (c) 2019-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 "bufio" 21 "context" 22 "fmt" 23 "io" 24 "net/http" 25 "strings" 26 27 "github.com/vmware/govmomi/vapi/internal" 28 "github.com/vmware/govmomi/vapi/rest" 29 "github.com/vmware/govmomi/vim25/soap" 30 ) 31 32 // TransferEndpoint provides information on the source of a library item file. 33 type TransferEndpoint struct { 34 URI string `json:"uri,omitempty"` 35 SSLCertificate string `json:"ssl_certificate,omitempty"` 36 SSLCertificateThumbprint string `json:"ssl_certificate_thumbprint,omitempty"` 37 } 38 39 type ProbeResult struct { 40 Status string `json:"status"` 41 SSLThumbprint string `json:"ssl_thumbprint,omitempty"` 42 SSLCertificate string `json:"ssl_certificate,omitempty"` 43 ErrorMessages []rest.LocalizableMessage `json:"error_messages,omitempty"` 44 } 45 46 // UpdateFile is the specification for the updatesession 47 // operations file:add, file:get, and file:list. 48 type UpdateFile struct { 49 BytesTransferred int64 `json:"bytes_transferred,omitempty"` 50 Checksum *Checksum `json:"checksum_info,omitempty"` 51 ErrorMessage *rest.LocalizableMessage `json:"error_message,omitempty"` 52 Name string `json:"name"` 53 Size int64 `json:"size,omitempty"` 54 SourceEndpoint *TransferEndpoint `json:"source_endpoint,omitempty"` 55 SourceType string `json:"source_type"` 56 Status string `json:"status,omitempty"` 57 UploadEndpoint *TransferEndpoint `json:"upload_endpoint,omitempty"` 58 } 59 60 // FileValidationError contains the validation error of a file in the update session 61 type FileValidationError struct { 62 Name string `json:"name"` 63 ErrorMessage rest.LocalizableMessage `json:"error_message"` 64 } 65 66 // UpdateFileValidation contains the result of validating the files in the update session 67 type UpdateFileValidation struct { 68 HasErrors bool `json:"has_errors"` 69 MissingFiles []string `json:"missing_files,omitempty"` 70 InvalidFiles []FileValidationError `json:"invalid_files,omitempty"` 71 } 72 73 // AddLibraryItemFile adds a file 74 func (c *Manager) AddLibraryItemFile(ctx context.Context, sessionID string, updateFile UpdateFile) (*UpdateFile, error) { 75 url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("add") 76 spec := struct { 77 FileSpec UpdateFile `json:"file_spec"` 78 }{updateFile} 79 var res UpdateFile 80 err := c.Do(ctx, url.Request(http.MethodPost, spec), &res) 81 if err != nil { 82 return nil, err 83 } 84 if res.Status == "ERROR" { 85 return nil, res.ErrorMessage 86 } 87 return &res, nil 88 } 89 90 // AddLibraryItemFileFromURI adds a file from a remote URI. 91 func (c *Manager) AddLibraryItemFileFromURI(ctx context.Context, sessionID, name, uri string, checksum ...Checksum) (*UpdateFile, error) { 92 source := &TransferEndpoint{ 93 URI: uri, 94 } 95 96 file := UpdateFile{ 97 Name: name, 98 SourceType: "PULL", 99 SourceEndpoint: source, 100 } 101 102 if len(checksum) == 1 && checksum[0].Checksum != "" { 103 file.Checksum = &checksum[0] 104 } else if len(checksum) > 1 { 105 return nil, fmt.Errorf("expected 0 or 1 checksum, got %d", len(checksum)) 106 } 107 108 if res, err := c.Head(uri); err == nil { 109 file.Size = res.ContentLength 110 if res.TLS != nil { 111 source.SSLCertificateThumbprint = soap.ThumbprintSHA1(res.TLS.PeerCertificates[0]) 112 } 113 } else { 114 res, err := c.ProbeTransferEndpoint(ctx, *source) 115 if err != nil { 116 return nil, err 117 } 118 if res.SSLCertificate != "" { 119 source.SSLCertificate = res.SSLCertificate 120 } else { 121 source.SSLCertificateThumbprint = res.SSLThumbprint 122 } 123 } 124 125 return c.AddLibraryItemFile(ctx, sessionID, file) 126 } 127 128 // GetLibraryItemUpdateSessionFile retrieves information about a specific file 129 // that is a part of an update session. 130 func (c *Manager) GetLibraryItemUpdateSessionFile(ctx context.Context, sessionID string, fileName string) (*UpdateFile, error) { 131 url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("get") 132 spec := struct { 133 Name string `json:"file_name"` 134 }{fileName} 135 var res UpdateFile 136 return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res) 137 } 138 139 // ListLibraryItemUpdateSessionFile lists all files in the library item associated with the update session 140 func (c *Manager) ListLibraryItemUpdateSessionFile(ctx context.Context, sessionID string) ([]UpdateFile, error) { 141 url := c.Resource(internal.LibraryItemUpdateSessionFile).WithParam("update_session_id", sessionID) 142 var res []UpdateFile 143 return res, c.Do(ctx, url.Request(http.MethodGet), &res) 144 } 145 146 // ValidateLibraryItemUpdateSessionFile validates all files in the library item associated with the update session 147 func (c *Manager) ValidateLibraryItemUpdateSessionFile(ctx context.Context, sessionID string) (*UpdateFileValidation, error) { 148 url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("validate") 149 var res UpdateFileValidation 150 return &res, c.Do(ctx, url.Request(http.MethodPost), &res) 151 } 152 153 // RemoveLibraryItemUpdateSessionFile requests a file to be removed. The file will only be effectively removed when the update session is completed. 154 func (c *Manager) RemoveLibraryItemUpdateSessionFile(ctx context.Context, sessionID string, fileName string) error { 155 url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("remove") 156 spec := struct { 157 Name string `json:"file_name"` 158 }{fileName} 159 return c.Do(ctx, url.Request(http.MethodPost, spec), nil) 160 } 161 162 func (c *Manager) ProbeTransferEndpoint(ctx context.Context, endpoint TransferEndpoint) (*ProbeResult, error) { 163 url := c.Resource(internal.LibraryItemUpdateSessionFile).WithAction("probe") 164 spec := struct { 165 SourceEndpoint TransferEndpoint `json:"source_endpoint"` 166 }{endpoint} 167 var res ProbeResult 168 return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res) 169 } 170 171 // ReadManifest converts an ovf manifest to a map of file name -> Checksum. 172 func ReadManifest(m io.Reader) (map[string]*Checksum, error) { 173 // expected format: openssl sha1 *.{ovf,vmdk} 174 c := make(map[string]*Checksum) 175 176 scanner := bufio.NewScanner(m) 177 for scanner.Scan() { 178 line := strings.SplitN(scanner.Text(), ")=", 2) 179 if len(line) != 2 { 180 continue 181 } 182 name := strings.SplitN(line[0], "(", 2) 183 if len(name) != 2 { 184 continue 185 } 186 sum := &Checksum{ 187 Algorithm: strings.TrimSpace(name[0]), 188 Checksum: strings.TrimSpace(line[1]), 189 } 190 c[name[1]] = sum 191 } 192 193 return c, scanner.Err() 194 }