Skip to content
Snippets Groups Projects
generate.go 3.86 KiB
Newer Older
  • Learn to ignore specific revisions
  • package csbi
    
    import (
    	"context"
    	"fmt"
    	"strings"
    	"sync"
    
    	"github.com/openconfig/goyang/pkg/yang"
    	"github.com/prometheus/client_golang/prometheus"
    
    	pb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/csbi"
    	spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
    	"github.com/google/uuid"
    	gpb "github.com/openconfig/gnmi/proto/gnmi"
    
    	"github.com/openconfig/ygot/gogen"
    
    	"github.com/openconfig/ygot/ygen"
    	log "github.com/sirupsen/logrus"
    )
    
    func init() {
    	log.SetReportCaller(false)
    }
    
    var lock sync.Mutex
    
    // 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(ctx context.Context, models []*gpb.ModelData, repository Repository, sbiType spb.Type) (Deployment, error) {
    	labels := prometheus.Labels{"type": sbiType.String()}
    	start := promStartHook(labels, codeGenerationsTotal)
    	defer promEndHook(labels, start, codeGenerationDurationSecondsTotal, codeGenerationDurationSeconds)
    	codeGenerationNumberOfModels.With(labels).Observe(float64(len(models)))
    	id := uuid.New()
    
    	if models == nil {
    		models = ModelData
    		id = uuid.Nil
    	}
    
    	yangFiles, errs := repository.FindYANGFiles(models)
    	for _, err := range errs {
    		log.Error(promHandleError(labels, err, codeGenerationErrorsTotal))
    	}
    	if len(yangFiles) == 0 {
    		return Deployment{}, fmt.Errorf("no yang files found, too many errors")
    	} else if len(yangFiles) != len(models) {
    		log.Warn("could not find all models")
    	}
    
    
    	generator := gogen.New("GoCodeGenerator", ygen.IROptions{
    
    		ParseOptions: ygen.ParseOpts{
    			YANGParseOptions: yang.Options{
    				IgnoreSubmoduleCircularDependencies: true,
    				StoreUses:                           false,
    			},
    		},
    		TransformationOptions: ygen.TransformationOpts{
    			GenerateFakeRoot: true,
    
    	}, gogen.GoOpts{
    		PackageName:          "main",
    		GenerateJSONSchema:   true,
    		IncludeModelData:     true,
    		GenerateSimpleUnions: true,
    		ValidateFunctionName: "Validate",
    	})
    
    
    	lock.Lock()
    
    	searchpath, err := repository.YANGPathsWithSuffix()
    	if err != nil {
    		return Deployment{}, promHandleError(labels, err, codeGenerationErrorsTotal)
    	}
    
    
    	code, errs := generator.Generate(yangFiles, searchpath)
    
    	for _, e := range errs {
    		if strings.Contains(e.Error(), "duplicate entry interfaces at the root") {
    			if strings.Contains(e.Error(), "ietf-interfaces") {
    
    				generator.IROptions.ParseOptions.ExcludeModules = append(generator.IROptions.ParseOptions.ExcludeModules, "ietf-interfaces")
    
    			} else {
    				splitted := strings.SplitAfter(e.Error(), "new: ")
    				model := strings.Split(splitted[0], "/")[1]
    
    				generator.IROptions.ParseOptions.ExcludeModules = append(generator.IROptions.ParseOptions.ExcludeModules, model)
    
    			}
    		}
    		log.Warnf("error during first round %v", promHandleError(labels, e, codeGenerationErrorsTotal))
    	}
    
    
    	log.Infof("excluded models: %v", generator.IROptions.ParseOptions.ExcludeModules)
    
    
    	if code == nil {
    		log.Info("running second round")
    
    		code, errs = generator.Generate(yangFiles, searchpath)
    
    		n := len(errs)
    		log.Errorf("%v errors during code generation", n)
    		for _, err := range errs {
    
    			handledErrs = append(handledErrs, promHandleError(labels, err, codeGenerationErrorsTotal))
    
    		log.Errorf("error details after handling: \n%v\n", handledErrs)
    
    	}
    
    	if code == nil {
    		return Deployment{}, promHandleError(labels, fmt.Errorf("code generation failed"), codeGenerationErrorsTotal)
    	}
    
    	if err := write(ctx, code, id.String(), sbiType); err != nil {
    		return Deployment{}, promHandleError(labels, err, codeGenerationErrorsTotal)
    	}
    
    	return Deployment{
    		State: pb.State_STATE_ANNOUNCED,
    		ID:    id,
    	}, nil
    }