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