github.com/vmware/govmomi@v0.51.0/simulator/host_datastore_browser.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 simulator 6 7 import ( 8 "os" 9 "path" 10 "strings" 11 12 "github.com/vmware/govmomi/internal" 13 "github.com/vmware/govmomi/vim25/methods" 14 "github.com/vmware/govmomi/vim25/mo" 15 "github.com/vmware/govmomi/vim25/soap" 16 "github.com/vmware/govmomi/vim25/types" 17 ) 18 19 type HostDatastoreBrowser struct { 20 mo.HostDatastoreBrowser 21 } 22 23 type searchDatastore struct { 24 *HostDatastoreBrowser 25 26 DatastorePath string 27 SearchSpec *types.HostDatastoreBrowserSearchSpec 28 29 res []types.HostDatastoreBrowserSearchResults 30 31 recurse bool 32 } 33 34 func (s *searchDatastore) addFile(fname string, file os.FileInfo, res *types.HostDatastoreBrowserSearchResults) { 35 details := s.SearchSpec.Details 36 if details == nil { 37 details = new(types.FileQueryFlags) 38 } 39 40 name := file.Name() 41 42 info := types.FileInfo{ 43 Path: name, 44 FriendlyName: fname, 45 } 46 47 var finfo types.BaseFileInfo = &info 48 49 if details.FileSize { 50 info.FileSize = file.Size() 51 } 52 53 if details.Modification { 54 mtime := file.ModTime() 55 info.Modification = &mtime 56 } 57 58 if isTrue(details.FileOwner) { 59 // Assume for now this process created all files in the datastore 60 user := os.Getenv("USER") 61 62 info.Owner = user 63 } 64 65 if file.IsDir() { 66 finfo = &types.FolderFileInfo{FileInfo: info} 67 } else if details.FileType { 68 switch path.Ext(name) { 69 case ".img": 70 finfo = &types.FloppyImageFileInfo{FileInfo: info} 71 case ".iso": 72 finfo = &types.IsoImageFileInfo{FileInfo: info} 73 case ".log": 74 finfo = &types.VmLogFileInfo{FileInfo: info} 75 case ".nvram": 76 finfo = &types.VmNvramFileInfo{FileInfo: info} 77 case ".vmdk": 78 // TODO: lookup device to set other fields 79 finfo = &types.VmDiskFileInfo{FileInfo: info} 80 case ".vmx": 81 finfo = &types.VmConfigFileInfo{FileInfo: info} 82 } 83 } 84 85 res.File = append(res.File, finfo) 86 } 87 88 func (s *searchDatastore) queryMatch(file os.FileInfo) bool { 89 if len(s.SearchSpec.Query) == 0 { 90 return true 91 } 92 93 name := file.Name() 94 ext := path.Ext(name) 95 96 for _, q := range s.SearchSpec.Query { 97 switch q.(type) { 98 case *types.FileQuery: 99 return true 100 case *types.FolderFileQuery: 101 if file.IsDir() { 102 return true 103 } 104 case *types.FloppyImageFileQuery: 105 if ext == ".img" { 106 return true 107 } 108 case *types.IsoImageFileQuery: 109 if ext == ".iso" { 110 return true 111 } 112 case *types.VmConfigFileQuery: 113 if ext == ".vmx" { 114 // TODO: check Filter and Details fields 115 return true 116 } 117 case *types.VmDiskFileQuery: 118 if ext == ".vmdk" { 119 // TODO: check Filter and Details fields 120 return !strings.HasSuffix(name, "-flat.vmdk") 121 } 122 case *types.VmLogFileQuery: 123 if ext == ".log" { 124 return strings.HasPrefix(name, "vmware") 125 } 126 case *types.VmNvramFileQuery: 127 if ext == ".nvram" { 128 return true 129 } 130 case *types.VmSnapshotFileQuery: 131 if ext == ".vmsn" { 132 return true 133 } 134 } 135 } 136 137 return false 138 } 139 140 func friendlyName(ctx *Context, root bool, ds *Datastore, p string) string { 141 if !root || p == "" || !internal.IsDatastoreVSAN(ds.Datastore) { 142 return "" 143 } 144 145 unlock := ctx.Map.AcquireLock(ctx, ds.Self) 146 defer unlock() 147 148 if ds.namespace == nil { 149 return "" 150 } 151 152 for name, id := range ds.namespace { 153 if p == id { 154 return name 155 } 156 } 157 158 return "" 159 } 160 161 func (s *searchDatastore) search(ctx *Context, ds *Datastore, folder string, dir string, root bool) error { 162 files, err := os.ReadDir(dir) 163 if err != nil { 164 tracef("search %s: %s", dir, err) 165 return err 166 } 167 168 res := types.HostDatastoreBrowserSearchResults{ 169 Datastore: &ds.Self, 170 FolderPath: folder, 171 } 172 173 for _, file := range files { 174 name := file.Name() 175 info, _ := file.Info() 176 if s.queryMatch(info) { 177 for _, m := range s.SearchSpec.MatchPattern { 178 if ok, _ := path.Match(m, name); ok { 179 s.addFile(friendlyName(ctx, root, ds, name), info, &res) 180 break 181 } 182 } 183 } 184 185 if s.recurse && file.IsDir() { 186 _ = s.search(ctx, ds, path.Join(folder, name), path.Join(dir, name), false) 187 } 188 } 189 190 s.res = append(s.res, res) 191 192 return nil 193 } 194 195 func (s *searchDatastore) Run(task *Task) (types.AnyType, types.BaseMethodFault) { 196 p, fault := parseDatastorePath(s.DatastorePath) 197 if fault != nil { 198 return nil, fault 199 } 200 201 ref := task.ctx.Map.FindByName(p.Datastore, s.Datastore) 202 if ref == nil { 203 return nil, &types.InvalidDatastore{Name: p.Datastore} 204 } 205 206 ds := ref.(*Datastore) 207 208 task.ctx.WithLock(task, func() { 209 task.Info.Entity = &ds.Self // TODO: CreateTask() should require mo.Entity, rather than mo.Reference 210 task.Info.EntityName = ds.Name 211 }) 212 213 dir := ds.resolve(task.ctx, p.Path) 214 215 err := s.search(task.ctx, ds, s.DatastorePath, dir, p.Path == "") 216 if err != nil { 217 ff := types.FileFault{ 218 File: p.Path, 219 } 220 221 if os.IsNotExist(err) { 222 return nil, &types.FileNotFound{FileFault: ff} 223 } 224 225 return nil, &types.InvalidArgument{InvalidProperty: p.Path} 226 } 227 228 if s.recurse { 229 return types.ArrayOfHostDatastoreBrowserSearchResults{ 230 HostDatastoreBrowserSearchResults: s.res, 231 }, nil 232 } 233 234 return s.res[0], nil 235 } 236 237 func (b *HostDatastoreBrowser) SearchDatastoreTask(ctx *Context, s *types.SearchDatastore_Task) soap.HasFault { 238 task := NewTask(&searchDatastore{ 239 HostDatastoreBrowser: b, 240 DatastorePath: s.DatastorePath, 241 SearchSpec: s.SearchSpec, 242 }) 243 244 return &methods.SearchDatastore_TaskBody{ 245 Res: &types.SearchDatastore_TaskResponse{ 246 Returnval: task.Run(ctx), 247 }, 248 } 249 } 250 251 func (b *HostDatastoreBrowser) SearchDatastoreSubFoldersTask(ctx *Context, s *types.SearchDatastoreSubFolders_Task) soap.HasFault { 252 task := NewTask(&searchDatastore{ 253 HostDatastoreBrowser: b, 254 DatastorePath: s.DatastorePath, 255 SearchSpec: s.SearchSpec, 256 recurse: true, 257 }) 258 259 return &methods.SearchDatastoreSubFolders_TaskBody{ 260 Res: &types.SearchDatastoreSubFolders_TaskResponse{ 261 Returnval: task.Run(ctx), 262 }, 263 } 264 }