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  }