Skip to content

Commit

Permalink
Merge pull request #280 from linkernetworks/hwchiu/VX-62
Browse files Browse the repository at this point in the history
[Task]  Support the new API to get the port status of ovs.
  • Loading branch information
Hung-Wei Chiu authored Aug 28, 2018
2 parents 94d1cd8 + 11df4dd commit 3d47c18
Show file tree
Hide file tree
Showing 21 changed files with 481 additions and 1 deletion.
43 changes: 42 additions & 1 deletion API.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@
- [List Namespaces](#list-namespaces)
- [Get Namespace](#get-namespace)
- [Delete Namespace](#delete-namespace)

- [OVS](#ovs)
- [Get PortStats](#get-portstats)



## User
Expand Down Expand Up @@ -1670,3 +1672,42 @@ Response Data:
"message": "Delete success"
}
```

## OVS
In the ovs api, we should use two parameter to indicate what OVS we want to operate in.
1. NodeName: the node name in the kubernetes cluster
2. BridgeName: the bridge name when admin create the network in the network page.
the portal can use the list network to fetch the actual bridge name of each network.

### Get PortStats

**GET /v1/ovs/portstats/?nodeName=xxx&bridge=xxx**

Example:

```
curl http://localhost:7890/v1/ovs/portstats?nodeName=vortex-dev&bridgeName=system-47f8ce
```

Response Data:

```json=
{
{
"PortID": 2,
"received": {
"packets": 0,
"bytes": 0,
"dropped": 0,
"errors": 0
},
"traansmitted": {
"packets": 8,
"bytes": 648,
"dropped": 0,
"errors": 0,
"collisions": 0
}
}
}
```
28 changes: 28 additions & 0 deletions src/entity/ovs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package entity

// PortStatsReceive contains information regarding the number of received
// packets, bytes, etc.
type PortStatsReceive struct {
Packets uint64 `json:"packets"`
Bytes uint64 `json:"bytes"`
Dropped uint64 `json:"dropped"`
Errors uint64 `json:"errors"`
Frame uint64 `json:"-"`
Over uint64 `json:"-"`
CRC uint64 `json:"-"`
}

// PortStatsTransmit contains information regarding the number of transmitted
// packets, bytes, etc.
type PortStatsTransmit struct {
Packets uint64 `json:"packets"`
Bytes uint64 `json:"bytes"`
Dropped uint64 `json:"dropped"`
Errors uint64 `json:"errors"`
Collisions uint64 `json:"collisions"`
}
type OVSPortStat struct {
PortID uint32 `json:'portID"`
Received PortStatsReceive `json:"received"`
Transmitted PortStatsTransmit `json:"traansmitted"`
}
21 changes: 21 additions & 0 deletions src/networkcontroller/network_controller.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package networkcontroller

import (
"bytes"
"encoding/binary"
"time"

pb "github.com/linkernetworks/network-controller/messages"
Expand Down Expand Up @@ -127,3 +129,22 @@ func (nc *NetworkController) DeleteOVSNetwork(bridgeName string) error {
}
return nil
}

// DumpOVSPorts will dump ports infromation of the target ovs
func (nc *NetworkController) DumpOVSPorts(bridgeName string) ([]entity.OVSPortStat, error) {
data, err := nc.ClientCtl.DumpPorts(
nc.Context,
&pb.DumpPortsRequest{
BridgeName: bridgeName,
})
if err != nil {
return nil, err
}

ovsPortStats := make([]entity.OVSPortStat, len(data.Ports))
for i, p := range data.Ports {
buf := bytes.NewBuffer(p)
err = binary.Read(buf, binary.BigEndian, &ovsPortStats[i])
}
return ovsPortStats, nil
}
3 changes: 3 additions & 0 deletions src/networkcontroller/network_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ func (suite *NetworkControllerTestSuite) TestCreateOVSUserpsaceNetwork() {
err = nc.CreateOVSNetwork("netdev", tName, network.Nodes[0].PhyInterfaces, network.VlanTags)
suite.NoError(err)

data, err := nc.DumpOVSPorts(tName)
suite.NoError(err)
suite.Equal(2, len(data))
//TODO we need support the list function to check the ovs is existed
defer exec.Command("ovs-vsctl", "del-br", tName).Run()
}
Expand Down
21 changes: 21 additions & 0 deletions src/ovscontroller/ovs_system.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ovscontroller

import (
"net"

"github.com/linkernetworks/vortex/src/entity"
"github.com/linkernetworks/vortex/src/networkcontroller"
"github.com/linkernetworks/vortex/src/serviceprovider"
)

func DumpPorts(sp *serviceprovider.Container, nodeName string, bridgeName string) ([]entity.OVSPortStat, error) {
nodeIP, err := sp.KubeCtl.GetNodeInternalIP(nodeName)
if err != nil {
return nil, err
}

nodeAddr := net.JoinHostPort(nodeIP, networkcontroller.DEFAULT_CONTROLLER_PORT)
nc, err := networkcontroller.New(nodeAddr)

return nc.DumpOVSPorts(bridgeName)
}
100 changes: 100 additions & 0 deletions src/ovscontroller/ovs_system_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package ovscontroller

import (
"bytes"
"fmt"
"math/rand"
"os"
"os/exec"
"runtime"
"testing"
"time"

"github.com/linkernetworks/vortex/src/config"
_ "github.com/linkernetworks/vortex/src/entity"
kc "github.com/linkernetworks/vortex/src/kubernetes"
"github.com/linkernetworks/vortex/src/serviceprovider"

"github.com/moby/moby/pkg/namesgenerator"
"github.com/stretchr/testify/suite"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fakeclientset "k8s.io/client-go/kubernetes/fake"
)

const OVS_LOCAL_IP = "127.0.0.1"

func init() {
rand.Seed(time.Now().UnixNano())
}

func execute(suite *suite.Suite, cmd *exec.Cmd) {
w := bytes.NewBuffer(nil)
cmd.Stderr = w
err := cmd.Run()
suite.NoError(err)
fmt.Printf("Stderr: %s\n", string(w.Bytes()))
}

type OVSControllerTestSuite struct {
suite.Suite
sp *serviceprovider.Container
nodeName string
bridgeName string
}

func (suite *OVSControllerTestSuite) SetupSuite() {
cf := config.MustRead("../../config/testing.json")
suite.sp = serviceprovider.NewForTesting(cf)

// init fakeclient
fakeclient := fakeclientset.NewSimpleClientset()
suite.sp.KubeCtl = kc.New(fakeclient)

suite.bridgeName = namesgenerator.GetRandomName(0)[0:6]

// Create a fake clinet
// Initial nodes
suite.nodeName = namesgenerator.GetRandomName(0)
_, err := suite.sp.KubeCtl.Clientset.CoreV1().Nodes().Create(&corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: suite.nodeName,
},
Status: corev1.NodeStatus{
Addresses: []corev1.NodeAddress{
{
Type: "InternalIP",
Address: OVS_LOCAL_IP,
},
},
},
})
suite.NoError(err)

execute(&suite.Suite, exec.Command("ovs-vsctl", "add-br", suite.bridgeName))
}

func (suite *OVSControllerTestSuite) TearDownSuite() {
defer exec.Command("ovs-vsctl", "del-br", suite.bridgeName).Run()
}

func TestOVSNetworkSuite(t *testing.T) {
if runtime.GOOS != "linux" {
fmt.Println("We only testing the ovs function on Linux Host")
t.Skip()
return
}
if _, defined := os.LookupEnv("TEST_GRPC"); !defined {
t.SkipNow()
return
}
suite.Run(t, new(OVSControllerTestSuite))
}

// OK
func (suite *OVSControllerTestSuite) TestDumpOVSPorts() {
portStats, err := DumpPorts(suite.sp, suite.nodeName, suite.bridgeName)
suite.NoError(err)
suite.Equal(1, len(portStats))
}
36 changes: 36 additions & 0 deletions src/server/handler_ovs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package server

import (
"fmt"

response "github.com/linkernetworks/vortex/src/net/http"
"github.com/linkernetworks/vortex/src/net/http/query"
"github.com/linkernetworks/vortex/src/ovscontroller"
"github.com/linkernetworks/vortex/src/web"
)

func getOVSPortStatsHandler(ctx *web.Context) {
sp, req, resp := ctx.ServiceProvider, ctx.Request, ctx.Response

//Get the parameter
query := query.New(req.Request.URL.Query())
nodeName, exist := query.Str("nodeName")
if !exist {
response.BadRequest(req.Request, resp.ResponseWriter, fmt.Errorf("The nodeName must not be empty"))
return
}

bridgeName, exist := query.Str("bridgeName")
if !exist {
response.BadRequest(req.Request, resp.ResponseWriter, fmt.Errorf("The bridgeName must not be empty"))
return
}

fmt.Println(nodeName, bridgeName)
portStats, err := ovscontroller.DumpPorts(sp, nodeName, bridgeName)
if err != nil {
response.InternalServerError(req.Request, resp.ResponseWriter, err)
return
}
resp.WriteEntity(portStats)
}
74 changes: 74 additions & 0 deletions src/server/handler_ovs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package server

import (
_ "encoding/json"
"math/rand"
"net/http"
"net/http/httptest"

"time"

restful "github.com/emicklei/go-restful"
"github.com/linkernetworks/mongo"
"github.com/linkernetworks/vortex/src/config"
"github.com/linkernetworks/vortex/src/entity"
"github.com/linkernetworks/vortex/src/serviceprovider"
_ "github.com/moby/moby/pkg/namesgenerator"
"github.com/stretchr/testify/suite"
_ "gopkg.in/mgo.v2/bson"
//corev1 "k8s.io/api/core/v1"

"testing"
)

func init() {
rand.Seed(time.Now().UnixNano())
}

type OVSTestSuite struct {
suite.Suite
sp *serviceprovider.Container
wc *restful.Container
session *mongo.Session
storage entity.Storage
}

func (suite *OVSTestSuite) SetupSuite() {
cf := config.MustRead("../../config/testing.json")
sp := serviceprovider.NewForTesting(cf)

suite.sp = sp
//init session
suite.session = sp.Mongo.NewSession()
//init restful container
suite.wc = restful.NewContainer()
service := newOVSService(suite.sp)
suite.wc.Add(service)
}

func (suite *OVSTestSuite) TearDownSuite() {
}

func TestOVSSuite(t *testing.T) {
suite.Run(t, new(OVSTestSuite))
}

func (suite *OVSTestSuite) TestGetOVSPortStatsFail() {
//Empty data
httpRequest, err := http.NewRequest("GET", "http://localhost:7890/v1/ovs/portstat", nil)
suite.NoError(err)

httpWriter := httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusBadRequest, httpWriter)

httpRequest, err = http.NewRequest("GET", "http://localhost:7890/v1/ovs/portstat?nodeName=11", nil)
httpWriter = httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusBadRequest, httpWriter)

