Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add convenience functions to generated p4info code #508

Merged
merged 34 commits into from
Feb 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
513b0ae
* make consts private
Feb 21, 2022
7791972
Refactor
Feb 21, 2022
3480521
Merge branch 'master' of github.com:omec-project/upf-epc into improve…
Feb 21, 2022
20d29bd
Refactor
Feb 21, 2022
bb18deb
Refactor
Feb 21, 2022
c24ac19
Refactor
Feb 21, 2022
2fe9a22
Refactor. Make list builder and map builder independent using differe…
Feb 21, 2022
e8d7cf1
Add CI verification step
Feb 21, 2022
295e7ce
Refactor workflow
Feb 22, 2022
793f29c
Make maps and slices immutable
Feb 22, 2022
28d8850
Refactor workflow
Feb 22, 2022
9bf4dfd
Merge branch 'master' into improve-gen-p4
Feb 23, 2022
bc1db35
Refactor. Fix case in autogenerated const names
Feb 23, 2022
fadbc9b
Merge branch 'improve-gen-p4' of github.com:EmanueleGallone/upf-epc i…
Feb 23, 2022
0f4509f
Merge branch 'master' into improve-gen-p4
EmanueleGallone Feb 24, 2022
0e53516
Add interpreter to evaluate script output
Feb 24, 2022
3c9cec0
Split generator in multiple functions
Feb 24, 2022
aa536e9
Add test for constants and table generation
Feb 24, 2022
10c76c3
Refactor
Feb 24, 2022
8c2c393
Refactor
Feb 24, 2022
0dbebe1
Add generate Tables test
Feb 24, 2022
66bd763
Refactor
Feb 24, 2022
598219a
Add dummy action
Feb 24, 2022
e31c2f7
Refactor. One big table driven test
Feb 24, 2022
16c4da6
Refactor
Feb 24, 2022
d25c1aa
Add direct and indirect counters
Feb 25, 2022
f14adcd
Merge branch 'master' of github.com:omec-project/upf-epc into improve…
Feb 25, 2022
dc262e4
Add scripts folder when running tests and sync with latest changes
Feb 25, 2022
c6d5bf2
Merge branch 'master' into improve-gen-p4
EmanueleGallone Feb 25, 2022
393ba4a
Cleanup
pudelkoM Feb 25, 2022
4fd1181
Merge branch 'improve-gen-p4' of https://github.com/EmanueleGallone/u…
pudelkoM Feb 25, 2022
92cdb77
Merge branch 'master' into improve-gen-p4
Feb 26, 2022
2da4ab3
Add copyright header
Feb 28, 2022
332f252
Merge branch 'master' of github.com:omec-project/upf-epc into improve…
Feb 28, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ jobs:
- name: Show all CI changes
run: |
git --no-pager diff
# Verify P4 constants
- name: Build P4 constants
id: check
run: |
make p4-constants
git update-index --refresh

# Build again and commit
- name: Build the BESS-UPF Docker image (after format)
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ test: .coverage
-coverprofile=.coverage/coverage-unit.txt \
-covermode=atomic \
-v \
./pfcpiface
./pfcpiface ./cmd/...

p4-constants:
$(info *** Generating go constants...)
@docker run --rm -v $(CURDIR):/app -w /app \
golang:latest go run ./scripts/go_gen_p4_const.go \
golang:latest go run ./cmd/p4info_code_gen/p4info_code_gen.go \
-output internal/p4constants/p4constants.go -p4info conf/p4/bin/p4info.txt
@docker run --rm -v $(CURDIR):/app -w /app \
golang:latest gofmt -w internal/p4constants/p4constants.go
Expand Down
278 changes: 278 additions & 0 deletions cmd/p4info_code_gen/p4info_code_gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022-present Open Networking Foundation

package main

import (
"flag"
"fmt"
"io/ioutil"
"os"
"strings"

"github.com/ettle/strcase"
"github.com/golang/protobuf/proto"
p4ConfigV1 "github.com/p4lang/p4runtime/go/p4/config/v1"
)

const (
p4infoPath = "conf/p4/bin/p4info.txt"

defaultPackageName = "p4constants"
// copyrightHeader uses raw strings to avoid issues with reuse
copyrightHeader = `// SPDX-License-Identifier: Apache-2.0
// Copyright 2022-present Open Networking Foundation
`

constOpen = "const (\n"
mapFormatString = "%v:\"%v\",\n"
listFormatString = "%v,\n"
constOrVarClose = ")\n"

idTypeString = "uint32"
sizeTypeString = "uint64"

hfVarPrefix = "Hdr_"
tblVarPrefix = "Table_"
ctrVarPrefix = "Counter_"
ctrSizeVarPrefix = "CounterSize_"
dirCtrVarPrefix = "DirectCounter_"
actVarPrefix = "Action_"
actparamVarPrefix = "ActionParam_"
actprofVarPrefix = "ActionProfile_"
packetmetaVarPrefix = "PacketMeta_"
mtrVarPrefix = "Meter_"
mtrSizeVarPrefix = "MeterSize_"
)

