Skip to content
Snippets Groups Projects
generate.go 3.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • Manuel Kieweg's avatar
    Manuel Kieweg committed
    package csbi
    
    import (
    	"fmt"
    	"io/fs"
    	"path/filepath"
    	"strings"
    
    	"github.com/openconfig/goyang/pkg/yang"
    
    	pb "code.fbi.h-da.de/danet/api/go/gosdn/csbi"
    	spb "code.fbi.h-da.de/danet/api/go/gosdn/southbound"
    	"github.com/google/uuid"
    	gpb "github.com/openconfig/gnmi/proto/gnmi"
    	"github.com/openconfig/ygot/ygen"
    	log "github.com/sirupsen/logrus"
    )
    
    func init() {
    	log.SetReportCaller(true)
    }
    
    // Generate takes gnmi.ModelData, a Repository, and an southbound.Type
    // It searches for the model contained in the models slice in the provided
    // Repository. Assuming all necessary models are found Go code is generated
    // and written to Disk. Depending on the southbound.Type additional files
    // for either containerised or plugin mode are created.
    func Generate(models []*gpb.ModelData, repository Repository, sbiType spb.Type) (Deployment, error) {
    	id := uuid.New()
    
    	if models == nil {
    		models = ModelData
    		id = uuid.Nil
    	}
    
    	yangFiles := findYANGFiles(models, repository.Paths())
    	cfg := &ygen.GeneratorConfig{
    		PackageName:        "main",
    		GenerateJSONSchema: true,
    		StoreRawSchema:     true,
    		ParseOptions: ygen.ParseOpts{
    			YANGParseOptions: yang.Options{
    				IgnoreSubmoduleCircularDependencies: true,
    				StoreUses:                           false,
    			},
    		},
    		TransformationOptions: ygen.TransformationOpts{
    			GenerateFakeRoot: true,
    		},
    		GoOptions: ygen.GoOpts{
    			IncludeModelData: true,
    		},
    	}
    	generator := ygen.NewYANGCodeGenerator(cfg)
    
    	code, errs := generator.GenerateGoCode(yangFiles, repository.WithSuffix())
    	for _, e := range errs {
    		if strings.Contains(e.Error(), "duplicate entry interfaces at the root") {
    			splitted := strings.SplitAfter(e.Error(), "new: ")
    			model := strings.Split(splitted[0], "/")[1]
    			generator.Config.ParseOptions.ExcludeModules = append(generator.Config.ParseOptions.ExcludeModules, model)
    		}
    		log.Errorf("first round error %v", e)
    	}
    
    	log.Infof("excluded models: %v", generator.Config.ParseOptions.ExcludeModules)
    
    	if code == nil {
    		log.Info("running second round")
    		code, errs = generator.GenerateGoCode(yangFiles, repository.WithSuffix())
    	}
    
    	if len(errs) != 0 {
    		log.Errorf("%v errors during code generation", len(errs))
    	}
    
    	if code == nil {
    		return Deployment{}, fmt.Errorf("code generation failed")
    	}
    
    	if err := write(code, id.String(), sbiType); err != nil {
    		return Deployment{}, err
    	}
    	return Deployment{
    		State: pb.State_ANNOUNCED,
    		ID:    id,
    	}, nil
    }
    
    func findYANGFiles(models []*gpb.ModelData, repository []string) []string {
    	filePaths := make([]string, len(models))
    
    	for _, r := range repository {
    		for i, model := range models {
    			path, err := searchYANGFile(model.Name, model.Organization, model.Version, r)
    			if err != nil {
    				log.Error(err)
    				continue
    			}
    			filePaths[i] = path
    		}
    	}
    	return filePaths
    }
    
    func searchYANGFile(name, org, version, repository string) (string, error) {
    	paths := make([]string, 0)
    	if err := filepath.WalkDir(repository, func(path string, d fs.DirEntry, err error) error {
    		if d.Name() == name+".yang" {
    			p := filepath.Join(path)
    			log.Debug(p)
    			paths = append(paths, p)
    		}
    		return nil
    	}); err != nil {
    		return "", err
    	}
    	if len(paths) == 0 {
    		return "", fmt.Errorf("did not find %v.yang", name)
    	}
    	return paths[0], nil
    }