def sync(src_phase, dst_phase, appname, meta_version): check_phase(src_phase) check_phase(dst_phase) src_domain = get_domain(src_phase) dst_domain = get_domain(dst_phase) src_registry = "registry.{domain}".format(domain=src_domain) dst_registry = "registry.{domain}".format(domain=dst_domain) src_phase_meta_tag = docker.gen_image_name( appname, 'meta', meta_version, src_registry) src_phase_release_tag = docker.gen_image_name( appname, 'release', meta_version, src_registry) dst_phase_meta_tag = docker.gen_image_name( appname, 'meta', meta_version, dst_registry) dst_phase_release_tag = docker.gen_image_name( appname, 'release', meta_version, dst_registry) return_code = transfer_to(src_phase_meta_tag, dst_phase_meta_tag) if return_code != 0: sys.exit(return_code) return_code = transfer_to(src_phase_release_tag, dst_phase_release_tag) if return_code != 0: sys.exit(return_code) access_token = SSOAccess.get_token(dst_phase) auth_header = get_auth_header(access_token) info("notifying lain push.") notify_pushs(dst_domain, appname, auth_header) info("notify lain push done.")
def show(cls, phase, procname, path=None): """ show secret file of special procname in different phase path: absolute path of config file, eg : /lain/app/config """ check_phase(phase) yml = lain_yaml(ignore_prepare=True) authorize_and_check(phase, yml.appname) auth_header = get_auth_header(SSOAccess.get_token(phase)) proc = yml.procs.get(procname, None) if proc is None: error('proc {} does not exist'.format(procname)) exit(1) podgroup_name = "{}.{}.{}".format(yml.appname, proc.type.name, proc.name) lvault_url = "http://lvault.%s/v2/secrets?app=%s&proc=%s" % ( get_domain(phase), yml.appname, podgroup_name) if path: lvault_url += "&path=%s" % path show_response = requests.get(lvault_url, headers=auth_header) if show_response.status_code < 300: info("secret file detail:") print(json.dumps(show_response.json(), encoding="utf-8", ensure_ascii=False, indent=2)) else: error("shit happened : %s" % show_response.text)
def print_available_version(version, version_list): if not version_list: error("No available versions, please push first.") return if version: error("Version %s not exist." % version) info("Below are the available versions: ") for version in version_list: print(version)
def render_scale_result(scale_result, output): try: result = scale_result.json() msg = result.pop('msg', '') if msg: print msg.decode('string_escape') info("proc status: ") render_proc_status(result.get('proc'), get_apptype(), output=output) except Exception: pprint.pprint(scale_result.content)
def print_available_repos(console, auth_header): repos_url = "http://%s/api/v1/repos/" % console repos_res = requests.get(repos_url, headers=auth_header) info('Available repos are :') if repos_res.status_code == 200: repos = repos_res.json()["repos"] render_repos([repo["appname"] for repo in repos]) print('') else: error("shit happened : %s" % repos_res.content)
def run(proc_name): """ Run proc instance in the local host """ container_name, image, working_dir, port, cmd, envs, volumes, _ = gen_run_ctx(proc_name) docker.proc_run(container_name, image, working_dir, port, cmd, envs, volumes) info("container name: {}".format(container_name)) if port: docker.inspect_port(container_name)
def meta(): """ Show current meta version """ meta_version = lain_yaml(ignore_prepare=True).repo_meta_version() if meta_version is None: error("please git commit.") else: info("meta version : %s" % lain_yaml(ignore_prepare=True).repo_meta_version())
def check(phase): """ Check current version of release and meta images in the remote registry """ check_phase(phase) tag_ok = _check_phase_tag(phase) if tag_ok: info("Image Tag OK in registry") else: error("Image Tag not OK in registry")
def print_available_apps(console, auth_header, sort_type): apps_url = "http://%s/api/v1/apps/" % console apps_res = requests.get(apps_url, headers=auth_header) info('Available apps are :') print("{:<30} {:<20} {:<60} {:<10}".format( "Appname", "AppType", "MetaVersion", "State")) if apps_res.status_code == 200: apps = apps_res.json()["apps"] render_apps([AppInfo.new(app) for app in apps], sort_type) else: error("shit happened: %s" % apps_res.content)
def refresh(phase): """ Refresh sso token """ check_phase(phase) refresh_success = sso_refresh(phase) if refresh_success: info("Refresh successfully!") else: warn('Refresh failed, Please try again!')
def prepare(): """ Build prepare image """ validate_only_warning() info("Generating prepare image...") if not lain_yaml().build_prepare()[0]: error("Error lain prepare.") sys.exit(1) else: info("Done lain prepare.") sys.exit(0)
def logout(phase): """ Logout specific phase """ check_phase(phase) domain = get_domain(phase) logout_success = SSOAccess.clear_token(phase) if logout_success: docker.logout('registry.%s'%domain) info("Logout successfully!") else: warn('Logout failed!')
def undeploy_app(appname, console, auth_header): delete_url = "http://%s/api/v1/apps/%s/" % (console, appname) delete_r = requests.delete(delete_url, headers=auth_header) try: if delete_r.status_code == 202: info("delete app %s success." % appname) info("delete result details: ") print(delete_r.json()['msg']) else: warn("delete app %s fail: %s" % (appname, delete_r.json()['msg'])) except Exception: error("shit happend: %s" % delete_r.content) exit(1)
def push(phase): """ Push release and meta images """ check_phase(phase) info("Pushing meta and release images ...") yml = lain_yaml(ignore_prepare=True) meta_version = yml.repo_meta_version() if meta_version is None: error("please git commit.") return None domain = get_domain(phase) registry = "registry.%s" % domain phase_meta_tag = docker.gen_image_name( yml.appname, 'meta', meta_version, registry) phase_release_tag = docker.gen_image_name( yml.appname, 'release', meta_version, registry) meta_code = docker.push(phase_meta_tag) release_code = docker.push(phase_release_tag) if meta_code or release_code: error("Error lain push.") sys.exit(1) else: info("Done lain push.") info("notifying lain push.") access_token = SSOAccess.get_token(phase) auth_header = get_auth_header(access_token) last_commit_id = fetch_last_commit_id(domain, yml.appname, auth_header) if last_commit_id is not None: notify_diffs(domain, yml.appname, last_commit_id, auth_header) else: warn("Notified Nothing!") info("Done notifying lain push.")
def validate(): """ Validate lain.yaml """ valid, msg = _validate() if valid: info('valid lain.yaml.') else: error('invalid lain.yaml.') warn('error message:') info(msg) # TODO : show related doc url warn('for details of lain.yaml schema please check related docs.') sys.exit(1)
def reposit(phase): """ Initialize a repository in lain """ check_phase(phase) validate_only_warning() info("Repositing ...") yml = lain_yaml(ignore_prepare=True) authorize_and_check(phase, yml.appname) access_token = SSOAccess.get_token(phase) auth_header = get_auth_header(access_token) console = "console.%s" % get_domain(phase) result = reposit_app(phase, yml.appname, console, auth_header) info("Done, %s" % result)
def login(phase, cid=None, secret=None, redirect_uri=None): """ Login specific phase, need open auth first cid: Client id get from the sso system, default: 3 secret: Client secret get from the sso system, default: lain-cli_admin redirect_uri: Redirect uri get from the sso system, default: https://example.com/ """ check_phase(phase) username = raw_input('SSO Username:'******'SSO Password:'******'sso Login failed, Please try again!') exit(1) docker_login_success = docker_login(phase, username, password) if not docker_login_success: error('docker Login failed, Please try again!') exit(1) info("Login successfully!")
def _check_phase_tag(phase): yml = lain_yaml(ignore_prepare=True) meta_version = yml.repo_meta_version() if meta_version is None: error("please git commit.") return None domain = get_domain(phase) registry = "registry.%s" % domain metatag = "meta-%s"%meta_version releasetag = "release-%s"%meta_version tag_list = docker.get_tag_list_in_registry(registry, yml.appname) tag_ok = True if metatag not in tag_list: tag_ok = False error("%s/%s:%s not exist." % (registry, yml.appname, metatag)) else: info("%s/%s:%s exist." % (registry, yml.appname, metatag)) if releasetag not in tag_list: tag_ok = False error("%s/%s:%s not exist." % (registry, yml.appname, releasetag)) else: info("%s/%s:%s exist." % (registry, yml.appname, releasetag)) return tag_ok
def delete(cls, phase, procname, path): """ delete secret file for different phase """ check_phase(phase) yml = lain_yaml(ignore_prepare=True) authorize_and_check(phase, yml.appname) auth_header = get_auth_header(SSOAccess.get_token(phase)) proc = yml.procs.get(procname, None) if proc is None: error('proc {} does not exist'.format(procname)) exit(1) podgroup_name = "{}.{}.{}".format(yml.appname, proc.type.name, proc.name) lvault_url = "http://lvault.%s/v2/secrets?app=%s&proc=%s&path=%s" % ( get_domain(phase), yml.appname, podgroup_name, path) delete_response = requests.delete(lvault_url, headers=auth_header) if delete_response.status_code < 300: info("delete successfully.") else: error("shit happened : %s" % delete_response.text)
def tag(phase): """ Tag release and meta images """ check_phase(phase) info("Taging meta and relese image ...") yml = lain_yaml(ignore_prepare=True) meta_version = yml.repo_meta_version() if meta_version is None: error("please git commit.") return None domain = get_domain(phase) registry = "registry.%s" % domain meta_tag = "%s:meta-%s" % (yml.appname, meta_version) release_tag = "%s:release-%s" % (yml.appname, meta_version) phase_meta_tag = docker.gen_image_name(yml.appname, 'meta', meta_version, registry) phase_release_tag = docker.gen_image_name(yml.appname, 'release', meta_version, registry) meta_code = docker.tag(meta_tag, phase_meta_tag) release_code = docker.tag(release_tag, phase_release_tag) if meta_code or release_code: error("Error lain tag.") else: info("Done lain tag.")
def tag(phase): """ Tag release and meta images """ check_phase(phase) info("Taging meta and relese image ...") yml = lain_yaml(ignore_prepare=True) meta_version = yml.repo_meta_version() if meta_version is None: error("please git commit.") return None domain = get_domain(phase) registry = "registry.%s" % domain meta_tag = "%s:meta" % (yml.appname, ) release_tag = "%s:release" % (yml.appname, ) phase_meta_tag = docker.gen_image_name(yml.appname, 'meta', meta_version, registry) phase_release_tag = docker.gen_image_name(yml.appname, 'release', meta_version, registry) meta_code = docker.tag(meta_tag, phase_meta_tag) release_code = docker.tag(release_tag, phase_release_tag) if meta_code or release_code: error("Error lain tag.") else: info("Done lain tag.")
def attach(phase, proc_name, instance_no, target=None): """ Attach the stdout/stderr of the container """ check_phase(phase) yml = lain_yaml(ignore_prepare=True) appname = target if target else yml.appname authorize_and_check(phase, appname) domain = get_domain(phase) access_token = SSOAccess.get_token(phase) endpoint = "wss://entry.%s/attach" % domain header_data = ["access-token: %s" % access_token, "app-name: %s" % appname, "proc-name: %s" % proc_name, "instance-no: %s" % instance_no] try: client = EntryClient(endpoint, header=header_data) info("Start to attach the stdout/stderr of the container. Press <Ctrl+c> to stop...") client.attach_container() except KeyboardInterrupt: pass except: error("Server stops the connection. Ask admin for help.")
def deploy_proc(proc, appname, console, auth_header, output): info("Begin deploy proc %s from app %s ..." % (proc, appname)) url = "http://%s/api/v1/apps/%s/procs/" % (console, appname) payload = {'procname': proc} deploy_r = requests.post(url, headers=auth_header, data=json.dumps(payload), timeout=120) if deploy_r.status_code < 300: info("deploy proc %s successfully." % proc) info("deploy result detail:") try: result = deploy_r.json() msg = result.pop('msg', '') if msg: print msg.decode('string_escape') info("proc status: ") render_proc_status(result.get('proc'), output=output) except Exception: pprint.pprint(deploy_r.content) else: error("deploy proc %s fail : %s" % (proc, deploy_r.json()['msg'])) exit(1)
def render_app_status(app_status, output='pretty'): if not app_status: print("\tNot found, may not exist or has been undeployed.") return table = [['appname', app_status.get('appname')], ['state', get_app_state(app_status)], ['apptype', app_status.get('apptype')], ['metaversion', app_status.get('metaversion')], ['updatetime', app_status.get('updatetime')], ['deploy_error', app_status.get('deployerror')]] if output == 'pretty': info('App info:') print(tabulate(table, tablefmt='fancy_grid')) if output == 'json': print(json.dumps(app_status)) return if output == 'json-pretty': print(json.dumps(app_status, indent=2)) return if app_status.get('procs'): info('Proc list:') for proc_status in app_status.get("procs"): render_proc_status(proc_status, app_status.get('apptype'), output=output) if app_status.get('portals'): info('Portal list:') for portal_status in app_status.get("portals"): render_portal_status(portal_status, output=output) if app_status.get('useservices'): info('Service Portal list:') for use_service in app_status.get("useservices"): render_service_portal_status(use_service, output=output) if app_status.get('useresources'): info('Use Resources list:') for use_resource in app_status.get("useresources"): render_app_status(use_resource.get("resourceinstance"), output=output) if app_status.get('resourceinstances'): info('Resource Instances list:') for instance in app_status.get("resourceinstances"): render_app_status(instance, output=output)
def deploy_app(phase, appname, console, auth_header, version, output): info("Begin deploy app %s to %s ..." % (appname, phase)) app_url = "http://%s/api/v1/apps/%s/" % (console, appname) apps_url = "http://%s/api/v1/apps/" % console app_r = requests.get(app_url, headers=auth_header) former_version, deploy_version = None, version if app_r.status_code == 200: operation = "upgrading" deploy_params = None if not is_resource_instance(appname): former_version = app_r.json()["app"]["metaversion"] exist, valid_version = check_meta_version(phase, appname, deploy_version) if not exist: print_available_version(deploy_version, valid_version) exit(1) if deploy_version: deploy_params = {"meta_version": deploy_version} else: deploy_version = valid_version deploy_r = requests.put(app_url, headers=auth_header, json=deploy_params) elif app_r.status_code == 404: operation = "deploying" payload = {'appname': appname} deploy_r = requests.post(apps_url, headers=auth_header, data=json.dumps(payload), timeout=120) else: error("shit happend: %s" % app_r.content) exit(1) if deploy_r.status_code < 300: if output != 'pretty': info("%s" % deploy_r.json()['msg']) info("app status: ") render_app_status(deploy_r.json()['app'], output=output) else: result = check_deploy_result(operation, console, appname, auth_header) if result != 'Done': error("deploy latest version of %s to %s failed: %s" % (appname, phase, result)) exit(1) if former_version: info("app {} deploy operation:".format(appname)) info(" last version: {}".format(former_version)) info(" this version: {}".format(deploy_version)) info("if shit happened, rollback your app by:") info(" lain deploy -v {}".format(former_version)) else: error("deploy latest version of %s to %s failed: %s" % (appname, phase, deploy_r.json()['msg'])) exit(1)
def scale(phase, proc, target=None, cpu=None, memory=None, numinstances=None, output='pretty'): """ Scale proc with cpu/memory/num_instances """ check_phase(phase) yml = lain_yaml(ignore_prepare=True) appname = target if target else yml.appname authorize_and_check(phase, appname) domain = get_domain(phase) console = "console.%s" % domain url = "http://{}/api/v1/apps/{}/procs/{}/".format( console, appname, proc ) access_token = SSOAccess.get_token(phase) auth_header = get_auth_header(access_token) cpu, memory, numinstances = validate_parameters(cpu, memory, numinstances) proc_status = requests.get(url, headers=auth_header) if proc_status.status_code == 200: # proc exists info( "Start to scale Proc {} of App {} in Lain Cluster {} with domain {}".format( proc, appname, phase, domain ) ) elif proc_status.status_code == 404: # proc does not exist warn( "Proc {} of App {} does not exists in Lain Cluster {} with domain {}".format( proc, appname, phase, domain ) ) info("Please deploy it first") exit(1) else: error("shit happend: %s"%proc_status.content) exit(1) scale_results = {} payload1 = {} payload2 = {} if cpu is not None: payload1['cpu'] = cpu if memory is not None: payload1['memory'] = memory if len(payload1) > 0: info("Scaling......") info(str(payload1)) scale_r = requests.patch(url, headers=auth_header, data=json.dumps(payload1), timeout=120) scale_results['cpu_or_memory'] = { 'payload': payload1, 'success': scale_r.status_code < 300 } render_scale_result(scale_r, output) if numinstances is not None: payload2['num_instances'] = numinstances if len(payload2) > 0: info("Scaling...") info(str(payload2)) scale_r = requests.patch(url, headers=auth_header, data=json.dumps(payload2), timeout=120) scale_results['num_instances'] = { 'payload': payload2, 'success': scale_r.status_code < 300 } render_scale_result(scale_r, output) info("Outline of scale result: ") for k, v in scale_results.iteritems(): success = v['success'] output_func = None if success: output_func = info else: output_func = error output_func(" scale of {} {}".format(k, "success" if success else "failed")) output_func(" params: {}".format(v['payload']))
class SecretCommands(TwoLevelCommandBase): ''' allow add secret files for app, lain will add the secret file into image of the proc when deploying. ''' @classmethod def subcommands(self): return [self.show, self.add, self.delete] @classmethod def namespace(self): return "secret" @classmethod def help_message(self): return "secret operation: including add, delete or show secret files of the app" @classmethod @arg('phase', help="lain cluster phase id, can be added by lain config save") @arg('procname', help="proc name of the app") def show(cls, phase, procname, path=None): """ show secret file of special procname in different phase path: absolute path of config file, eg : /lain/app/config """ check_phase(phase) yml = lain_yaml(ignore_prepare=True) authorize_and_check(phase, yml.appname) auth_header = get_auth_header(SSOAccess.get_token(phase)) proc = yml.procs.get(procname, None) if proc is None: error('proc {} does not exist'.format(procname)) exit(1) podgroup_name = "{}.{}.{}".format(yml.appname, proc.type.name, proc.name) lvault_url = "http://lvault.%s/v2/secrets?app=%s&proc=%s" % ( get_domain(phase), yml.appname, podgroup_name) if path: lvault_url += "&path=%s" % path show_response = requests.get(lvault_url, headers=auth_header) if show_response.status_code < 300: info("secret file detail:") print( json.dumps(show_response.json(), encoding="utf-8", ensure_ascii=False, indent=2)) else: error("shit happened : %s" % show_response.text) @classmethod @arg('phase', help="lain cluster phase id, can be added by lain config save") @arg('procname', help="proc name of the app") @arg('path', help='absolute path of config file, eg : /lain/app/config') def add(cls, phase, procname, path, content=None, file=None): """ add secret file for different phase content: content of the secret file file: read secret content from a specify file """ if file is None and content is None: error("need specify the content use -c or -f parameter") exit(1) if file is not None: try: f = open(file) content = f.read() except Exception, e: error("error read file %s : %s" % (file, str(e))) exit(1) check_phase(phase) yml = lain_yaml(ignore_prepare=True) authorize_and_check(phase, yml.appname) auth_header = get_auth_header(SSOAccess.get_token(phase)) proc = yml.procs.get(procname, None) if proc is None: error('proc {} does not exist'.format(procname)) exit(1) podgroup_name = "{}.{}.{}".format(yml.appname, proc.type.name, proc.name) lvault_url = "http://lvault.%s/v2/secrets?app=%s&proc=%s&path=%s" % ( get_domain(phase), yml.appname, podgroup_name, path) payload = {"content": content} add_response = requests.put(lvault_url, headers=auth_header, json=payload) if add_response.status_code < 300: info("add successfully.") else: error("shit happened : %s" % add_response.text)
def print_workflows(): info('Below is the recommended workflows :') info( ' lain reposit => lain prepare => lain build => lain tag => lain push => lain deploy' )
def deploy_app(phase, appname, console, auth_header, version, output): info("Begin deploy app %s to %s ..." % (appname, phase)) app_url = "http://%s/api/v1/apps/%s/" % (console, appname) apps_url = "http://%s/api/v1/apps/" % console app_r = requests.get(app_url, headers=auth_header) former_version, deploy_version = None, version if app_r.status_code == 200: operation = "upgrading" deploy_params = None if not is_resource_instance(appname): former_version = app_r.json()["app"]["metaversion"] exist, valid_version = check_meta_version(phase, appname, deploy_version) if not exist: print_available_version(deploy_version, valid_version) exit(1) if deploy_version: deploy_params = {"meta_version": deploy_version} else: deploy_version = valid_version deploy_r = requests.put(app_url, headers=auth_header, json=deploy_params) elif app_r.status_code == 404: operation = "deploying" payload = {'appname': appname} deploy_r = requests.post(apps_url, headers=auth_header, data=json.dumps(payload), timeout=120) else: error("shit happend: %s" % app_r.content) exit(1) if deploy_r.status_code < 300: if output != 'pretty': info("%s" % deploy_r.json()['msg']) info("app status: ") render_app_status(deploy_r.json()['app'], output=output) else: check_deploy_result(operation, console, appname, auth_header) if former_version: info("app {} deploy operation:".format(appname)) info(" last version: {}".format(former_version)) info(" this version: {}".format(deploy_version)) info("if shit happened, rollback your app by:") info(" lain deploy -v {}".format(former_version)) else: error("deploy latest version of %s to %s failed: %s" % (appname, phase, deploy_r.json()['msg']))
def print_welecome(): info('##############################') info('# Welcome to Lain! #') info('##############################')
def print_welecome(): info('##############################') info('# Welcome to Lain! #') info('##############################')
def print_workflows(): info('Below is the recommended workflows :') info(' lain reposit => lain prepare => lain build => lain tag => lain push => lain deploy')
def render_app_status(app_status, output='pretty'): if not app_status: print("\tNot found, may not exist or has been undeployed.") return table = [ ['appname', app_status.get('appname')], ['state', get_app_state(app_status)], ['apptype', app_status.get('apptype')], ['metaversion', app_status.get('metaversion')], ['updatetime', app_status.get('updatetime')], ['deploy_error', app_status.get('deployerror')] ] if output == 'pretty': info('App info:') print(tabulate(table, tablefmt='fancy_grid')) if output == 'json': print(json.dumps(app_status)) return if output == 'json-pretty': print(json.dumps(app_status, indent=2)) return if app_status.get('procs'): info('Proc list:') for proc_status in app_status.get("procs"): render_proc_status(proc_status, app_status.get( 'apptype'), output=output) if app_status.get('portals'): info('Portal list:') for portal_status in app_status.get("portals"): render_portal_status(portal_status, output=output) if app_status.get('useservices'): info('Service Portal list:') for use_service in app_status.get("useservices"): render_service_portal_status(use_service, output=output) if app_status.get('useresources'): info('Use Resources list:') for use_resource in app_status.get("useresources"): render_app_status(use_resource.get( "resourceinstance"), output=output) if app_status.get('resourceinstances'): info('Resource Instances list:') for instance in app_status.get("resourceinstances"): render_app_status(instance, output=output)
def scale(phase, proc, target=None, cpu=None, memory=None, numinstances=None, output='pretty'): """ Scale proc with cpu/memory/num_instances """ check_phase(phase) yml = lain_yaml(ignore_prepare=True) appname = target if target else yml.appname authorize_and_check(phase, appname) domain = get_domain(phase) console = "console.%s" % domain url = "http://{}/api/v1/apps/{}/procs/{}/".format(console, appname, proc) access_token = SSOAccess.get_token(phase) auth_header = get_auth_header(access_token) cpu, memory, numinstances = validate_parameters(cpu, memory, numinstances) proc_status = requests.get(url, headers=auth_header) if proc_status.status_code == 200: # proc exists info( "Start to scale Proc {} of App {} in Lain Cluster {} with domain {}" .format(proc, appname, phase, domain)) elif proc_status.status_code == 404: # proc does not exist warn( "Proc {} of App {} does not exists in Lain Cluster {} with domain {}" .format(proc, appname, phase, domain)) info("Please deploy it first") exit(1) else: error("shit happend: %s" % proc_status.content) exit(1) scale_results = {} payload1 = {} payload2 = {} if cpu is not None: payload1['cpu'] = cpu if memory is not None: payload1['memory'] = memory if len(payload1) > 0: info("Scaling......") info(str(payload1)) scale_r = requests.patch(url, headers=auth_header, data=json.dumps(payload1), timeout=120) scale_results['cpu_or_memory'] = { 'payload': payload1, 'success': scale_r.status_code < 300 } render_scale_result(scale_r, output) if numinstances is not None: payload2['num_instances'] = numinstances if len(payload2) > 0: info("Scaling...") info(str(payload2)) scale_r = requests.patch(url, headers=auth_header, data=json.dumps(payload2), timeout=120) scale_results['num_instances'] = { 'payload': payload2, 'success': scale_r.status_code < 300 } render_scale_result(scale_r, output) info("Outline of scale result: ") for k, v in scale_results.iteritems(): success = v['success'] output_func = None if success: output_func = info else: output_func = error output_func(" scale of {} {}".format( k, "success" if success else "failed")) output_func(" params: {}".format(v['payload']))