├── debian ├── compat ├── changelog ├── rules ├── control └── copyright ├── MANIFEST.in ├── COPYING.txt ├── bae ├── __init__.py ├── cli │ ├── __init__.py │ ├── code_tool.py │ ├── messages.py │ └── client.py ├── rest │ ├── __init__.py │ └── rest.py ├── config │ ├── __init__.py │ ├── constants.py │ ├── parser.py │ └── config.py └── errors.py ├── cli └── build.sh ├── README.txt ├── README.md ├── bin └── bae ├── Makefile └── setup.py /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include COPYING.txt 2 | -------------------------------------------------------------------------------- /COPYING.txt: -------------------------------------------------------------------------------- 1 | BAE Cli Tool is an open source client chatting with BAE server, it use MIT license 2 | -------------------------------------------------------------------------------- /bae/__init__.py: -------------------------------------------------------------------------------- 1 | from pkgutil import extend_path 2 | __path__ = extend_path(__path__, __name__) 3 | -------------------------------------------------------------------------------- /bae/cli/__init__.py: -------------------------------------------------------------------------------- 1 | from pkgutil import extend_path 2 | __path__ = extend_path(__path__, __name__) 3 | -------------------------------------------------------------------------------- /bae/rest/__init__.py: -------------------------------------------------------------------------------- 1 | from pkgutil import extend_path 2 | __path__ = extend_path(__path__, __name__) 3 | -------------------------------------------------------------------------------- /bae/config/__init__.py: -------------------------------------------------------------------------------- 1 | from pkgutil import extend_path 2 | __path__ = extend_path(__path__, __name__) 3 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | bae (0.0.1) UNRELEASED; urgency=low 2 | 3 | * Initial release. 4 | 5 | -- zhangguanxing01 Wed, 10 Jul 2013 20:50:32 +0800 6 | -------------------------------------------------------------------------------- /cli/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | WORKPATH=`dirname $0` 4 | 5 | if [ ! -d ${WORKPATH}/output ]; then 6 | mkdir ${WORKPATH}/output 7 | fi 8 | 9 | tar czvf cli.tar.gz * 10 | mv cli.tar.gz ${WORKPATH}/output/ 11 | 12 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | 4 | DEB_PYTHON_SYSTEM := pysupport 5 | 6 | include /usr/share/cdbs/1/rules/debhelper.mk 7 | include /usr/share/cdbs/1/class/python-distutils.mk 8 | 9 | clean:: 10 | rm -rf build build-stamp configure-stamp build/ MANIFEST 11 | dh_clean 12 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | BAE Cli Tools is provided by Baidu Inc. BAE develop team, in BAE, you can create a php/python/java/nodejs/siteapp web application or worker application in a flexiable and reliable environment. You can migreate your exist application to BAE to have equal stable as http://www.hao123.com/ , http://xiuxiu.meitu.com/ etc. You can get more information about BAE in http://developer.baidu.com Thank you 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: bae 2 | Section: python 3 | Priority: optional 4 | Maintainer: Baidu Inc. (BAE Team) 5 | Build-Depends: python-requests (>=1.1.0), python-dev (>=2.7.0), python-colorama (>=0.2.4), python-crypto(>=2.6) 6 | 7 | Package: bae 8 | Architecture: all 9 | Homepage: http://developer.baidu.com 10 | XB-Python-Version: ${python:Depends} 11 | Depends: ${python:Depends} 12 | Description: A Bae Cli Tool 13 | You can Visit http://developer.baidu.com to get more information about BAE 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | cli 2 | === 3 | 4 | A Client tool used for Baidu App Engine(BAE) 5 | BAE Cli Tools is provided by Baidu Inc. BAE develop team, in BAE, you can create a php/python/java/nodejs/siteapp web application or worker application in a flexiable and reliable environment. You can migreate your exist application to BAE to have equal stable as http://www.hao123.com/ , http://xiuxiu.meitu.com/ etc. You can get more information about BAE in http://developer.baidu.com Thank you 6 | 7 | you can also visit 8 | http://godbae.duapp.com/?p=338 9 | get more help 10 | -------------------------------------------------------------------------------- /bin/bae: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding : utf-8 -*- 3 | 4 | from bae.cli.client import BaeClient 5 | import codecs 6 | import locale 7 | import sys 8 | try: 9 | import sys 10 | reload(sys) 11 | sys.setdefaultencoding('utf-8') 12 | locale.setlocale(locale.LC_ALL, '') 13 | stream_writer = codecs.getwriter('utf-8') 14 | if not isinstance(sys.stdout, stream_writer): 15 | sys.stdout = stream_writer(sys.stdout) 16 | sys.stderr = stream_writer(sys.stderr) 17 | cli = BaeClient(); 18 | cli.start() 19 | except (KeyboardInterrupt, EOFError): 20 | print "BaeClient Cancelled" 21 | -------------------------------------------------------------------------------- /bae/config/constants.py: -------------------------------------------------------------------------------- 1 | API_ENTRY = "https://openapi.baidu.com/rest/2.0" 2 | ONEKEY_ENTRY = "http://csdk.baidu.com/cli_get_token" 3 | VERSION = "0.0.4" 4 | PROG = "Bae Client" 5 | PROG_NAME = "bae" 6 | LICENSE = '''Bae Client Tools 7 | @Copyright 2013 Baidu Inc.''' 8 | AUTHOR = "zhangguanxing01" 9 | CONTACT = "zhangguanxing01@baidu.com" 10 | WIKI = "http://developer.baidu.com/wiki" 11 | DEVELOPER = "http://developer.baidu.com/" 12 | EPILOG = "If you have any question about Bae Client, please Conatct " + CONTACT + " or visit " + WIKI + " to get more info" 13 | BAE_SUPPORT = "zhangguanxing01@baidu.com" 14 | USER_AGENT = "BAE Cli " + VERSION 15 | BAE_APP_CONFIG = ".baeapp" 16 | DEV_APP_CONFIG = ".devapp" 17 | BAE_GLOBAL_CONFIG = ".bae" 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # $Id: Makefile,v 1.6 2008/10/29 01:01:35 ghantoos Exp $ 2 | # 3 | 4 | PYTHON=`which python` 5 | DESTDIR=/ 6 | BUILDIR=$(CURDIR)/debian/bae 7 | PROJECT=bae 8 | VERSION=0.0.1 9 | 10 | all: 11 | @echo "make source - Create source package" 12 | @echo "make install - Install on local system" 13 | @echo "make buildrpm - Generate a rpm package" 14 | @echo "make builddeb - Generate a deb package" 15 | @echo "make clean - Get rid of scratch and byte files" 16 | 17 | source: 18 | $(PYTHON) setup.py sdist $(COMPILE) 19 | 20 | install: 21 | $(PYTHON) setup.py install --root $(DESTDIR) $(COMPILE) 22 | 23 | buildrpm: 24 | $(PYTHON) setup.py bdist_rpm --post-install=rpm/postinstall --pre-uninstall=rpm/preuninstall 25 | 26 | builddeb: 27 | # build the source package in the parent directory 28 | # then rename it to project_version.orig.tar.gz 29 | $(PYTHON) setup.py sdist $(COMPILE) --dist-dir=../ 30 | rename -f 's/$(PROJECT)-(.*)\.tar\.gz/$(PROJECT)_$$1\.orig\.tar\.gz/' ../* 31 | # build the package 32 | dpkg-buildpackage -i -I -rfakeroot 33 | 34 | clean: 35 | $(PYTHON) setup.py clean 36 | $(MAKE) -f $(CURDIR)/debian/rules clean 37 | rm -rf build/ MANIFEST 38 | find . -name '*.pyc' -delete 39 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Upstream Author: 2 | Baidu Inc. (BAE Team) 3 | 4 | Files: * 5 | Copyright: 6 | 2013, Baidu Inc. 7 | LicenseL MIT 8 | 9 | The MIT License (MIT) 10 | 11 | Copyright (c) 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in 21 | all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | THE SOFTWARE. 30 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding:utf-8 -*- 3 | ''' 4 | Bae Client contains main apis for BAE 5 | 6 | @Author : zhangguanxing01@baidu.com 7 | @Copyright : 2013 Baidu Inc. 8 | @Date : 2013-06-26 11:09:00 9 | ''' 10 | 11 | 12 | 13 | import os 14 | import sys 15 | from bae.config.constants import VERSION 16 | 17 | try: 18 | from setuptools import * 19 | except ImportError: 20 | from distutils.core import * 21 | 22 | def read(fname): 23 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 24 | 25 | if os.environ.has_key("BAE_OFFLINE"): 26 | offline = os.environ["BAE_OFFLINE"] 27 | else: 28 | offline = "yes" 29 | 30 | if sys.argv[-1] == "publish": 31 | os.system("rm -rf bae.egg-info") 32 | os.system("rm -rf build") 33 | os.system("rm -rf dist") 34 | os.system("find . -name '*.pyc' | xargs rm -rf") 35 | sys.exit(-1) 36 | 37 | requires = ['requests', 'colorama', 'PyYAML', 'prettytable>=0.7.0'] 38 | 39 | setup( 40 | name = "bae", 41 | version = VERSION, 42 | author = "Zhang Guanxing", 43 | author_email = "zhangguanxing01@baidu.com", 44 | description = ("A BAE Client Tool"), 45 | keywords = "bae client tool", 46 | url = "http://developer.baidu.com", 47 | packages = find_packages(exclude=["debian", "Makefile", "*.tests", "*.tests.*", "tests.*", "tests", "third"]), 48 | scripts = ["bin/bae"], 49 | install_requires = requires, 50 | zip_safe = False, 51 | long_description=read('README.txt'), 52 | classifiers=[ 53 | "Development Status :: 4 - Beta", 54 | "Topic :: Utilities", 55 | "Programming Language :: Python :: 2 :: Only", 56 | ], 57 | ) 58 | -------------------------------------------------------------------------------- /bae/cli/code_tool.py: -------------------------------------------------------------------------------- 1 | #-*- coding : utf-8 -*- 2 | 3 | import os 4 | from ..errors import * 5 | from ..config.constants import BAE_APP_CONFIG 6 | from .messages import g_messager 7 | SVN = "svn" 8 | GIT = "git" 9 | 10 | 11 | def get_tool(name, repos, localdir): 12 | if name == "svn": 13 | return SvnTool(repos, localdir) 14 | elif name == "git": 15 | return GitTool(repos, localdir) 16 | else: 17 | raise NotImplementError(name + " tool case not exist") 18 | 19 | class SvnTool: 20 | def __init__(self, repos, localdir): 21 | self._repos = repos 22 | self._localdir = localdir 23 | 24 | def run(self, cmd): 25 | g_messager.debug(cmd) 26 | os.system(cmd) 27 | 28 | def pull(self): 29 | if not os.path.exists(os.path.join(self._localdir, ".svn")): 30 | svncmd = "{0} co {1} {2}".format(SVN, self._repos, self._localdir) 31 | else: 32 | svncmd = "{0} up {1}".format(SVN, self._localdir) 33 | self.run(svncmd) 34 | 35 | def add(self): 36 | svncmd = "{0} ps svn:ignore {1} {2}".format(SVN, BAE_APP_CONFIG, self._localdir) 37 | self.run(svncmd) 38 | svncmd = "{0} add {1}/* --force".format(SVN, self._localdir) 39 | self.run(svncmd) 40 | 41 | def push(self): 42 | self.add() 43 | svncmd = "{0} ci {1} -m 'Bae Client automate'".format(SVN, self._localdir) 44 | self.run(svncmd) 45 | 46 | 47 | class GitTool: 48 | def __init__(self, repos, localdir): 49 | self._repos = repos 50 | self._localdir = localdir 51 | 52 | def run(self, cmd): 53 | g_messager.debug(cmd) 54 | os.system(cmd) 55 | 56 | def pull(self): 57 | c = os.getcwd() 58 | if not os.path.exists(os.path.join(self._localdir, ".git")): 59 | #FIXME BUG#3358 zhou 60 | gitcmd = "cd {2};{0} init;{0} remote add origin {1};{0} fetch;{0} branch master origin/master;{0} checkout master;cd {3}".format(GIT, self._repos, self._localdir, c) 61 | #gitcmd = "{0} clone {1} {2};cd {3}".format(GIT, self._repos, self._localdir, c) 62 | else: 63 | gitcmd = "cd {1};{0} pull;cd {2} ".format(GIT, self._localdir, c) 64 | 65 | self.run(gitcmd) 66 | 67 | def push(self): 68 | c = os.getcwd() 69 | os.system("touch {0}/.gitignore".format(self._localdir)) 70 | os.system("cat '.baeapp' > {0}/.gitignore".format(self._localdir)) 71 | gitcmd = "{0} commit -m 'Bae Client autoamte' {1}".format(GIT, self._localdir) 72 | self.run(gitcmd) 73 | gitcmd = "cd {1};{0} push origin master; cd {2}".format(GIT, self._localdir, c) 74 | self.run(gitcmd) 75 | 76 | 77 | -------------------------------------------------------------------------------- /bae/errors.py: -------------------------------------------------------------------------------- 1 | #-*- coding : utf-8 -*- 2 | 3 | class ErrorCode: 4 | pass 5 | 6 | 7 | bae_codes = ErrorCode() 8 | svn_codes = ErrorCode() 9 | 10 | _errors = { 11 | "-1" : ("uas_err", "uas error"), 12 | "0" : ("ok", "EveryThing is ok"), 13 | "1" : ("unknown", "Unknown error"), 14 | "2" : ("service_unavail", "Service temporarily unavailable"), 15 | "3" : ("unsupported", "Open api not supported"), 16 | "4" : ("noperm", "No permission to do this operation"), 17 | "5" : ("unauth_ip", "Not Authorized client ip addr"), 18 | "100" : ("invalid_param", "Invalid Parameter"), 19 | "101" : ("invalid_api_key", "Invalid API key"), 20 | "102" : ("session_invalid", "Session key invalid or timeout"), 21 | "104" : ("sign_err", "Incorrect signature"), 22 | "106" : ("sign_method_err", "Unsupported singnature method"), 23 | "107" : ("invalid_ts", "Invalid timestamp"), 24 | "110" : ("token_invalid", "Access Token Invalid or Timeout"), 25 | #bae errors 26 | "78000" : ("bae_ok", "EveryThing is Ok!"), 27 | "78001" : ("need_login", "Need login first"), 28 | "78002" : ("need_mco_login", "Need login first for mobile"), 29 | "78003" : ("api_error", "Call API error"), 30 | "78005" : ("param_error", "Parameter Error"), 31 | "78006" : ("appid_not_bae", "This appid is not deploy to BAE"), 32 | "78007" : ("call_service_err", "Call thirdparty API error") 33 | } 34 | 35 | svn_errors = { 36 | "-1" : ("ci_error", "Committ Error"), 37 | "-2" : ("create_error", "Create Error"), 38 | "-3" : ("build_error", "Build Error"), 39 | "-4" : ("update_error", "Updated Error"), 40 | "-5" : ("remove_error", "Removed Error"), 41 | "-6" : ("done_error", "Instance Alloc Error"), 42 | "0" : ("new", "New"), 43 | "1" : ("ci", "Committed"), 44 | "2" : ("create", "Created"), 45 | "3" : ("build", "Builded"), 46 | "4" : ("update", "Updated"), 47 | "5" : ("remove", "Removed"), 48 | "6" : ("done", "Instance Allocated"), 49 | "101" : ("ciing", "Committing"), 50 | "102" : ("creating", "Creating"), 51 | "103" : ("building", "Building"), 52 | "104" : ("updating", "Updating"), 53 | "105" : ("removing", "Removing"), 54 | "106" : ("d0ing", "Allocating Instance") 55 | } 56 | 57 | for code, t in _errors.iteritems(): 58 | setattr(bae_codes, t[0], code) 59 | 60 | class BaeCliError(Exception): 61 | def __init__(self, messages): 62 | self._messages = messages 63 | 64 | def __str__(self): 65 | return self._messages 66 | 67 | class BaeRestError(BaeCliError): 68 | def __init__(self, error_code, messages): 69 | self.error_code = str(error_code) 70 | self.messages = messages 71 | 72 | def __str__(self): 73 | if _errors.has_key(self.error_code): 74 | detail = "Error Code {0}".format(self.error_code) 75 | else: 76 | detail = "Unknown error code {0}".format(self.error_code) 77 | 78 | return "{detail} -- {messages}".format( 79 | detail = detail, 80 | messages = self.messages) 81 | 82 | class BaeConfigError(BaeCliError): 83 | pass 84 | 85 | class NotImplementError(BaeCliError): 86 | pass 87 | -------------------------------------------------------------------------------- /bae/cli/messages.py: -------------------------------------------------------------------------------- 1 | #-*- coding : utf-8 -*- 2 | 3 | ''' 4 | colored message. 5 | https://pypi.python.org/pypi/colorama 6 | ''' 7 | 8 | import colorama 9 | import getpass 10 | import traceback 11 | import sys 12 | 13 | try: 14 | import readline 15 | except ImportError: 16 | pass 17 | 18 | from ..config.constants import BAE_SUPPORT 19 | 20 | colorama.init() 21 | NORMAL = 0 22 | INPUT = 1 23 | YES_OR_NO = 2 24 | PASSWORD = 3 25 | SELECT = 4 26 | OUTPUT = 5 27 | 28 | DEBUG = False 29 | 30 | class BaeMessage: 31 | def __init__(self, use_color = True, use_cn = False): 32 | self.use_color = use_color 33 | self.use_cn = use_cn 34 | 35 | def colorstr(self, message, color): 36 | if self.use_color: 37 | return u"{color}{message}{reset}".format( 38 | color = color, 39 | message = message, 40 | reset = colorama.Style.RESET_ALL) 41 | else: 42 | return message 43 | 44 | def redstr(self, message): 45 | return self.colorstr(message, colorama.Fore.RED) 46 | 47 | def magentastr(self, message): 48 | return self.colorstr(message, colorama.Fore.MAGENTA) 49 | 50 | def greenstr(self, message): 51 | return self.colorstr(message, colorama.Fore.GREEN) 52 | 53 | def yellowstr(self, message): 54 | return self.colorstr(message, colorama.Fore.YELLOW) 55 | 56 | def _print(self, message, color = None, type = NORMAL, *args): 57 | #message = message.decode("utf-8") 58 | if type == NORMAL: 59 | msgs = message.splitlines() 60 | print "\n".join(self.colorstr("<< ", color) + msg for msg in msgs) 61 | 62 | if type == OUTPUT: 63 | 64 | msgs = message.splitlines() 65 | print "\n".join(msg for msg in msgs) 66 | 67 | if type == INPUT: 68 | msg = self.colorstr("{0} >> ".format(message), color) 69 | return raw_input(msg) 70 | 71 | if type == YES_OR_NO: 72 | msg = self.colorstr("{0} >> ".format(message), color) 73 | answer = raw_input(msg) 74 | 75 | while True: 76 | if answer and len(answer) == 1 and answer.upper() == "Y": 77 | return True 78 | elif answer and len(answer) == 1 and answer.upper() == "N": 79 | return False 80 | else: 81 | self.warning("Please set 'Y' for yes or 'N' for no") 82 | answer = raw_input(msg) 83 | if type == SELECT: 84 | if len(args) > 1: 85 | print "\n".join("{0} : {1}".format(index+1,getattr(option,args[1])) for index, option in enumerate(args[0])) 86 | else: 87 | print "\n".join("{0} : {1}".format(index+1,option) for index, option in enumerate(args[0])) 88 | 89 | while True: 90 | msg = self.colorstr("{0} [{1}-{2}]>> ".format(message, 1, len(args[0])) , color) 91 | answer = raw_input(msg) 92 | if answer.isdigit(): 93 | answer = int(answer) 94 | if answer <= len(args[0]) and answer >=1: 95 | return (answer, args[0][answer-1]) 96 | 97 | if type == PASSWORD: 98 | msg = self.colorstr("{0} >> ".format(message), color) 99 | return getpass.getpass(msg) 100 | 101 | def debug(self, message): 102 | if DEBUG == True: 103 | self._print(message, colorama.Fore.CYAN) 104 | else: 105 | pass 106 | 107 | def output(self, message): 108 | self._print(message, type = OUTPUT) 109 | def trace(self, message): 110 | self._print(message, colorama.Fore.BLUE) 111 | 112 | def error(self, message): 113 | self._print(message, colorama.Fore.RED) 114 | 115 | def exception(self): 116 | if DEBUG: 117 | self.error(traceback.format_exc()) 118 | else: 119 | self.error(traceback.format_exc().splitlines()[-1]) 120 | 121 | def bug(self, message): 122 | self.error(message) 123 | self.warning('oops, there may be a bug in bae cli, please no hesitate send email to {support} report this, Please accept our apology for the inconvenience this matter have give you.' .format(support = BAE_SUPPORT)) 124 | 125 | def success(self, message): 126 | self._print(message, colorama.Fore.GREEN) 127 | 128 | def suggestion(self, message): 129 | self._print(message, colorama.Fore.YELLOW) 130 | 131 | def warning(self, message): 132 | self._print(message, colorama.Fore.MAGENTA) 133 | 134 | def input(self, message): 135 | return self._print(message, colorama.Fore.GREEN, INPUT) 136 | 137 | def select(self, message, *args): 138 | return self._print(message, colorama.Fore.GREEN, SELECT, *args) 139 | 140 | def password(self, message): 141 | return self._print(message, colorama.Fore.GREEN, PASSWORD) 142 | 143 | def yes_or_no(self, message): 144 | return self._print(message, colorama.Fore.GREEN, YES_OR_NO) 145 | 146 | g_messager = BaeMessage() 147 | -------------------------------------------------------------------------------- /bae/rest/rest.py: -------------------------------------------------------------------------------- 1 | #-*- coding : utf-8 -*- 2 | 3 | import json 4 | import urllib 5 | import requests 6 | import uuid 7 | import os 8 | import platform 9 | 10 | from ..config.constants import ONEKEY_ENTRY, API_ENTRY, VERSION, PROG_NAME 11 | from ..errors import * 12 | from ..cli.messages import g_messager 13 | 14 | RETRY = 3 15 | TIMEOUT = 20 16 | class BaeRest: 17 | def __init__(self, access_token, debug = False): 18 | self._debug = debug 19 | if debug: 20 | try: 21 | import urllib3 22 | urllib3.connectionpool.HTTPSConnection.debuglevel = 1 23 | urllib3.connectionpool.HTTPConnection.debuglevel = 1 24 | except ImportError: 25 | try: 26 | requests.packages.urllib3.connectionpool.HTTPSConnection.debuglevel = 1 27 | requests.packages.urllib3.connectionpool.HTTPConnection.debuglevel = 1 28 | except ImportError: 29 | g_messager.bug("You havn't install python-requests or urllib3, debug mode will DOWN") 30 | self._access_token = access_token 31 | 32 | def _get_user_agent(self): 33 | try: 34 | plat = "%s %s" %(platform.platform(), platform.version()) 35 | except Exception: 36 | plat = "unknown" 37 | 38 | if os.environ.has_key("BAE_LOCALENV_VERSION"): 39 | plat = "LOCALENV : %s" %(os.environ["BAE_LOCALENV_VERSION"]) 40 | 41 | return 'BAE CLI %s "%s"' %(VERSION, plat) 42 | 43 | 44 | def add_token(self, data): 45 | if data is None or len(data) == 0: 46 | data = {"access_token": self._access_token} 47 | else: 48 | data["access_token"] = self._access_token 49 | return data 50 | 51 | def on_response(self, response, **kw): 52 | g_messager.debug(u"Server returns {0}".format(response.text)) 53 | pass 54 | 55 | def post(self, path, data = None, require_code = False, require_token = True): 56 | if require_token: 57 | url_path = path + "?" + urllib.urlencode({"access_token":self._access_token}) 58 | else: 59 | url_path = path 60 | return self._request('POST', url_path, json.dumps(data), require_code = require_code, 61 | headers = {'Content-Type': 'application/json'}) 62 | 63 | def get(self, path = '/', data = None, require_code = False, require_token = True, timeout = TIMEOUT): 64 | if require_token: 65 | data = self.add_token(data) 66 | if data: 67 | url_path = path + "?" + urllib.urlencode(data) 68 | else: 69 | url_path = path 70 | 71 | return self._request('GET', url_path, data = None, require_code = require_code, timeout = timeout) 72 | 73 | #Developer center not support session right now 74 | def _session(self): 75 | if not hasattr(self, "session") or not self.session: 76 | headers = {'Accept' : 'application/json', 77 | 'User-Agent' : self._get_user_agent()} 78 | self.session = requests.session() 79 | self.session.headers = headers 80 | self.hooks = { 81 | 'response' : self.on_response 82 | } 83 | return self.session 84 | 85 | def _request(self, method, path, data = None, require_code = False, **kw): 86 | def _server_error(): 87 | g_messager.exception() 88 | errmsg = u"Can't understand servers infomation " 89 | errmsg += unicode(res.text) 90 | raise BaeRestError(bae_codes.api_error, errmsg) 91 | 92 | def _bae_msg(obj): 93 | if g_messager.use_cn and obj.has_key("error_msg"): 94 | return obj["error_msg"].encode("utf-8") 95 | elif obj.has_key("error_msg_en"): 96 | return obj["error_msg_en"] 97 | else: 98 | return obj["error_msg"] 99 | 100 | for i in range(0, RETRY): 101 | res = self._session().request(method, path, 102 | data = data, hooks = self.hooks, **kw) 103 | try: 104 | obj = json.loads(res.text) 105 | 106 | if not require_code: 107 | if not obj.has_key("error_code"): 108 | return obj 109 | else: 110 | if str(obj["error_code"]) == bae_codes.ok: 111 | return obj 112 | 113 | if str(obj["error_code"]) == bae_codes.need_login \ 114 | or str(obj["error_code"]) == bae_codes.need_mco_login \ 115 | or str(obj["error_code"]) == bae_codes.token_invalid: 116 | msg = "Authenticate error: {%s}\n please get a token from %s, then use '{%s} login'" %(_bae_msg(obj), ONEKEY_ENTRY, PROG_NAME) 117 | else: 118 | msg = _bae_msg(obj) 119 | raise BaeRestError(obj["error_code"], msg) 120 | except KeyError: 121 | _server_error() 122 | except ValueError: 123 | _server_error() 124 | -------------------------------------------------------------------------------- /bae/config/parser.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | import sys 3 | import os 4 | 5 | from argparse import ArgumentParser, SUPPRESS 6 | from constants import * 7 | 8 | class BaeBaseParser(ArgumentParser): 9 | def error(self, message): 10 | print >> sys.stderr, message 11 | self.print_help() 12 | sys.exit(-1) 13 | 14 | class BaeParser: 15 | def __init__(self) : 16 | self._parse() 17 | 18 | def print_help(self): 19 | self.base_parser.print_help() 20 | 21 | def _parse(self): 22 | program_version_message = '%%(prog)s %s' % (VERSION) 23 | 24 | self.base_parser = BaeBaseParser(prog = "bae", description = LICENSE, epilog = EPILOG) 25 | self.base_parser.add_argument("-v", "--version", action="version", version = program_version_message) 26 | self.base_parser.add_argument("-D", "--debug", action="store_true") 27 | 28 | cmd_parser = self.base_parser.add_subparsers(dest = "cmd") 29 | init_parser = cmd_parser.add_parser("login", help = "login and init local environment") 30 | app_parser = cmd_parser.add_parser("app", help = "manage application") 31 | domain_parser = cmd_parser.add_parser("domain", help = "manage domain") 32 | instance_parser = cmd_parser.add_parser("instance", help = "manage working instances") 33 | log_parser = cmd_parser.add_parser("log", help = "View log (server, user and compile)") 34 | config_parser = cmd_parser.add_parser("config", help = "config local environment") 35 | service_parser = cmd_parser.add_parser("service", help = "Manage service") 36 | 37 | config_parser.add_argument("configitem", help = "Config a items in key=value pair") 38 | 39 | app_sub_parser = app_parser.add_subparsers(dest = "appcmd") 40 | app_common_parser = BaeBaseParser(add_help = False) 41 | app_common_parser.add_argument("-I", "--appid", 42 | help = "you should goto {DEVELOPER} apply a new Baidu application id, or you can use '{PROG} setupapp' to store a local cache appid".format(DEVELOPER = DEVELOPER, PROG = PROG_NAME), 43 | required = False) 44 | 45 | app_support_parser = app_sub_parser.add_parser("support", help = "Get your Bae Supported languages, services", parents = [app_common_parser]) 46 | app_setup_parser = app_sub_parser.add_parser("setup", help = "Setup a developer app to local directory", parents = [app_common_parser]) 47 | app_publish_parser = app_sub_parser.add_parser("publish", help = "Publish your code") 48 | app_publish_parser.add_argument("--local", action = "store_true", help = "[For local environment] publish your code in local environment") 49 | app_update_parser = app_sub_parser.add_parser("update", help = "Update a Bae app by appid, if no bae id given ,it will update all bae app") 50 | app_update_parser.add_argument("baeappids", help = "setup a bae app with bae appid, your can use '{0} app list' get bae appid".format(PROG_NAME), 51 | nargs = "*") 52 | app_update_parser.add_argument("-f", "--force", action = "store_true", help = "Force update from server") 53 | app_create_parser = app_sub_parser.add_parser("create", add_help = False, parents=[app_common_parser]) 54 | ''' 55 | app_create_parser.add_argument("-T", "--version-tool", dest="tool", action="store", 56 | help = "Version control tools") 57 | app_create_parser.add_argument("-d", "--domain", action="store", help = "Domain prefix") 58 | app_create_parser.add_argument("-L", "--lang", action="store", 59 | help = "Programming language you can use '{0} app status' to get more information".format(PROG_NAME)) 60 | app_create_parser.add_argument("-t", "--type", action="store", 61 | help = "Bae App type, you can use '{0} app status' to get more information".format(PROG_NAME)) 62 | ''' 63 | 64 | app_create_parser.add_argument("appname", action = "store", help = "BAE app name") 65 | 66 | app_delete_parser = app_sub_parser.add_parser("delete", help = "Delete BAE app(NOT baidu developer App)", parents=[app_common_parser]) 67 | app_delete_parser.add_argument("-f", "--force", action = "store_true", help = "Delete app without warning") 68 | app_delete_parser.add_argument("baeappid", nargs = "?") 69 | 70 | app_list_parser = app_sub_parser.add_parser("list", help = "List all bae app, you can set a list of bae appid or bae appname to get certain app infos", parents = [app_common_parser]) 71 | app_list_parser.add_argument("-v", "--detail", action = "store_true", help = "Get more detail infos") 72 | app_list_parser.add_argument("-l", action = "store_true", dest = "single_list", help = "List app as Single element (NOT Table format)") 73 | app_list_parser.add_argument("-f", "--force", action = "store_true", help = "Force load config from server") 74 | app_list_parser.add_argument("baeappids", nargs = '*') 75 | 76 | domain_sub_parser = domain_parser.add_subparsers(dest = "domaincmd") 77 | domain_base_parser = BaeBaseParser(add_help = False) 78 | domain_base_parser.add_argument("domain", action = "store", help = "domain name") 79 | domain_base_parser.add_argument("--baeappid", action = "store", help = "bae app id", required = False) 80 | add_parser = domain_sub_parser.add_parser("add" , parents = [domain_base_parser], help = "add a domain alias") 81 | del_parser = domain_sub_parser.add_parser("delete", parents = [domain_base_parser], help = "delete domain alias") 82 | list_parser = domain_sub_parser.add_parser("list" , help = "list domain alias") 83 | 84 | #Log is not supported now 85 | log_sub_parser = log_parser.add_subparsers(dest = "logcmd") 86 | log_common_parser = BaeBaseParser(add_help = False) 87 | log_common_parser.add_argument("--instanceid", "-I", action = "store", help = "whinc container log do you want to view", required = True) 88 | log_common_parser.add_argument("--file", "-f", help = "your log file name", required = True) 89 | log_common_parser.add_argument("--max", "-M", action = "store", type = int, help = "view log count, max to 1000, default 200", default = 20) 90 | log_common_parser.add_argument("--baeappid", action = "store", help = "set bae appid") 91 | 92 | log_list_parser = log_sub_parser.add_parser("list", help = "list your log files") 93 | log_list_parser.add_argument("--instanceid", "-s", action = "store", help = "container id (please use 'bae instance list' to get instance id)", required = True) 94 | log_list_parser.add_argument("--baeappid", "-I", action = "store", help = "set bae appid") 95 | log_sub_parser.add_parser("tail", help = "get latestest log", parents = [log_common_parser]) 96 | log_sub_parser.add_parser("head", help = "get oledest log", parents = [log_common_parser]) 97 | 98 | service_sub_parser = service_parser.add_subparsers(dest = "servicecmd") 99 | service_list_parser = service_sub_parser.add_parser("list", help = "list BAE supported services") 100 | service_status_parser = service_sub_parser.add_parser("status", help = "list all service of your application") 101 | service_apply_parser = service_sub_parser.add_parser("create", help = "apply a service flavor") 102 | service_apply_parser.add_argument("-t", "--type",action = "store", help = "set your service flavor type") 103 | 104 | service_mysql_parser = service_sub_parser.add_parser("mysql", help = "manage your mysql service") 105 | service_mysql_sub_parser = service_mysql_parser.add_subparsers(dest = "mysqlaction") 106 | mysql_common_parser = BaeBaseParser(add_help = False) 107 | mysql_common_parser.add_argument("--db", action = "store", dest = "database_id", help = SUPPRESS) 108 | mysql_action_common_parser = BaeBaseParser(add_help = False) 109 | mysql_action_common_parser.add_argument("--progress", "-P", action = "store_true", help = "print information showing the progress") 110 | service_mysql_import_parser = service_mysql_sub_parser.add_parser("import", help = "MySQL Import: restore your database from url or bcs", parents = [mysql_common_parser, mysql_action_common_parser]) 111 | service_mysql_import_parser.add_argument("FROM", action = "store", help = "url or bcs info as 'bucket:object'") 112 | service_mysql_export_parser = service_mysql_sub_parser.add_parser("export", help = "MySQL Export: export your database as a backup", parents = [mysql_common_parser, mysql_action_common_parser]) 113 | service_mysql_export_parser.add_argument("TO", action = "store", help = "bcs bucket") 114 | service_mysql_export_parser.add_argument("--format", action = "store", help = "backup format, including sql(DEFAULT), zip, gzip, bzip2.", default = "sql") 115 | service_mysql_status_parser = service_mysql_sub_parser.add_parser("status", help = "list mysql job (import|export) status", parents = [mysql_common_parser]) 116 | service_mysql_status_parser.add_argument("JOB", action = "store", help = "set job type ['import', 'export']") 117 | 118 | instance_sub_parser = instance_parser.add_subparsers(dest = "instancecmd") 119 | instance_common_parser = BaeBaseParser(add_help = False) 120 | instance_common_parser.add_argument("--baeappid", action = "store", required=False) 121 | instance_localenv_common_parser = BaeBaseParser(add_help = False) 122 | instance_localenv_common_parser.add_argument("--local", action = "store_true", help = "[For local environment] manage local web server") 123 | instance_list_parser = instance_sub_parser.add_parser("list", help = "List all your instance", parents=[instance_common_parser]) 124 | instance_list_parser.add_argument("insids", nargs = "*") 125 | instance_scale_parser = instance_sub_parser.add_parser("scale", help = "Scale your instance", parents = [instance_common_parser]) 126 | instance_scale_parser.add_argument("scalenum", type=int, action="store") 127 | instance_restart_parser = instance_sub_parser.add_parser("restart", help = "Restart a instance", parents = [instance_common_parser, instance_localenv_common_parser]) 128 | instance_restart_parser.add_argument("insids", nargs = "*",action = "store") 129 | instance_start_parser = instance_sub_parser.add_parser("start", help = "Start a instance", parents = [instance_common_parser, instance_localenv_common_parser]) 130 | instance_stop_parser = instance_sub_parser.add_parser("stop", help = "Stop a instance", parents = [instance_common_parser, instance_localenv_common_parser]) 131 | 132 | self.args = self.base_parser.parse_args() 133 | 134 | def __getattr__(self, name): 135 | if hasattr(self.args, name): 136 | return getattr(self.args, name) 137 | else: 138 | return None 139 | -------------------------------------------------------------------------------- /bae/config/config.py: -------------------------------------------------------------------------------- 1 | #-*- coding : utf-8 -*- 2 | import os 3 | import traceback 4 | import yaml 5 | import prettytable 6 | import json 7 | 8 | from .constants import BAE_GLOBAL_CONFIG, BAE_APP_CONFIG, DEV_APP_CONFIG 9 | from ..errors import BaeConfigError, svn_errors 10 | from ..cli.messages import g_messager 11 | 12 | class AttrDict: 13 | def __init__(self, config, expect_keys): 14 | if config is None: 15 | #just a model 16 | for expect_key, clz, required in expect_keys: 17 | self.__dict__[expect_key] = clz() 18 | else: 19 | for expect_key, clz, required in expect_keys: 20 | if config.has_key(expect_key): 21 | if isinstance(config[expect_key], list): 22 | self.__dict__[expect_key] = [] 23 | for elem in config[expect_key]: 24 | self.__dict__[expect_key].append(clz(elem)) 25 | else: 26 | self.__dict__[expect_key] = clz(config[expect_key]) 27 | else: 28 | if required: 29 | raise BaeConfigError("config : {0} not exists".format(expect_key)) 30 | else: 31 | self.__dict__[expect_key] = None 32 | 33 | self._expect_keys = expect_keys 34 | 35 | def configs(self): 36 | config = {} 37 | for expect_key, clz, required in self._expect_keys: 38 | if self.__dict__.has_key(expect_key) and self.__dict__[expect_key] is not None: 39 | if isinstance(self.__dict__[expect_key],list): 40 | config[expect_key] = [] 41 | for elem in self.__dict__[expect_key]: 42 | if isinstance(elem, str) or isinstance(elem, bool): 43 | config[expect_key].append(elem) 44 | else: 45 | config[expect_key].append(elem.configs()) 46 | elif clz.__name__ == "str" or clz.__name__ == "bool": 47 | config[expect_key] = self.__dict__[expect_key] 48 | else: 49 | config[expect_key] = self.__dict__[expect_key].configs() 50 | return config 51 | 52 | class DevUser(AttrDict): 53 | _expect_keys = ( 54 | ("access_token", str, False), 55 | ("name", str, False) 56 | ) 57 | 58 | def __init__(self, config = None): 59 | AttrDict.__init__(self, config, DevUser._expect_keys) 60 | 61 | def __str__(self): 62 | return '''User Infomations: 63 | User name:{0}'''.format(self.name) 64 | return "" 65 | 66 | class BasicPackage(AttrDict): 67 | _expect_keys = ( 68 | ("id", str, True), 69 | ("display", str, True), 70 | ("price", str, True), 71 | ("resource", str, True), 72 | ("type", str, True), 73 | ) 74 | def __init__(self, config = None): 75 | AttrDict.__init__(self, config, BasicPackage._expect_keys) 76 | 77 | def __str__(self): 78 | return "" 79 | 80 | 81 | class BaeInfo(AttrDict): 82 | _expect_keys = ( 83 | ("max_app_num", str, True), 84 | ("cur_app_num", str, True), 85 | ("cidWorkerNum", str, True), 86 | ("cidWebappNum", str, True) 87 | ) 88 | def __init__(self, config = None): 89 | AttrDict.__init__(self, config, BaeInfo._expect_keys) 90 | 91 | def __str__(self): 92 | return '''Your Baidu App Infos 93 | Max App Number : {0} 94 | Cur App Number : {1} 95 | Cur Web Apps Number: {2} 96 | Cur Worker AppsNumber : {3}'''.format( 97 | str(self.max_app_num), 98 | str(self.cur_app_num), 99 | str(self.cidWorkerNum), 100 | str(self.cidWebappNum)) 101 | 102 | class BaeSolution(AttrDict): 103 | _expect_keys = ( 104 | ("lang", str, True), 105 | ("name", str, True), 106 | ("type", str, True) 107 | ) 108 | 109 | def __init__(self, config = None): 110 | AttrDict.__init__(self, config, BaeSolution._expect_keys) 111 | 112 | def __str__(self): 113 | return self.name 114 | 115 | class BaeGlobals(AttrDict): 116 | _expect_keys = ( 117 | ("user", DevUser, True), 118 | ("use_color", bool, False), 119 | ("api_entry", str, False), 120 | ('use_cn', bool, False) 121 | ) 122 | 123 | def __init__(self, config = None): 124 | AttrDict.__init__(self, config, BaeGlobals._expect_keys) 125 | 126 | def __str__(self): 127 | return '''Global Configs : 128 | {0}'''.format(str(self.user)) 129 | 130 | 131 | class DomainAlias(AttrDict): 132 | _expect_keys = ( 133 | ("alias_domain", str, True), 134 | ("cdatetime", str, False), 135 | ) 136 | 137 | def __init__(self, config = None): 138 | AttrDict.__init__(self, config, DomainAlias._expect_keys) 139 | 140 | def __str__(self): 141 | return self.alias_domain 142 | 143 | class BaeApp(AttrDict): 144 | _expect_keys = ( 145 | ("domain" ,str, False), 146 | ("createtype" ,str, True), 147 | ("lang_type" ,str, True), 148 | ("version_type" ,str, False), 149 | ("repos_url" ,str, False), 150 | ("release_revision" ,str, False), 151 | ("appid" ,str, True), 152 | ('appname' ,str, False), 153 | ('name' ,str, True), 154 | ('cdatetime' ,str, True), 155 | ("status" ,str, True), 156 | ('inum' ,str, False), 157 | ("alias" ,DomainAlias, True) 158 | ) 159 | def __init__(self, config = None): 160 | AttrDict.__init__(self, config, BaeApp._expect_keys) 161 | 162 | def tuple(self): 163 | datestr, statusstr = self._get_str() 164 | return (self.appid, self.name, self.lang_type, self.domain ,datestr, self.repos_url, 165 | self.createtype, self.version_type, self.release_revision[0:8], 166 | ";".join([str(x) for x in self.alias]), statusstr, self.inum) 167 | 168 | def _get_str(self): 169 | try: 170 | import time 171 | datestr = time.strftime("%Y/%b/%d %H:%M:%S", time.localtime(float(self.cdatetime))) 172 | except IOError: 173 | datestr = None 174 | 175 | if svn_errors.has_key(str(self.status)): 176 | statusstr = svn_errors[str(self.status)][1] 177 | else: 178 | statusstr = "Unknown" 179 | 180 | if int(self.status) < 0: 181 | statusstr = g_messager.redstr(statusstr) 182 | elif int(self.status) < 100: 183 | statusstr = g_messager.greenstr(statusstr) 184 | else: 185 | statusstr = g_messager.yellowstr(statusstr) 186 | 187 | return (datestr, statusstr) 188 | 189 | def __str__(self): 190 | datestr, statusstr = self._get_str() 191 | return'''-------------------------- 192 | BAE app {0} {1}: 193 | description : {2} 194 | pro language : {3} 195 | domain : {4} 196 | created at : {5} 197 | code repos url : {6} 198 | runtime Type : {7} 199 | code tool : {8} 200 | code revision : {9} 201 | Domain Alias : {10} 202 | status : {11} 203 | --------------------------'''.format( 204 | self.appid, 205 | self.name, 206 | self.appname, 207 | self.lang_type, 208 | self.domain, 209 | datestr, 210 | self.repos_url, 211 | self.createtype, 212 | self.version_type, 213 | self.release_revision, 214 | "\n".join([str(x) for x in self.alias]), 215 | statusstr) 216 | 217 | 218 | class DevApp(AttrDict): 219 | _expect_keys = ( 220 | ("app_id", str, True), 221 | ("solutions", BaeSolution, False), 222 | ("packages", BasicPackage, False) 223 | ) 224 | 225 | def __init__(self, config = None): 226 | AttrDict.__init__(self, config, DevApp._expect_keys) 227 | 228 | class BaeInstanceGroup(AttrDict): 229 | _expect_keys = ( 230 | ("gid", str, True), 231 | ('name', str, True), 232 | ('lang', str, True), 233 | ('type', str, True), 234 | ('userid', str, True), 235 | ('inum', int, True), 236 | ('checktime', str, True), 237 | ('checknum', int, True), 238 | ('status', str, True) 239 | ) 240 | 241 | _status = { 242 | 'new' : 0, 243 | 'running' : 1, 244 | 'deploying' : 0, 245 | 'deployfail' : -1, 246 | 'checking' : 0, 247 | 'checkfail' : -1, 248 | 'restarting' : 0, 249 | 'restartfail' : -1 250 | } 251 | 252 | def __init__(self, config = None): 253 | AttrDict.__init__(self, config, BaeInstanceGroup._expect_keys) 254 | 255 | def __str__(self): 256 | return '''group ID {0} infomation : 257 | group name: {1} 258 | language type: {2} 259 | group type: {3} 260 | user ID : {4} 261 | instance count: {5} 262 | status: {6} 263 | '''.format(self.gid, self.name, self.lang, self.type, self.userid, self.inum, _instance_status_str(BaeInstanceGroup._status, self.status)) 264 | 265 | class ServicePackage(AttrDict): 266 | _expect_keys = ( 267 | #("type_name", str, True), ###comments by pysqz 268 | #("type_detail", str, True), ###comments by pysqz 269 | ("type_name", str, False), 270 | ("type_detail", str, False), 271 | ) 272 | 273 | def __init__(self, config = None): 274 | AttrDict.__init__(self, config, ServicePackage._expect_keys) 275 | 276 | def __str__(self): 277 | return "{0} : {1}".format(self.type_name, self.type_detail) 278 | 279 | class Resource(AttrDict): 280 | _expect_keys = ( 281 | ("resource_name", str, True), 282 | ("service_name", str, True), 283 | ("service_type", str, True), 284 | #("service_package", ServicePackage, True), ###comments by pysqz 285 | ("base_info", dict, True), 286 | ) 287 | 288 | def __init__(self, config = None): 289 | AttrDict.__init__(self, config, Resource._expect_keys) 290 | 291 | def tuple(self): 292 | base_info = "\n".join(["{0}:{1}".format(k,v) for k, v in self.base_info.iteritems()]) 293 | #return (self.resource_name, self.resource_name, self.service_name, self.service_package.type_name, base_info) ###comments by pysqz 294 | return (self.resource_name, self.service_name, self.service_name, "N/A", base_info) 295 | 296 | 297 | class Service(AttrDict): 298 | _expect_keys = ( 299 | ("service_name", str, True), 300 | ("display_name", str, True), 301 | #("provider", str, True), 302 | #("service_package", ServicePackage, True), ###comments by pysqz 303 | ("service_package", ServicePackage, False), 304 | ) 305 | 306 | def __init__(self, config = None): 307 | AttrDict.__init__(self, config, Service._expect_keys) 308 | 309 | def __str__(self): 310 | return self.service_name 311 | 312 | def tuple(self): 313 | #return (self.service_name, self.display_name, "".join([x.type_name for x in self.service_package])) ###comments by pysqz 314 | return (self.service_name, self.display_name, "N/A") 315 | 316 | class BaeInstance(AttrDict): 317 | _expect_keys = ( 318 | ("fid", str, True), 319 | ("displayname", str, True), 320 | ("status", str, True), 321 | ) 322 | 323 | _status = { 324 | 'blank' : 0, 325 | 'creating' : 0, 326 | 'createfail' : -1, 327 | 'running' : 1, 328 | 'deploying' : 0, 329 | 'deployfail' : -1, 330 | 'deleting' : 0, 331 | 'deletefail' : -1, 332 | 'restarting' : 0, 333 | 'restartfail' : -1 334 | } 335 | 336 | def __init__(self, config = None): 337 | AttrDict.__init__(self, config, BaeInstance._expect_keys) 338 | 339 | def tuple(self): 340 | return (self.fid, self.displayname, _instance_status_str(BaeInstance._status, self.status)) 341 | 342 | def __str__(self): 343 | return '''---------------------------- 344 | instance ID : {0} 345 | name : {1} 346 | display name: {2} 347 | host : {3} 348 | webport : {4} 349 | SSHPort : {5} 350 | status : {6} 351 | -----------------------------'''.format( 352 | self.fid, 353 | self.name, 354 | self.displayname, 355 | self.host, 356 | self.webport, 357 | self.sshport, 358 | _instance_status_str(BaeInstance._status, self.status) 359 | ) 360 | 361 | class BaeConfigFile: 362 | def __init__(self, path): 363 | self.confpath = path 364 | 365 | def load(self): 366 | config = yaml.load(open(self.confpath, "r")) 367 | self._loadenv() #env varaible overrides config files 368 | 369 | return config 370 | 371 | #TODO support BAE environment 372 | def _loadenv(self): 373 | pass 374 | 375 | def save(self, config): 376 | yaml.dump(config, stream = open(self.confpath, "w"), default_flow_style=False) 377 | 378 | #0600 is relative safe 379 | os.chmod(self.confpath, 0600) 380 | 381 | def exists(self): 382 | return os.path.exists(self.confpath) 383 | 384 | def dirname(self): 385 | if self.confpath: 386 | return os.path.dirname(self.confpath) 387 | else: 388 | raise IOError("confpath not found") 389 | 390 | class BaeGlobalConfig: 391 | def __init__(self): 392 | home_dir = os.path.expanduser("~") 393 | path = os.path.join(home_dir, BAE_GLOBAL_CONFIG) 394 | 395 | self._configfile = BaeConfigFile(path) 396 | 397 | self.model = BaeGlobals() 398 | 399 | #reload configs 400 | def load(self): 401 | _config = self._configfile.load() 402 | self.model = BaeGlobals(_config) 403 | 404 | def save(self): 405 | self._configfile.save(self.model.configs()) 406 | 407 | class DevAppConfig: 408 | def __init__(self, path = None): 409 | if not path: 410 | cur_dir = os.getcwd() 411 | home_dir = os.path.expanduser("~") 412 | 413 | while cur_dir != home_dir and cur_dir != '/': 414 | tmppath = os.path.join(cur_dir, DEV_APP_CONFIG) 415 | if os.path.exists(tmppath): 416 | path = tmppath 417 | break 418 | #search parent 419 | cur_dir = os.path.realpath(os.path.join(cur_dir, os.pardir)) 420 | 421 | if cur_dir == home_dir or cur_dir == '/': 422 | #search in home dir 423 | tmppath = os.path.join(cur_dir, DEV_APP_CONFIG) 424 | if os.path.exists(tmppath): 425 | path = tmppath 426 | else: 427 | raise IOError("Can't find bae app config file") 428 | 429 | self._configfile = BaeConfigFile(path) 430 | 431 | if os.path.exists(path): 432 | _config = self._configfile.load() 433 | self.model = DevApp(_config) 434 | else: 435 | self.model = DevApp() 436 | 437 | def load_bae_app(self): 438 | self.bae_app_configs = [] 439 | 440 | self.cur_bae_app = None 441 | 442 | path = self.appdir() 443 | for subdir in os.listdir(path): 444 | cur_dir = os.path.join(path, subdir) 445 | bae_config = os.path.join(cur_dir, BAE_APP_CONFIG) 446 | 447 | if os.path.isdir(cur_dir) and os.path.exists(bae_config): 448 | bae_app_conf = BaeAppConfig(bae_config) 449 | bae_app_conf.load() 450 | self.bae_app_configs.append(bae_app_conf) 451 | if os.getcwd().startswith(os.path.normpath(cur_dir)): 452 | self.cur_bae_app = bae_app_conf 453 | 454 | def appdir(self): 455 | return self._configfile.dirname() 456 | 457 | def load(self): 458 | _config = self._configfile.load() 459 | self.model = DevApp(_config) 460 | self.load_bae_app() 461 | 462 | def save(self): 463 | self._configfile.save(self.model.configs()) 464 | 465 | for bae_app_config in self.bae_app_configs: 466 | bae_app_config.save() 467 | 468 | class BaeAppConfig: 469 | def __init__(self, path = None): 470 | self._configfile = BaeConfigFile(path) 471 | 472 | def load(self): 473 | if self._configfile.exists(): 474 | _config = self._configfile.load() 475 | self.model = BaeApp(_config) 476 | else: 477 | self.model = BaeApp() 478 | def save(self): 479 | return self._configfile.save(self.model.configs()) 480 | 481 | def dirname(self): 482 | return self._configfile.dirname() 483 | 484 | 485 | 486 | def _format_table(title, headers, rows): 487 | print g_messager.magentastr(title) 488 | table = prettytable.PrettyTable(headers) 489 | table.padding_width = 1 490 | for row in rows: 491 | table.add_row(row) 492 | return table 493 | 494 | def _instance_status_str(status_dict, key): 495 | if not status_dict.has_key(key): 496 | status = -1 497 | key = "Unknown" 498 | else: 499 | status = status_dict[key] 500 | 501 | if status == -1: 502 | return g_messager.redstr(key) 503 | elif status == 1: 504 | return g_messager.greenstr(key) 505 | else: 506 | return g_messager.yellowstr(key) 507 | 508 | _BAE_APP_HEADER = map(lambda x: g_messager.magentastr(x), ("appid", 'appname','language type','domain','created at','code repos URL', 'runtime type', 'code tool', 'revision', 'domain alias', 'status', 'instance count')) 509 | _INSTANCE_HEADER = map(lambda x: g_messager.magentastr(x), ('instance ID','display name', 'status')) 510 | _SERVICE_HEADER = map(lambda x: g_messager.magentastr(x), ('index', 'service name', 'name' , 'flavor')) 511 | _RESOURCE_HEADER = map(lambda x: g_messager.magentastr(x), ('index', "resource name", 'service name', 'name' , 'flavor', 'service infomation')) 512 | 513 | def bae_app_detail_table(title, rows): 514 | return _format_table(title, _BAE_APP_HEADER, rows).get_string(sortby = _BAE_APP_HEADER[4], 515 | reversesort = True) 516 | def bae_app_table(title, rows): 517 | return _format_table(title, _BAE_APP_HEADER, rows).get_string(fields = list(_BAE_APP_HEADER[i] for i in [0, 1, 2, 5, 11])) 518 | 519 | def instance_table(title, rows): 520 | return _format_table(title, _INSTANCE_HEADER, rows) 521 | 522 | def service_table(title, rows): 523 | return _format_table(title, _SERVICE_HEADER, rows) 524 | 525 | def resource_table(title, rows): 526 | tb = _format_table(title, _RESOURCE_HEADER, rows) 527 | #tb[5].align = "l" 528 | return tb 529 | -------------------------------------------------------------------------------- /bae/cli/client.py: -------------------------------------------------------------------------------- 1 | #*- coding : utf-8 -*- 2 | ''' 3 | Bae Client contains main apis for BAE 4 | 5 | @Author : zhangguanxing01@baidu.com 6 | @Copyright : 2013 Baidu Inc. 7 | @Date : 2013-07-26 11:09:00 8 | ''' 9 | 10 | import sys 11 | import re 12 | import os 13 | import time 14 | import messages 15 | import code_tool 16 | import shutil 17 | import json 18 | 19 | from .messages import g_messager 20 | from ..config.parser import BaeParser 21 | from ..rest.rest import BaeRest 22 | from ..config.constants import * 23 | from ..config.config import * 24 | from ..errors import * 25 | 26 | class BaeClient: 27 | def __init__(self): 28 | pass 29 | 30 | def start(self): 31 | parser = BaeParser() 32 | 33 | if parser.debug: 34 | messages.DEBUG = True 35 | g_messager.debug("Debug mode ON") 36 | else: 37 | messages.DEBUG = False 38 | 39 | #Load Global Configs or Local App Configs 40 | #if cmd is not init or setup, config non-exist will considered an error 41 | try: 42 | self.globalconfig = BaeGlobalConfig() 43 | self.globalconfig.model.use_color = True 44 | self.globalconfig.load() 45 | 46 | #set global message settings 47 | g_messager.use_color = self.globalconfig.model.use_color 48 | g_messager.use_cn = self.globalconfig.model.use_cn or False 49 | 50 | if parser.cmd == "login": 51 | raise BaeConfigError("Nothing") 52 | API_ENTRY = self.globalconfig.model.api_entry 53 | self.rest = BaeRest(access_token = self.globalconfig.model.user.access_token, debug = parser.debug) 54 | self._check_version() 55 | except (BaeConfigError, IOError): 56 | if parser.cmd != "login": 57 | g_messager.suggestion("Bae Configuration not founded or broken, please use '{prog} login' to " 58 | "init your bae environment" 59 | .format(prog=PROG_NAME)) 60 | sys.exit(-1) 61 | else: 62 | self.rest = BaeRest(None, debug = parser.debug) 63 | 64 | try: 65 | self.appconfig = DevAppConfig() 66 | self.appconfig.load() 67 | except (BaeConfigError, IOError): 68 | if parser.cmd != "login" and parser.appcmd != "setup" and not parser.force: 69 | g_messager.suggestion("NO local app directory founded, Please visit "+\ 70 | "{0} apply a appid and use '{1} app setup' ".format(DEVELOPER, PROG_NAME) +\ 71 | "to connect current directory to bae") 72 | g_messager.exception() 73 | sys.exit(-1) 74 | else: 75 | g_messager.debug("Load app config done") 76 | 77 | #If User set appid mannualy, this means he didn't want any local cache 78 | self.appconfig = None 79 | 80 | subcmd = "parser.{0}cmd".format(parser.cmd) 81 | 82 | if eval (subcmd): 83 | fullcmd = "{0}_{1}".format(parser.cmd, eval(subcmd)) 84 | else: 85 | fullcmd = parser.cmd 86 | 87 | try: 88 | #call subcmd functions 89 | getattr(self, fullcmd)(parser) 90 | except (BaeCliError, BaeRestError, BaeConfigError, KeyError, ValueError, TypeError, IOError): 91 | g_messager.exception() 92 | 93 | def _check_version(self): 94 | def cmp_version(a, b): 95 | return cmp(a.split('.'), b.split(".")) 96 | 97 | try: 98 | data = {} 99 | data["tool_name"] = "cli" 100 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/getVersionInfo", data = data) 101 | min_ver = ret["min_version"] 102 | cur_ver = ret["cur_version"] 103 | my_ver = VERSION 104 | if cmp(my_ver, min_ver) < 0: 105 | g_messager.error("your BAE cli version is out of date, please run 'pip install bae --upgrade' to update") 106 | sys.exit(-1) 107 | if cmp(my_ver, cur_ver) < 0: 108 | g_messager.warning("new BAE cli version {0} availiable, please run 'pip install bae --upgrade'to update") 109 | except KeyError: 110 | pass 111 | 112 | def config(self, parser): 113 | try: 114 | k,v = parser.configitem.split("=") 115 | if v.lower() in ['y', 'yes', 'true', '1']: 116 | v = True 117 | elif v.lower() in ['n', 'no', 'false', '0']: 118 | v = False 119 | else: 120 | v = False 121 | 122 | setattr(self.globalconfig.model, k, v) 123 | self.globalconfig.save() 124 | except ValueError: 125 | g_messager.error("Config Format Error, Please use = pair (set one key once)") 126 | 127 | #Init Global Varaibles 128 | def login(self, parser): 129 | g_messager.trace("please visit %s to get a token" %(ONEKEY_ENTRY)) 130 | access_token = g_messager.input("input your token:") 131 | self.globalconfig.model.user.access_token = access_token 132 | self.globalconfig.save() 133 | self.rest = BaeRest(access_token = access_token, debug = parser.debug) 134 | try: 135 | self._check_version() 136 | g_messager.trace("login success") 137 | except Exception: 138 | g_messager.warning("token is invalid") 139 | 140 | def app_support(self, parser): 141 | data = {} 142 | data['app_id'] = self._get_app_id(parser) 143 | 144 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/precreate", data = data) 145 | 146 | self.appconfig.model.solutions = [BaeSolution(_) for _ in ret["solutions"]] 147 | self.appconfig.save() 148 | 149 | g_messager.output("suppport language types:") 150 | 151 | for index, solution in enumerate(ret["solutions"]): 152 | g_messager.output("%d : %s" %(index+1, solution["name"])) 153 | 154 | ret = self.rest.get(API_ENTRY + "/bae/bce/package/getPackageInfo") 155 | self.appconfig.model.packages = [BasicPackage(_) for _ in ret["content"]["packlist"]] 156 | self.appconfig.save() 157 | 158 | g_messager.output("suppport packages:") 159 | for index, solution in enumerate(ret["content"]["packlist"]): 160 | if solution["type"] != "runtime": 161 | continue 162 | resources = [] 163 | 164 | for k,v in solution["resource"].iteritems(): 165 | resources.append(" ;%s %sM" %(k, v)) 166 | 167 | g_messager.output("%d : %s" %(index+1, ",".join(resources))) 168 | 169 | def app_setup(self, parser): 170 | if self.appconfig: 171 | parser.force = True 172 | self.app_update(parser) 173 | g_messager.trace("local app exists, try to update") 174 | return 175 | 176 | app_id = self._get_app_id(parser) 177 | #Require User Input a appid 178 | if not app_id: 179 | app_id = g_messager.input("please input your appid in baidu developer center (NOT BAE appid) : ") 180 | 181 | g_messager.output("your appid is {app_id}, BAE cli will setup this app in {curdir}".format(app_id = app_id, curdir= os.getcwd())) 182 | cwd = os.getcwd() 183 | self.appconfig = DevAppConfig(os.path.join(cwd, DEV_APP_CONFIG)) 184 | 185 | try: 186 | self.appconfig.load_bae_app() 187 | except BaeConfigError: 188 | g_messager.warning("Load Bae Config Error, But setup will continued") 189 | 190 | self.appconfig.model.app_id = app_id 191 | self.appconfig.bae_app_configs = self._app_cat(app_id) 192 | self.appconfig.save() 193 | 194 | for bae_app_config in self.appconfig.bae_app_configs: 195 | self._app_setup_bae(bae_app_config) 196 | 197 | #init support information 198 | self.app_support(parser) 199 | 200 | def app_update(self, parser): 201 | appid = self._get_app_id(parser) 202 | bae_app_confs = self._get_bae_confs(appid, parser) 203 | 204 | if parser.force and self.appconfig: 205 | #TODO add delete logic 206 | #server_del_set = [conf for bae_app_confs if conf not in self.appconfig.bae_app_configs] 207 | #local_del_set = [conf for self.appconfig.bae_app_configs if conf not in bae_app_confs] 208 | 209 | #for server_del_conf in server_del_set: 210 | # g_messager.output("Local Cache {0} is Deleted in server side, would want delete local one?") 211 | pass 212 | 213 | if not bae_app_confs: 214 | return 215 | 216 | for bae_app_conf in bae_app_confs: 217 | self._app_setup_bae(bae_app_conf) 218 | bae_app_conf.save() 219 | 220 | def app_create(self, parser): 221 | app_id = self._get_app_id(parser) 222 | data = {} 223 | 224 | data["version_type"] = g_messager.select("select code version tool" , ['svn', 'git'])[1] 225 | index, solution = g_messager.select("programming language", self.appconfig.model.solutions, "name") 226 | #index, package = g_messager.select("package type", self.appconfig.model.packages, "resource") 227 | 228 | data["solution"] = solution.name 229 | data["solution_type"] = solution.type 230 | if data["solution_type"] == "web": 231 | data["domain"] = g_messager.input("input your domain") 232 | if data["domain"].endswith(".duapp.com"): 233 | data["domain"] = data["domain"][:-len(".duapp.com")] 234 | 235 | data["appname"] = parser.appname 236 | data["name"] = data["appname"] 237 | data["packageid"] = "3" #256M 238 | data["ins_num"] = "1" 239 | requestid = self._gen_request_id() 240 | data["requestid"] = requestid 241 | data["app_id"] = app_id 242 | 243 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/createapp", data = data) 244 | new_bae_app = self._app_cat_bae(app_id, ret["bae_appid"]) 245 | 246 | if self.appconfig: 247 | appdir = self.appconfig.appdir() 248 | self.appconfig.bae_app_configs.append(new_bae_app) 249 | self.appconfig.save() 250 | g_messager.trace("Starting create app, this may take several seconds...".format(ret["bae_appid"])) 251 | 252 | try: 253 | self._get_operation_log(requestid) 254 | except BaeCliError, e: 255 | g_messager.error(str(e)) 256 | finally: 257 | self._app_setup_bae(new_bae_app) 258 | new_bae_app.save() 259 | 260 | def app_delete(self, parser): 261 | app_id = self._get_app_id(parser) 262 | bae_app_conf = self._get_cur_bae_conf(app_id, parser) 263 | if not bae_app_conf: 264 | g_messager.error("%s not exists in local cache or in server" %(parser.baeappid)) 265 | sys.exit(-1) 266 | 267 | data = {} 268 | data["app_id"] = app_id 269 | data["bae_appid"] = bae_app_conf.model.appid 270 | 271 | if not parser.force: 272 | app_name = g_messager.input("WARNING!!!! you will delete this app, this is UNRECOVERABLE action, " +\ 273 | "please input the app's name {0}".format(bae_app_conf.model.appname)) 274 | if app_name != bae_app_conf.model.appname: 275 | g_messager.warning("your input isn't right, delete bae app fail") 276 | sys.exit(-1) 277 | 278 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/delete", data = data) 279 | 280 | localdir = bae_app_conf.dirname() 281 | answer = False 282 | if localdir and os.path.exists(localdir): 283 | answer = g_messager.yes_or_no("Please make sure if we delete local_dir '{0}' (Y/N) :".format(localdir)) 284 | if answer: 285 | try: 286 | shutil.rmtree(localdir) 287 | except OSError, e: 288 | g_messager.warning(str(e)) 289 | if answer: 290 | g_messager.trace("Delete " + ret["bae_appid"] + " with local_dir Success") 291 | else: 292 | g_messager.trace("Delete " + ret["bae_appid"] + " without local_dir Success") 293 | 294 | def _do_publish(self, bae_app_conf): 295 | data = {} 296 | data["bae_appid"] = bae_app_conf.model.appid 297 | data["url"] = "" 298 | requestid = self._gen_request_id() 299 | data["requestid"] = requestid 300 | 301 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/republish", data = data) 302 | self._get_operation_log(requestid) 303 | 304 | def app_publish(self, parser): 305 | app_id = self._get_app_id(parser) 306 | bae_app_conf = self._get_cur_bae_conf(app_id, parser) 307 | 308 | if not bae_app_conf: 309 | g_messager.error("no local bae app found, please goto a bae app dir to publish code") 310 | sys.exit(-1) 311 | if not parser.local: 312 | self._do_publish(bae_app_conf) 313 | else: 314 | cmd = "bae_build %s %s %s"%(bae_app_conf.model.lang_type, bae_app_conf.dirname(), bae_app_conf.model.domain) 315 | os.system(cmd) 316 | 317 | def app_list(self, parser): 318 | if parser.detail: 319 | parser.force = True 320 | 321 | app_id = self._get_app_id(parser) 322 | bae_app_confs = self._get_bae_confs(app_id, parser) 323 | 324 | if len(bae_app_confs) == 1: 325 | parser.single_list = True 326 | 327 | if parser.single_list: 328 | for bae_app_conf in bae_app_confs: 329 | g_messager.output(str(bae_app_conf.model)) 330 | else: 331 | if parser.detail: 332 | print bae_app_detail_table("Application Detail Table", [bae_app_conf.model.tuple() for bae_app_conf in bae_app_confs]) 333 | else: 334 | print bae_app_table("Application General Infos (use --detail to see more)", [bae_app_conf.model.tuple() for bae_app_conf in bae_app_confs]) 335 | 336 | if not parser.force: 337 | self.appconfig.bae_app_configs = bae_app_confs 338 | self.appconfig.save() 339 | 340 | def service_list(self, parser): 341 | app_id = self._get_app_id(parser) 342 | data = {} 343 | 344 | data["app_id"] = app_id 345 | 346 | ret = self.rest.get(API_ENTRY + "/bae/service/usermgr/getServiceList", data = data) 347 | 348 | services = [Service(service_conf) for service_conf in ret["serv_list"]] 349 | #add an index to each tuple 350 | service_tuple = [tuple([idx] + list(service)) for idx, service in 351 | (zip ([str(i) for i in range(1, len(services)+1)], [service.tuple() for service in services])) 352 | ] 353 | 354 | print service_table("Bae Service list", service_tuple) 355 | 356 | def service_status(self, parser): 357 | app_id = self._get_app_id(parser) 358 | data = {} 359 | 360 | data["app_id"] = app_id 361 | 362 | ret = self.rest.get(API_ENTRY + "/bae/service/usermgr/getResourceList", data = data) 363 | resources = [Resource(resource_conf) for resource_conf in ret["resource_list"]] 364 | #This ugly code add index to a tuple 365 | resource_tuple = [tuple([idx] + list(resource)) for idx, resource in 366 | (zip ([str(i) for i in range(1, len(resources)+1)], [resource.tuple() for resource in resources])) 367 | ] 368 | print resource_table("Your BAE Service List", resource_tuple) 369 | 370 | def service_create(self, parser): 371 | app_id = self._get_app_id(parser) 372 | data = {} 373 | 374 | data["app_id"] = app_id 375 | 376 | ret = self.rest.get(API_ENTRY + "/bae/service/usermgr/getServiceList", data = data) 377 | services = [Service(service_conf) for service_conf in ret["serv_list"]] 378 | service = g_messager.select("Select a service", services)[1] 379 | 380 | if service: 381 | #idx, package = g_messager.select("Select a falvor", service.service_package) ### comments by pysqz 382 | data["service_name"] = service.service_name 383 | #data["service_package"] = idx-1 ### comments by pysqz 384 | data["service_package"] = 1 385 | ret = self.rest.get(API_ENTRY + "/bae/service/usermgr/createResource", data = data) 386 | 387 | g_messager.success("Create service {0} success".format(service.service_name)) 388 | for k, v in ret["resource_info"].iteritems(): 389 | g_messager.output("{0} : {1}".format(k, v)) 390 | 391 | def service_mysql(self, parser): 392 | def _progress(uri, flag = True): 393 | timeout = 300 394 | if uri.startswith("import"): 395 | status_info = {"1": "waiting", "2": "downloading", "3": "importing", "10": "imported", "-1": "fail to import"} 396 | else: 397 | status_info = {"1": "waiting", "2": "exporting", "3": "compressing", "4": "uploading", "10": "exported", "-1": "fail to export"} 398 | start_time = time.time() 399 | while 1: 400 | job_status = self.rest.get(API_ENTRY + "/bae/sqld/db/" + uri, data = data) 401 | msg = status_info.get(job_status.get("job_status"), "") 402 | if job_status["job_status"] == "-1": 403 | msg += "\t Err:%s"%job_status["errmsg"] 404 | g_messager.trace("Status: %s"%msg) 405 | if job_status["job_status"] in ["10", "-1"] or time.time() - start_time > timeout or not flag: 406 | break 407 | time.sleep(1) 408 | 409 | app_id = self._get_app_id(parser) 410 | data = {} 411 | data["app_id"] = app_id 412 | 413 | ret = self.rest.get(API_ENTRY + "/bae/service/usermgr/getResourceList", data = data) 414 | database = filter(lambda x: x.service_name == "BaeMySQLService", [Resource(resource_conf) for resource_conf in ret["resource_list"]]) 415 | if len(database) != 1: 416 | g_messager.warning("failed to get valid MySQL resource, please make sure your MySQL is enabled") 417 | sys.exit(-1) 418 | 419 | data["database_id"] = database[0].resource_name 420 | 421 | ### FIXME: the number of users' MySQL is more than 1, we also can deal with --db 422 | #if not parser.database_id: 423 | # g_messager.warning("please set argument '--db'") 424 | # sys.exit(-1) 425 | #data["database_id"] = parser.database_id 426 | # comments by pysqz 427 | 428 | mysql_action = parser.mysqlaction 429 | 430 | if mysql_action == "import": 431 | if parser.FROM.startswith("http://") or parser.FROM.startswith("https://"): 432 | data['url'] = parser.FROM 433 | rest_uri = "importTask" 434 | else: 435 | if ":" not in parser.FROM: 436 | g_messager.warning("please set bcs FROM with 'bucket:object'") 437 | sys.exit(-1) 438 | data['bucket'], data['object'] = parser.FROM.split(":", 1) 439 | rest_uri = "importBCS" 440 | 441 | ret = self.rest.get(API_ENTRY + "/bae/sqld/db/" + rest_uri, data = data) 442 | if ret.get("condition", -1) != 0: 443 | g_messager.error("failed to run mysql import, Err: %s"%ret["errmsg"]) 444 | sys.exit(-1) 445 | 446 | if parser.progress: 447 | _progress("importStat") 448 | 449 | elif mysql_action == "export": 450 | data['bucket'] = parser.TO 451 | data['compress'] = parser.format 452 | 453 | ret = self.rest.get(API_ENTRY + "/bae/sqld/db/exportTask", data = data) 454 | if ret.get("condition", -1) != 0: 455 | g_messager.error("failed to run mysql export, Err: %s"%ret["errmsg"]) 456 | sys.exit(-1) 457 | 458 | if parser.progress: 459 | _progress("exportStat") 460 | 461 | elif mysql_action == "status": 462 | if not parser.JOB or parser.JOB not in ["import", "export"]: 463 | g_messager.warning("please set argument with 'import' or 'export'") 464 | sys.exit(-1) 465 | 466 | _progress("%sStat"%parser.JOB, False) 467 | 468 | else: 469 | g_messager.error("invalid argument, just suportted (import | export | status)") 470 | 471 | def domain_list(self, parser): 472 | parser.force = True 473 | app_id = self._get_app_id(parser) 474 | bae_app_conf = self._get_cur_bae_conf(app_id, parser) 475 | 476 | if bae_app_conf is None: 477 | g_messager.warning("please set baeappid or change to that directory") 478 | sys.exit(-1) 479 | if bae_app_conf.model.alias: 480 | g_messager.trace("domain alias: " + "||".join([str(alias) for alias in bae_app_conf.model.alias])) 481 | else: 482 | g_messager.warning("this app has no domain alias") 483 | 484 | def domain_add(self, parser): 485 | app_id = self._get_app_id(parser) 486 | bae_app_conf = self._get_cur_bae_conf(app_id, parser) 487 | 488 | if not bae_app_conf: 489 | g_messager.error("Bae app not set or not exists in local cache") 490 | sys.exit(-1) 491 | 492 | data = {} 493 | data["alias_domain"] = parser.domain 494 | data["bae_appid"] = bae_app_conf.model.appid 495 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/adddomain", data = data) 496 | 497 | g_messager.trace("Bind to " + ret["alias_domain"] +" Success") 498 | 499 | def domain_delete(self, parser): 500 | app_id = self._get_app_id(parser) 501 | bae_app_conf = self._get_cur_bae_conf(app_id, parser) 502 | 503 | if not bae_app_conf: 504 | g_messager.error("Bae app not set or not exists in local cache") 505 | sys.exit(-1) 506 | 507 | data = {} 508 | data["alias_domain"] = parser.domain 509 | data["bae_appid"] = bae_app_conf.model.appid 510 | 511 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/deldomain", data = data) 512 | 513 | g_messager.trace("Del domain alias" + ret["alias_domain"] +" Success") 514 | 515 | def instance_list(self, parser): 516 | data = {} 517 | app_id = self._get_app_id(parser) 518 | bae_app_conf = self._get_cur_bae_conf(app_id,parser) 519 | 520 | if not bae_app_conf: 521 | g_messager.warning("Please use set baeappid or at least cd to a bae app directory") 522 | sys.exit(-1) 523 | 524 | data["bae_appid"] = bae_app_conf.model.appid 525 | 526 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/catInsList", data = data) 527 | 528 | g_messager.output(str(BaeInstanceGroup(ret["ig_info"]))) 529 | instances = [BaeInstance(ins).tuple() for ins in ret["ins_list"]] 530 | print instance_table("Instance List", instances) 531 | 532 | def instance_scale(self, parser): 533 | data = {} 534 | app_id = self._get_app_id(parser) 535 | bae_app_conf = self._get_cur_bae_conf(app_id, parser) 536 | 537 | if not bae_app_conf: 538 | g_messager.warning("Please use set baeappid or at least cd to a bae app directory") 539 | sys.exit(-1) 540 | 541 | data["bae_appid"] = bae_app_conf.model.appid 542 | data["ins_num"] = parser.scalenum 543 | 544 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/setInsNum", data = data) 545 | g_messager.trace("Scale instance number to {0} Success".format(parser.scalenum)) 546 | #self.instance_list(parser) 547 | 548 | def instance_restart(self, parser): 549 | data = {} 550 | app_id = self._get_app_id(parser) 551 | bae_app_conf = self._get_cur_bae_conf(app_id, parser) 552 | 553 | if not bae_app_conf: 554 | g_messager.warning("Please use set baeappid or at least cd to a bae app directory") 555 | sys.exit(-1) 556 | 557 | if not parser.local: 558 | data["bae_appid"] = bae_app_conf.model.appid 559 | data["ins_ids"] = json.dumps(parser.insids) 560 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/restartIns", data = data) 561 | taskid = ret["taskid"] 562 | 563 | g_messager.trace("Restart success") 564 | else: 565 | cmd = "bae_run %s start"%bae_app_conf.model.lang_type 566 | os.system(cmd) 567 | 568 | def instance_start(self, parser): 569 | data = {} 570 | app_id = self._get_app_id(parser) 571 | bae_app_conf = self._get_cur_bae_conf(app_id, parser) 572 | 573 | if not bae_app_conf: 574 | g_messager.warning("Please use set baeappid or at least cd to a bae app directory") 575 | sys.exit(-1) 576 | if not parser.local: 577 | g_messager.trace("start your online server. Comming soon...") 578 | else: 579 | cmd = "bae_run %s start"%bae_app_conf.model.lang_type 580 | os.system(cmd) 581 | 582 | def instance_stop(self, parser): 583 | data = {} 584 | app_id = self._get_app_id(parser) 585 | bae_app_conf = self._get_cur_bae_conf(app_id, parser) 586 | 587 | if not bae_app_conf: 588 | g_messager.warning("Please use set baeappid or at least cd to a bae app directory") 589 | sys.exit(-1) 590 | if not parser.local: 591 | g_messager.trace("stop your online server. Comming soon...") 592 | else: 593 | cmd = "bae_run %s stop"%bae_app_conf.model.lang_type 594 | os.system(cmd) 595 | 596 | def log_list(self, parser): 597 | data = {} 598 | app_id = self._get_app_id(parser) 599 | bae_app_conf = self._get_cur_bae_conf(app_id, parser) 600 | 601 | if not bae_app_conf: 602 | g_messager.error("Can't found your bae app or not set bae appid") 603 | sys.exit(-1) 604 | data["app_id"] = app_id 605 | data["bae_appid"] = bae_app_conf.model.appid 606 | data["fid"] = parser.instanceid 607 | data["log_type"] = "local" 608 | 609 | ret = self.rest.get(API_ENTRY + "/bae/farsee/log/filelist", data = data) 610 | 611 | if 0 == len(ret["files"]): 612 | g_message.warning("no log file in container now") 613 | else: 614 | g_messager.output("log file names in container(%d) :" %(len(ret["files"]))) 615 | g_messager.output("\n".join(ret["files"])) 616 | 617 | def log_tail(self, parser): 618 | self._query_log(parser, "tail") 619 | 620 | def log_head(self, parser): 621 | self._query_log(parser, "head") 622 | 623 | def _query_log(self, parser, method): 624 | data = {} 625 | app_id = self._get_app_id(parser) 626 | bae_app_conf = self._get_cur_bae_conf(app_id, parser) 627 | 628 | if not bae_app_conf: 629 | g_messager.error("Can't found your bae app or not set bae appid") 630 | sys.exit(-1) 631 | 632 | data["app_id"] = app_id 633 | data["bae_appid"] = bae_app_conf.model.appid 634 | data["fid"] = parser.instanceid 635 | data["filename"] = parser.file 636 | data["limit"] = parser.max or 50 637 | data["log_type"] = "local" 638 | 639 | ret = self.rest.get(API_ENTRY + "/bae/farsee/log/%s" %(method), data = data) 640 | 641 | if 0 == len(ret["contents"]): 642 | g_message.warning("no log in %s now" %(parser.file)) 643 | else: 644 | g_messager.output("\n".join(ret["contents"])) 645 | 646 | def _get_app_id(self, parser): 647 | appid = None 648 | 649 | if parser.appid: 650 | appid = parser.appid 651 | elif self.appconfig and self.appconfig.model.app_id: 652 | appid = self.appconfig.model.app_id 653 | return appid 654 | 655 | def _get_cur_bae_conf(self, appid, parser): 656 | appid = self._get_app_id(parser) 657 | baeappid = None 658 | conf = None 659 | if self.appconfig and self.appconfig.cur_bae_app: 660 | baeappid = self.appconfig.cur_bae_app.model.appid 661 | conf = self.appconfig.cur_bae_app 662 | if parser.force: 663 | conf = self._app_cat_bae(appid, conf.model.appid) 664 | 665 | if parser.baeappid: 666 | baeappid = parser.baeappid 667 | try: 668 | conf = self._app_cat_bae(appid, baeappid) 669 | except BaeRestError as e: 670 | #try get conf from localdir 671 | if self.appconfig.bae_app_configs: 672 | for bae_app_conf in self.appconfig.bae_app_configs: 673 | if os.path.basename(bae_app_conf.dirname()) == parser.baeappid: 674 | return bae_app_conf 675 | raise e 676 | if not conf: 677 | return None 678 | 679 | return conf 680 | 681 | def _get_bae_confs(self, appid, parser): 682 | confs = [] 683 | 684 | if parser.baeappids: 685 | if parser.force: 686 | confs = self._app_cat_bae(appid, parser.baeappids) 687 | elif self.appconfig: 688 | confs = [conf for conf in self.appconfig.bae_app_configs if conf.model.appid in parser.baeappids] 689 | else: 690 | if self.appconfig: 691 | if self.appconfig.cur_bae_app: 692 | confs = self._app_cat_bae(appid, [self.appconfig.cur_bae_app.model.appid]) 693 | else: 694 | if parser.force: 695 | confs = self._app_cat(self.appconfig.model.app_id) 696 | self.appconfig.bae_app_configs = confs 697 | self.appconfig.save() 698 | else: 699 | confs = self.appconfig.bae_app_configs 700 | 701 | return confs 702 | 703 | def _app_cat_bae(self, app_id, bae_appids): 704 | if not bae_appids: 705 | return [] 706 | if not isinstance(bae_appids, list): 707 | bae_appids = [bae_appids] 708 | issingle = True 709 | else: 710 | issingle = False 711 | 712 | data = {} 713 | data["bae_appids"] = json.dumps(bae_appids) 714 | data["app_id"] = app_id 715 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/catCodeBatch", data = data) 716 | bae_app_configs = [] 717 | 718 | for bae_app_conf in ret["appinfo"]: 719 | new_bae_app = BaeApp(bae_app_conf) 720 | g_messager.trace("Loading config for {0}".format(new_bae_app.name)) 721 | if self.appconfig: 722 | app_dir = self.appconfig.appdir() 723 | local_dir = os.path.join(app_dir, new_bae_app.name) 724 | local_conf = os.path.join(local_dir, BAE_APP_CONFIG) 725 | bae_config = BaeAppConfig(local_conf) 726 | 727 | if not os.path.exists(local_dir): 728 | import distutils 729 | import distutils.dir_util 730 | distutils.dir_util.mkpath(local_dir) 731 | elif not os.path.isdir(local_dir): 732 | g_messager.error(local_dir + "exists and it's not a dir") 733 | sys.exit(-1) 734 | else: 735 | bae_config = BaeAppConfig() 736 | bae_config.model = new_bae_app 737 | bae_app_configs.append(bae_config) 738 | 739 | if issingle: 740 | return bae_app_configs[0] 741 | else: 742 | return bae_app_configs 743 | 744 | def _app_setup_bae(self, bae_app_conf): 745 | bae_app = bae_app_conf.model 746 | 747 | g_messager.trace("begin setup {0}".format(bae_app.appid)) 748 | try: 749 | tool = code_tool.get_tool(bae_app.version_type, bae_app.repos_url, bae_app_conf.dirname()) 750 | tool.pull() 751 | except NotImplementError, e: 752 | g_messager.bug("Bae App {0} Tool not supported".format(str(bae_app))) 753 | 754 | def _app_cat(self, app_id = None): 755 | g_messager.trace("Loading Configs for Developer Application {appid}".format(appid = app_id)) 756 | data = {} 757 | data["app_id"] = app_id 758 | data["status"] = "all" 759 | data["limit"] = "10000" 760 | data["start"] = "0" 761 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/list", data = data) 762 | 763 | bae_app_ids = [bae_app_conf["appid"] for bae_app_conf in ret["app_list"]] 764 | bae_app_conf_list = self._app_cat_bae(app_id, bae_app_ids) 765 | 766 | return bae_app_conf_list 767 | 768 | def _gen_request_id(self): 769 | import uuid 770 | return uuid.uuid4() 771 | 772 | 773 | def _format_operation_log(self, log_json): 774 | END = 0 775 | ERROR = 1 776 | WARNING = 2 777 | TRACE = 3 778 | 779 | try: 780 | import json 781 | log = json.loads(log_json) 782 | 783 | tm = log["timestamp"] 784 | date = time.strftime("%T", time.localtime(tm)) 785 | logfmt = "{0} : {1}".format(date, log["log"]) 786 | 787 | if log["status"] == 3: 788 | g_messager.trace(logfmt) 789 | elif log["status"] == 2: 790 | g_messager.warning(logfmt) 791 | elif log["status"] == 1: 792 | raise BaeCliError(logfmt) 793 | else: 794 | g_messager.success(logfmt) 795 | return True 796 | except KeyError: 797 | pass 798 | 799 | return False 800 | 801 | def _get_operation_log(self, requestid): 802 | TIMEOUT = 30 803 | start = int(time.time()) 804 | log_end = False 805 | data = {} 806 | data["requestid"] = requestid 807 | 808 | while True: 809 | ret = self.rest.get(API_ENTRY + "/bae/bce/app/clilog", data, timeout = 3) 810 | 811 | for log in ret["logs"]: 812 | log_end = self._format_operation_log(log) 813 | 814 | now = int(time.time()) 815 | if log_end : 816 | break 817 | if now - start >= TIMEOUT: 818 | raise BaeCliError("get Server infomation error") 819 | else: 820 | time.sleep(1) 821 | --------------------------------------------------------------------------------