func emitEntityConstant(prefix string, p4EntityName string, id uint32) string {
// see: https://go.dev/ref/spec#Identifiers
p4EntityName = prefix + "_" + p4EntityName
p4EntityName = strings.Replace(p4EntityName, ".", "_", -1)
p4EntityName = strcase.ToPascal(p4EntityName)
return fmt.Sprintf("%s \t %s = %v\n", p4EntityName, idTypeString, id)
}

// TODO: collapse with emitEntityConstant
func emitEntitySizeConstant(prefix string, p4EntityName string, id int64) string {
// see: https://go.dev/ref/spec#Identifiers
p4EntityName = prefix + "_" + p4EntityName
p4EntityName = strings.Replace(p4EntityName, ".", "_", -1)
p4EntityName = strcase.ToPascal(p4EntityName)
return fmt.Sprintf("%s \t %s = %v\n", p4EntityName, sizeTypeString, id)
}

func getPreambles(info *p4ConfigV1.P4Info, p4Type string) (preambles []*p4ConfigV1.Preamble) {
switch p4Type {
case "Table":
for _, e := range info.GetTables() {
preambles = append(preambles, e.GetPreamble())
}
case "Action":
for _, e := range info.GetActions() {
preambles = append(preambles, e.GetPreamble())
}
case "ActionProfile":
for _, e := range info.GetActionProfiles() {
preambles = append(preambles, e.GetPreamble())
}
case "Counter":
for _, e := range info.GetCounters() {
preambles = append(preambles, e.GetPreamble())
}
case "DirectCounter":
for _, e := range info.GetDirectCounters() {
preambles = append(preambles, e.GetPreamble())
}
case "Meter":
for _, e := range info.GetMeters() {
preambles = append(preambles, e.GetPreamble())
}
case "DirectMeter":
for _, e := range info.GetDirectMeters() {
preambles = append(preambles, e.GetPreamble())
}
case "ControllerPacketMetadata":
for _, e := range info.GetControllerPacketMetadata() {
preambles = append(preambles, e.GetPreamble())
}
case "ValueSet":
for _, e := range info.GetValueSets() {
preambles = append(preambles, e.GetPreamble())
}
case "Register":
for _, e := range info.GetRegisters() {
preambles = append(preambles, e.GetPreamble())
}
case "Digest":
for _, e := range info.GetDigests() {
preambles = append(preambles, e.GetPreamble())
}
default:
panic("unknown p4 type " + p4Type)
}

return
}

func generateP4DataFunctions(info *p4ConfigV1.P4Info, p4Type string) string {
const mapFuncTemplate = "func Get%sIDToNameMap() map[%s]string {\n return map[%s]string {\n"
const listFuncTemplate = "func Get%sIDList() []%s {\n return []%s {\n"

mapBuilder, listBuilder := strings.Builder{}, strings.Builder{}
mapBuilder.WriteString(fmt.Sprintf(mapFuncTemplate, p4Type, idTypeString, idTypeString))
listBuilder.WriteString(fmt.Sprintf(listFuncTemplate, p4Type, idTypeString, idTypeString))

preambles := getPreambles(info, p4Type)

for _, element := range preambles {
name, ID := element.GetName(), element.GetId()

mapBuilder.WriteString(fmt.Sprintf(mapFormatString, ID, name))
listBuilder.WriteString(fmt.Sprintf(listFormatString, ID))
}
mapBuilder.WriteString("}\n}\n\n")
listBuilder.WriteString("}\n}\n\n") //Close declarations

return mapBuilder.String() + listBuilder.String()
}

