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