v1.0.0
This commit is contained in:
commit
e8e9fa1d4a
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
IPCenter
|
||||||
|
ipcenter
|
28
actives.json
Normal file
28
actives.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"switch": {
|
||||||
|
"192.168.6.100:8080": {
|
||||||
|
"HostMap": {}
|
||||||
|
},
|
||||||
|
"192.168.6.100:8082": {
|
||||||
|
"HostMap": {}
|
||||||
|
},
|
||||||
|
"192.168.6.100:8083": {
|
||||||
|
"HostMap": {}
|
||||||
|
},
|
||||||
|
"192.168.6.100:8084": {
|
||||||
|
"HostMap": {}
|
||||||
|
},
|
||||||
|
"192.168.6.100:8085": {
|
||||||
|
"HostMap": {}
|
||||||
|
},
|
||||||
|
"192.168.6.100:8086": {
|
||||||
|
"HostMap": {}
|
||||||
|
},
|
||||||
|
"192.168.6.100:8087": {
|
||||||
|
"HostMap": {}
|
||||||
|
},
|
||||||
|
"192.168.6.100:8088": {
|
||||||
|
"HostMap": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
actives.yaml
Normal file
9
actives.yaml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
switch:
|
||||||
|
192.168.6.100:8080: ["10.10.10.70", "10.10.10.71"]
|
||||||
|
192.168.6.100:8082: ["10.10.10.74", "10.10.10.75"]
|
||||||
|
192.168.6.100:8083: ["10.10.10.76", "10.10.10.77"]
|
||||||
|
192.168.6.100:8085: ["10.10.10.80", "10.10.10.81"]
|
||||||
|
192.168.6.100:8087: ["10.10.10.84", "10.10.10.85"]
|
||||||
|
192.168.6.100:8088: ["10.10.10.86", "10.10.10.87"]
|
||||||
|
192.168.6.100:8091: ["10.10.10.92", "10.10.10.93"]
|
||||||
|
restart: 65
|
60
alertover.go
Normal file
60
alertover.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/levigross/grequests"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AlertOver 报警相关
|
||||||
|
type AlertOver struct {
|
||||||
|
Collection sync.Map // k 是title v是时间戳 用于统计上次的发送时间, 发送题目不能重复
|
||||||
|
Setting map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaultSetting 设置默认Setting
|
||||||
|
func (ao *AlertOver) SetDefaultSetting() {
|
||||||
|
ao.Setting = map[string]string{
|
||||||
|
"source": "s-577f047d-763a-4f45-a652-475595dc",
|
||||||
|
"receiver": "g-5b4ce1ea-7f6b-422a-9127-a9049c65",
|
||||||
|
"priority": "1",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ao *AlertOver) send(data map[string]string) {
|
||||||
|
// 合并初始化设置
|
||||||
|
for k, v := range ao.Setting {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := grequests.Post("https://api.alertover.com/v1/alert",
|
||||||
|
&grequests.RequestOptions{Data: data})
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
ao.Collection.Store(data["title"], now)
|
||||||
|
|
||||||
|
ErrorLog(err)
|
||||||
|
log.Println(resp.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alert 报警API
|
||||||
|
func (ao *AlertOver) Alert(title, content string) {
|
||||||
|
|
||||||
|
data := map[string]string{
|
||||||
|
"title": title,
|
||||||
|
"content": content,
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
if _t, ok := ao.Collection.Load(title); ok {
|
||||||
|
lasttime := _t.(int64)
|
||||||
|
if now-lasttime >= 600 {
|
||||||
|
ao.send(data)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ao.Collection.Store(title, now)
|
||||||
|
ao.send(data)
|
||||||
|
}
|
||||||
|
}
|
158
dip.go
Normal file
158
dip.go
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"474420502.top/eson/requests"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DIPWorker struct {
|
||||||
|
ForMatch map[string]*DIPGroup `yaml:"switch"`
|
||||||
|
ForLoop map[string]*DIPGroup
|
||||||
|
Restart int64 `yaml:"restart"`
|
||||||
|
Mutex *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShowGroupInfo 展示实时的 Group 信息动态
|
||||||
|
func (worker *DIPWorker) ShowGroupInfo() string {
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
|
content := ""
|
||||||
|
for addr, group := range worker.ForLoop {
|
||||||
|
content += addr + ": ["
|
||||||
|
for _, dhost := range group.Group {
|
||||||
|
|
||||||
|
overtimeLabel := ""
|
||||||
|
if now-dhost.ActiveTime >= 150 {
|
||||||
|
overtimeLabel = "<☠?>"
|
||||||
|
}
|
||||||
|
tm := time.Unix(dhost.ActiveTime, 0)
|
||||||
|
tm.Format("2006-01-02 15:04:05") //2018-07-11 15:10:19
|
||||||
|
content += dhost.Host + "(" + tm.Format("2006-01-02 15:04:05") + overtimeLabel + "),"
|
||||||
|
}
|
||||||
|
content = strings.TrimRight(content, ",") + "]\n"
|
||||||
|
}
|
||||||
|
content = strings.TrimRight(content, "\n")
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|
||||||
|
type DIPGroup struct {
|
||||||
|
Group map[string]*DHost // 原始参照
|
||||||
|
Ready map[string]*DHost
|
||||||
|
IPTableNum string
|
||||||
|
Current *DHost
|
||||||
|
|
||||||
|
Mutex *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDIPGroup() *DIPGroup {
|
||||||
|
group := DIPGroup{}
|
||||||
|
group.Group = make(map[string]*DHost)
|
||||||
|
group.Ready = make(map[string]*DHost)
|
||||||
|
group.Mutex = &sync.Mutex{}
|
||||||
|
return &group
|
||||||
|
}
|
||||||
|
|
||||||
|
func (group *DIPGroup) Choose(addr string) {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
for ip, dhost := range group.Ready {
|
||||||
|
// 设置转换的iptable
|
||||||
|
if group.Current == nil {
|
||||||
|
group.Current = dhost
|
||||||
|
group.Current.ActiveTime = now
|
||||||
|
SetAddrForward(group.IPTableNum, addr, ip)
|
||||||
|
} else {
|
||||||
|
restartAddr := "http://" + group.Current.Host + ":8800/pppoe/restart"
|
||||||
|
if _, err := requests.NewSession().Get(restartAddr).Execute(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
// group.Current.ActiveTime += 12
|
||||||
|
} else {
|
||||||
|
group.Current = dhost
|
||||||
|
group.Current.ActiveTime = now
|
||||||
|
// log.Println("new set addr:", group.Current.Host, "restartAddr:", restartAddr, "resp", resp.Content())
|
||||||
|
SetAddrForward(group.IPTableNum, addr, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(group.Ready, ip)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DHost struct {
|
||||||
|
Host string
|
||||||
|
ActiveTime int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDHost(host string) *DHost {
|
||||||
|
dh := DHost{}
|
||||||
|
dh.ActiveTime = time.Now().Unix()
|
||||||
|
dh.Host = host
|
||||||
|
return &dh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dh *DHost) OverTime(now int64) int64 {
|
||||||
|
if now-dh.ActiveTime >= 150 {
|
||||||
|
return now - dh.ActiveTime
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dip *DIPWorker) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
|
var data map[string]interface{}
|
||||||
|
if err := unmarshal(&data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println(data["switch"])
|
||||||
|
myswitch := data["switch"]
|
||||||
|
|
||||||
|
dip.Restart = int64(data["restart"].(int))
|
||||||
|
|
||||||
|
num := 1
|
||||||
|
for k, v := range myswitch.(map[interface{}]interface{}) {
|
||||||
|
|
||||||
|
group := NewDIPGroup()
|
||||||
|
|
||||||
|
// sudo iptables -t nat -R IPSWITCH 1 -p icmp -j DNAT --to 1.1.1.1:8885
|
||||||
|
for _, host := range v.([]interface{}) {
|
||||||
|
dhost := NewDHost(host.(string))
|
||||||
|
group.Group[dhost.Host] = dhost
|
||||||
|
dip.ForMatch[dhost.Host] = group
|
||||||
|
}
|
||||||
|
|
||||||
|
dip.ForLoop[k.(string)] = group
|
||||||
|
group.IPTableNum = strconv.Itoa(num)
|
||||||
|
num++
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDipWorker 创建一个
|
||||||
|
func NewDipWorker(filename string) *DIPWorker {
|
||||||
|
worker := DIPWorker{}
|
||||||
|
worker.ForMatch = make(map[string]*DIPGroup)
|
||||||
|
worker.ForLoop = make(map[string]*DIPGroup)
|
||||||
|
|
||||||
|
worker.Mutex = &sync.Mutex{}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = yaml.Unmarshal(data, &worker)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &worker
|
||||||
|
}
|
31
main.go
Normal file
31
main.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var passwd = "youmi"
|
||||||
|
|
||||||
|
// ErrorLog 错误打日志
|
||||||
|
func ErrorLog(err error) bool {
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorPanic 错误打日志
|
||||||
|
func ErrorPanic(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(log.Llongfile | log.LstdFlags)
|
||||||
|
swi := NewSwitch()
|
||||||
|
swi.Run("0.0.0.0:3333", `10\.10\..+`)
|
||||||
|
// swi.Run("0.0.0.0:3333", ``)
|
||||||
|
log.Fatal()
|
||||||
|
}
|
175
switch.go
Normal file
175
switch.go
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Switch 轮换IP相关类
|
||||||
|
type Switch struct {
|
||||||
|
// config map[string]interface{}
|
||||||
|
// configMutex sync.Mutex
|
||||||
|
ipregion *regexp.Regexp
|
||||||
|
lastShow int64
|
||||||
|
lastSwitch int64
|
||||||
|
|
||||||
|
Worker *DIPWorker
|
||||||
|
AO *AlertOver
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAddrForward 设置当前ip 转发的节点
|
||||||
|
func SetAddrForward(num, addr, ip string) {
|
||||||
|
port := strings.Split(addr, ":")[1]
|
||||||
|
cmd := exec.Command("/bin/sh", "-c", "sudo iptables -t nat -R IPSWITCH "+num+" -p tcp --dport "+port+" -j DNAT --to "+ip+":8885")
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSwitch 初始化默认
|
||||||
|
func NewSwitch() *Switch {
|
||||||
|
swi := Switch{}
|
||||||
|
swi.Worker = NewDipWorker("actives.yaml")
|
||||||
|
swi.AO = &AlertOver{}
|
||||||
|
swi.AO.SetDefaultSetting()
|
||||||
|
|
||||||
|
log.Println(swi.Worker.ShowGroupInfo())
|
||||||
|
|
||||||
|
http.HandleFunc("/ippool/switch/imactive", swi.imActive)
|
||||||
|
http.HandleFunc("/ippool/switch/update", swi.updateActives)
|
||||||
|
http.HandleFunc("/ippool/switch/actives", swi.switchActives)
|
||||||
|
|
||||||
|
return &swi
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run addr 监听的地址addr
|
||||||
|
// ipregion 匹配adsl的规则, 作为ping服务器并且控制网络转发的调度
|
||||||
|
func (swi *Switch) Run(addr string, ipregion string) {
|
||||||
|
swi.ipregion = regexp.MustCompile(ipregion)
|
||||||
|
|
||||||
|
cmd := exec.Command("/bin/sh", "-c", "sudo iptables -t nat -N IPSWITCH")
|
||||||
|
cmd.Run()
|
||||||
|
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", "sudo iptables -t nat -F IPSWITCH")
|
||||||
|
cmd.Run()
|
||||||
|
|
||||||
|
for range swi.Worker.ForLoop {
|
||||||
|
cmd := exec.Command("/bin/sh", "-c", "sudo iptables -t nat -A IPSWITCH -p icmp -s 1.1.1.1 -j DNAT --to 1.1.1.1:8885")
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
http.ListenAndServe(addr, nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// timeToSwitch 计算是否到切换时间
|
||||||
|
func (swi *Switch) timeToSwitch(now int64) {
|
||||||
|
for addr, group := range swi.Worker.ForLoop {
|
||||||
|
if group.Current == nil {
|
||||||
|
group.Choose(addr)
|
||||||
|
} else {
|
||||||
|
if now >= group.Current.ActiveTime+swi.Worker.Restart {
|
||||||
|
// log.Println(now, group.Current.ActiveTime, swi.Worker.Restart)
|
||||||
|
group.Choose(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkInReady 签到的IP(vps的vpn节点IP))
|
||||||
|
func (swi *Switch) checkInReady(ip string, now int64) {
|
||||||
|
|
||||||
|
if dipg, ok := swi.Worker.ForMatch[ip]; ok {
|
||||||
|
|
||||||
|
dipg.Group[ip].ActiveTime = now
|
||||||
|
|
||||||
|
if dipg.Current == nil {
|
||||||
|
dhost := NewDHost(ip)
|
||||||
|
dhost.ActiveTime = now
|
||||||
|
dipg.Ready[ip] = dhost
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if dipg.Current.Host != ip {
|
||||||
|
if v, ok := dipg.Ready[ip]; ok {
|
||||||
|
v.ActiveTime = now
|
||||||
|
} else {
|
||||||
|
dhost := NewDHost(ip)
|
||||||
|
dhost.ActiveTime = now
|
||||||
|
dipg.Ready[ip] = dhost
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// imActive 子节点访问 证明自己是活跃
|
||||||
|
func (swi *Switch) imActive(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
w.Write([]byte("ok"))
|
||||||
|
ip := strings.Split(r.RemoteAddr, ":")[0]
|
||||||
|
// log.Println(ip) // 后续可以把这些节点, 自动活跃与更新
|
||||||
|
|
||||||
|
swi.Worker.Mutex.Lock()
|
||||||
|
defer swi.Worker.Mutex.Unlock()
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
|
// 60秒show一次日志
|
||||||
|
if now >= swi.lastShow+60 {
|
||||||
|
swi.lastShow = now
|
||||||
|
log.Println("\n" + swi.Worker.ShowGroupInfo())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5秒一次检测
|
||||||
|
if now >= swi.lastSwitch+5 {
|
||||||
|
swi.lastSwitch = now
|
||||||
|
swi.timeToSwitch(now)
|
||||||
|
}
|
||||||
|
|
||||||
|
swi.checkInReady(ip, now)
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateActives 更新最新配置
|
||||||
|
func (swi *Switch) updateActives(w http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
|
swi.Worker.Mutex.Lock()
|
||||||
|
defer swi.Worker.Mutex.Unlock()
|
||||||
|
|
||||||
|
swi.Worker = NewDipWorker("actives.yaml")
|
||||||
|
w.Write([]byte("update success!"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (swi *Switch) switchActives(w http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
|
swi.Worker.Mutex.Lock()
|
||||||
|
defer swi.Worker.Mutex.Unlock()
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
|
||||||
|
var content []string
|
||||||
|
for addr, group := range swi.Worker.ForLoop {
|
||||||
|
|
||||||
|
isappend := int64(1)
|
||||||
|
for _, dhost := range group.Group {
|
||||||
|
isappend *= dhost.OverTime(now)
|
||||||
|
}
|
||||||
|
if isappend == 0 {
|
||||||
|
content = append(content, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jdata, err := json.Marshal(content)
|
||||||
|
ErrorLog(err)
|
||||||
|
_, err = w.Write(jdata)
|
||||||
|
ErrorLog(err)
|
||||||
|
|
||||||
|
}
|
14
switch_test.go
Normal file
14
switch_test.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSwitchYaml(t *testing.T) {
|
||||||
|
t.Error()
|
||||||
|
|
||||||
|
swi := NewSwitch()
|
||||||
|
if swi == nil {
|
||||||
|
t.Error("swi is error")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user