终端环境运维菜单-yaml

菜单功能说明:支持层级深入;选定的父菜单和子集菜单同屏显示,用字母及数字区分;包含子菜单的后缀多..;菜单由执行脚本和菜单模板文件构成,实际执行功能通过配置调用外部脚本。
菜单演示:

    *** 系统管理控制台 ***
    1. 应用发布..
       a. 生产环境..
       b. 测试环境..
       c. 发布..
    2. 应用重启..
    3. 服务管理..
    4. 数据库同步..
#coding=utf-8

##20161219
##@jians

import os, sys
import json, yaml
import subprocess

class MenuNode(object):
    def __init__(self, title):
        self.title = title
        self.cmd = None
        self.directory = None
        self.firstneibour = None
        self.firstchild = None
    def add_cmd(self, cmd):
        self.cmd = cmd
    def add_neibour(self, other_node):
        self.firstneibour = other_node
    def add_cwd(self, cwd):
        self.directory = cwd
    def add_child(self, first_child):
        self.firstchild = first_child


def parseMenu(obj, last_node=None):
    node = None
    if type(obj) == type({}):
        node = MenuNode(obj['title'])
        if obj.has_key('cmd'):
            node.add_cmd(obj['cmd'])
        if obj.has_key('directory'):
            node.add_cwd(obj['directory'])
        if obj.has_key('child'):
            parseMenu(obj['child'], node)    #递归调用,并传入当前节点,作为之后的父节点或邻节点
    if type(obj) == type([]):
        for i in range(len(obj)):
            if i == 0:
                nd =  parseMenu(obj[i])
                last_node.add_child(nd)  
            else:
                nd =  parseMenu(obj[i])
                last_node.add_neibour(nd)
            last_node = nd
    return node

def displayMenu(menu, key=None):
    os.system('clear')
    print("    *** 系统管理控制台 ***\n")
    menu_map = {}

    i = 1
    while True:
        menu_appd = '..'
        if menu.cmd:
            menu_appd = ''
        menu_item_text = str(i) + '. ' + menu.title + menu_appd

        menu_map[str(i)] = menu
        i += 1
        print("\t" + menu_item_text)

        if menu == key:
            j = 97
            child = menu.firstchild
            while True:
                child_appd = '..'
                if child.cmd:
                    child_appd = ''
                menu_item_text = chr(j) + '. ' + child.title + child_appd
                menu_map[chr(j)] = child
                j += 1
                print("\t" + '   ' + menu_item_text +"\n")
                if child.firstneibour:
                    child = child.firstneibour
                else:
                    break
        if menu.firstneibour:
            menu = menu.firstneibour
            print(" ")
        else:
            break
    return menu_map


if __name__ == '__main__':
    dirname = os.path.dirname(sys.argv[0])
    abspath = os.path.abspath(dirname)
    with open(os.path.join(abspath,'menu.yml'), 'r') as f:
        j = yaml.load(f)
        menu_struct = parseMenu(j)

    menu_saved = []     ##历史菜单入栈,用于返回
    menu_map = {}       ##当前菜单与按键映射

    menu_pos = menu_struct.firstchild
    menu_hot = None

    while True:
        menu_map = displayMenu(menu_pos, menu_hot)
        input = raw_input("\n  请输入选择(r返回上级,x退出):")
        input = input.strip()
        if input.lower() == 'r':
            if menu_hot:
                menu_hot = None
                menu_map = displayMenu(menu_pos, menu_hot)
            else:
                if len(menu_saved) > 0:
                    menu_pos = menu_saved.pop(-1)

        if input.lower() == 'x':
            sys.exit() 

        if menu_map.has_key(input.lower()):
            if menu_map[input].cmd:
                k = raw_input("确认执行请按y,其他返回: ")
                if k != 'y':
                    continue
                print(menu_map[input].cmd.split())
                ret = subprocess.Popen(menu_map[input].cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=menu_map[input].directory)
                print("\n======>\n")
                if ret.stdout:
                    for line in ret.stdout:
                        line = line.strip()
                        print(line)
                    wait = raw_input("\n任意键返回..")
            else:
                if input.isdigit():
                    menu_hot = menu_map[input]

                if input.isalpha():
                    menu_saved.append(menu_pos) # 保存历史
                    menu_pos = menu_map['a']
                    menu_hot = menu_map[input]

        menu_map = displayMenu(menu_pos, menu_hot)

菜单结构配置采用yaml格式书写,也可以用json格式,用json包导入处理:
title: 菜单中显示的名称
child:该菜单下包含的子菜单
cmd: 菜单对应的执行脚本,包含子菜单则为空
directory:菜单执行时切换的目录

title: 系统菜单
child:
  - title: 应用发布
    cmd:
    child:
      - title: 生产环境
        child:
          - title: 预演
            cmd: /usr/bin/ansible-playbook yun1_prop3p.yml restart_yun1_prop3p.yml yun2_prop3p.yml restart_yun2_prop3p.yml
            directory: /data/local/auto/deploys/product
          - title: 生产pro1
            child:
              - title: 只更新不重启
                cmd: /usr/bin/ansible-playbook yun1_prop.yml yun2_prop.yml
                directory: /data/local/auto/deploys/product
              - title: 更新、重启
                cmd: /usr/bin/ansible-playbook yun1_prop.yml restart_yun1_prop.yml yun2_prop.yml restart_yun2_prop.yml
                directory: /data/local/auto/deploys/product
  - title: 应用重启        
    child:
      - title: shop
        cmd: /usr/bin/ansible-playbook restart_yun1_proshop.yml
        directory: /data/local/auto/deploys/product
      - title: pro1
        cmd: /usr/bin/ansible-playbook restart_yun1_prop.yml
        directory: /data/local/auto/deploys/product
  - title: 服务管理
    child:
      - title: dns域名
        cmd: show_dns.sh
      - title: 内网proxy映射
        cmd: show_proxy.sh

  - title: 数据库同步
    child:
      - title: db1同步至本地
        cmd: /yunwei/scripts/sync_prod.sh prod_db1
      - title: db2同步至本地
        cmd: /yunwei/scripts/sync_prod.sh prod_db2

你可能感兴趣的:(Python)