Newer
Older
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"
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
"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,
FakeRootName: "Device",
}, 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)
}
lock.Unlock()
if len(errs) != 0 {
var handledErrs []error
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
}