1ba991379Sopenharmony_ci/*
2ba991379Sopenharmony_ci * Copyright (c) 2022 Huawei Device Co., Ltd.
3ba991379Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4ba991379Sopenharmony_ci * you may not use this file except in compliance with the License.
5ba991379Sopenharmony_ci * You may obtain a copy of the License at
6ba991379Sopenharmony_ci *
7ba991379Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8ba991379Sopenharmony_ci *
9ba991379Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10ba991379Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11ba991379Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12ba991379Sopenharmony_ci * See the License for the specific language governing permissions and
13ba991379Sopenharmony_ci * limitations under the License.
14ba991379Sopenharmony_ci */
15ba991379Sopenharmony_ci
16ba991379Sopenharmony_cipackage utils
17ba991379Sopenharmony_ci
18ba991379Sopenharmony_ciimport (
19ba991379Sopenharmony_ci	"context"
20ba991379Sopenharmony_ci	"errors"
21ba991379Sopenharmony_ci	"fmt"
22ba991379Sopenharmony_ci	"github.com/pkg/sftp"
23ba991379Sopenharmony_ci	"github.com/sirupsen/logrus"
24ba991379Sopenharmony_ci	"golang.org/x/crypto/ssh"
25ba991379Sopenharmony_ci	"io"
26ba991379Sopenharmony_ci	"os"
27ba991379Sopenharmony_ci	"path/filepath"
28ba991379Sopenharmony_ci	"time"
29ba991379Sopenharmony_ci)
30ba991379Sopenharmony_ci
31ba991379Sopenharmony_cifunc newSSHClient(addr string, user string, passwd string) (*ssh.Client, error) {
32ba991379Sopenharmony_ci	config := &ssh.ClientConfig{
33ba991379Sopenharmony_ci		User:            user,
34ba991379Sopenharmony_ci		Auth:            []ssh.AuthMethod{ssh.Password(passwd)},
35ba991379Sopenharmony_ci		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
36ba991379Sopenharmony_ci	}
37ba991379Sopenharmony_ci	config.SetDefaults()
38ba991379Sopenharmony_ci	return ssh.Dial("tcp", addr, config)
39ba991379Sopenharmony_ci}
40ba991379Sopenharmony_ci
41ba991379Sopenharmony_cifunc RunCmdViaSSHContext(ctx context.Context, addr string, user string, passwd string, cmd string) (err error) {
42ba991379Sopenharmony_ci	ctx, fn := context.WithTimeout(ctx, 6*time.Hour)
43ba991379Sopenharmony_ci	defer fn()
44ba991379Sopenharmony_ci	if err := RunCmdViaSSHContextNoRetry(ctx, addr, user, passwd, cmd); err != nil {
45ba991379Sopenharmony_ci		if errors.Is(err, context.Canceled) {
46ba991379Sopenharmony_ci			return err
47ba991379Sopenharmony_ci		}
48ba991379Sopenharmony_ci		logrus.Errorf("exec cmd via SSH at %s failed: %v, try again...", addr, err)
49ba991379Sopenharmony_ci		return RunCmdViaSSHContextNoRetry(ctx, addr, user, passwd, cmd)
50ba991379Sopenharmony_ci	}
51ba991379Sopenharmony_ci	return nil
52ba991379Sopenharmony_ci}
53ba991379Sopenharmony_ci
54ba991379Sopenharmony_cifunc RunCmdViaSSHContextNoRetry(ctx context.Context, addr string, user string, passwd string, cmd string) (err error) {
55ba991379Sopenharmony_ci	exit := make(chan struct{})
56ba991379Sopenharmony_ci	client, err := newSSHClient(addr, user, passwd)
57ba991379Sopenharmony_ci	if err != nil {
58ba991379Sopenharmony_ci		logrus.Errorf("new SSH client to %s err: %v", addr, err)
59ba991379Sopenharmony_ci		return err
60ba991379Sopenharmony_ci	}
61ba991379Sopenharmony_ci	defer client.Close()
62ba991379Sopenharmony_ci	session, err := client.NewSession()
63ba991379Sopenharmony_ci	if err != nil {
64ba991379Sopenharmony_ci		return err
65ba991379Sopenharmony_ci	}
66ba991379Sopenharmony_ci	defer func() {
67ba991379Sopenharmony_ci		select {
68ba991379Sopenharmony_ci		case <-ctx.Done():
69ba991379Sopenharmony_ci			err = ctx.Err()
70ba991379Sopenharmony_ci		default:
71ba991379Sopenharmony_ci		}
72ba991379Sopenharmony_ci	}()
73ba991379Sopenharmony_ci	defer close(exit)
74ba991379Sopenharmony_ci	go func() {
75ba991379Sopenharmony_ci		select {
76ba991379Sopenharmony_ci		case <-ctx.Done():
77ba991379Sopenharmony_ci		case <-exit:
78ba991379Sopenharmony_ci		}
79ba991379Sopenharmony_ci		session.Close()
80ba991379Sopenharmony_ci	}()
81ba991379Sopenharmony_ci	logrus.Infof("run at %s: %s", addr, cmd)
82ba991379Sopenharmony_ci	stdin, err := session.StdinPipe()
83ba991379Sopenharmony_ci	if err != nil {
84ba991379Sopenharmony_ci		return err
85ba991379Sopenharmony_ci	}
86ba991379Sopenharmony_ci	defer stdin.Close()
87ba991379Sopenharmony_ci	stdout, err := session.StdoutPipe()
88ba991379Sopenharmony_ci	if err != nil {
89ba991379Sopenharmony_ci		return err
90ba991379Sopenharmony_ci	}
91ba991379Sopenharmony_ci	stderr, err := session.StderrPipe()
92ba991379Sopenharmony_ci	if err != nil {
93ba991379Sopenharmony_ci		return err
94ba991379Sopenharmony_ci	}
95ba991379Sopenharmony_ci	if err := session.Shell(); err != nil {
96ba991379Sopenharmony_ci		return err
97ba991379Sopenharmony_ci	}
98ba991379Sopenharmony_ci	cmd = fmt.Sprintf("%s\nexit $?\n", cmd)
99ba991379Sopenharmony_ci	go stdin.Write([]byte(cmd))
100ba991379Sopenharmony_ci	go io.Copy(os.Stdout, stdout)
101ba991379Sopenharmony_ci	go io.Copy(os.Stderr, stderr)
102ba991379Sopenharmony_ci	fmt.Printf("[%s] exec at %s %s :\n", time.Now(), addr, cmd)
103ba991379Sopenharmony_ci	return session.Wait()
104ba991379Sopenharmony_ci}
105ba991379Sopenharmony_ci
106ba991379Sopenharmony_citype Direct string
107ba991379Sopenharmony_ci
108ba991379Sopenharmony_ciconst (
109ba991379Sopenharmony_ci	Download Direct = "download"
110ba991379Sopenharmony_ci	Upload   Direct = "upload"
111ba991379Sopenharmony_ci)
112ba991379Sopenharmony_ci
113ba991379Sopenharmony_cifunc TransFileViaSSH(verb Direct, addr string, user string, passwd string, remoteFile string, localFile string) error {
114ba991379Sopenharmony_ci	c, err := newSSHClient(addr, user, passwd)
115ba991379Sopenharmony_ci	if err != nil {
116ba991379Sopenharmony_ci		logrus.Errorf("new SSH client to %s err: %v", addr, err)
117ba991379Sopenharmony_ci		return err
118ba991379Sopenharmony_ci	}
119ba991379Sopenharmony_ci	defer c.Close()
120ba991379Sopenharmony_ci	client, err := sftp.NewClient(c)
121ba991379Sopenharmony_ci	if err != nil {
122ba991379Sopenharmony_ci		logrus.Errorf("new SFTP client to %s err: %v", addr, err)
123ba991379Sopenharmony_ci		return err
124ba991379Sopenharmony_ci	}
125ba991379Sopenharmony_ci	defer client.Close()
126ba991379Sopenharmony_ci	var prep string
127ba991379Sopenharmony_ci	var src, dst io.ReadWriteCloser
128ba991379Sopenharmony_ci	if verb == Download {
129ba991379Sopenharmony_ci		prep = "to"
130ba991379Sopenharmony_ci		if src, err = client.Open(remoteFile); err != nil {
131ba991379Sopenharmony_ci			return fmt.Errorf("open remote file %s at %s err: %v", remoteFile, addr, err)
132ba991379Sopenharmony_ci		}
133ba991379Sopenharmony_ci		defer src.Close()
134ba991379Sopenharmony_ci		os.RemoveAll(localFile)
135ba991379Sopenharmony_ci		os.MkdirAll(filepath.Dir(localFile), 0755)
136ba991379Sopenharmony_ci		if dst, err = os.Create(localFile); err != nil {
137ba991379Sopenharmony_ci			return fmt.Errorf("create local file err: %v", err)
138ba991379Sopenharmony_ci		}
139ba991379Sopenharmony_ci		defer dst.Close()
140ba991379Sopenharmony_ci	} else {
141ba991379Sopenharmony_ci		prep = "from"
142ba991379Sopenharmony_ci		if src, err = os.Open(localFile); err != nil {
143ba991379Sopenharmony_ci			return fmt.Errorf("open local file err: %v", err)
144ba991379Sopenharmony_ci		}
145ba991379Sopenharmony_ci		defer src.Close()
146ba991379Sopenharmony_ci		client.Remove(remoteFile)
147ba991379Sopenharmony_ci		client.MkdirAll(filepath.Dir(remoteFile))
148ba991379Sopenharmony_ci		if dst, err = client.Create(remoteFile); err != nil {
149ba991379Sopenharmony_ci			return fmt.Errorf("create remote file %s at %s err: %v", remoteFile, addr, err)
150ba991379Sopenharmony_ci		}
151ba991379Sopenharmony_ci		defer dst.Close()
152ba991379Sopenharmony_ci	}
153ba991379Sopenharmony_ci	logrus.Infof("%sing %s at %s %s %s...", verb, remoteFile, addr, prep, localFile)
154ba991379Sopenharmony_ci	t1 := time.Now()
155ba991379Sopenharmony_ci	n, err := io.CopyBuffer(dst, src, make([]byte, 32*1024*1024))
156ba991379Sopenharmony_ci	if err != nil {
157ba991379Sopenharmony_ci		logrus.Errorf("%s %s at %s %s %s err: %v", verb, remoteFile, addr, prep, localFile, err)
158ba991379Sopenharmony_ci		return err
159ba991379Sopenharmony_ci	}
160ba991379Sopenharmony_ci	t2 := time.Now()
161ba991379Sopenharmony_ci	cost := t2.Sub(t1).Seconds()
162ba991379Sopenharmony_ci	logrus.Infof("%s %s at %s %s %s done, size: %d cost: %.2fs speed: %.2fMB/s", verb, remoteFile, addr, prep, localFile, n, cost, float64(n)/cost/1024/1024)
163ba991379Sopenharmony_ci	return nil
164ba991379Sopenharmony_ci}
165