github.com/openconfig/goyang@v1.4.5/pkg/yang/modules.go (about) 1 // Copyright 2015 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package yang 16 17 // This file implements the Modules type. This includes the processing of 18 // include and import statements, which must be done prior to turning the 19 // module into an Entry tree. 20 21 import ( 22 "fmt" 23 "sync" 24 ) 25 26 // Modules contains information about all the top level modules and 27 // submodules that are read into it via its Read method. 28 type Modules struct { 29 Modules map[string]*Module // All "module" nodes 30 SubModules map[string]*Module // All "submodule" nodes 31 includes map[*Module]bool // Modules we have already done include on 32 nsMu sync.Mutex // nsMu protects the byNS map. 33 byNS map[string]*Module // Cache of namespace lookup 34 typeDict *typeDictionary // Cache for type definitions. 35 entryCacheMu sync.RWMutex // entryCacheMu protects the entryCache map. 36 // entryCache is used to prevent unnecessary recursion into previously 37 // converted nodes. To access the map, use the get/set/ClearEntryCache() 38 // thread-safe functions. 39 entryCache map[Node]*Entry 40 // mergedSubmodule is used to prevent re-parsing a submodule that has already 41 // been merged into a particular entity when circular dependencies are being 42 // ignored. The keys of the map are a string that is formed by concatenating 43 // the name of the including (sub)module and the included submodule. 44 mergedSubmodule map[string]bool 45 // ParseOptions sets the options for the current YANG module parsing. It can be 46 // directly set by the caller to influence how goyang will behave in the presence 47 // of certain exceptional cases. 48 ParseOptions Options 49 // Path is the list of directories to look for .yang files in. 50 Path []string 51 // pathMap is used to prevent adding dups in Path. 52 pathMap map[string]bool 53 } 54 55 // NewModules returns a newly created and initialized Modules. 56 func NewModules() *Modules { 57 ms := &Modules{ 58 Modules: map[string]*Module{}, 59 SubModules: map[string]*Module{}, 60 includes: map[*Module]bool{}, 61 byNS: map[string]*Module{}, 62 typeDict: newTypeDictionary(), 63 mergedSubmodule: map[string]bool{}, 64 entryCache: map[Node]*Entry{}, 65 pathMap: map[string]bool{}, 66 } 67 return ms 68 } 69 70 // Read reads the named yang module into ms. The name can be the name of an 71 // actual .yang file or a module/submodule name (the base name of a .yang file, 72 // e.g., foo.yang is named foo). An error is returned if the file is not 73 // found or there was an error parsing the file. 74 func (ms *Modules) Read(name string) error { 75 name, data, err := ms.findFile(name) 76 if err != nil { 77 return err 78 } 79 return ms.Parse(data, name) 80 } 81 82 // Parse parses data as YANG source and adds it to ms. The name should reflect 83 // the source of data. 84 // Note: If an error is returned, valid modules might still have been added to 85 // the Modules cache. 86 func (ms *Modules) Parse(data, name string) error { 87 ss, err := Parse(data, name) 88 if err != nil { 89 return err 90 } 91 for _, s := range ss { 92 n, err := buildASTWithTypeDict(s, ms.typeDict) 93 if err != nil { 94 return err 95 } 96 if err := ms.add(n); err != nil { 97 return err 98 } 99 } 100 return nil 101 } 102 103 // GetModule returns the Entry of the module named by name. GetModule will 104 // search for and read the file named name + ".yang" if it cannot satisfy the 105 // request from what it has currently read. 106 // 107 // GetModule is a convenience function for calling Read and Process, and 108 // then looking up the module name. It is safe to call Read and Process prior 109 // to calling GetModule. 110 func (ms *Modules) GetModule(name string) (*Entry, []error) { 111 if ms.Modules[name] == nil { 112 if err := ms.Read(name); err != nil { 113 return nil, []error{err} 114 } 115 if ms.Modules[name] == nil { 116 return nil, []error{fmt.Errorf("module not found: %s", name)} 117 } 118 } 119 // Make sure that the modules have all been processed and have no 120 // errors. 121 if errs := ms.Process(); len(errs) != 0 { 122 return nil, errs 123 } 124 return ToEntry(ms.Modules[name]), nil 125 } 126 127 // GetModule optionally reads in a set of YANG source files, named by sources, 128 // and then returns the Entry for the module named module. If sources is 129 // missing, or the named module is not yet known, GetModule searches for name 130 // with the suffix ".yang". GetModule either returns an Entry or returns 131 // one or more errors. 132 // 133 // GetModule is a convenience function for calling NewModules, Read, and Process, 134 // and then looking up the module name. 135 func GetModule(name string, sources ...string) (*Entry, []error) { 136 var errs []error 137 ms := NewModules() 138 for _, source := range sources { 139 if err := ms.Read(source); err != nil { 140 errs = append(errs, err) 141 } 142 } 143 if len(errs) > 0 { 144 return nil, errs 145 } 146 return ms.GetModule(name) 147 } 148 149 // add adds Node n to ms. n must be assignable to *Module (i.e., it is a 150 // "module" or "submodule"). An error is returned if n is a duplicate of 151 // a name already added, or n is not assignable to *Module. 152 func (ms *Modules) add(n Node) error { 153 var m map[string]*Module 154 155 name := n.NName() 156 kind := n.Kind() 157 switch kind { 158 case "module": 159 m = ms.Modules 160 case "submodule": 161 m = ms.SubModules 162 default: 163 return fmt.Errorf("not a module or submodule: %s is of type %s", name, kind) 164 } 165 166 mod := n.(*Module) 167 fullName := mod.FullName() 168 mod.Modules = ms 169 170 if o := m[fullName]; o != nil { 171 return fmt.Errorf("duplicate %s %s at %s and %s", kind, fullName, Source(o), Source(n)) 172 } 173 m[fullName] = mod 174 if fullName == name { 175 return nil 176 } 177 178 // Add us to the map if: 179 // name has not been added before 180 // fullname is a more recent version of the entry. 181 if o := m[name]; o == nil || o.FullName() < fullName { 182 m[name] = mod 183 } 184 return nil 185 } 186 187 // FindModule returns the Module/Submodule specified by n, which must be a 188 // *Include or *Import. If n is a *Include then a submodule is returned. If n 189 // is a *Import then a module is returned. 190 func (ms *Modules) FindModule(n Node) *Module { 191 name := n.NName() 192 rev := name 193 var m map[string]*Module 194 195 switch i := n.(type) { 196 case *Include: 197 m = ms.SubModules 198 if i.RevisionDate != nil { 199 rev = name + "@" + i.RevisionDate.Name 200 } 201 // TODO(borman): we should check the BelongsTo field below? 202 case *Import: 203 m = ms.Modules 204 if i.RevisionDate != nil { 205 rev = name + "@" + i.RevisionDate.Name 206 } 207 default: 208 return nil 209 } 210 if n := m[rev]; n != nil { 211 return n 212 } 213 if n := m[name]; n != nil { 214 return n 215 } 216 217 // Try to read first a module by revision 218 if err := ms.Read(rev); err != nil { 219 // if failed, try to read a module by its bare name 220 if err := ms.Read(name); err != nil { 221 return nil 222 } 223 } 224 if n := m[rev]; n != nil { 225 return n 226 } 227 return m[name] 228 } 229 230 // FindModuleByNamespace either returns the Module specified by the namespace 231 // or returns an error. 232 func (ms *Modules) FindModuleByNamespace(ns string) (*Module, error) { 233 // Protect the byNS map from concurrent accesses 234 ms.nsMu.Lock() 235 defer ms.nsMu.Unlock() 236 237 if m, ok := ms.byNS[ns]; ok { 238 return m, nil 239 } 240 var found *Module 241 for _, m := range ms.Modules { 242 if m.Namespace.Name == ns { 243 switch { 244 case m == found: 245 case found != nil: 246 return nil, fmt.Errorf("namespace %s matches two or more modules (%s, %s)", 247 ns, found.Name, m.Name) 248 default: 249 found = m 250 } 251 } 252 } 253 if found == nil { 254 return nil, fmt.Errorf("%q: no such namespace", ns) 255 } 256 // Don't cache negative results because new modules could be added. 257 ms.byNS[ns] = found 258 return found, nil 259 } 260 261 // process satisfies all include and import statements and verifies that all 262 // link ref paths reference a known node. If an import or include references 263 // a [sub]module that is not already known, Process will search for a .yang 264 // file that contains it, returning an error if not found. An error is also 265 // returned if there is an unknown link ref path or other parsing errors. 266 // 267 // Process must be called once all the source modules have been read in and 268 // prior to converting Node tree into an Entry tree. 269 func (ms *Modules) process() []error { 270 var mods []*Module 271 var errs []error 272 273 // Collect the list of modules we know about now so when we range 274 // below we don't pick up new modules. We assume the user tells 275 // us explicitly which modules they are interested in. 276 for _, m := range ms.Modules { 277 mods = append(mods, m) 278 } 279 for _, m := range mods { 280 if err := ms.include(m); err != nil { 281 errs = append(errs, err) 282 } 283 } 284 285 // Resolve identities before resolving typedefs, otherwise when we resolve a 286 // typedef that has an identityref within it, then the identity dictionary 287 // has not yet been built. 288 errs = append(errs, ms.resolveIdentities()...) 289 // Append any errors found trying to resolve typedefs 290 errs = append(errs, ms.typeDict.resolveTypedefs()...) 291 292 return errs 293 } 294 295 // Process processes all the modules and submodules that have been read into 296 // ms. While processing, if an include or import is found for which there 297 // is no matching module, Process attempts to locate the source file (using 298 // Path) and automatically load them. If a file cannot be found then an 299 // error is returned. When looking for a source file, Process searches for a 300 // file using the module's or submodule's name with ".yang" appended. After 301 // searching the current directory, the directories in Path are searched. 302 // 303 // Process builds Entry trees for each modules and submodules in ms. These 304 // trees are accessed using the ToEntry function. Process does augmentation 305 // on Entry trees once all the modules and submodules in ms have been built. 306 // Following augmentation, Process inserts implied case statements. I.e., 307 // 308 // choice interface-type { 309 // container ethernet { ... } 310 // } 311 // 312 // has a case statement inserted to become: 313 // 314 // choice interface-type { 315 // case ethernet { 316 // container ethernet { ... } 317 // } 318 // } 319 // 320 // Process may return multiple errors if multiple errors were encountered 321 // while processing. Even though multiple errors may be returned, this does 322 // not mean these are all the errors. Process will terminate processing early 323 // based on the type and location of the error. 324 func (ms *Modules) Process() []error { 325 // Reset globals that may remain stale if multiple Process() calls are 326 // made by the same caller. 327 ms.mergedSubmodule = map[string]bool{} 328 ms.ClearEntryCache() 329 330 errs := ms.process() 331 if len(errs) > 0 { 332 return errorSort(errs) 333 } 334 335 for _, m := range ms.Modules { 336 errs = append(errs, ToEntry(m).GetErrors()...) 337 } 338 for _, m := range ms.SubModules { 339 errs = append(errs, ToEntry(m).GetErrors()...) 340 } 341 342 if len(errs) > 0 { 343 return errorSort(errs) 344 } 345 346 // Now handle all the augments. We don't have a good way to know 347 // what order to process them in, so repeat until no progress is made 348 349 mods := make([]*Module, 0, len(ms.Modules)+len(ms.SubModules)) 350 for _, m := range ms.Modules { 351 mods = append(mods, m) 352 } 353 for _, m := range ms.SubModules { 354 mods = append(mods, m) 355 } 356 for len(mods) > 0 { 357 var processed int 358 for i := 0; i < len(mods); { 359 m := mods[i] 360 p, s := ToEntry(m).Augment(false) 361 processed += p 362 if s == 0 { 363 mods[i] = mods[len(mods)-1] 364 mods = mods[:len(mods)-1] 365 continue 366 } 367 i++ 368 } 369 if processed == 0 { 370 break 371 } 372 } 373 374 // Now fix up all the choice statements to add in the missing case 375 // statements. 376 for _, m := range ms.Modules { 377 ToEntry(m).FixChoice() 378 } 379 for _, m := range ms.SubModules { 380 ToEntry(m).FixChoice() 381 } 382 383 // Go through any modules that have remaining augments and collect 384 // the errors. 385 for _, m := range mods { 386 ToEntry(m).Augment(true) 387 errs = append(errs, ToEntry(m).GetErrors()...) 388 } 389 390 // The deviation statement is only valid under a module or submodule, 391 // which allows us to avoid having to process it within ToEntry, and 392 // rather we can just walk all modules and submodules *after* entries 393 // are resolved. This means we do not need to concern ourselves that 394 // an entry does not exist. 395 dvP := map[string]bool{} // cache the modules we've handled since we have both modname and modname@revision-date 396 for _, devmods := range []map[string]*Module{ms.Modules, ms.SubModules} { 397 for _, m := range devmods { 398 e := ToEntry(m) 399 if !dvP[e.Name] { 400 errs = append(errs, e.ApplyDeviate(ms.ParseOptions.DeviateOptions)...) 401 dvP[e.Name] = true 402 } 403 } 404 } 405 406 return errorSort(errs) 407 } 408 409 // include resolves all the include and import statements for m. It returns 410 // an error if m, or recursively, any of the modules it includes or imports, 411 // reference a module that cannot be found. 412 func (ms *Modules) include(m *Module) error { 413 if ms.includes[m] { 414 return nil 415 } 416 ms.includes[m] = true 417 418 // First process any includes in this module. 419 for _, i := range m.Include { 420 im := ms.FindModule(i) 421 if im == nil { 422 return fmt.Errorf("no such submodule: %s", i.Name) 423 } 424 // Process the include statements in our included module. 425 if err := ms.include(im); err != nil { 426 return err 427 } 428 i.Module = im 429 } 430 431 // Next process any imports in this module. Imports are used 432 // when searching. 433 for _, i := range m.Import { 434 im := ms.FindModule(i) 435 if im == nil { 436 return fmt.Errorf("no such module: %s", i.Name) 437 } 438 // Process the include statements in our included module. 439 if err := ms.include(im); err != nil { 440 return err 441 } 442 443 i.Module = im 444 } 445 return nil 446 } 447 448 func (ms *Modules) getEntryCache(n Node) *Entry { 449 ms.entryCacheMu.RLock() 450 defer ms.entryCacheMu.RUnlock() 451 return ms.entryCache[n] 452 } 453 454 func (ms *Modules) setEntryCache(n Node, e *Entry) { 455 ms.entryCacheMu.Lock() 456 defer ms.entryCacheMu.Unlock() 457 ms.entryCache[n] = e 458 } 459 460 // ClearEntryCache clears the entryCache containing previously converted nodes 461 // used by the ToEntry function. 462 func (ms *Modules) ClearEntryCache() { 463 ms.entryCacheMu.Lock() 464 defer ms.entryCacheMu.Unlock() 465 ms.entryCache = map[Node]*Entry{} 466 }