Skip to content

Commit 589f706

Browse files
author
root
committed
gwm -> netgwm
1 parent d7f8260 commit 589f706

File tree

5 files changed

+191
-139
lines changed

5 files changed

+191
-139
lines changed

gwm.py

Lines changed: 0 additions & 135 deletions
This file was deleted.

gwm/post-replace.d/script.sh

Lines changed: 0 additions & 3 deletions
This file was deleted.

netgwm.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
import sys, os, stat
5+
import yaml
6+
import time
7+
import socket
8+
import optparse
9+
import re
10+
11+
configfile = '/etc/netgwm/netgwm.yml'
12+
gwstorefile = '/var/run/netgwm/gwstore.yml'
13+
modefile = '/var/lib/netgwm/mode'
14+
15+
def main():
16+
parser = optparse.OptionParser(add_help_option = False)
17+
parser.add_option('-h', '--help', action = 'help')
18+
parser.add_option('-c', '--config', default = configfile)
19+
options, args = parser.parse_args()
20+
21+
config = yaml.load(open(options.config, 'r'))
22+
if not os.path.exists('/var/run/netgwm/'): os.mkdir('/var/run/netgwm/')
23+
24+
try: gwstore = yaml.load(open(gwstorefile, 'r'))
25+
except: gwstore = {}
26+
27+
gateways = []
28+
if 'gateways' in config and not config['gateways'] is None:
29+
for gw_identifier, gw_data in config['gateways'].iteritems():
30+
gateways.append(GatewayManager(gwstore, identifier=gw_identifier, **gw_data))
31+
32+
currentgw = GatewayManager.get_current_gateway(gateways)
33+
34+
try:
35+
if 'mode' in config: mode = config['mode']
36+
else: mode = open(modefile, 'r').read().strip()
37+
if mode not in config['gateways']: raise Exception()
38+
except: mode = 'auto'
39+
40+
if mode == 'auto':
41+
if currentgw is not None and currentgw.check(config['check_sites']):
42+
# если доступен интернет
43+
# ищем доступный роутер с приоритетом выше, чем у текущего
44+
candidates = [x for x in gateways if x.priority < currentgw.priority]
45+
for gw in sorted(candidates, key = lambda x: x.priority):
46+
if gw.check(config['check_sites']) and gw.wakeuptime < (time.time() - config['min_uptime']):
47+
# роутер работает и работает без сбоев достаточно долго
48+
gw.setdefault()
49+
post_replace_trigger(newgw=gw, oldgw=currentgw)
50+
break
51+
else: continue
52+
else:
53+
# Срочно переключаемся на самый приоритетный доступный шлюз
54+
for gw in sorted(gateways, key = lambda x: x.priority):
55+
if gw == currentgw: continue # и так понятно, что текущий роутер не работает
56+
if gw.check(config['check_sites']):
57+
gw.setdefault()
58+
post_replace_trigger(newgw=gw, oldgw=currentgw)
59+
break
60+
else: continue
61+
# ни один роутер не работает
62+
else:
63+
fixedgw = [x for x in gateways if x.identifier == mode].pop()
64+
if currentgw is None or currentgw != fixedgw:
65+
fixedgw.setdefault()
66+
post_replace_trigger(newgw=fixedgw, oldgw=currentgw)
67+
68+
if 'check_all_gateways' in config and config['check_all_gateways'] is True:
69+
for gw in [x for x in gateways if not x.is_checked]: gw.check(config['check_sites'])
70+
71+
GatewayManager.store_gateways(gateways)
72+
73+
74+
def post_replace_trigger(newgw, oldgw):
75+
# post-replace.d
76+
args = []
77+
args.append(newgw.identifier)
78+
args.append(newgw.ip if hasattr(newgw, 'ip') else 'NaN')
79+
args.append(newgw.dev if hasattr(newgw, 'dev') else 'NaN')
80+
args.append(oldgw.identifier if not oldgw is None else 'Nan')
81+
args.append(oldgw.ip if not oldgw is None and hasattr(oldgw, 'ip') else 'NaN')
82+
args.append(oldgw.dev if not oldgw is None and hasattr(oldgw, 'dev') else 'NaN')
83+
for filename in sorted(os.listdir('/etc/netgwm/post-replace.d/')):
84+
execpath = '/etc/netgwm/post-replace.d/'+filename
85+
if os.path.isfile(execpath) and (os.stat(execpath).st_mode & stat.S_IXUSR):
86+
os.system(execpath+' '+' '.join(args))
87+
88+
89+
class GatewayManager:
90+
def __init__(self, gwstore, **kwargs):
91+
self.priority = kwargs['priority']
92+
self.identifier = kwargs['identifier']
93+
self.is_checked = False
94+
if 'ip' in kwargs and kwargs['ip'] is not None: self.ip = kwargs['ip']
95+
if 'dev' in kwargs and kwargs['dev'] is not None: self.dev = kwargs['dev']
96+
97+
if self.identifier in gwstore: self.wakeuptime = gwstore[self.identifier]['wakeuptime']
98+
else: self.wakeuptime = 0 # считаем, что при первом появлении роутера в системе, его аптайм -- много лет.
99+
100+
def __eq__(self, other):
101+
if other is None: return False
102+
else: return self.identifier == other.identifier
103+
104+
def check(self, check_sites):
105+
# check gw status
106+
print 'checking ' + self.identifier
107+
ipresult = not os.system('/sbin/ip route replace default %s table netgwm_check' % self.generate_route())
108+
109+
if ipresult is True:
110+
for site in check_sites:
111+
site_ip = socket.gethostbyname(site)
112+
113+
os.system('/sbin/ip rule add iif lo to %s lookup netgwm_check' % site_ip)
114+
115+
p = os.popen('ping -q -n -W 1 -c 2 %s 2> /dev/null' % site_ip)
116+
pingout = p.read()
117+
status = not p.close()
118+
119+
os.system('/sbin/ip rule del iif lo to %s lookup netgwm_check' % site_ip)
120+
121+
if status is True:
122+
# ping success
123+
rtt = re.search('\d+\.\d+/(\d+\.\d+)/\d+\.\d+/\d+\.\d+', pingout).group(1)
124+
info = 'up:'+site+':'+rtt
125+
break
126+
else:
127+
# ping fail
128+
info = 'down'
129+
os.system('/sbin/ip route del default %s table netgwm_check' % self.generate_route())
130+
else:
131+
status = False
132+
info = 'down'
133+
134+
try:
135+
with open('/var/run/netgwm/'+self.identifier, 'w') as f: f.write(info)
136+
except: pass
137+
138+
if self.wakeuptime is None and status is True: self.wakeuptime = time.time() # Если не установлено время подъема и сервак пинганулся -- устанавливае
139+
elif status is False: self.wakeuptime = None # Если не пинганулся -- затираем
140+
141+
self.is_checked = True
142+
143+
return status
144+
145+
def setdefault(self):
146+
# replace
147+
print '/sbin/ip route replace default ' + self.generate_route()
148+
os.system('/sbin/ip route replace default ' + self.generate_route())
149+
150+
def generate_route(self):
151+
res = []
152+
if hasattr(self, 'ip'): res.append('via ' + self.ip)
153+
if hasattr(self, 'dev'): res.append('dev ' + self.dev)
154+
return ' '.join(res)
155+
156+
@staticmethod
157+
def get_current_gateway(gateways):
158+
currentgw_ip = os.popen("/sbin/ip route | grep 'default via' | sed -r 's/default via (([0-9]+\.){3}[0-9]+) dev .+/\\1/g'").read().strip()
159+
currentgw_dev = os.popen("/sbin/ip route | grep 'default dev' | sed -r 's/default dev ([a-z0-9]+)(\s+.*)?/\\1/g'").read().strip()
160+
161+
if currentgw_ip == '' and currentgw_dev == '':
162+
return None
163+
elif currentgw_ip != '':
164+
for g in [x for x in gateways if hasattr(x, 'ip')]:
165+
if g.ip == currentgw_ip: return g
166+
elif currentgw_dev != '':
167+
for g in [x for x in gateways if hasattr(x, 'dev')]:
168+
if g.dev == currentgw_dev: return g
169+
else: raise Exception('current gw is not listed in config.')
170+
171+
@staticmethod
172+
def store_gateways(gateways):
173+
gwstore = {}
174+
for gw in gateways: gwstore[gw.identifier] = {'wakeuptime': gw.wakeuptime}
175+
open(gwstorefile, 'w').write(yaml.dump(gwstore))
176+
177+
178+
if __name__ == '__main__':
179+
main()
180+

gwm/gwm.yml renamed to netgwm/netgwm.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
gateways: []
1+
gateways: {}
22

33
min_uptime: 900 #15 min
44

netgwm/post-replace.d/script.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/bash
2+
3+
# $1 newgw identifier
4+
# $2 newgw ip or NaN
5+
# $3 newgw dev or NaN
6+
# $4 oldgw identifier or Nan
7+
# $5 oldgw ip or NaN
8+
# $6 oldgw dev or NaN
9+
10+

0 commit comments

Comments
 (0)