diff --git a/api/resources_inventory_v1.go b/api/resources_inventory_v1.go new file mode 100644 index 0000000..f717c39 --- /dev/null +++ b/api/resources_inventory_v1.go @@ -0,0 +1,216 @@ +// +// Copyright (c) 2016-2017, Arista Networks, Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of Arista Networks nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARISTA NETWORKS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +package cvpapi + +import ( + "encoding/json" + "time" + + "github.com/google/uuid" + "github.com/pkg/errors" +) + +// Device struct for onboarding. +type OnboardDeviceRequest struct { + Key struct { + RequestID string `json:"requestId"` + } `json:"key"` + HostnameOrIP string `json:"hostnameOrIp"` + DeviceType string `json:"device_type"` +} + +// Device struct for Response of onboarding. +type OnboardDeviceResponse struct { + Value struct { + Key struct { + RequestID string `json:"requestId"` + } `json:"key"` + DeviceID string `json:"deviceId"` + Status string `json:"status"` + StatusMessage string `json:"statusMessage"` + } `json:"value"` + Time time.Time `json:"time"` +} + +// DecomRequest struct. +type DecomDeviceRequest struct { + Key struct { + RequestID string `json:"request_id"` + } `json:"key"` + DeviceID string `json:"device_id"` +} + +// Device Response for Decom. +type DecomDeviceResponse struct { + Value struct { + Key struct { + RequestID string `json:"requestId"` + } `json:"key"` + Status string `json:"status"` + StatusMessage string `json:"statusMessage"` + } `json:"value"` + Time time.Time `json:"time"` +} + +// Device status response struct. +type OnboardStatusResponse struct { + Value struct { + Key struct { + RequestID string `json:"requestId"` + } `json:"key"` + DeviceID string `json:"deviceId"` + Status string `json:"status"` + StatusMessage string `json:"statusMessage"` + } `json:"value"` + Time time.Time `json:"time"` +} + +//Decom Status response. +type DecomStatusResponse struct { + Value struct { + Key struct { + RequestID string `json:"requestId"` + } `json:"key"` + Status string `json:"status"` + StatusMessage string `json:"statusMessage"` + } `json:"value"` + Time time.Time `json:"time"` +} + +//Decom Status all struct. +type DecomStatusallResponse struct { + Result struct { + Value struct { + Key struct { + RequestID string `json:"requestId"` + } `json:"key"` + Status string `json:"status"` + StatusMessage string `json:"statusMessage"` + } `json:"value"` + Time time.Time `json:"time"` + Type string `json:"type"` + } `json:"result"` +} + +// Method to Onboard a device. This requires a device to have a username logged into cvp that is also on the switch. As well as a CVP token that is used by that username. +// For example cvpadmin needs to give a token that is being used while cvpadmin is logged into CVP and cvpadmin needs to exist on the switch itself. +func (c CvpRestAPI) OnboardDevice(deviceIPAddress, devtype string) (*OnboardDeviceResponse, error) { + //https://aristanetworks.github.io/cloudvision-apis/examples/rest/inventory/ + //device ip address is the hostname or device ip addrress. + //devtype is eos at the time being but could be another type like wifi. + + //The uuid is going to be completely random but is required for the request. + id := uuid.NewString() + //initialize info and device. + info := &OnboardDeviceResponse{} + data := &OnboardDeviceRequest{} + data.Key.RequestID = id + data.DeviceType = devtype + data.HostnameOrIP = deviceIPAddress + + resp, err := c.client.Post("/api/resources/inventory/v1/DeviceOnboardingConfig", nil, data) + if err != nil { + return nil, errors.Errorf("Issue adding device: %s", err) + } + + if err = json.Unmarshal(resp, &info); err != nil { + return nil, errors.Errorf("OnboardDevice: %s Payload:\n%s", err, resp) + } + return info, nil +} + +// This decoms a device and requires the deviceid which is the devices serial number. So at any time this will stop streaming into CVP by calling this method. +func (c CvpRestAPI) DecomDevice(deviceid string) (*DecomDeviceResponse, error) { + //https://aristanetworks.github.io/cloudvision-apis/examples/rest/inventory/ + //deviceid is going to be the devices serial number as presented in CVP + //The uuid is going to be completely random but is required for the request. + id := uuid.NewString() + data := &DecomDeviceRequest{} + info := &DecomDeviceResponse{} + + data.Key.RequestID = id + data.DeviceID = deviceid + + resp, err := c.client.Post("/api/resources/inventory/v1/DeviceDecommissioningConfig", nil, data) + if err != nil { + return nil, errors.Errorf("Issue removing device: %s", err) + } + + if err = json.Unmarshal(resp, &info); err != nil { + return nil, errors.Errorf("DecomDevice: %s Payload:\n%s", err, resp) + } + return info, nil +} + +// Returns the Status of a device while onboarding. RequestID is the UUID of a previous Onboard. +func (c CvpRestAPI) OnboardStatus(requestid string) (string, error) { + info := OnboardStatusResponse{} + + resp, err := c.client.Get("/api/resources/inventory/v1/DeviceOnboarding?key.requestId="+requestid, nil) + if err != nil { + return info.Value.Status, errors.Errorf("Error querying for device status: %s", err) + } + if err = json.Unmarshal(resp, &info); err != nil { + return info.Value.Status, errors.Errorf("DecomDevice: %s Payload:\n%s", err, resp) + } + return info.Value.Status, err + +} + +// Returns the decom status of a device. This requires the requestid which is the UUID of the Decom. +func (c CvpRestAPI) DecomStatus(requestid string) (string, error) { + info := DecomStatusResponse{} + + resp, err := c.client.Get("/api/resources/inventory/v1/DeviceDecommissioning?key.requestId="+requestid, nil) + if err != nil { + return info.Value.Status, errors.Errorf("Error querying for device status: %s", err) + } + if err = json.Unmarshal(resp, &info); err != nil { + return info.Value.Status, errors.Errorf("DecomDevice: %s Payload:\n%s", err, resp) + } + return info.Value.Status, err + +} + +// Returns the Decomstatus of all devices. +func (c CvpRestAPI) DecomStatusAll() ([]DecomStatusallResponse, error) { + info := []DecomStatusallResponse{} + + resp, err := c.client.Get("/api/resources/inventory/v1/DeviceDecommissioning/all", nil) + if err != nil { + return info, errors.Errorf("Error querying for device status: %s", err) + } + if err = json.Unmarshal(resp, &info); err != nil { + return info, errors.Errorf("DecomDevice: %s Payload:\n%s", err, resp) + } + return info, err +} \ No newline at end of file diff --git a/api/resources_inventory_v1_test.go b/api/resources_inventory_v1_test.go new file mode 100644 index 0000000..4b99186 --- /dev/null +++ b/api/resources_inventory_v1_test.go @@ -0,0 +1,69 @@ +// +// Copyright (c) 2016-2017, Arista Networks, Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of Arista Networks nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARISTA NETWORKS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +package cvpapi + +import ( + "encoding/json" + "testing" +) + +func Test_CvpOnboardDeviceError_UnitTest(t *testing.T) { + expectedResp := OnboardDeviceResponse{} + + respStr := `{"value": {"key":{"requestId":"123456789"},"hostnameOrIp":"1.2.3.4","device_type":"eos"}}` + + json.Unmarshal([]byte(respStr), expectedResp) + + client := NewMockClient(respStr, nil) + api := NewCvpRestAPI(client) + + _, err := api.OnboardDevice("1.2.3.4", "eos") + if err != nil { + t.Fatalf("Valid case failed with error: %v", err) + } +} + +func Test_CvpDecomDeviceError_UnitTest(t *testing.T) { + expectedResp := DecomDeviceResponse{} + + respStr := `{"value": {"key":{"request_id":"123456789"},"device_id": "123456789"}}` + + json.Unmarshal([]byte(respStr), expectedResp) + + client := NewMockClient(respStr, nil) + api := NewCvpRestAPI(client) + + _, err := api.DecomDevice("123456789") + if err != nil { + t.Fatalf("Valid case failed with error: %v", err) + } +} diff --git a/go.mod b/go.mod index a58601d..3d074e6 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/aristanetworks/go-cvprac/v3 go 1.13 require ( + github.com/google/uuid v1.3.0 github.com/pkg/errors v0.9.1 gopkg.in/gcfg.v1 v1.2.3 gopkg.in/resty.v1 v1.12.0 diff --git a/go.sum b/go.sum index 0016b3c..b863b66 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=