github.com/vmware/govmomi@v0.37.2/govc/library/import.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 "context" 21 "errors" 22 "flag" 23 "fmt" 24 "net/url" 25 "os" 26 "path/filepath" 27 "strings" 28 "time" 29 30 "github.com/vmware/govmomi/govc/cli" 31 "github.com/vmware/govmomi/govc/flags" 32 "github.com/vmware/govmomi/govc/importx" 33 "github.com/vmware/govmomi/vapi/library" 34 "github.com/vmware/govmomi/vim25/soap" 35 ) 36 37 type item struct { 38 *flags.ClientFlag 39 *flags.OutputFlag 40 library.Item 41 library.Checksum 42 43 manifest bool 44 pull bool 45 } 46 47 func init() { 48 cli.Register("library.import", &item{}) 49 } 50 51 func (cmd *item) Register(ctx context.Context, f *flag.FlagSet) { 52 cmd.ClientFlag, ctx = flags.NewClientFlag(ctx) 53 cmd.ClientFlag.Register(ctx, f) 54 55 cmd.OutputFlag, ctx = flags.NewOutputFlag(ctx) 56 cmd.OutputFlag.Register(ctx, f) 57 58 f.StringVar(&cmd.Name, "n", "", "Library item name") 59 f.StringVar(&cmd.Type, "t", "", "Library item type") 60 f.BoolVar(&cmd.manifest, "m", false, "Require ova manifest") 61 f.BoolVar(&cmd.pull, "pull", false, "Pull library item from http endpoint") 62 f.StringVar(&cmd.Checksum.Checksum, "c", "", "Checksum value to verify the pulled library item") 63 f.StringVar(&cmd.Checksum.Algorithm, "a", "SHA256", "Algorithm used to calculate the checksum. Possible values are: SHA1, MD5, SHA256 (default), SHA512") 64 } 65 66 func (cmd *item) Usage() string { 67 return "LIBRARY ITEM" 68 } 69 70 func (cmd *item) Description() string { 71 return `Import library items. 72 73 Examples: 74 govc library.import library_name file.ova 75 govc library.import library_name file.ovf 76 govc library.import library_name file.iso 77 govc library.import library_id file.iso # Use library id if multiple libraries have the same name 78 govc library.import library_name/item_name file.ova # update existing item 79 govc library.import library_name http://example.com/file.ovf # download and push to vCenter 80 govc library.import -pull library_name http://example.com/file.ova # direct pull from vCenter 81 govc library.import -pull -c=<checksum> -a=<SHA1|MD5|SHA256|SHA512> library_name http://example.com/file.ova # direct pull from vCenter with checksum validation` 82 } 83 84 func (cmd *item) Process(ctx context.Context) error { 85 if err := cmd.ClientFlag.Process(ctx); err != nil { 86 return err 87 } 88 return cmd.OutputFlag.Process(ctx) 89 } 90 91 func (cmd *item) Run(ctx context.Context, f *flag.FlagSet) error { 92 if f.NArg() != 2 { 93 return flag.ErrHelp 94 } 95 96 // Checksums are verified after the file is uploaded to a server. 97 // Check the algorithm and fail early if it's not supported. 98 if cmd.pull && cmd.Checksum.Checksum != "" { 99 switch cmd.Checksum.Algorithm { 100 case "SHA1", "MD5", "SHA256", "SHA512": 101 default: 102 return fmt.Errorf("invalid checksum algorithm: %s", cmd.Checksum.Algorithm) 103 } 104 } 105 106 file := f.Arg(1) 107 base := filepath.Base(file) 108 ext := filepath.Ext(base) 109 mf := strings.Replace(base, ext, ".mf", 1) 110 kind := "" 111 client, err := cmd.Client() 112 if err != nil { 113 return err 114 } 115 opener := importx.Opener{Client: client} 116 archive := &importx.ArchiveFlag{Archive: &importx.FileArchive{Path: file, Opener: opener}} 117 118 manifest := make(map[string]*library.Checksum) 119 if cmd.Name == "" { 120 cmd.Name = strings.TrimSuffix(base, ext) 121 } 122 123 switch ext { 124 case ".ova": 125 archive.Archive = &importx.TapeArchive{Path: file, Opener: opener} 126 base = "*.ovf" 127 mf = "*.mf" 128 kind = library.ItemTypeOVF 129 case ".ovf": 130 kind = library.ItemTypeOVF 131 case ".iso": 132 kind = library.ItemTypeISO 133 } 134 135 if cmd.Type == "" { 136 cmd.Type = kind 137 } 138 139 if !cmd.pull && cmd.Type == library.ItemTypeOVF { 140 f, _, err := archive.Open(mf) 141 if err == nil { 142 sums, err := library.ReadManifest(f) 143 _ = f.Close() 144 if err != nil { 145 return err 146 } 147 manifest = sums 148 } else { 149 msg := fmt.Sprintf("manifest %q: %s", mf, err) 150 if cmd.manifest { 151 return errors.New(msg) 152 } 153 fmt.Fprintln(os.Stderr, msg) 154 } 155 } 156 157 c, err := cmd.RestClient() 158 if err != nil { 159 return err 160 } 161 cmd.KeepAlive(c) 162 163 m := library.NewManager(c) 164 res, err := flags.ContentLibraryResult(ctx, c, "", f.Arg(0)) 165 if err != nil { 166 return err 167 } 168 169 switch t := res.GetResult().(type) { 170 case library.Library: 171 cmd.LibraryID = t.ID 172 cmd.ID, err = m.CreateLibraryItem(ctx, cmd.Item) 173 if err != nil { 174 return err 175 } 176 case library.Item: 177 cmd.Item = t 178 default: 179 return fmt.Errorf("%q is a %T", f.Arg(0), t) 180 } 181 182 session, err := m.CreateLibraryItemUpdateSession(ctx, library.Session{ 183 LibraryItemID: cmd.ID, 184 }) 185 if err != nil { 186 return err 187 } 188 if cmd.pull { 189 _, err = m.AddLibraryItemFileFromURI(ctx, session, filepath.Base(file), file, cmd.Checksum) 190 if err != nil { 191 return err 192 } 193 194 err = m.CompleteLibraryItemUpdateSession(ctx, session) 195 if err != nil { 196 return err 197 } 198 199 return m.WaitOnLibraryItemUpdateSession(ctx, session, 3*time.Second, nil) 200 } 201 202 upload := func(name string) error { 203 f, size, err := archive.Open(name) 204 if err != nil { 205 return err 206 } 207 defer f.Close() 208 209 if e, ok := f.(*importx.TapeArchiveEntry); ok { 210 name = e.Name // expand path.Match's (e.g. "*.ovf" -> "name.ovf") 211 } 212 213 info := library.UpdateFile{ 214 Name: name, 215 SourceType: "PUSH", 216 Checksum: manifest[name], 217 Size: size, 218 } 219 220 update, err := m.AddLibraryItemFile(ctx, session, info) 221 if err != nil { 222 return err 223 } 224 225 p := soap.DefaultUpload 226 p.ContentLength = size 227 u, err := url.Parse(update.UploadEndpoint.URI) 228 if err != nil { 229 return err 230 } 231 if cmd.OutputFlag.TTY { 232 logger := cmd.ProgressLogger(fmt.Sprintf("Uploading %s... ", name)) 233 p.Progress = logger 234 defer logger.Wait() 235 } 236 return c.Upload(ctx, f, u, &p) 237 } 238 239 if err = upload(base); err != nil { 240 return err 241 } 242 243 if cmd.Type == library.ItemTypeOVF { 244 o, err := archive.ReadOvf(base) 245 if err != nil { 246 return err 247 } 248 249 e, err := archive.ReadEnvelope(o) 250 if err != nil { 251 return fmt.Errorf("failed to parse ovf: %s", err) 252 } 253 254 for i := range e.References { 255 if err = upload(e.References[i].Href); err != nil { 256 return err 257 } 258 } 259 } 260 261 return m.CompleteLibraryItemUpdateSession(ctx, session) 262 }