httpRequest, err = http.NewRequest("GET", "http://localhost:7890/v1/ovs/portstat?nodeName=11&&bridgeName=111", nil)
httpWriter = httptest.NewRecorder()
suite.wc.Dispatch(httpWriter, httpRequest)
assertResponseCode(suite.T(), http.StatusInternalServerError, httpWriter)
}
8 changes: 8 additions & 0 deletions src/server/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func (a *App) AppRoute() *mux.Router {
container.Add(newNamespaceService(a.ServiceProvider))
container.Add(newMonitoringService(a.ServiceProvider))
container.Add(newAppService(a.ServiceProvider))
container.Add(newOVSService(a.ServiceProvider))

router.PathPrefix("/v1/").Handler(container)
return router
Expand Down Expand Up @@ -177,3 +178,10 @@ func newMonitoringService(sp *serviceprovider.Container) *restful.WebService {
webService.Route(webService.GET("/controllers/{id}").To(handler.RESTfulServiceHandler(sp, getControllerMetricsHandler)))
return webService
}

func newOVSService(sp *serviceprovider.Container) *restful.WebService {
webService := new(restful.WebService)
webService.Path("/v1/ovs").Consumes(restful.MIME_JSON, restful.MIME_JSON).Produces(restful.MIME_JSON, restful.MIME_JSON)
webService.Route(webService.GET("/portstat").To(handler.RESTfulServiceHandler(sp, getOVSPortStatsHandler)))
return webService
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions tests/07-ovs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.json
4 changes: 4 additions & 0 deletions tests/07-ovs/credential
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"username":"[email protected]",
"password":"p@ssw0rd"
}
Loading

0 comments on commit 3d47c18

Please sign in to comment.