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  }