mirror of https://github.com/chaos-mesh/chaosd.git
278 lines
5.9 KiB
Go
278 lines
5.9 KiB
Go
// Copyright 2020 Chaos Mesh Authors.
|
|
//
|
|
// 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,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package core
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/pingcap/errors"
|
|
|
|
pb "github.com/chaos-mesh/chaos-daemon/pkg/server/serverpb"
|
|
"github.com/chaos-mesh/chaos-daemon/pkg/utils"
|
|
)
|
|
|
|
type NetworkCommand struct {
|
|
Action string
|
|
Latency string
|
|
Jitter string
|
|
Correlation string
|
|
Percent string
|
|
Device string
|
|
SourcePort string
|
|
EgressPort string
|
|
IPAddress string
|
|
IPProtocol string
|
|
Hostname string
|
|
}
|
|
|
|
const (
|
|
NetworkDelayAction = "delay"
|
|
NetworkLossAction = "loss"
|
|
)
|
|
|
|
func (n *NetworkCommand) Validate() error {
|
|
switch n.Action {
|
|
case NetworkDelayAction:
|
|
return n.validNetworkDelay()
|
|
case NetworkLossAction:
|
|
return n.validNetworkLoss()
|
|
default:
|
|
return errors.Errorf("network action %s not supported", n.Action)
|
|
}
|
|
}
|
|
|
|
func (n *NetworkCommand) validNetworkDelay() error {
|
|
if len(n.Latency) == 0 {
|
|
return errors.New("delay is required")
|
|
}
|
|
|
|
if _, err := time.ParseDuration(n.Latency); err != nil {
|
|
return errors.WithMessage(err, fmt.Sprintf("latency %s not valid", n.Latency))
|
|
}
|
|
|
|
if len(n.Jitter) > 0 {
|
|
if _, err := time.ParseDuration(n.Jitter); err != nil {
|
|
return errors.WithMessage(err, fmt.Sprintf("jitter %s not valid", n.Jitter))
|
|
}
|
|
}
|
|
|
|
if !utils.CheckPercent(n.Correlation) {
|
|
return errors.Errorf("correlation %s not valid", n.Correlation)
|
|
}
|
|
|
|
if len(n.Device) == 0 {
|
|
return errors.New("device is required")
|
|
}
|
|
|
|
if !utils.CheckIPs(n.IPAddress) {
|
|
return errors.Errorf("ip addressed %s not valid", n.IPAddress)
|
|
}
|
|
|
|
return checkProtocolAndPorts(n.IPProtocol, n.SourcePort, n.EgressPort)
|
|
}
|
|
|
|
func (n *NetworkCommand) validNetworkLoss() error {
|
|
if len(n.Percent) == 0 {
|
|
return errors.New("percent is required")
|
|
}
|
|
|
|
if !utils.CheckPercent(n.Percent) {
|
|
return errors.Errorf("percent %s not valid", n.Percent)
|
|
}
|
|
|
|
if !utils.CheckPercent(n.Correlation) {
|
|
return errors.Errorf("correlation %s not valid", n.Correlation)
|
|
}
|
|
|
|
if len(n.Device) == 0 {
|
|
return errors.New("device is required")
|
|
}
|
|
|
|
if !utils.CheckIPs(n.IPAddress) {
|
|
return errors.Errorf("ip addressed %s not valid", n.IPAddress)
|
|
}
|
|
|
|
return checkProtocolAndPorts(n.IPProtocol, n.SourcePort, n.EgressPort)
|
|
}
|
|
|
|
func (n *NetworkCommand) SetDefaultForNetworkDelay() {
|
|
if len(n.Jitter) == 0 {
|
|
n.Jitter = "0ms"
|
|
}
|
|
|
|
if len(n.Correlation) == 0 {
|
|
n.Correlation = "0"
|
|
}
|
|
}
|
|
|
|
func (n *NetworkCommand) SetDefaultForNetworkLoss() {
|
|
if len(n.Correlation) == 0 {
|
|
n.Correlation = "0"
|
|
}
|
|
}
|
|
|
|
func checkProtocolAndPorts(p string, sports string, dports string) error {
|
|
if !utils.CheckPorts(sports) {
|
|
return errors.Errorf("source ports %s not valid", sports)
|
|
}
|
|
|
|
if !utils.CheckPorts(dports) {
|
|
return errors.Errorf("egress ports %s not valid", dports)
|
|
}
|
|
|
|
if !utils.CheckIPProtocols(p) {
|
|
return errors.Errorf("ip protocols %s not valid", p)
|
|
}
|
|
|
|
if len(sports) > 0 || len(dports) > 0 {
|
|
if p == "tcp" || p == "udp" {
|
|
return nil
|
|
}
|
|
|
|
return errors.New("ip protocol is required")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (n *NetworkCommand) String() string {
|
|
data, _ := json.Marshal(n)
|
|
|
|
return string(data)
|
|
}
|
|
|
|
func (n *NetworkCommand) ToDelayNetem() (*pb.Netem, error) {
|
|
delayTime, err := time.ParseDuration(n.Latency)
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
jitter, err := time.ParseDuration(n.Jitter)
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
corr, err := strconv.ParseFloat(n.Correlation, 32)
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
netem := &pb.Netem{
|
|
Device: n.Device,
|
|
Time: uint32(delayTime.Nanoseconds() / 1e3),
|
|
DelayCorr: float32(corr),
|
|
Jitter: uint32(jitter.Nanoseconds() / 1e3),
|
|
}
|
|
|
|
return netem, nil
|
|
}
|
|
|
|
func (n *NetworkCommand) ToLossNetem() (*pb.Netem, error) {
|
|
percent, err := strconv.ParseFloat(n.Percent, 32)
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
corr, err := strconv.ParseFloat(n.Correlation, 32)
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
return &pb.Netem{
|
|
Loss: float32(percent),
|
|
LossCorr: float32(corr),
|
|
}, nil
|
|
}
|
|
|
|
func (n *NetworkCommand) ToTC(ipset string) (*pb.Tc, error) {
|
|
tc := &pb.Tc{
|
|
Type: pb.Tc_NETEM,
|
|
Ipset: ipset,
|
|
Protocol: n.IPProtocol,
|
|
SourcePort: n.SourcePort,
|
|
EgressPort: n.EgressPort,
|
|
}
|
|
|
|
var (
|
|
netem *pb.Netem
|
|
err error
|
|
)
|
|
switch n.Action {
|
|
case NetworkDelayAction:
|
|
if netem, err = n.ToDelayNetem(); err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
case NetworkLossAction:
|
|
if netem, err = n.ToLossNetem(); err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
default:
|
|
return nil, errors.Errorf("action %s not supported", n.Action)
|
|
}
|
|
|
|
tc.Netem = netem
|
|
|
|
return tc, nil
|
|
}
|
|
|
|
func (n *NetworkCommand) ToIPSet(name string) (*pb.IPSet, error) {
|
|
var (
|
|
cidrs []string
|
|
err error
|
|
)
|
|
if len(n.IPAddress) > 0 {
|
|
cidrs, err = utils.ResolveCidrs(strings.Split(n.IPAddress, ","))
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
}
|
|
|
|
if len(n.Hostname) > 0 {
|
|
cs, err := utils.ResolveCidrs(strings.Split(n.Hostname, ","))
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
cidrs = append(cidrs, cs...)
|
|
}
|
|
|
|
return &pb.IPSet{
|
|
Name: name,
|
|
Cidrs: cidrs,
|
|
}, nil
|
|
}
|
|
|
|
func (n *NetworkCommand) NeedApplyIPSet() bool {
|
|
if len(n.IPAddress) > 0 || len(n.Hostname) > 0 {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (n *NetworkCommand) NeedApplyIptables() bool {
|
|
return false
|
|
}
|
|
|
|
func (n *NetworkCommand) NeedApplyTC() bool {
|
|
return true
|
|
}
|
|
|
|
func (n *NetworkCommand) ToChain() (*pb.Chain, error) {
|
|
return nil, nil
|
|
}
|