python生成表格源代码_[python]路由表脚本生成程序

老实说这是老东西了,自己之前用scala写过,用ruby写过,这次是用python第二次实现,github上这种生成脚本也挺多的,自己全当是练手。

这个脚本其实就是把apnic提供的数据过滤出指定数据并解析然后生成路由表更新脚本的程序。用途相信各位也清楚,以下是自己重复造轮子的过程:

程序的主逻辑是读取apnic数据,用正则表达式过滤和解析,用解析出来的数据生成路由表修改脚本和恢复脚本。

首先是读取apnic数据。个人的方式是查找当前目录,然后脚本所在目录,如果再没有自动下载文件到脚本所在目录并返回。这里没有read on fly,即边下载边读,也没有比对并确保最新的逻辑。之后可以考虑加上。

Python

APNIC_URL = 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest'

SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))

def locate_apnic_path(filename = 'delegated-apnic-latest'):

if os.path.exists(filename):

return filename

apnic_path = os.path.join(SCRIPT_DIR, filename)

if not os.path.exists(apnic_path):

print 'Download apnic file from %s to %s' % (APNIC_URL, apnic_path)

urllib.urlretrieve(APNIC_URL, apnic_path)

return apnic_path

1

2

3

4

5

6

7

8

9

10

11

APNIC_URL='http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest'

SCRIPT_DIR=os.path.dirname(os.path.realpath(__file__))

deflocate_apnic_path(filename='delegated-apnic-latest'):

ifos.path.exists(filename):

returnfilename

apnic_path=os.path.join(SCRIPT_DIR,filename)

ifnotos.path.exists(apnic_path):

print'Download apnic file from %s to %s'%(APNIC_URL,apnic_path)

urllib.urlretrieve(APNIC_URL,apnic_path)

returnapnic_path

使用正则表达式过滤并解析的代码如下。解析结果是个CIDR格式,即网关的IP和对应子网掩码的路由前缀数。原始数据中IP后面的数据是子网主机数。

Python

NET_PATTERN = re.compile(

r'^apnic\|CN\|ipv4\|([0-9.]+)\|(\d+)\|\d+\|a.*$', re.IGNORECASE)

def parse_cn_net(apnic_path):

with open(apnic_path) as lines:

for line in lines:

# example: apnic|CN|ipv4|1.0.32.0|8192|20110412|allocated

m = NET_PATTERN.match(line)

if not m: continue

# (IP, routing prefix), e.g (1.2.3.4, 24) means 1.2.3.4/24

yield (m.group(1), 32 - int(math.log(int(m.group(2)), 2)))

1

2

3

4

5

6

7

8

9

10

11

NET_PATTERN=re.compile(

r'^apnic\|CN\|ipv4\|([0-9.]+)\|(\d+)\|\d+\|a.*$',re.IGNORECASE)

defparse_cn_net(apnic_path):

withopen(apnic_path)aslines:

forlineinlines:

# example: apnic|CN|ipv4|1.0.32.0|8192|20110412|allocated

m=NET_PATTERN.match(line)

ifnotm:continue

# (IP, routing prefix), e.g (1.2.3.4, 24) means 1.2.3.4/24

yield(m.group(1),32-int(math.log(int(m.group(2)),2)))

理论上接下就直接用解析出来的CIDR生成脚本了,但是考虑到部分网站或者内网也要纳入这个脚本,所以个人额外加了个excluding_net的配置,解析代码如下:

excluding_net中支持#开始的注释,忽略空行,允许直接host或者CIDR格式的单行配置。

Python

def load_excluding_net(filename = 'excluding_net'):

filepath = os.path.join(SCRIPT_DIR, filename)

if os.path.exists(filepath):

with open(filepath) as lines:

for line in lines:

if line.startswith('#'): continue # skip comment

host_or_cidr = line.rstrip() # remove line break

if not host_or_cidr: continue # skip empty line

i = host_or_cidr.find('/')

if i < 0: # host

yield (host_or_cidr, 32)

else:

yield (host_or_cidr[:i], int(host_or_cidr[(i + 1):]))

1

2

3

4

5

6

7

8

9

10

11

12

13

defload_excluding_net(filename='excluding_net'):

filepath=os.path.join(SCRIPT_DIR,filename)

ifos.path.exists(filepath):

withopen(filepath)aslines:

forlineinlines:

ifline.startswith('#'):continue# skip comment

host_or_cidr=line.rstrip()# remove line break

ifnothost_or_cidr:continue# skip empty line

i=host_or_cidr.find('/')

ifi<0:# host

yield(host_or_cidr,32)

else:

yield(host_or_cidr[:i],int(host_or_cidr[(i+1):]))

excluding_net配置样例

# xnnyygn.in, gssxgss.me

116.251.214.29/32

# internal net

192.168.1.0/24

192.168.3.0/24

1

2

3

4

5

6

# xnnyygn.in, gssxgss.me

116.251.214.29/32

# internal net

192.168.1.0/24

