[VOL-4819] REST Interface for device and ports
Change-Id: I10e136ad5cfe32878fbbca5f897491de721c2fba
diff --git a/internal/pkg/controller/auditdevice.go b/internal/pkg/controller/auditdevice.go
index 33be2e5..2ba8772 100644
--- a/internal/pkg/controller/auditdevice.go
+++ b/internal/pkg/controller/auditdevice.go
@@ -174,7 +174,7 @@
// Error is ignored as it only drops duplicate ports
logger.Infow(ctx, "Calling AddPort", log.Fields{"No": mp.PortNo, "Name": mp.Name})
- if err := ad.device.AddPort(cntx, mp.PortNo, mp.Name); err != nil {
+ if err := ad.device.AddPort(cntx, mp); err != nil {
logger.Warnw(ctx, "AddPort Failed", log.Fields{"No": mp.PortNo, "Name": mp.Name, "Reason": err})
}
if mp.State == uint32(ofp.OfpPortState_OFPPS_LIVE) {
diff --git a/internal/pkg/controller/changeevent.go b/internal/pkg/controller/changeevent.go
index 46b1be5..9a14d1a 100644
--- a/internal/pkg/controller/changeevent.go
+++ b/internal/pkg/controller/changeevent.go
@@ -74,7 +74,7 @@
state := status.PortStatus.Desc.State
logger.Infow(ctx, "Process Port Change Event", log.Fields{"Port No": portNo, "Port Name": portName, "State": state, "Reason": status.PortStatus.Reason})
if status.PortStatus.Reason == ofp.OfpPortReason_OFPPR_ADD {
- _ = cet.device.AddPort(ctx, portNo, portName)
+ _ = cet.device.AddPort(ctx, status.PortStatus.Desc)
if state == uint32(ofp.OfpPortState_OFPPS_LIVE) {
cet.device.ProcessPortState(ctx, portNo, state)
}
diff --git a/internal/pkg/controller/controller.go b/internal/pkg/controller/controller.go
index eb48c28..ead1182 100644
--- a/internal/pkg/controller/controller.go
+++ b/internal/pkg/controller/controller.go
@@ -91,7 +91,7 @@
// AddDevice to add device
func (v *VoltController) AddDevice(cntx context.Context, config *intf.VPClientCfg) intf.IVPClient {
- d := NewDevice(cntx, config.DeviceID, config.SerialNum, config.VolthaClient, config.SouthBoundID)
+ d := NewDevice(cntx, config.DeviceID, config.SerialNum, config.VolthaClient, config.SouthBoundID, config.MfrDesc, config.HwDesc, config.SwDesc)
v.devices[config.DeviceID] = d
v.app.AddDevice(cntx, d.ID, d.SerialNum, config.SouthBoundID)
diff --git a/internal/pkg/controller/device.go b/internal/pkg/controller/device.go
index d60855a..9fbe172 100644
--- a/internal/pkg/controller/device.go
+++ b/internal/pkg/controller/device.go
@@ -19,8 +19,10 @@
"context"
"encoding/json"
"errors"
+ "fmt"
infraerror "voltha-go-controller/internal/pkg/errorcodes"
"strconv"
+ "strings"
"sync"
"time"
@@ -53,18 +55,26 @@
// DevicePort structure
type DevicePort struct {
tasks.Tasks
- Name string
- ID uint32
- State PortState
- Version string
+ Name string
+ ID uint32
+ State PortState
+ Version string
+ HwAddr string
+ CurrSpeed uint32
+ MaxSpeed uint32
}
// NewDevicePort is the constructor for DevicePort
-func NewDevicePort(id uint32, name string) *DevicePort {
+func NewDevicePort(mp *ofp.OfpPort) *DevicePort {
var port DevicePort
- port.ID = id
- port.Name = name
+ port.ID = mp.PortNo
+ port.Name = mp.Name
+
+ //port.HwAddr = strings.Trim(strings.Join(strings.Fields(fmt.Sprint("%02x", mp.HwAddr)), ":"), "[]")
+ port.HwAddr = strings.Trim(strings.ReplaceAll(fmt.Sprintf("%02x", mp.HwAddr), " ", ":"), "[]")
+ port.CurrSpeed = mp.CurrSpeed
+ port.MaxSpeed = mp.MaxSpeed
port.State = PortStateDown
return &port
}
@@ -127,10 +137,14 @@
flowQueue map[uint32]*UniIDFlowQueue // key is hash ID generated and value is UniIDFlowQueue.
deviceAuditInProgress bool
SouthBoundID string
+ MfrDesc string
+ HwDesc string
+ SwDesc string
+ TimeStamp time.Time
}
// NewDevice is the constructor for Device
-func NewDevice(cntx context.Context, id string, slno string, vclientHldr *holder.VolthaServiceClientHolder, southBoundID string) *Device {
+func NewDevice(cntx context.Context, id string, slno string, vclientHldr *holder.VolthaServiceClientHolder, southBoundID, mfr, hwDesc, swDesc string) *Device {
var device Device
device.ID = id
device.SerialNum = slno
@@ -143,6 +157,10 @@
device.flowQueue = make(map[uint32]*UniIDFlowQueue)
//Get the flowhash from db and update the flowhash variable in the device.
device.SouthBoundID = southBoundID
+ device.MfrDesc = mfr
+ device.HwDesc = hwDesc
+ device.SwDesc = swDesc
+ device.TimeStamp = time.Now()
flowHash, err := db.GetFlowHash(cntx, id)
if err != nil {
device.flowHash = DefaultMaxFlowQueues
@@ -446,10 +464,11 @@
// AddPort to add the port as requested by the device/VOLTHA
// Inform the application if the port is successfully added
-func (d *Device) AddPort(cntx context.Context, id uint32, name string) error {
+func (d *Device) AddPort(cntx context.Context, mp *ofp.OfpPort) error {
d.portLock.Lock()
defer d.portLock.Unlock()
-
+ id := mp.PortNo
+ name := mp.Name
if _, ok := d.PortsByID[id]; ok {
return errors.New("Duplicate port")
}
@@ -457,7 +476,7 @@
return errors.New("Duplicate port")
}
- p := NewDevicePort(id, name)
+ p := NewDevicePort(mp)
d.PortsByID[id] = p
d.PortsByName[name] = p
d.WritePortToDb(cntx, p)
@@ -631,6 +650,7 @@
logger.Warnw(ctx, "Device State change Ind: UP", log.Fields{"Device": d.ID})
d.State = DeviceStateUP
+ d.TimeStamp = time.Now()
GetController().DeviceUpInd(d.ID)
logger.Warnw(ctx, "Device State change Ind: UP, trigger Audit Tasks", log.Fields{"Device": d.ID})
@@ -667,6 +687,7 @@
func (d *Device) DeviceUpInd() {
logger.Warnw(ctx, "Device State change Ind: UP", log.Fields{"Device": d.ID})
d.State = DeviceStateUP
+ d.TimeStamp = time.Now()
GetController().DeviceUpInd(d.ID)
logger.Warnw(ctx, "Device State change Ind: UP, trigger Audit Tasks", log.Fields{"Device": d.ID})
@@ -684,6 +705,7 @@
func (d *Device) DeviceDownInd() {
logger.Warnw(ctx, "Device State change Ind: Down", log.Fields{"Device": d.ID})
d.State = DeviceStateDOWN
+ d.TimeStamp = time.Now()
GetController().DeviceDownInd(d.ID)
}
@@ -698,6 +720,7 @@
}
d.State = DeviceStateREBOOTED
+ d.TimeStamp = time.Now()
GetController().SetRebootInProgressForDevice(d.ID)
GetController().DeviceRebootInd(cntx, d.ID, d.SerialNum, d.SouthBoundID)
d.ReSetAllPortStates(cntx)
@@ -707,6 +730,7 @@
func (d *Device) DeviceDisabledInd(cntx context.Context) {
logger.Warnw(ctx, "Device State change Ind: Disabled", log.Fields{"Device": d.ID})
d.State = DeviceStateDISABLED
+ d.TimeStamp = time.Now()
GetController().DeviceDisableInd(cntx, d.ID)
}
diff --git a/internal/pkg/intf/vpagent.go b/internal/pkg/intf/vpagent.go
index 094c58b..3dfbcb2 100644
--- a/internal/pkg/intf/vpagent.go
+++ b/internal/pkg/intf/vpagent.go
@@ -17,6 +17,7 @@
import (
"context"
+ "time"
"voltha-go-controller/internal/pkg/holder"
@@ -28,6 +29,10 @@
DeviceID string
SerialNum string
SouthBoundID string
+ MfrDesc string
+ HwDesc string
+ SwDesc string
+ TimeStamp time.Time
VolthaClient *holder.VolthaServiceClientHolder
PacketOutChannel chan *ofp.PacketOut
}
diff --git a/internal/pkg/vpagent/refresh.go b/internal/pkg/vpagent/refresh.go
index 2761408..71e33ad 100644
--- a/internal/pkg/vpagent/refresh.go
+++ b/internal/pkg/vpagent/refresh.go
@@ -105,15 +105,25 @@
vpa.mapLock.Lock()
defer vpa.mapLock.Unlock()
var serialNum = "Unknown"
+ var mfrDesc = "Unknown"
+ var hwDesc = "Unknown"
+ var swDesc = "Unknown"
if device.Desc != nil {
serialNum = device.Desc.SerialNum
+ mfrDesc = device.Desc.MfrDesc
+ hwDesc = device.Desc.HwDesc
+ swDesc = device.Desc.SwDesc
}
vpc := vpa.clientMap[device.Id]
if vpc == nil {
vpa.VPClientAgent.AddNewDevice(&intf.VPClientCfg{
DeviceID: device.Id,
SerialNum: serialNum,
+ MfrDesc: mfrDesc,
+ HwDesc: hwDesc,
+ SwDesc: swDesc,
SouthBoundID: device.RootDeviceId,
+ TimeStamp: time.Now(),
VolthaClient: vpa.volthaClient,
PacketOutChannel: vpa.packetOutChannel,
})
diff --git a/voltha-go-controller/nbi/rest.go b/voltha-go-controller/nbi/rest.go
index e768cb4..d1db3cc 100644
--- a/voltha-go-controller/nbi/rest.go
+++ b/voltha-go-controller/nbi/rest.go
@@ -34,6 +34,8 @@
IgmpProxyPath string = "/igmp-proxy/"
MulticastPath string = "/multicast/"
FlowsPath string = "/flows/"
+ DevicesPath string = "/devices"
+ PortsPath string = "/devices/ports"
FlowsPerDeviceIDPath string = "/flows/{deviceId}"
FlowPerDeviceIDFlowIDPath string = "/flows/{deviceId}/{flowId}"
PendingFlowsPath string = "/flows/pending/"
@@ -63,6 +65,8 @@
mu.HandleFunc(ServicePortStagCtagTpIDPath, (&onos_nbi.ServiceAdapter{}).ServeHTTPWithPortName)
mu.HandleFunc(AllocationsPath, (&onos_nbi.DhcpRelayHandle{}).ServeHTTP)
mu.HandleFunc(AllocationsDeviceIDPath, (&onos_nbi.DhcpRelayHandle{}).ServeHTTP)
+ mu.HandleFunc(DevicesPath, (&onos_nbi.DeviceHandle{}).ServeHTTP)
+ mu.HandleFunc(PortsPath, (&onos_nbi.DevicePortHandle{}).ServeHTTP)
err := http.ListenAndServe(":8181", mu)
logger.Infow(ctx, "Rest Server Started", log.Fields{"Error": err})
diff --git a/voltha-go-controller/onos_nbi/deviceportadapter.go b/voltha-go-controller/onos_nbi/deviceportadapter.go
new file mode 100644
index 0000000..cd8538b
--- /dev/null
+++ b/voltha-go-controller/onos_nbi/deviceportadapter.go
@@ -0,0 +1,121 @@
+/*
+* Copyright 2022-present Open Networking Foundation
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package onos_nbi
+
+import (
+ "encoding/json"
+ "net/http"
+
+ app "voltha-go-controller/internal/pkg/application"
+ "voltha-go-controller/log"
+)
+
+// DeviceHandle Handle DeviceIDList Requests
+type DeviceHandle struct {
+}
+
+// DevicePortHandle Handle Ports Requests
+type DevicePortHandle struct {
+}
+
+// ServeHTTP to serve HTTP requests
+func (dh *DeviceHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ logger.Infow(ctx, "Received-northbound-request", log.Fields{"Method": r.Method, "URL": r.URL})
+ switch r.Method {
+ case "GET":
+ dh.GetDeviceList(w, r)
+ default:
+ logger.Warnw(ctx, "Unsupported Method", log.Fields{"Method": r.Method})
+ }
+}
+
+// GetDeviceList to get device id list
+func (dh *DeviceHandle) GetDeviceList(w http.ResponseWriter, r *http.Request) {
+
+ va := app.GetApplication()
+ var deviceListResp DeviceEntry
+ deviceListResp.Devices = []Device{}
+
+ getDeviceList := func(key, value interface{}) bool {
+ voltDevice := value.(*app.VoltDevice)
+ device := convertVoltDeviceToDevice(voltDevice)
+ deviceListResp.Devices = append(deviceListResp.Devices, device)
+ return true
+ }
+ va.DevicesDisc.Range(getDeviceList)
+
+ deviceListJSON, err := json.Marshal(deviceListResp)
+ if err != nil {
+ logger.Errorw(ctx, "Error occurred while marshaling device list response", log.Fields{"Error": err})
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Add("Content-Type", "application/json")
+ _, err = w.Write(deviceListJSON)
+ if err != nil {
+ logger.Errorw(ctx, "error in sending deviceList response", log.Fields{"Error": err})
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+}
+
+// ServeHTTP to serve HTTP requests
+func (dh *DevicePortHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ logger.Infow(ctx, "Received-northbound-request", log.Fields{"Method": r.Method, "URL": r.URL})
+ switch r.Method {
+ case "GET":
+ dh.GetPortList(w, r)
+ default:
+ logger.Warnw(ctx, "Unsupported Method", log.Fields{"Method": r.Method})
+ }
+}
+
+// GetPortList to get device id list
+func (dh *DevicePortHandle) GetPortList(w http.ResponseWriter, r *http.Request) {
+
+ va := app.GetApplication()
+ var portListResp PortEntry
+ portListResp.Ports = []Port{}
+
+ getPortList := func(key, value interface{}) bool {
+ voltPort := value.(*app.VoltPort)
+ port := convertVoltPortToPort(voltPort)
+ portListResp.Ports = append(portListResp.Ports, port)
+ return true
+ }
+
+ getDeviceList := func(key, value interface{}) bool {
+ voltDevice := value.(*app.VoltDevice)
+ voltDevice.Ports.Range(getPortList)
+ return true
+ }
+ va.DevicesDisc.Range(getDeviceList)
+
+ portListJSON, err := json.Marshal(portListResp)
+ if err != nil {
+ logger.Errorw(ctx, "Error occurred while marshaling port list response", log.Fields{"Error": err})
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Add("Content-Type", "application/json")
+ _, err = w.Write(portListJSON)
+ if err != nil {
+ logger.Errorw(ctx, "error in sending portList response", log.Fields{"Error": err})
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+}
+
diff --git a/voltha-go-controller/onos_nbi/models.go b/voltha-go-controller/onos_nbi/models.go
index 101f160..9f83404 100644
--- a/voltha-go-controller/onos_nbi/models.go
+++ b/voltha-go-controller/onos_nbi/models.go
@@ -1,4 +1,5 @@
/*
+
* Copyright 2022-present Open Networking Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +20,7 @@
"strconv"
"voltha-go-controller/internal/pkg/of"
app "voltha-go-controller/internal/pkg/application"
+ "voltha-go-controller/internal/pkg/controller"
)
const (
@@ -397,6 +399,7 @@
func ConvertFlowToFlowEntry (subFlow *of.VoltSubFlow) FlowEntry {
var flowEntry FlowEntry
+ flowEntry.Flows = []Flow{}
flow := ConvertVoltSubFlowToOnosFlow(subFlow)
flowEntry.Flows = append(flowEntry.Flows, flow)
return flowEntry
@@ -404,6 +407,7 @@
func ConvertFlowsToFlowEntry (subFlows []*of.VoltSubFlow) FlowEntry {
var flowEntry FlowEntry
+ flowEntry.Flows = []Flow{}
for _, subFlow := range subFlows {
flow := ConvertVoltSubFlowToOnosFlow(subFlow)
flowEntry.Flows = append(flowEntry.Flows, flow)
@@ -574,3 +578,97 @@
return subs
}
+type DeviceEntry struct {
+ Devices []Device `json:"devices"`
+}
+
+type Device struct {
+ ID string `json:"id"`
+ Type string `json:"type"`
+ Available bool `json:"available"`
+ Role string `json:"role"`
+ Mfr string `json:"mfr"`
+ Hw string `json:"hw"`
+ Sw string `json:"sw"`
+ Serial string `json:"serial"`
+ Driver string `json:"driver"`
+ ChassisID string `json:"chassisId"`
+ LastUpdate string `json:"lastUpdate"`
+ HumanReadableLastUpdate string `json:"humanReadableLastUpdate"`
+ Annotations DeviceAnnotations `json:"annotations"`
+}
+type DeviceAnnotations struct {
+ ChannelID string `json:"channelId"`
+ ManagementAddress string `json:"managementAddress"`
+ Protocol string `json:"protocol"`
+}
+
+func convertVoltDeviceToDevice(voltDevice *app.VoltDevice) Device {
+ var device Device
+
+ d, err := controller.GetController().GetDevice(voltDevice.Name)
+ if err != nil {
+ device.ID = voltDevice.Name
+ return device
+ }
+ device.ID = d.ID
+ if d.State == controller.DeviceStateUP {
+ device.Available = true
+ } else {
+ device.Available = false
+ }
+ device.Serial = d.SerialNum
+ device.Mfr = d.MfrDesc
+ device.Hw = d.HwDesc
+ device.Sw = d.SwDesc
+ device.LastUpdate = d.TimeStamp.String()
+ device.HumanReadableLastUpdate = d.TimeStamp.String()
+ return device
+}
+type PortEntry struct {
+ Ports []Port `json:"ports"`
+}
+
+type Port struct {
+ Element string `json:"element"`
+ Port string `json:"port"`
+ IsEnabled bool `json:"isEnabled"`
+ Type string `json:"type"`
+ PortSpeed int `json:"portSpeed"`
+ Annotations PortAnnotations `json:"annotations"`
+}
+type PortAnnotations struct {
+ AdminState string `json:"adminState"`
+ PortMac string `json:"portMac"`
+ PortName string `json:"portName"`
+}
+
+func convertVoltPortToPort(voltPort *app.VoltPort) Port {
+ var port Port
+ port.Port = strconv.Itoa(int(voltPort.ID))
+ port.Element = voltPort.Device
+ if voltPort.State == app.PortStateUp {
+ port.IsEnabled = true
+ } else {
+ port.IsEnabled = false
+ }
+ if voltPort.Type == app.VoltPortTypeNni {
+ port.Type = "fiber"
+ } else {
+ port.Type = "copper"
+ }
+ port.Annotations.AdminState = "enabled"
+ port.Annotations.PortName = voltPort.Name
+
+ device, err := controller.GetController().GetDevice(voltPort.Device)
+ if err != nil {
+ return port
+ }
+
+ devicePort := device.GetPortByName(voltPort.Name)
+ if devicePort != nil {
+ port.PortSpeed = int(devicePort.MaxSpeed)
+ port.Annotations.PortMac = devicePort.HwAddr
+ }
+ return port
+}