github.com/vmware/govmomi@v0.43.0/find/recurser.go (about) 1 /* 2 Copyright (c) 2014-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 find 18 19 import ( 20 "context" 21 "os" 22 "path" 23 "strings" 24 25 "github.com/vmware/govmomi/list" 26 "github.com/vmware/govmomi/object" 27 "github.com/vmware/govmomi/property" 28 "github.com/vmware/govmomi/vim25/mo" 29 ) 30 31 // spec is used to specify per-search configuration, independent of the Finder instance. 32 type spec struct { 33 // Relative returns the root object to resolve Relative paths (starts with ".") 34 Relative func(ctx context.Context) (object.Reference, error) 35 36 // ListMode can be used to optionally force "ls" behavior, rather than "find" behavior 37 ListMode *bool 38 39 // Contents configures the Recurser to list the Contents of traversable leaf nodes. 40 // This is typically set to true when used from the ls command, where listing 41 // a folder means listing its Contents. This is typically set to false for 42 // commands that take managed entities that are not folders as input. 43 Contents bool 44 45 // Parents specifies the types which can contain the child types being searched for. 46 // for example, when searching for a HostSystem, parent types can be 47 // "ComputeResource" or "ClusterComputeResource". 48 Parents []string 49 50 // Include specifies which types to be included in the results, used only in "find" mode. 51 Include []string 52 53 // Nested should be set to types that can be Nested, used only in "find" mode. 54 Nested []string 55 56 // ChildType avoids traversing into folders that can't contain the Include types, used only in "find" mode. 57 ChildType []string 58 } 59 60 func (s *spec) traversable(o mo.Reference) bool { 61 ref := o.Reference() 62 63 switch ref.Type { 64 case "Datacenter": 65 if len(s.Include) == 1 && s.Include[0] == "Datacenter" { 66 // No point in traversing deeper as Datacenters cannot be nested 67 return false 68 } 69 return true 70 case "Folder": 71 if f, ok := o.(mo.Folder); ok { 72 // TODO: Not making use of this yet, but here we can optimize when searching the entire 73 // inventory across Datacenters for specific types, for example: 'govc ls -t VirtualMachine /**' 74 // should not traverse into a Datacenter's host, network or datatore folders. 75 if !s.traversableChildType(f.ChildType) { 76 return false 77 } 78 } 79 80 return true 81 } 82 83 for _, kind := range s.Parents { 84 if kind == ref.Type { 85 return true 86 } 87 } 88 89 return false 90 } 91 92 func (s *spec) traversableChildType(ctypes []string) bool { 93 if len(s.ChildType) == 0 { 94 return true 95 } 96 97 for _, t := range ctypes { 98 for _, c := range s.ChildType { 99 if t == c { 100 return true 101 } 102 } 103 } 104 105 return false 106 } 107 108 func (s *spec) wanted(e list.Element) bool { 109 if len(s.Include) == 0 { 110 return true 111 } 112 113 w := e.Object.Reference().Type 114 115 for _, kind := range s.Include { 116 if w == kind { 117 return true 118 } 119 } 120 121 return false 122 } 123 124 // listMode is a global option to revert to the original Finder behavior, 125 // disabling the newer "find" mode. 126 var listMode = os.Getenv("GOVMOMI_FINDER_LIST_MODE") == "true" 127 128 func (s *spec) listMode(isPath bool) bool { 129 if listMode { 130 return true 131 } 132 133 if s.ListMode != nil { 134 return *s.ListMode 135 } 136 137 return isPath 138 } 139 140 type recurser struct { 141 Collector *property.Collector 142 143 // All configures the recurses to fetch complete objects for leaf nodes. 144 All bool 145 } 146 147 func (r recurser) List(ctx context.Context, s *spec, root list.Element, parts []string) ([]list.Element, error) { 148 if len(parts) == 0 { 149 // Include non-traversable leaf elements in result. For example, consider 150 // the pattern "./vm/my-vm-*", where the pattern should match the VMs and 151 // not try to traverse them. 152 // 153 // Include traversable leaf elements in result, if the contents 154 // field is set to false. 155 // 156 if !s.Contents || !s.traversable(root.Object.Reference()) { 157 return []list.Element{root}, nil 158 } 159 } 160 161 k := list.Lister{ 162 Collector: r.Collector, 163 Reference: root.Object.Reference(), 164 Prefix: root.Path, 165 } 166 167 if r.All && len(parts) < 2 { 168 k.All = true 169 } 170 171 in, err := k.List(ctx) 172 if err != nil { 173 return nil, err 174 } 175 176 // This folder is a leaf as far as the glob goes. 177 if len(parts) == 0 { 178 return in, nil 179 } 180 181 all := parts 182 pattern := parts[0] 183 parts = parts[1:] 184 185 var out []list.Element 186 for _, e := range in { 187 matched, err := path.Match(pattern, path.Base(e.Path)) 188 if err != nil { 189 return nil, err 190 } 191 192 if !matched { 193 matched = strings.HasSuffix(e.Path, "/"+path.Join(all...)) 194 if matched { 195 // name contains a '/' 196 out = append(out, e) 197 } 198 199 continue 200 } 201 202 nres, err := r.List(ctx, s, e, parts) 203 if err != nil { 204 return nil, err 205 } 206 207 out = append(out, nres...) 208 } 209 210 return out, nil 211 } 212 213 func (r recurser) Find(ctx context.Context, s *spec, root list.Element, parts []string) ([]list.Element, error) { 214 var out []list.Element 215 216 if len(parts) > 0 { 217 pattern := parts[0] 218 matched, err := path.Match(pattern, path.Base(root.Path)) 219 if err != nil { 220 return nil, err 221 } 222 223 if matched && s.wanted(root) { 224 out = append(out, root) 225 } 226 } 227 228 if !s.traversable(root.Object) { 229 return out, nil 230 } 231 232 k := list.Lister{ 233 Collector: r.Collector, 234 Reference: root.Object.Reference(), 235 Prefix: root.Path, 236 } 237 238 in, err := k.List(ctx) 239 if err != nil { 240 return nil, err 241 } 242 243 for _, e := range in { 244 nres, err := r.Find(ctx, s, e, parts) 245 if err != nil { 246 return nil, err 247 } 248 249 out = append(out, nres...) 250 } 251 252 return out, nil 253 }