老男孩Day3作业:练习文件操作(更新ha_proxy程序)

作业要求

1、实现针对ha_proxy文件的增删查改操作


 1)编写思路

编写思路参考下面GitHub链接中的流程图

https://github.com/ChuixinZeng/PythonStudyCode/blob/master/PythonCode-OldBoy/Day3/作业/Day3练习文件操作流程图.png


 2)具体实现

# -*- coding:utf-8 -*-

# Author:Chuixin Zeng

# 导入正则表达式、时间日期模块
import re,datetime

# 定义ha_config变量,内容是ha_config.conf文件
ha_config = "ha_config.conf"
# 定义时间日期变量,供后面备份文件时使用
bk_flag = datetime.datetime.now().strftime("%Y%H%M%S")

# 定义update_file函数,该函数包含两个参数f1和f2,用于更新文件内容
# 作用:用于删除backend,原理是,如果在ha_config中匹配到要删除的backend文件的话,不处理,不匹配的内容全部写入到ha_config.bk
# 这样结束循环后,把ha_config.bk的内容写回到ha_config,那些匹配的内容自然就删掉了。。。
def update_file(f1,f2):
    # 以写方式打开文件f1,以读方式打开文件f2
    # 下面这种with as写法的好处是不需要再使用close关闭文件了
    # f代表f1,f_old代表f2
    # 目的是把f2的内容更新到f1中
    with open(f1,"w") as f,open(f2,"r") as f_old:
        f.write(f_old.read())
        # flush操作执行后,才会把数据真正写入到f1对应的文件里面
        f.flush()

# 定义一个backup_file的函数,该函数包含一个参数f,用于将原始文件备份
def backup_file(f):
    # 定义f_bk变量,用于写打开.bk备份文件,文件名由f参数指定的名称+bk_flag定义的时间组成
    f_bk = open("%s_%s.bk" %(f,bk_flag),"w")
    # 这里f的文件名实际上原始文件和备份文件是一样的,只不过备份文件名多了一个日期
    # 所以,备份文件名是f+bk_flag,而原始文件名是f
    with open(f,"r",encoding="utf-8") as f:
        # 将原始文件内容备份到bk备份文件中
        f_bk.write(f.read())
        f_bk.flush()

# 定义update函数,进行haconfig.conf文件的更新
def update(args):
    if args:
        # 调用backup_file函数,针对原始的ha_config进行备份,备份完成的文件名是带日期的
        backup_file(ha_config)
        # 备份完成后,再执行下面的update操作
        # 以读打开ha_config文件,以写模式打开ha_config.bak文件,如果没有就新建
        with open(ha_config,"r") as f,open("%s.bk"% ha_config,"w") as f_new:
            # 从原始的ha_config文件循环读取内容,按行读取
            for line in f:
                # 利用正则表达式匹配每一行,如果能匹配用户输入的server info中的backend和record的话执行if进行更新
                # 不匹配的每一行都执行else
                if re.match(args["backend"],line):
                    # 更新backend行的值
                    f_new.write(line)
                    # 更新record行的值,	是制表符
                    f_new.write("	%s" % args["record"])
                    line = f.readline() # 继续读取下一行,直到循环结束
                else:
                    # 如果正则表达式没有匹配到backend字典的值,则将ha_config的值逐行写入到ha_config.bk中
                    f_new.write(line)
        # ,最终update完成的信息会放在bk文件里,然后调用更新文件的函数,将更新完成的ha_config.bk文件内容写回到ha_config
        update_file(ha_config,"%s.bk" % ha_config)

