diff --git a/cmd/getDevice.go b/cmd/getDevice.go index 331885717a43723d657292ea68590bcf97301078..ac38362886656823f04c0a79c55d3f9d0a15c7b8 100644 --- a/cmd/getDevice.go +++ b/cmd/getDevice.go @@ -43,12 +43,12 @@ var getDeviceCmd = &cobra.Command{ Short: "gets device information from the controller", Long: `Gets device information from the controller. -Device UUID needs to be specified as positional argument.`, +Device UUID or name needs to be specified as positional argument.`, RunE: func(cmd *cobra.Command, args []string) error { return cli.HTTPGet( apiEndpoint, "getDevice", - "uuid="+args[0], + "identifier="+args[0], "sbi="+cliSbi, "pnd="+cliPnd, ) diff --git a/nucleus/http.go b/nucleus/http.go index 516402e36e977ef313bee5db2278bfc7db944a03..8e13958c66a6585330e2171bd22a1a3c4401e34f 100644 --- a/nucleus/http.go +++ b/nucleus/http.go @@ -164,19 +164,50 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) { } writer.WriteHeader(http.StatusOK) case "getDevice": - device, err := pnd.MarshalDevice(id) - if err != nil { - switch err.(type) { - case *ErrNotFound: - writer.WriteHeader(http.StatusNotFound) - default: - writer.WriteHeader(http.StatusInternalServerError) + deviceIdentifier := query.Get("identifier") + + // TODO: Think about something better than this. + // deviceIdentifier can be a uuid or a string. + // Because of that we check at first if the parsed uuid is 00000000-0000-0000-0000-000000000000. + // If yes it was not a valid uuid and it must be a device name. + // If not we check if the provided identifier should be a device UUID and we search for a uuid. + id, _ := uuid.Parse(deviceIdentifier) + idAsString := id.String() + + if idAsString == "00000000-0000-0000-0000-000000000000" { + device, err := pnd.MarshalDeviceByName(deviceIdentifier) + if err != nil { + switch err.(type) { + case *ErrNotFound: + writer.WriteHeader(http.StatusNotFound) + default: + writer.WriteHeader(http.StatusInternalServerError) + } + log.Error(err) + + return } - log.Error(err) - return + + writer.Header().Set("Content-Type", "application/json") + fmt.Fprintf(writer, "%v", device) + + } else { + device, err := pnd.MarshalDevice(id) + if err != nil { + switch err.(type) { + case *ErrNotFound: + writer.WriteHeader(http.StatusNotFound) + default: + writer.WriteHeader(http.StatusInternalServerError) + } + log.Error(err) + + return + } + writer.Header().Set("Content-Type", "application/json") + fmt.Fprintf(writer, "%v", device) } - writer.Header().Set("Content-Type", "application/json") - fmt.Fprintf(writer, "%v", device) + case "getIDs": writeIDs := func(typ string, ids []uuid.UUID) { fmt.Fprintf(writer, "%v:\n", typ) diff --git a/nucleus/http_test.go b/nucleus/http_test.go index 9efa72110a1fe89cf1507424d147e7faac6fd71b..7a13bd9c512db6e41d169c81aa8d4198461e59d5 100644 --- a/nucleus/http_test.go +++ b/nucleus/http_test.go @@ -1,13 +1,14 @@ package nucleus import ( - "code.fbi.h-da.de/cocsn/gosdn/mocks" "errors" + "net/http" + "testing" + + "code.fbi.h-da.de/cocsn/gosdn/mocks" "github.com/google/uuid" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/mock" - "net/http" - "testing" ) func testSetupHTTP() { @@ -30,6 +31,7 @@ func testSetupHTTP() { } args = "&uuid=" + mdid.String() + "&pnd=" + defaultPndID.String() + "&sbi=" + defaultSbiID.String() argsNotFound = "&uuid=" + uuid.New().String() + "&pnd=" + defaultPndID.String() + "&sbi=" + defaultSbiID.String() + argsNotFoundGetDevice = "&identifier=" + uuid.New().String() + "&pnd=" + defaultPndID.String() + "&sbi=" + defaultSbiID.String() if err := c.sbic.add(sbi); err != nil { log.Fatal(err) } @@ -117,7 +119,7 @@ func Test_httpApi(t *testing.T) { }, { name: "get-device not found", - request: apiEndpoint + "/api?q=getDevice" + argsNotFound, + request: apiEndpoint + "/api?q=getDevice" + argsNotFoundGetDevice, want: &http.Response{StatusCode: http.StatusNotFound}, wantErr: false, }, diff --git a/nucleus/initialise_test.go b/nucleus/initialise_test.go index 6c90b054e44d009e03542821ced09f46cd65bb49..fd226ae5fd35ffcbd027f952064a615e43d8790d 100644 --- a/nucleus/initialise_test.go +++ b/nucleus/initialise_test.go @@ -1,18 +1,19 @@ package nucleus import ( + "context" + "os" + "testing" + "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" "code.fbi.h-da.de/cocsn/gosdn/mocks" "code.fbi.h-da.de/cocsn/gosdn/nucleus/util/proto" "code.fbi.h-da.de/cocsn/gosdn/test" - "context" "github.com/google/uuid" gpb "github.com/openconfig/gnmi/proto/gnmi" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/mock" pb "google.golang.org/protobuf/proto" - "os" - "testing" ) const apiEndpoint = "http://localhost:8080" @@ -36,6 +37,7 @@ var startGnmiTarget chan string var stopGnmiTarget chan bool var args string var argsNotFound string +var argsNotFoundGetDevice string var mockContext = mock.MatchedBy(func(ctx context.Context) bool { return true }) @@ -131,6 +133,7 @@ func readTestUUIDs() { if err != nil { log.Fatal(err) } + // TODO: iid not used? iid, err = uuid.Parse("8495a8ac-a1e8-418e-b787-10f5878b2690") altIid, err = uuid.Parse("edc5de93-2d15-4586-b2a7-fb1bc770986b") if err != nil { @@ -149,10 +152,11 @@ func mockDevice() Device { func newPnd() pndImplementation { return pndImplementation{ - name: "default", - description: "default test pnd", - sbic: sbiStore{store{}}, - devices: deviceStore{store{}}, - id: defaultPndID, + name: "default", + description: "default test pnd", + sbic: sbiStore{store{}}, + devices: deviceStore{store{}}, + id: defaultPndID, + deviceNameToUUIDLookup: make(map[string]uuid.UUID), } } diff --git a/nucleus/principalNetworkDomain.go b/nucleus/principalNetworkDomain.go index ebb372081a3636a2e0629200c9b56822d11981ae..8f8f74993aecc0598d4739207826cac42b116289 100644 --- a/nucleus/principalNetworkDomain.go +++ b/nucleus/principalNetworkDomain.go @@ -1,11 +1,13 @@ package nucleus import ( - "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" "context" + + "code.fbi.h-da.de/cocsn/gosdn/forks/goarista/gnmi" log "github.com/sirupsen/logrus" "encoding/json" + "github.com/google/uuid" ) @@ -22,27 +24,30 @@ type PrincipalNetworkDomain interface { GetName() string GetDescription() string MarshalDevice(uuid.UUID) (string, error) + MarshalDeviceByName(name string) (string, error) ContainsDevice(uuid.UUID) bool GetSBIs() interface{} ID() uuid.UUID } type pndImplementation struct { - name string - description string - sbic sbiStore - devices deviceStore - id uuid.UUID + name string + description string + sbic sbiStore + devices deviceStore + deviceNameToUUIDLookup map[string]uuid.UUID + id uuid.UUID } // NewPND creates a Principle Network Domain func NewPND(name, description string, id uuid.UUID, sbi SouthboundInterface) (PrincipalNetworkDomain, error) { pnd := &pndImplementation{ - name: name, - description: description, - sbic: sbiStore{store{}}, - devices: deviceStore{store{}}, - id: id, + name: name, + description: description, + sbic: sbiStore{store{}}, + devices: deviceStore{store{}}, + id: id, + deviceNameToUUIDLookup: make(map[string]uuid.UUID), } if err := pnd.sbic.add(sbi); err != nil { return nil, &ErrAlreadyExists{item: sbi} @@ -131,36 +136,73 @@ func (pnd *pndImplementation) removeSbi(id uuid.UUID) error { } func (pnd *pndImplementation) addDevice(device *Device) error { - return pnd.devices.add(device) + err := pnd.devices.add(device) + if err != nil { + return err + } + + pnd.deviceNameToUUIDLookup[device.Name] = device.UUID + + return nil } -func (pnd *pndImplementation) getDevice(id uuid.UUID) (*Device, error) { +func (pnd *pndImplementation) getDeviceByUUID(id uuid.UUID) (*Device, error) { return pnd.devices.get(id) } +func (pnd *pndImplementation) getDeviceByName(name string) (*Device, error) { + deviceUUID, found := pnd.deviceNameToUUIDLookup[name] + if !found { + return nil, &ErrNotFound{id: name} + } + return pnd.devices.get(deviceUUID) +} + func (pnd *pndImplementation) removeDevice(id uuid.UUID) error { return pnd.devices.delete(id) } func (pnd *pndImplementation) MarshalDevice(uuid uuid.UUID) (string, error) { - d, err := pnd.getDevice(uuid) + d, err := pnd.getDeviceByUUID(uuid) if err != nil { return "", err } + jsonTree, err := json.MarshalIndent(d.GoStruct, "", "\t") if err != nil { return "", err } log.WithFields(log.Fields{ - "pnd": pnd.id, - "device": uuid, + "pnd": pnd.id, + "ID": uuid, + "Name": d.Name, }).Info("marshalled device") + + return string(jsonTree), nil +} + +func (pnd *pndImplementation) MarshalDeviceByName(name string) (string, error) { + d, err := pnd.getDeviceByName(name) + if err != nil { + return "", err + } + + jsonTree, err := json.MarshalIndent(d.GoStruct, "", "\t") + if err != nil { + return "", err + } + log.WithFields(log.Fields{ + "pnd": pnd.id, + "ID": d.UUID, + "Name": d.Name, + }).Info("marshalled device") + return string(jsonTree), nil } // Request sends a get request to a specific device func (pnd *pndImplementation) Request(uuid uuid.UUID, path string) error { - d, err := pnd.getDevice(uuid) + d, err := pnd.getDeviceByUUID(uuid) if err != nil { return err } @@ -193,7 +235,7 @@ func (pnd *pndImplementation) RequestAll(path string) error { // Set sets the value to the given device path // TODO: Design commit/confirm mechanism func (pnd *pndImplementation) Set(uuid uuid.UUID, path string, value string) (interface{}, error) { - d, err := pnd.getDevice(uuid) + d, err := pnd.getDeviceByUUID(uuid) if err != nil { return nil, err } diff --git a/nucleus/store.go b/nucleus/store.go index 0563a5a30533467809380ac25d949bfc4b578e68..ac3d6c931ce0d3d9e8cbb7552488ab6c4afe6fd1 100644 --- a/nucleus/store.go +++ b/nucleus/store.go @@ -1,10 +1,11 @@ package nucleus import ( - "github.com/google/uuid" - log "github.com/sirupsen/logrus" "reflect" "sync" + + "github.com/google/uuid" + log "github.com/sirupsen/logrus" ) var storeLock sync.RWMutex @@ -136,6 +137,7 @@ func (s deviceStore) get(id uuid.UUID) (*Device, error) { } log.WithFields(log.Fields{ "uuid": id, + "name": device.Name, }).Debug("device was accessed") return device, nil }