-
Manuel Kieweg authored
This reverts merge request !85
Manuel Kieweg authoredThis reverts merge request !85
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
client.go 6.96 KiB
package ciena
import (
"bytes"
"code.fbi.h-da.de/cocsn/gosdn/database"
"code.fbi.h-da.de/cocsn/gosdn/nucleus/interfaces"
"code.fbi.h-da.de/cocsn/gosdn/sbi/restconf/util"
apiclient "code.fbi.h-da.de/cocsn/swagger/apis/mcp/client"
"code.fbi.h-da.de/cocsn/yang-modules/generated/tapi"
"crypto/tls"
"encoding/json"
"github.com/go-openapi/runtime"
httptransport "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
"github.com/openconfig/ygot/ygot"
"github.com/tidwall/gjson"
log "github.com/sirupsen/logrus"
"net/http"
"strings"
)
//MCPClient handles requests to a Ciena MCP RESTCONF endpoint
type MCPClient struct {
transport *httptransport.Runtime
client *apiclient.ServiceTopologyTAPI
database *database.Database
buffer *bytes.Buffer
config *interfaces.ClientConfig
device ygot.GoStruct
}
// GetConfig returns a ClientConfig struct containing
// the current configuration stat of the Ciena SBI client
func (c MCPClient) GetConfig() interfaces.ClientConfig {
return *c.config
}
//NewMCPClient creates a Ciena flavores TAPI client
func NewMCPClient(endpoint, username, password string, database *database.Database, config *interfaces.ClientConfig) *MCPClient {
// create the transport
transport := httptransport.New(endpoint, "/", nil)
transport.Transport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
// create the API client, with the transport
basicAuth := httptransport.BasicAuth(username, password)
// authenticate client
transport.DefaultAuthentication = basicAuth
client := apiclient.New(transport, strfmt.Default)
buffer := new(bytes.Buffer)
transport.Consumers[runtime.JSONMime] = util.YANGConsumer{Data: buffer}
return &MCPClient{
transport: transport,
client: client,
database: database,
buffer: buffer,
config: config,
device: &tapi.Device{},
}
}
// GetConnections implements the TAPI Connectivity GetConnections call with a grain of
// Ciena salt. The response is written to the client's buffer and passed to the database
func (c *MCPClient) GetConnections() error {
defer c.buffer.Reset()
_, err := c.client.TapiConnectivityCore.GetTapiCoreContextConnection(nil)
if err != nil {
return err
}
json := preformatJSON(c.buffer.String(), c.config.GjsonConnectionsPath)
for _, jsonEntry := range json.Array() {
dest := &tapi.TapiCommon_Context_ConnectivityContext_Connection{}
if err := tapi.Unmarshal([]byte(jsonEntry.String()), dest); err != nil {
//TODO: think about a way how to handle this.
//logging every error is kinda ugly, since ciena tapi throws a
//lot of them.
log.Info(err)
}
log.Info(*dest.Uuid)
}
// c.database.StoreConnections(c.buffer.String())
// log.Debug(c.buffer.Next(25))
return err
}
// GetLinks implements the TAPI Topology GetLinks call with a grain of
// Ciena salt. The response is written to the client's buffer and passed to the database
func (c *MCPClient) GetLinks() error {
defer c.buffer.Reset()
_, err := c.client.TapiTopologyCore.GetTapiCoreContextTopologyMcpBaseTopologyLink(nil)
if err != nil {
return err
}
json := preformatJSON(c.buffer.String(), c.config.GjsonDefaultPath)
for _, jsonEntry := range json.Array() {
dest := &tapi.TapiCommon_Context_TopologyContext_Topology_Link{}
if err := tapi.Unmarshal([]byte(jsonEntry.String()), dest); err != nil {
//TODO: think about a way how to handle this.
//logging every error is kinda ugly, since ciena tapi throws a
//lot of them.
log.Info(err)
}
log.Info(*dest.Uuid)
}
// c.database.StoreLinks(c.buffer.String())
// log.Debug(c.buffer.Next(25))
return err
}
// GetNodes implements the TAPI Topology GetNodes call with a grain of
// Ciena salt. The response is written to the client's buffer and passed to the database
func (c *MCPClient) GetNodes() error {
defer c.buffer.Reset()
_, err := c.client.TapiTopologyCore.GetTapiCoreContextTopologyMcpBaseTopologyNode(nil)
if err != nil {
return err
}
json := preformatJSON(c.buffer.String(), c.config.GjsonDefaultPath)
for _, jsonEntry := range json.Array() {
dest := &tapi.TapiCommon_Context_TopologyContext_Topology_Node{}
if err := tapi.Unmarshal([]byte(jsonEntry.String()), dest); err != nil {
//TODO: think about a way how to handle this.
//logging every error is kinda ugly, since ciena tapi throws a
//lot of them.
log.Info(err)
}
log.Info(*dest.Uuid)
}
// c.database.StoreNodes(c.buffer.String())
// log.Debug(c.buffer.Next(25))
return err
}
// GetNodeEdgePoints implements the TAPI Topology GetNodeEdgePoints call with a grain of
// Ciena salt. The response is written to the client's buffer and passed to the database
func (c *MCPClient) GetNodeEdgePoints() error {
defer c.buffer.Reset()
_, err := c.client.TapiTopologyCore.GetTapiCoreContextTopologyMcpBaseTopologyNodeEdgePoint(nil)
if err != nil {
return err
}
//TODO: there is no tapi ygot struct that fits the ciena node-edge-point
dest := &tapi.TapiCommon_Context_TopologyContext_Topology_Link_NodeEdgePoint{}
if err := tapi.Unmarshal(c.buffer.Bytes(), dest); err != nil {
return err
}
c.database.StoreNodeEdgePoints(c.buffer.String())
log.Debug(c.buffer.Next(25))
return err
}
//preformatJSON preformats the recieved JSON for further processing
func preformatJSON(jsn string, path string) *gjson.Result {
//TODO: move this!
modifierName := "uppercase"
gjson.AddModifier(modifierName, func(jsonString, arg string) string {
var jsonMap interface{}
err := json.Unmarshal([]byte(jsonString), &jsonMap)
if err != nil {
log.Info("failed unmarshal for JSON")
}
jsonMap = uppercaseJSONValues(jsonMap)
result, err := json.Marshal(jsonMap)
if err != nil {
log.Info("failed marshal for JSON")
}
return string(result)
})
formattedJSON := gjson.Get(jsn, path+"|@"+modifierName)
return &formattedJSON
}
//uppercaseJSONValues takes a interface{} created with json.Unmarshal() and changes the containing
//string values to uppercase
//returns a interface{} which can be changed back into a JSON string via
//json.Marshal()
func uppercaseJSONValues(jsonMap interface{}) interface{} {
switch jsonMap := jsonMap.(type) {
//check if []interface{} and go through every entry recursively
case []interface{}:
for i := range jsonMap {
jsonMap[i] = uppercaseJSONValues(jsonMap[i])
}
return jsonMap
//check if map[string]interface, handle ciena tapi json specific fixes
//and go on recursively
case map[string]interface{}:
tmpInterfaceMap := make(map[string]interface{}, len(jsonMap))
for k, v := range jsonMap {
//TODO: maybe we can uppercase them too, but for now:
//DO NOT uppercase uuid's
if strings.Contains(k, "uuid") {
tmpInterfaceMap[k] = v
} else {
tmpInterfaceMap[k] = uppercaseJSONValues(v)
}
}
return tmpInterfaceMap
//ygot: requires enums in uppercase and since CIENA TAPI does sometimes
//provide faulty JSON values we need to uppercase them before we process
//them further
case string:
return strings.ToUpper(jsonMap)
//default: do nothing (like for bool or other stuff)
default:
return jsonMap
}
}