Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
}