func generateConstants(p4info *p4ConfigV1.P4Info) string {
constBuilder := strings.Builder{}

constBuilder.WriteString(constOpen)

//HeaderField IDs
constBuilder.WriteString("// HeaderFields\n")
for _, element := range p4info.GetTables() {
for _, matchField := range element.MatchFields {
tableName, name := element.GetPreamble().GetName(), matchField.GetName()

constBuilder.WriteString(emitEntityConstant(hfVarPrefix+tableName, name, matchField.GetId()))
}
}

// Tables
constBuilder.WriteString("// Tables\n")
for _, element := range p4info.GetTables() {
name, ID := element.GetPreamble().GetName(), element.GetPreamble().GetId()

constBuilder.WriteString(emitEntityConstant(tblVarPrefix, name, ID))
}

// Actions
constBuilder.WriteString("// Actions\n")
for _, element := range p4info.GetActions() {
name, ID := element.GetPreamble().GetName(), element.GetPreamble().GetId()

constBuilder.WriteString(emitEntityConstant(actVarPrefix, name, ID))
}

// Action Param IDs
constBuilder.WriteString("// ActionParams\n")
for _, element := range p4info.GetActions() {
for _, actionParam := range element.GetParams() {
actionName, name := element.GetPreamble().GetName(), actionParam.GetName()

constBuilder.WriteString(emitEntityConstant(actparamVarPrefix+actionName, name, actionParam.GetId()))
}
}

// Indirect Counters
constBuilder.WriteString("// IndirectCounters\n")
for _, element := range p4info.GetCounters() {
name, ID := element.GetPreamble().GetName(), element.GetPreamble().GetId()

constBuilder.WriteString(emitEntityConstant(ctrVarPrefix, name, ID))
constBuilder.WriteString(emitEntitySizeConstant(ctrSizeVarPrefix, name, element.GetSize()))
}

// Direct Counters
constBuilder.WriteString("// DirectCounters\n")
for _, element := range p4info.GetDirectCounters() {
name, ID := element.GetPreamble().GetName(), element.GetPreamble().GetId()

constBuilder.WriteString(emitEntityConstant(dirCtrVarPrefix, name, ID))
}

// Action profiles
constBuilder.WriteString("// ActionProfiles\n")
for _, element := range p4info.GetActionProfiles() {
name, ID := element.GetPreamble().GetName(), element.GetPreamble().GetId()

constBuilder.WriteString(emitEntityConstant(actprofVarPrefix, name, ID))
}

// Packet metadata
constBuilder.WriteString("// PacketMetadata\n")
for _, element := range p4info.GetControllerPacketMetadata() {
name, ID := element.GetPreamble().GetName(), element.GetPreamble().GetId()

constBuilder.WriteString(emitEntityConstant(packetmetaVarPrefix, name, ID))
}

// Meters
constBuilder.WriteString("// Meters\n")
for _, element := range p4info.GetMeters() {
name, ID := element.GetPreamble().GetName(), element.GetPreamble().GetId()

constBuilder.WriteString(emitEntityConstant(mtrVarPrefix, name, ID))
constBuilder.WriteString(emitEntitySizeConstant(mtrSizeVarPrefix, name, element.GetSize()))
}

constBuilder.WriteString(constOrVarClose + "\n")

return constBuilder.String()
}

func mustGetP4Config(p4infopath string) *p4ConfigV1.P4Info {
p4infoBytes, err := ioutil.ReadFile(p4infopath)
if err != nil {
panic(fmt.Sprintf("Could not read P4Info file: %v", err))
}

var p4info p4ConfigV1.P4Info

err = proto.UnmarshalText(string(p4infoBytes), &p4info)
if err != nil {
panic("Could not parse P4Info file")
}

return &p4info
}

func main() {
p4infoPath := flag.String("p4info", p4infoPath, "Path of the p4info file")
outputPath := flag.String("output", "-", "Default will print to Stdout")
packageName := flag.String("package", defaultPackageName, "Set the package name")

flag.Parse()

p4info := mustGetP4Config(*p4infoPath)

sb := strings.Builder{}

sb.WriteString(copyrightHeader + "\n")
sb.WriteString(fmt.Sprintf("package %s\n", *packageName))

sb.WriteString(generateConstants(p4info))
sb.WriteString(generateP4DataFunctions(p4info, "Table"))
sb.WriteString(generateP4DataFunctions(p4info, "Action"))
sb.WriteString(generateP4DataFunctions(p4info, "ActionProfile"))
sb.WriteString(generateP4DataFunctions(p4info, "Counter"))
sb.WriteString(generateP4DataFunctions(p4info, "DirectCounter"))
sb.WriteString(generateP4DataFunctions(p4info, "Meter"))
sb.WriteString(generateP4DataFunctions(p4info, "DirectMeter"))
sb.WriteString(generateP4DataFunctions(p4info, "ControllerPacketMetadata"))
sb.WriteString(generateP4DataFunctions(p4info, "Register"))

result := sb.String()

if *outputPath == "-" {
fmt.Println(result)
} else {
if err := os.WriteFile(*outputPath, []byte(result), 0644); err != nil {
panic(fmt.Sprintf("Error while creating File: %v", err))
}
}
}
Loading