192.168.3.0/24

接下来才是用上面解析出来的CIDR数据生成脚本。在撰写如何生成脚本的代码中个人考虑了好久,主要是因为不同平台生成的脚本文件名,命令模式不同。

首先我可以确定一点,默认网关IP是可以通过命令得到,而不应该在生成脚本时决定。否则笔记本一会儿有线一会儿无线网关地址变化了就麻烦了。

第二是如果默认网关地址变化,那么恢复默认网关时即恢复脚本时,需要通过类似/tmp/prev_gw来交互。

再加上脚本头,一些容错处理的话,程序中嵌入脚本文本会很多,自然而然会想到模板。不过我并没有使用通用的模板,直接分离为脚本头和route设置命令,同时增加输出脚本名配置用于定位脚本头模板和输出的脚本的名字。配置如下:

Python

PLATFORM_META = {

'linux': (

'linux-ip-pre-up', 'route add -net %s/%d gw ${PREV_GW} metric 5\n',

'linux-ip-down', 'route delete -net %s/%d gw ${PREV_GW} metric 5\n'

),

'mac': (

'mac-ip-up', 'route add %s/%d ${PREV_GW}\n',

'mac-ip-down', 'route delete %s/%d ${PREV_GW}\n'

)

}

1

2

3

4

5

6

7

8

9

10

PLATFORM_META={

'linux':(

'linux-ip-pre-up','route add -net %s/%d gw ${PREV_GW} metric 5\n',

'linux-ip-down','route delete -net %s/%d gw ${PREV_GW} metric 5\n'

),

'mac':(

'mac-ip-up','route add %s/%d ${PREV_GW}\n',

'mac-ip-down','route delete %s/%d ${PREV_GW}\n'

)

}

这里字典的key是转换过的平台名,value的四个值分别是启动脚本名,启动脚本命令模板,恢复脚本名,恢复脚本命令模板。每个命令模板接受CIDR格式的tuple。

为了方便输出,个人用了个类来集合输出操作,这个类在初始化时就会把头模板填充进来。

Python

HEADER_DIR = 'header'

class RouteScript:

def __init__(self, name, cmd_pattern):

self.cmd_pattern = cmd_pattern

self.script = open(name, 'w')

header_path = os.path.join(SCRIPT_DIR, HEADER_DIR, name)

if os.path.exists(header_path):

with open(header_path) as f:

self.script.write(f.read())

def append(self, cidr):

self.script.write(self.cmd_pattern % cidr)

def close(self):

self.script.close()

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

HEADER_DIR='header'

classRouteScript:

def__init__(self,name,cmd_pattern):

self.cmd_pattern=cmd_pattern

self.script=open(name,'w')

header_path=os.path.join(SCRIPT_DIR,HEADER_DIR,name)

ifos.path.exists(header_path):

withopen(header_path)asf:

self.script.write(f.read())

defappend(self,cidr):

self.script.write(self.cmd_pattern%cidr)

defclose(self):

self.script.close()

基于上面类的输出代码,比没有类的时候要短小很多:

Python

def generate(platform):

config = PLATFORM_META[platform]

up_script = RouteScript(config[0], config[1])

down_script = RouteScript(config[2], config[3])

for cidr in itertools.chain(

load_excluding_net(), parse_cn_net(locate_apnic_path())):

up_script.append(cidr)

down_script.append(cidr)

up_script.close()

down_script.close()

1

2

3

4

5

6

7

8

9

10

defgenerate(platform):

config=PLATFORM_META[platform]

up_script=RouteScript(config[0],config[1])

down_script=RouteScript(config[2],config[3])

forcidrinitertools.chain(

load_excluding_net(),parse_cn_net(locate_apnic_path())):

up_script.append(cidr)

down_script.append(cidr)

up_script.close()

down_script.close()

最后脚本还做了一点平台支持的逻辑,即根据sys.platform决定用那个平台,比如linux2 -> linux, darwin -> mac。上面代码的platform是转换过的平台关键字。另外支持命令行第一个参数指定平台。

Python

PLATFORM_MAPPING = {

'linux2': 'linux',

'darwin': 'mac'

}

def determine_platform():

if len(sys.argv) > 1:

return sys.argv[1]

key = sys.platform

return PLATFORM_MAPPING.get(key) or key

if __name__ == '__main__':

platform = determine_platform()

if platform in PLATFORM_META:

generate(platform)

else:

print >>sys.stderr, 'unsupported platform [%s], or you can specify one' % platform

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

PLATFORM_MAPPING={

'linux2':'linux',

'darwin':'mac'

}

defdetermine_platform():

iflen(sys.argv)>1:

returnsys.argv[1]

key=sys.platform

returnPLATFORM_MAPPING.get(key)orkey

if__name__=='__main__':

platform=determine_platform()

ifplatforminPLATFORM_META:

generate(platform)

else:

print>>sys.stderr,'unsupported platform [%s], or you can specify one'%platform

完整代码在这里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值