# 定义一个函数,用于往ha_config文件里面新增backend内容
def add_new_backend(args):
    if args:
        # 操作前先备份文件,备份完成的文件名是带日期的
        backup_file(ha_config)
        # 利用追加模式打开ha_config文件,也就是说文件如果不存在则创建;存在则只追加内容
        fp = open(ha_config,"a")
        # 追加的内容就是backend和record的值
        fp.write("
%s
	%s" % (args["backend"],args["record"]))
        # 最佳写入完成后,关闭文件
        fp.close()

# 定义一个函数,用户判断特定backend和record的值是不是存在
def config_diff(backend,record):
    # 2代表backend和record都已存在,1代表backend已存在,但是record值不一样,0代表backend不存在
    # 以读模式打开ha_config文件
    fp =open(ha_config,"r")
    # 设置result的值为0
    result = 0
    # 从ha_config文件里面读取内容
    for line in fp:
        # 如果读到了包含backend的行
        if re.match(backend,line):
            # 则返回值1
            result = 1
            # 继续读取下一行
            line = fp.readline()
            # 如果读到了包含record的行
            if re.match(".*%s" % record,line):
                #则告诉用户已经发现匹配的文件
                print("The backend config is found!")
                # 返回值为2
                result = 2
                # 中断循环
                break
    return result # 函数的返回值为result的具体值

# 定义一个函数,用于添加或者更新ha_config.conf中的backend
def add(args):
    # backend_info的值来源于函数convert_dict_to_conf,这个函数是把字典的值转换成配置文件
    backend_info = convert_dict_to_conf(args)
    if backend_info:
        # 调用config_diff函数,将backend_info中的值和ha_config中的值进行对比,以方便判断是否要添加新的backend或者更新backend
        diff = config_diff(backend_info["backend"],backend_info["record"])
        # 如果config_diff函数的返回值是2,则代表已经存在backend下面的record的值
        if diff == 2:
            print("The backend is already exists!")
        # 如果config_diff的函数的返回值是1,则代表匹配backend行的值,但是不匹配record的值,代表record的值有更新
        # 这种情况下就不是添加新的backend,而是更新现有的backend信息
        elif diff == 1:
            update(backend_info)
        # 如果以上条件都不满足,代表需要往ha_config中增加内容
        else:
            # 调用add_new_backend函数,增加新的内容
            add_new_backend(backend_info)
    else:
        return 0

# 定义一个函数,用于删除ha_config中的backend行
def delete_backend(args):
    if args:
        # 操作之前先备份
        backup_file(ha_config)
        # 以读模式打开ha_config,以写模式打开ha_config.bk
        with open(ha_config,"r") as f,open("%s.bk" % ha_config,"w") as f_new:
            # 循环遍历ha_config中的每一行
            for line in f:
                # 如果有匹配backend的行
                if re.match(args["backend"],line):
                    # 将读取到的值放到Line中
                    line = f.readline()
                else:
                    #如果没有匹配到,则更新ha_config.bk文件
                    f_new.write(line)
        # 调用update_file函数,将ha_config.bk文件更新到ha_config,这样就达到了删除匹配的行的目的
        update_file(ha_config,"%s.bk" % ha_config)

# 定义一个函数,用于删除backend
def delete(args):
    backend_info = convert_dict_to_conf(args)
    # 调用config_diff函数,将backend_info中的值和ha_config中的值进行对比,以方便判断要删除的值是否在ha_config.conf中
    if backend_info:
        diff = config_diff(backend_info["backend"],backend_info["record"])
        # 如果函数返回值是2,代表存在要删除的值
        if diff ==2:
            # 调用delete_backend进行删除操作
            delete_backend(backend_info)
        else:
            # 如果对比后,要删除的值不存在,则告诉用户值不存在
            print("not found")
            print(backend_info["backend"],backend_info["record"])
    else:
        return 0

# 定义一个函数search,用于查询用户输入的值是否存在
def search(args):
    # 以读方式打开ha_config文件
    fp = open(ha_config,"r")
    for line in fp:
        # 如果匹配到了用户输入的内容
        if re.match("backend %s" % args.strip(), line):
            # 则打印该行
            print(line.strip())
            # 读取下一行,即backend包含的record是什么
            print(fp.readline())
            break
    else:
        # 如果没有匹配的内容,则输入下面的信息
        print("not found '%s' in config file" % args.strip())

# 定义一个函数convert_to_dict,将用户输入的字符串类型转换成字典类型
def convert_to_dict(args):
    # 通过使用eval函数,可以把list,tuple,dict和string相互转化
    args = eval(args)
    # 如果args的内容是字典类型,则返回args
    if type(args) == type({}):
        return args
    else:
        # 如果不是字典类型,则返回空数组
        return ""

# 定义函数convert_dict_to_conf,用于将字典的内容转换成配置文件的格式
def convert_dict_to_conf(args):
    # 将字典转换成配置文件所使用的格式
    # 判断convert_to_dict函数的返回值args里面的keys是否包含backend和record
    if "backend" in args.keys() and "record" in args.keys():
        # 如果存在,则将backend和record的key对应的value转换为下面的格式
        backend = "backend %s" % (args["backend"].strip())
        record = "server %s weight %s maxconn %s" % (args['record']["server"],
                                                     args['record']["weight"], args['record']["maxconn"],)
        # 函数返回转换后的格式
        return {"backend":backend,"record":record}
    else:
        # 如果匹配键key失败,则输出错误信息
        print("格式错误,key:record 或者record错误")


def get_input_info():
    # 打印server info文字信息
    print("server info:")
    # 定义一个空字符串,用于保存用户输入的server info信息,即backend信息
    str_t = ""
    # iter函数,迭代器用起来很灵巧,你可以迭代不是序列但表现处序列行为的对象,例如字典的键、一个文件的行,等等
    for line in iter(input,""):
        # 将iter函数中用户输入的backend信息循环添加并追加到str_t变量中
        str_t += line
    # 将str_t变量的字符串值通过函数convert_to_dict转换为字典
    args = convert_to_dict(str_t)
    # 函数返回字典args的值,即server info下面用户输入的值
    if args:
        return args
    else:
        print("格式错误")

while True:
    # 交互式提示用户要做哪方面的查询
    choose = input("what are you want to do?(add/delete/search/q):")
    # 如果用户输入了search
    if choose == "search":
        # 则进一步提示用户输入要查询的backendname,并告诉用户输入的格式
        args = input("输入backend 域名进行查找(如:www.oldboy.org):")
        # 如果输入的信息正确
        if args:
            # 则调用search函数,进行查找,输出用户查找的内容
            search(args)
        # 如果输入的信息不正确,提示格式错误
        else:
            print("格式错误")
    # 如果用户输入的是q。则退出程序
    elif choose == "q":
        break
    # 如果用户输入的是add
    elif choose == "add":
        print(input("请输入完整的backend和record信息进行添加或者在域名和IP不变的情况下,输入新的weight和maxconn值进行更新,格式如下:
"
                    "请按键盘enter键继续,在server info中输入完整的新的backend信息...."))
        # 则调用get_input_info函数,提示用户输入要添加的server info信息,并把添加的信息放到args中
        args = get_input_info()
        # 如果用户输入的信息没问题,则调用add函数,将用户提供的信息添加到ha_config文件中
        if args:
            add(args)
    # 如果用户输入的是delete
    elif choose == "delete":
        # 则调用get_input_info函数,提示用户输入要添加的server info信息,并把添加的信息放到args中
        args = get_input_info()
        # 如果用户输入的信息没问题,则调用delete函数,将用户提供的信息添加到ha_config文件中
        if args:
            delete(args)

 3)GitHub笔记

第三天的笔记地址:

https://github.com/ChuixinZeng/PythonStudyCode/tree/master/PythonCode-OldBoy/Day3/随堂练习

第三天的作业地址:

https://github.com/ChuixinZeng/PythonStudyCode/tree/master/PythonCode-OldBoy/Day3/作业


 4)Readme文档

https://github.com/ChuixinZeng/PythonStudyCode/blob/master/PythonCode-OldBoy/Day3/作业/Day3_练习文件操作.md

原文地址:https://www.cnblogs.com/ChuixinZeng/p/Jamie_Zeng_Day3.html