本文共 21828 字,大约阅读时间需要 72 分钟。
参考文章:
源码下载:
aliyun-ros-cli-master --bin 执行文件 --ros --resources --ros_completion --ros --apps 功能及配置相关 --__init__.py --config.py --NewConfigParser.py --utils.py --others 其他操作命令 --__init__.py --list_events_command.py --list_regions_command.py --userdata_command.py --resources 操作资源命令 --__init__.py --describe_resource_command.py --list_resources_command.py --resource_type_command.py --resource_type_detail_command.py --resource_type_template_command.py --stacks 资源栈操作命令 --__init__.py --abandon_stack_command.py --create_stack_command.py --delete_stack_command.py --describe_stack_command.py --list_stacks_command.py --preview_stack_command.py --update_stack_command.py --templates 模板操作命令 --__init__.py --get_template_command.py --validate_template_command.py --__init__.py
ros
本身支持如下参数:
命令 | 功能 |
---|---|
-h --help | 查看帮助信息 |
--config [CONFIG_FILE] | 使用指定配置文件,如果没有指定,默认使用当前目录下的 ros/ros.conf 作为配置文件 |
--json | 以json 格式输出查询信息,否则以阅读格式输出 |
--region-id [REGION_ID] | 指定区域信息,否则使用配置文件中的区域信息 |
json
格式的输出按照配置文件中的 JSON_INDENT
设置缩进。
ros
支持如下一级命令:
命令 | 功能 |
---|---|
set-userdata | 设置默认配置 |
create-stack | 创建堆栈 |
delete-stack | 删除堆栈 |
update-stack | 更新堆栈 |
preview-stack | 预览堆栈 |
abandon-stack | 废弃堆栈(开发中) |
list-stacks | 列出满足条件的堆栈 |
describe-stack | 列出指定堆栈的详细信息 |
list-resource | 列出指定堆栈的资源信息 |
describe-resource | 列出指定资源的详细信息 |
resource-type | 列出所有资源类型 |
resource-type-detail | 列出指定资源类型的详细信息 |
resource-type-template | 列出指定资源类型的模板信息 |
get-template | 列出指定堆栈的模板信息 |
validate-template | 验证模板信息 |
list-regions | 列出所有区域 |
list-events | 列出满足条件的事件信息 |
使用 ros abandon-stack
命令废弃堆栈,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--region-id | 指定堆栈所在区域 | 必须给出 |
--stack-name [STACK_NAME] | 指定堆栈的名称 | 必须给出 |
--stack-id [STACK_ID] | 指定堆栈的ID | 必须给出 |
使用 ros create-stack
命令创建堆栈,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--region-id | 指定堆栈所在区域 | region-id 将按如下优先级取用:当前命令指定值 > ros 命令指定值 > 配置文件指定值 |
--stack-name [STACK_NAME] | 指定创建堆栈的名称 | 必须给出 |
--template-url [TEMPLATE_URL] | 指定创建堆栈的模板文件 | 必须给出,模板文件内容为 json 格式的模板 |
--parameters [PARAMETERS] | 给出模板需要的参数 | 与模板中的参数匹配,否则会被服务器拒绝。格式为连续的字符串,形如key1=value1,key2=value2 |
--disable-rollback [DISABLE_ROLLBACK] | 指定回滚策略 | 默认 true 禁止回滚, |
--timeout-in-minutes [TIMEOUT_IN_MINUTES] | 指定超时时间 | 默认 60 (分钟) |
创建成功后,返回堆栈名称和ID,否则返回错误信息。
使用 ros preview-stack
命令创建堆栈,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--region-id | 指定堆栈所在区域 | region-id 将按如下优先级取用:当前命令指定值 > ros 命令指定值 > 配置文件指定值 |
--stack-name [STACK_NAME] | 指定预览堆栈的名称 | 必须给出 |
--template-url [TEMPLATE_URL] | 指定预览堆栈的模板文件 | 必须给出,模板文件内容为 json 格式的模板 |
--parameters [PARAMETERS] | 给出模板需要的参数 | 与模板中的参数匹配,否则会被服务器拒绝。格式为连续的字符串,形如key1=value1,key2=value2 |
--disable-rollback [DISABLE_ROLLBACK] | 指定回滚策略 | 默认 true 禁止回滚, |
--timeout-in-minutes [TIMEOUT_IN_MINUTES] | 指定超时时间 | 默认 60 (分钟) |
使用 ros update-stack
命令更新堆栈,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--region-id | 指定堆栈所在区域 | 必须给出 |
--stack-name [STACK_NAME] | 指定更新堆栈的名称 | 必须给出 |
--stack-id [STACK_ID] | 指定更新堆栈的ID | 必须给出 |
--template-url [TEMPLATE_URL] | 指定更新堆栈的模板文件 | 必须给出,模板文件内容为 json 格式的模板 |
--parameters [PARAMETERS] | 给出模板需要的参数 | 与模板中的参数匹配,否则会被服务器拒绝。格式为连续的字符串,形如key1=value1,key2=value2 |
--disable-rollback [DISABLE_ROLLBACK] | 指定回滚策略 | 默认 true 禁止回滚, |
--timeout-in-minutes [TIMEOUT_IN_MINUTES] | 指定超时时间 | 默认 60 (分钟) |
更新成功后,返回堆栈名称和ID,否则返回错误信息。
使用 ros delete-stack
命令删除堆栈,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--region-id | 指定堆栈所在区域 | 必须给出 |
--stack-name [STACK_NAME] | 指定堆栈的名称 | 必须给出 |
--stack-id [STACK_ID] | 指定堆栈的ID | 必须给出 |
删除成功后,提示成功,无返回值,否则返回错误信息。
使用 ros list-stacks
命令查看堆栈列表,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--region-id | 指定堆栈所在区域 | |
--stack-name [STACK_NAME] | 指定堆栈的名称 | |
--stack-id [STACK_ID] | 指定堆栈的ID | |
--status {CREATE_COMPLETE, CREATE_FAILED, CREATE_IN_PROGRESS, DELETE_COMPLETE, DELETE_FAILED, DELETE_IN_PROGRESS, ROLLBACK_COMPLETE, ROLLBACK_FAILED, ROLLBACK_IN_PROGRESS} | 指定堆栈的状态 | 必须使用指定值 |
--page-number [PAGE_NUMBER] | 输入查看的页码 | 查询结果将分页返回,从1开始,默认为1 |
--page-size [PAGE_SIZE] | 指定每页显示数量 | 默认为10,不超过100 |
输出当前的翻页情况及结果列表
使用 ros describe-stack
命令获取堆栈详细信息,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--stack-name [STACK_NAME] | 指定堆栈的名称 | 必须给出 |
--stack-id [STACK_ID] | 指定堆栈的ID | 必须给出 |
成功后输出堆栈信息,否则输出错误信息。
使用 ros list-resources
命令获取堆栈资源信息,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--stack-name [STACK_NAME] | 指定堆栈的名称 | 必须给出 |
--stack-id [STACK_ID] | 指定堆栈的ID | 必须给出 |
成功后输出堆栈资源信息,否则输出错误信息。
使用 ros describe-resource
命令获取堆栈资源信息,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--stack-name [STACK_NAME] | 指定堆栈的名称 | 必须给出 |
--stack-id [STACK_ID] | 指定堆栈的ID | 必须给出 |
--resource-name [RESOUCE_NAME] | 指定的资源名称 | 必须给出 |
成功后输出堆栈资源信息,否则输出错误信息。
使用 ros resoucre-type
命令获取资源种类信息,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--status {UNKNOWN, SUPPORTED, DEPRECATED, UNSUPPORTED, HIDDEN} | 资源状态 | 默认使用SUPPORTED |
成功后输出资源种类信息。如果没有符合要求的,无输出。
使用 ros resource-type-detail
命令获取资源种类信息,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--name [NAME] | 指定资源类型的名称 | 必须给出 |
成功后返回资源详细信息,否则输出错误信息。
使用 ros resource-type-template
命令获取资源种类模板,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--name [NAME] | 指定资源类型的名称 | 必须给出 |
成功后返回资源模板信息,否则输出错误信息。
使用 ros get-template
命令获取指定堆栈的模板,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--stack-name [STACK_NAME] | 指定堆栈的名称 | 必须给出 |
--stack-id [STACK_ID] | 指定堆栈的ID | 必须给出 |
获取成功后,输出模板,否则输出错误信息。
使用 ros validate-template
命令验证指定堆栈的模板,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--template-url [TEMPLATE_URL] | 指定模板地址 | 必须给出 |
获取成功后,输出模板,否则输出错误信息。
列出所有的区域,无需参数。
使用 ros list-events
命令查看事件列表,包含如下参数:
命令 | 功能 | 备注 |
---|---|---|
--stack-name [STACK_NAME] | 指定堆栈的名称 | |
--stack-id [STACK_ID] | 指定堆栈的ID | |
--resource-status {'COMPLETE', 'FAILED', 'IN_PROGRESS'} | 指定资源的状态 | 必须使用指定值 |
--resource-name | 指定筛选资源 | |
--resource-type | 指定筛选资源类型 | |
--page-number [PAGE_NUMBER] | 输入查看的页码 | 查询结果将分页返回,从1开始,默认为1 |
--page-size [PAGE_SIZE] | 指定每页显示数量 | 默认为10,不超过100 |
输出当前的翻页情况及结果列表
使用 set-userdata
命令设置默认的用户配置。
命令 | 功能 | 备注 |
---|---|---|
--key-id [KEY_ID] | 默认的 ALIYUN Access Key ID | |
--key-secret [KEY_SECRET] | 默认的 ALIYUN Access Key Secret | |
--region-id [REGION_ID] | 默认的 region-id | |
--json-indent [JSON_INDENT] | JSON输出时的缩进 |
#!/usr/bin/env python# coding=utf-8import argparseimport osimport sysPOSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), os.pardir, os.pardir))if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'ros', '__init__.py')): sys.path.insert(0, POSSIBLE_TOPDIR)from ros.stacks import create_stack_commandfrom ros.stacks import delete_stack_commandfrom ros.stacks import update_stack_commandfrom ros.stacks import preview_stack_commandfrom ros.stacks import abandon_stack_commandfrom ros.stacks import describe_stack_commandfrom ros.stacks import list_stacks_commandfrom ros.resources import list_resources_commandfrom ros.resources import describe_resource_commandfrom ros.resources import resource_type_commandfrom ros.resources import resource_type_detail_commandfrom ros.resources import resource_type_template_commandfrom ros.templates import validate_template_commandfrom ros.templates import get_template_commandfrom ros.others import list_regions_commandfrom ros.others import list_events_commandfrom ros.others import userdata_commandfrom ros.apps import configif __name__ == '__main__': # 设置程序名(文件名) parser = argparse.ArgumentParser(prog='ros') # 添加子命令 subparsers = parser.add_subparsers(title='commands', metavar='', help=None) # 添加可选参数: 配置文件、是否json格式输出、Region ID parser.add_argument('--config', metavar='CONFIG_FILE', help='Location of config file', default=None) parser.add_argument('--json', action='store_true', help="Print results as JSON format", default=False) parser.add_argument('--region-id', help="Region ID, if not set, use config file's field", default=None) # 设置各种相关命令 userdata_command.setup(subparsers) create_stack_command.setup(subparsers) delete_stack_command.setup(subparsers) update_stack_command.setup(subparsers) preview_stack_command.setup(subparsers) abandon_stack_command.setup(subparsers) list_stacks_command.setup(subparsers) describe_stack_command.setup(subparsers) list_resources_command.setup(subparsers) describe_resource_command.setup(subparsers) resource_type_command.setup(subparsers) resource_type_detail_command.setup(subparsers) resource_type_template_command.setup(subparsers) get_template_command.setup(subparsers) validate_template_command.setup(subparsers) list_regions_command.setup(subparsers) list_events_command.setup(subparsers) # 参数输入 args = parser.parse_args() config.set_client(args.config, args.region_id, POSSIBLE_TOPDIR) if(args.json): config.JSON_FORM = True args.func(args)
import ConfigParser# 处理配置文件的类,继承自python内置ConfigParser类 class NewConfigParser(ConfigParser.ConfigParser): ''' Make options keep upper case ''' def __init__(self,defaults=None): ConfigParser.ConfigParser.__init__(self, defaults=None) def optionxform(self, optionstr): return optionstr
from aliyunsdkcore.client import AcsClientfrom aliyunsdkcore.acs_exception.exceptions import ClientExceptionfrom aliyunsdkcore.acs_exception.exceptions import ServerExceptionimport NewConfigParserimport osimport sysimport rereload(sys)sys.setdefaultencoding('utf-8')# 初始化配置ACCESS_KEY_ID = NoneACCESS_KEY_SECRET = NoneREGION_ID = Noneclient = NoneJSON_FORM = FalseJSON_INDENT = 2ROS_DEBUG = False# 输出当前配置def current_conf(): """ Print current client configuration :return: None """ global ACCESS_KEY_ID global ACCESS_KEY_SECRET global REGION_ID print( "[DEBUG] Current Config:\nACCESS_KEY_ID: %s\nACCESS_KEY_SECRET: %s\nREGION_ID: %s\n" % (ACCESS_KEY_ID, ACCESS_KEY_SECRET, REGION_ID))def set_client(cfg_file, region_id, top_dir=None): """ Configure client :param cfg_file: specify the configuration file :param region_id: specify region id :param top_dir: working path :return: None """ global ACCESS_KEY_ID global ACCESS_KEY_SECRET global REGION_ID global client global JSON_INDENT global ROS_DEBUG # 指定默认配置文件 if top_dir is None: default_file = 'ros/ros.conf' else: default_file = os.path.normpath(top_dir + '/ros/ros.conf') # 如果未提供配置文件 则使用默认文件 cf = NewConfigParser.NewConfigParser() if cfg_file is None: if ROS_DEBUG: print("Use default config file: %s\n" % default_file) cfg_file = default_file if os.path.isfile(cfg_file): pass else: if os.path.isdir(top_dir + '/ros'): pass else: os.mkdir(top_dir + '/ros') print('Please set Aliyun access info first.') # 输入access-key-id access_key_id = raw_input('Enter your access key id:') while check_access_info(access_key_id) is False: access_key_id = raw_input('Enter your access key id, only characters and numbers:') # 输入access-key-secret access_key_secret = raw_input('Enter your access key secret, without quote:') while check_access_info(access_key_secret) is False: access_key_secret = raw_input('Enter your access key secret, only characters and numbers:') # 输入region-id default_region_id = raw_input('Enter default region id, without quote:') # 添加配置项ACCESS cf.add_section('ACCESS') cf.set('ACCESS', 'ACCESS_KEY_ID', access_key_id) cf.set('ACCESS', 'ACCESS_KEY_SECRET', access_key_secret) cf.set('ACCESS', 'REGION_ID', default_region_id) # 添加配置项other:是否json格式输出、是否debug模式 cf.add_section('OTHER') cf.set('OTHER', 'JSON_INDENT', 2) cf.set('OTHER', 'DEBUG', False) # 写出配置文件 with open(cfg_file, 'w') as configfile: cf.write(configfile) # 读取配置 try: cf.read(cfg_file) except BaseException: print("""Config file (%s) error, please write it like: [ACCESS] ACCESS_KEY_ID = YOUR_KEY_ID ACCESS_KEY_SECRET = YOUR_KEY_SECRET REGION_ID = cn-beijing [OTHER] JSON_INDENT = 2 DEBUG = False """ % cfg_file) sys.exit(1) ACCESS_KEY_ID = cf.get("ACCESS", "ACCESS_KEY_ID") ACCESS_KEY_SECRET = cf.get("ACCESS", "ACCESS_KEY_SECRET") if region_id is None: REGION_ID = cf.get("ACCESS", "REGION_ID") else: REGION_ID = region_id JSON_INDENT = int(cf.get("OTHER", "JSON_INDENT")) ROS_DEBUG = bool(cf.get("OTHER", "DEBUG") == 'True') # 创建连接 client = AcsClient( ACCESS_KEY_ID, ACCESS_KEY_SECRET, REGION_ID ) if ROS_DEBUG: current_conf()# 验证输入的access信息def check_access_info(info): """ Check if access info only has characters and numbers """ match = re.search('^[A-Za-z0-9]+$', info) if match: return True else: return False
from aliyunsdkcore.acs_exception.exceptions import ClientExceptionfrom aliyunsdkcore.acs_exception import error_code, error_msgimport ros.apps.config as connectimport sysimport jsonimport urllib2reload(sys)sys.setdefaultencoding('utf-8')# 输出错误def print_error(data): """ Output error response from aliyun server :param data: response body in json :return: None """ for (k, v) in data.items(): print('%-20s: %s' % (k, v))# 从指定地址读取templatedef read_template(template_url): """ Get template content, support local file and online url :param template_url: the url of the template :return: template content """ if template_url.startswith('http'): try: response = urllib2.urlopen(template_url) return response.read() except Exception as e: print('Something wrong:\n%s' % str(e)) sys.exit(1) else: try: with open(template_url, 'r') as file_object: file_context = file_object.read() return file_context except Exception as e: print('Something wrong:\n%s' % str(e)) sys.exit(1)def alignment(s, space, align='left'): """ In python 2.x, make Chinese characters keep align :param str: input str :param space: width of the str :param align: left\right\center :return: aligned str """ length = len(s.encode('gb2312')) space = space - length if space >= length else 0 if align == 'left': s = s + ' ' * space elif align == 'right': s = ' ' * space + s elif align == 'center': s = ' ' * (space // 2) + s + ' ' * (space - space // 2) return s# 发送请求def send_req(req): """ Send ros request :param req: request :return: None """ req.set_accept_format("JSON") if connect.ROS_DEBUG: print('[DEBUG] Send request:\n %s\n' % req) try: status, headers, body = get_raw_resp(req) except Exception, e: print('Something wrong:\n%s' % str(e)) sys.exit(1) return status, headers, body#处理响应内容def deal_resp(status, headers, body, print_response): """ Output response :param status: status code :param headers: response header :param body: response body :param print_response: print function :return: None """ if connect.ROS_DEBUG: print('[DEBUG] Response status:\n %s\n' % status) print('[DEBUG] Response headers:\n %s\n' % headers) print('[DEBUG] Response body:\n %s\n' % body) try: data = json.loads(body) if 200 <= status < 300: print("[Succeed]") print_response(data) else: print("[Failed]") print_error(data) except Exception, e: print("[Error]") print('Something wrong:\n%s' % str(e)) sys.exit(1)# 获取原生响应def get_raw_resp(request): """ Get RAW response of aliyunsdk :param client: aliyunsdk client :param request: request to send :return: None """ client = connect.client endpoint = client._resolve_endpoint(request) http_response = client._make_http_response(endpoint, request) if client._url_test_flag: raise ClientException("URLTestFlagIsSet", http_response.get_url()) # Do the actual network thing try: status, headers, body = http_response.get_response_object() return status, headers, body except IOError as e: raise ClientException( error_code.SDK_SERVER_UNREACHABLE, error_msg.get_msg('SDK_SERVER_UNREACHABLE') + ': ' + str(e)) except AttributeError: raise ClientException( error_code.SDK_INVALID_REQUEST, error_msg.get_msg('SDK_INVALID_REQUEST'))def recursively_print(data, flag=True, indent=0): """ Print dict\list recursively :param data: data to print :param flag: whether need to use indentation :param indent: passed father level's indent :return: None """ if isinstance(data, list): for item in data: print('') recursively_print(item, False, indent + 1) elif isinstance(data, dict): for (k, v) in data.items(): if indent == 0: print '\n\n===================================================\n', elif indent == 1: print '\n\n -----------------------------------------------\n', print('') recursively_print(k, False, indent) print': ', recursively_print(v, True, indent + 1) else: data_out = str(data) pre = '' if not flag: pre = ' ' for i in range(0, indent): data_out = pre + data_out print data_out,if __name__ == '__main__': resp = read_template('http://ros-template.cn-hangzhou.oss.aliyun-inc.com/ecs_vpc_instance.json') print(resp)
from aliyunsdkros.request.v20150901 import AbandonStackRequestimport ros.apps.config as connectimport ros.apps.utils as utilsimport jsonimport sysreload(sys)sys.setdefaultencoding('utf-8')def setup(subparsers): # 设置命令 parser = subparsers.add_parser('abandon-stack', help='Abandon the specified stack') # 设置可选参数: regionID、stackName、stackID parser.add_argument('--region-id', help='The region that is associated with the stack', required=True) parser.add_argument('--stack-name', help='The name that is associated with the stack', required=True) parser.add_argument('--stack-id', help='The id that is associated with the stack', required=True) # 指定abandon_stack为默认调用函数 parser.set_defaults(func=abandon_stack)def abandon_stack(args): # 根据参数创建请求 req = prepare_request(args) # 获取返回结果:状态码、响应头、body status, headers, body = utils.send_req(req) # 处理返回结果(status, headers, body,回调函数) utils.deal_resp(status, headers, body, print_response)def prepare_request(args): # 创建废弃资源栈的请求 req = AbandonStackRequest.AbandonStackRequest() # 设置headers:regionID req.set_headers({'x-acs-region-id': args.region_id}) # 设置资源栈name req.set_StackName(args.stack_name) # 设置资源栈ID req.set_StackId(args.stack_id) # 返回请求 return reqdef print_response(data): #if connect.JSON_FORM: jsonDumpsIndentStr = json.dumps(data, indent=connect.JSON_INDENT, ensure_ascii=False, sort_keys=True) print(jsonDumpsIndentStr)
from aliyunsdkros.request.v20150901 import CreateStacksRequestimport ros.apps.config as connectimport ros.apps.utils as utilsimport jsonimport sysreload(sys)sys.setdefaultencoding('utf-8')def setup(subparsers): # create a parser for the 'create-stack' command parser = subparsers.add_parser('create-stack', help='Creates a stack as specified in the template') # 添加可选参数regionID parser.add_argument('--region-id', help='The region that is associated with the stack') # 添加可选参数stack-name parser.add_argument('--stack-name', help='The name that is associated with the stack', required=True) # 添加可选参数stack-name parser.add_argument('--template-url', help='Location of file containing the template body', required=True) # 创建资源栈需要输入的参数列表 语法:key=value,key=value parser.add_argument('--parameters', help='A list of Parameter structures that specify input parameters for the stack. Synatax: key=value,key=value') # 创建失败是否回滚 parser.add_argument('--disable-rollback', help='Set to true to disable rollback of the stack if stack creation failed', default=True, type=bool) # 超时时间 -单位minutes int 默认60min parser.add_argument('--timeout-in-minutes', help='The amount of time that can pass before the stack status becomes CREATE_FAILED', default=60, type=int) parser.set_defaults(func=create_stack) def create_stack(args): req = prepare_request(args) status, headers, body = utils.send_req(req) utils.deal_resp(status, headers, body, print_response)def prepare_request(args): req = CreateStacksRequest.CreateStacksRequest() # 如果没有指定regionID,则使用默认配置的regionID if args.region_id is not None: req.set_headers({'x-acs-region-id': args.region_id}) else: req.set_headers({'x-acs-region-id': connect.REGION_ID}) content = {} content['Name'] = args.stack_name # 通过templateURL获取Template,支持http和本地文件 file_context = utils.read_template(args.template_url) content['Template'] = file_context content['DisableRollback'] = args.disable_rollback content['TimeoutMins'] = args.timeout_in_minutes # 处理parameters为dict ps = {} if args.parameters is not None: s = args.parameters.split(',') for item in s: pair = item.split('=') ps[pair[0]] = pair[1] content['Parameters'] = ps jsonDumpsIndentStr = json.dumps(content, indent=connect.JSON_INDENT, ensure_ascii=False, sort_keys=True) # print(jsonDumpsIndentStr) req.set_content(jsonDumpsIndentStr) return reqdef print_response(data): # 根据配置 是否以json格式输出结果 if connect.JSON_FORM: jsonDumpsIndentStr = json.dumps(data, indent=connect.JSON_INDENT, ensure_ascii=False, sort_keys=True) print(jsonDumpsIndentStr) else: for (k, v) in data.items(): print('%-20s: %s' % (k, v))
转载地址:http://tcvfm.baihongyu.com/