package network
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"github.com/openshift/source-to-image/pkg/tar"
s2iutil "github.com/openshift/source-to-image/pkg/util"
kapi "k8s.io/kubernetes/pkg/api"
kerrs "k8s.io/kubernetes/pkg/util/errors"
"github.com/openshift/origin/pkg/diagnostics/networkpod/util"
)
func (d *NetworkDiagnostic) CollectNetworkPodLogs() error {
podList, err := d.getPodList(d.nsName1, util.NetworkDiagPodNamePrefix)
if err != nil {
return err
}
errList := []error{}
for _, pod := range podList.Items {
if err := d.getNetworkPodLogs(&pod); err != nil {
errList = append(errList, err)
}
}
return kerrs.NewAggregate(errList)
}
func (d *NetworkDiagnostic) CollectNetworkInfo(diagsFailed bool) error {
if diagsFailed {
// Collect useful info from master
l := util.LogInterface{
Result: d.res,
Logdir: filepath.Join(d.LogDir, util.NetworkDiagMasterLogDirPrefix),
}
l.LogMaster()
}
podList, err := d.getPodList(d.nsName1, util.NetworkDiagPodNamePrefix)
if err != nil {
return err
}
errList := []error{}
for _, pod := range podList.Items {
if pod.Status.Phase != kapi.PodRunning {
continue
}
if diagsFailed {
if err := d.copyNetworkPodInfo(&pod); err != nil {
errList = append(errList, err)
}
}
if err := d.deleteRemoteNodeInfo(&pod); err != nil {
errList = append(errList, err)
}
}
return kerrs.NewAggregate(errList)
}
func (d *NetworkDiagnostic) copyNetworkPodInfo(pod *kapi.Pod) error {
tmp, err := ioutil.TempFile("", "network-diags")
if err != nil {
return fmt.Errorf("Can not create local temporary file for tar: %v", err)
}
defer os.Remove(tmp.Name())
// Tar logdir on the remote node and copy to a local temporary file
errBuf := &bytes.Buffer{}
nodeLogDir := filepath.Join(util.NetworkDiagDefaultLogDir, util.NetworkDiagNodeLogDirPrefix, pod.Spec.NodeName)
cmd := []string{"chroot", util.NetworkDiagContainerMountPath, "tar", "-C", nodeLogDir, "-c", "."}
if err = util.Execute(d.Factory, cmd, pod, nil, tmp, errBuf); err != nil {
return fmt.Errorf("Creating remote tar locally failed: %v, %s", err, errBuf.String())
}
if err := tmp.Close(); err != nil {
return fmt.Errorf("Closing temporary tar file %s failed: %v", tmp.Name(), err)
}
// Extract copied temporary file locally
tmp, err = os.Open(tmp.Name())
if err != nil {
return fmt.Errorf("Can not open temporary tar file %s: %v", tmp.Name(), err)
}
defer tmp.Close()
tarHelper := tar.New(s2iutil.NewFileSystem())
tarHelper.SetExclusionPattern(nil)
logdir := filepath.Join(d.LogDir, util.NetworkDiagNodeLogDirPrefix, pod.Spec.NodeName)
err = tarHelper.ExtractTarStream(logdir, tmp)
if err != nil {
return fmt.Errorf("Untar local directory failed: %v, %s", err, errBuf.String())
}
return nil
}
func (d *NetworkDiagnostic) deleteRemoteNodeInfo(pod *kapi.Pod) error {
tmp, err := ioutil.TempFile("", "network-diags")
if err != nil {
return fmt.Errorf("Can not create local temporary file for tar: %v", err)
}
defer os.Remove(tmp.Name())
errBuf := &bytes.Buffer{}
nodeLogDir := filepath.Join(util.NetworkDiagDefaultLogDir, util.NetworkDiagNodeLogDirPrefix, pod.Spec.NodeName)
cmd := []string{"chroot", util.NetworkDiagContainerMountPath, "sh", "-c", fmt.Sprintf("shopt -s dotglob && rm -rf %s", nodeLogDir)}
if err = util.Execute(d.Factory, cmd, pod, nil, tmp, errBuf); err != nil {
return fmt.Errorf("Deleting remote logdir %q on node %q failed: %v, %s", nodeLogDir, pod.Spec.NodeName, err, errBuf.String())
}
return nil
}
func (d *NetworkDiagnostic) getNetworkPodLogs(pod *kapi.Pod) error {
bytelim := int64(1024000)
opts := &kapi.PodLogOptions{
TypeMeta: pod.TypeMeta,
Container: pod.Name,
Follow: true,
LimitBytes: &bytelim,
}
req, err := d.Factory.LogsForObject(pod, opts)
if err != nil {
return fmt.Errorf("Request for network diagnostic pod on node %q failed unexpectedly: %v", pod.Spec.NodeName, err)
}
readCloser, err := req.Stream()
if err != nil {
return fmt.Errorf("Logs for network diagnostic pod on node %q failed: %v", pod.Spec.NodeName, err)
}
defer readCloser.Close()
scanner := bufio.NewScanner(readCloser)
podLogs, nwarnings, nerrors := "", 0, 0
errorRegex := regexp.MustCompile(`^\[Note\]\s+Errors\s+seen:\s+(\d+)`)
warnRegex := regexp.MustCompile(`^\[Note\]\s+Warnings\s+seen:\s+(\d+)`)
for scanner.Scan() {
line := scanner.Text()
podLogs += line + "\n"
if matches := errorRegex.FindStringSubmatch(line); matches != nil {
nerrors, _ = strconv.Atoi(matches[1])
} else if matches := warnRegex.FindStringSubmatch(line); matches != nil {
nwarnings, _ = strconv.Atoi(matches[1])
}
}
if err := scanner.Err(); err != nil { // Scan terminated abnormally
return fmt.Errorf("Unexpected error reading network diagnostic pod on node %q: (%T) %[1]v\nLogs are:\n%[2]s", pod.Spec.NodeName, err, podLogs)
} else {
if nerrors > 0 {
return fmt.Errorf("See the errors below in the output from the network diagnostic pod on node %q:\n%s", pod.Spec.NodeName, podLogs)
} else if nwarnings > 0 {
d.res.Warn("DNet4002", nil, fmt.Sprintf("See the warnings below in the output from the network diagnostic pod on node %q:\n%s", pod.Spec.NodeName, podLogs))
} else {
d.res.Info("DNet4003", fmt.Sprintf("Output from the network diagnostic pod on node %q:\n%s", pod.Spec.NodeName, podLogs))
}
}
return nil
}