diff --git a/models/yang/annotations/sonic-showtech-annot.yang b/models/yang/annotations/sonic-showtech-annot.yang new file mode 100644 index 000000000000..06ad6a71f8e1 --- /dev/null +++ b/models/yang/annotations/sonic-showtech-annot.yang @@ -0,0 +1,17 @@ +module sonic-showtech-annot { + + yang-version "1"; + + namespace "http://openconfig.net/Azure/sonic-showtech-annot"; + prefix "showtech-annot"; + + import sonic-extensions { prefix sonic-ext; } + import sonic-show-techsupport { prefix sshwtchspt; } + + deviation /sshwtchspt:sonic-show-techsupport-info { + deviate add { + sonic-ext:rpc-callback "rpc_showtech_cb"; + } + } + +} diff --git a/models/yang/sonic/sonic-show-techsupport.yang b/models/yang/sonic/sonic-show-techsupport.yang new file mode 100644 index 000000000000..1450bfdb5c7b --- /dev/null +++ b/models/yang/sonic/sonic-show-techsupport.yang @@ -0,0 +1,49 @@ +module sonic-show-techsupport { + namespace "http://github.com/Azure/sonic-show-techsupport"; + prefix sshwtchspt; + yang-version 1.1; + + import ietf-yang-types { + prefix yang; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONiC TECH SUPPORT INFORMATION"; + + revision 2019-10-15 { + description + "Initial revision."; + } + + rpc sonic-show-techsupport-info { + input { + leaf date { + type yang:date-and-time; + description + "Date and time specification of the desired start + point for collected log and core information"; + } + } + output { + leaf output-status { + type string; + description + "'Success' or detailed failure message for execution of the + 'show tech-support' request"; + } + leaf output-filename { + type string; + description + "Name of the host compressed tar file containing the collected + technical support information"; + } + } + } +} + diff --git a/translib/transformer/host_comm.go b/translib/transformer/host_comm.go new file mode 100644 index 000000000000..c33fa547e914 --- /dev/null +++ b/translib/transformer/host_comm.go @@ -0,0 +1,81 @@ +package transformer + +import ( + "strings" + + "github.com/godbus/dbus/v5" + log "github.com/golang/glog" +) + +// HostResult contains the body of the response and the error if any, when the +// endpoint finishes servicing the D-Bus request. +type HostResult struct { + Body []interface{} + Err error +} + +// HostQuery calls the corresponding D-Bus endpoint on the host and returns +// any error and response body +func HostQuery(endpoint string, args ...interface{}) (result HostResult) { + log.Infof("HostQuery called") + result_ch, err := hostQueryAsync(endpoint, args...) + + if err != nil { + result.Err = err + return + } + + result = <-result_ch + return +} + +// hostQueryAsync calls the corresponding D-Bus endpoint on the host and returns +// a channel for the result, and any error +func hostQueryAsync(endpoint string, args ...interface{}) (chan HostResult, error) { + log.Infof("HostQueryAsync called") + var result_ch = make(chan HostResult, 1) + conn, err := dbus.SystemBus() + if err != nil { + return result_ch, err + } + log.Infof("HostQueryAsync conn established") + + service := strings.SplitN(endpoint, ".", 2) + const bus_name_base = "org.SONiC.HostService." + bus_name := bus_name_base + service[0] + bus_path := dbus.ObjectPath("/org/SONiC/HostService/" + service[0]) + + obj := conn.Object(bus_name, bus_path) + dest := bus_name_base + endpoint + dbus_ch := make(chan *dbus.Call, 1) + //log.Infof("HostQueryAsync dbus called %s "% string(bus_path)) + //log.Infof("HostQueryAsync dbus called %s "% string(bus_name)) + + go func() { + var result HostResult + + // Wait for a read on the channel + call := <-dbus_ch + + if call.Err != nil { + log.Infof("HostQueryAsync Err is not nill while reading") + result.Err = call.Err + } else { + log.Infof("HostQueryAsync Body is taken") + result.Body = call.Body + } + + // Write the result to the channel + result_ch <- result + }() + + log.Infof("HostQueryAsync Before objgo") + call := obj.Go(dest, 0, dbus_ch, args...) + + if call.Err != nil { + log.Infof("HostQueryAsync Err is not after obj.Go") + return result_ch, call.Err + } + + return result_ch, nil +} diff --git a/translib/transformer/xfmr_showtech.go b/translib/transformer/xfmr_showtech.go new file mode 100644 index 000000000000..5aa98f600b67 --- /dev/null +++ b/translib/transformer/xfmr_showtech.go @@ -0,0 +1,100 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Dell, Inc. // +// // +// 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 transformer + +import ( + "encoding/json" + "github.com/Azure/sonic-mgmt-common/translib/tlerr" + "github.com/Azure/sonic-mgmt-common/translib/db" + "github.com/golang/glog" + "regexp" +) + +func init() { + XlateFuncBind("rpc_showtech_cb", rpc_showtech_cb) +} + +var rpc_showtech_cb RpcCallpoint = func(body []byte, dbs [db.MaxDB]*db.DB) ([]byte, error) { + var err error + var matched bool + var output string + var operand struct { + Input struct { + Date string `json:"date"` + } `json:"sonic-show-techsupport:input"` + } + + err = json.Unmarshal(body, &operand) + if err != nil { + glog.Errorf("%Error: Failed to parse rpc input; err=%v", err) + return nil,tlerr.InvalidArgs("Invalid rpc input") + } + + if operand.Input.Date == "" { + matched = true + } else { + matched, _ = regexp.MatchString((`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?` + + `(Z|[\+\-]\d{2}:\d{2})`), operand.Input.Date) + if err != nil { + glog.Errorf("%Error: Failed to match regex pattern for parsesd rpc input; err=%v", err) + } + + } + + var showtech struct { + Output struct { + Status string `json:"output-status"` + Filename string `json:"output-filename"` + } `json:"sonic-show-techsupport:output"` + } + + if !(matched) { + showtech.Output.Status = "Invalid input: Incorrect DateTime format" + showtech.Output.Filename = "" + result, _ := json.Marshal(&showtech) + return result, nil + } + + host_output := HostQuery("showtech.info", operand.Input.Date) + if host_output.Err != nil { + glog.Errorf("%Error: Showtech host Query failed: err=%v", host_output.Err) + glog.Flush() + showtech.Output.Status = host_output.Err.Error() + showtech.Output.Filename = "" + result, _ := json.Marshal(&showtech) + return result, nil + } + + output, _ = host_output.Body[1].(string) + matched, _ = regexp.MatchString(`\/var\/.*dump.*\.gz`, output) + if err != nil { + glog.Errorf("%Error: Failed to find a filename in rpc output: %v", output) + showtech.Output.Status = output + showtech.Output.Filename = "" + result, _ := json.Marshal(&showtech) + return result, nil + } + + showtech.Output.Status = "Success" + showtech.Output.Filename = output + result, _ := json.Marshal(&showtech) + + return result, nil +} +