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  }