def install_deps(self, args): package_name = args.package_name requirements = args.requirements upgrade = args.upgrade force_reinstall = args.force_reinstall pkgs = ' '.join(package_name) config_path = self.get_model_config_path() config = get_config_parser(config_path) section_name = DEFAULT_MODEL_SECTION model_id = config_get(config.get, section_name, 'model_id') runtime = config_get(config.get, section_name, 'runtime') client = self._get_docker_client() command = ['pip install', '--cache-dir', '/tmp/.cache/'] if upgrade: command.append('--upgrade') if force_reinstall: command.append('--force-reinstall') deps = [] if pkgs: deps.append(pkgs) if requirements: deps.append('-r {}'.format(requirements)) command.append('-t /code/lib {}'.format(' '.join(deps))) command = ' '.join(command) image_name = RUNTIMES[runtime] self._get_or_pull_image(client, image_name) with console.status('开始安装模型依赖包...', spinner='earth'): command = ('sh -c "{}"').format(command) container_name = generate_container_name(model_id) self._force_remove_container(client, container_name) self._run_by_container(client, image_name, command, container_name) output('[green]安装完成[/green]')
def upload_model_expfs(self, args): home = get_home() project = get_active_project() config_path = self.get_model_config_path() config = get_config_parser(config_path) section_name = DEFAULT_MODEL_SECTION model_id = config_get(config.get, section_name, 'model_id') oauth2_section = DEFAULT_OAUTH2_SECTION token_section = DEFAULT_TOKEN_SECTION config = read_config(project) access_token = config_get(config.get, token_section, 'access_token') endpoint = config_get(config.get, oauth2_section, 'endpoint') api = API(access_token, endpoint=endpoint, timeout=DEFAULT_MODEL_TIMEOUT) try: result = api.upload_model_expfs(model_id, 'uploadtest.zip') except APIError as e: output('[red]上传模型扩展集失败:[/red]') output_json(e.result) sys.exit(1) task_id = result.task_id output('上传模型扩展集任务:{}'.format(task_id)) task_path = join(home, '.bge', 'expfs.task_id') with open(task_path, 'w') as f: f.write(task_id) output('上传模型扩展集任务返回结果:') progress = self._wait_model_task(api, task_id, task_path) if 'SUCCESS' == progress: output('[green]模型 {} 上传模型扩展集成功。'.format(model_id)) elif 'FAILURE' == progress: output('[red]模型 {} 上传模型扩展集失败。'.format(model_id)) elif 'REVOKED' == progress: output('[white]模型 {} 上传模型扩展集任务已被撤销。'.format(model_id))
def _new_project(self, config_path): config_parser = get_config_parser(config_path) oauth2_section = constants.DEFAULT_OAUTH2_SECTION if oauth2_section not in config_parser.sections(): config_parser.add_section(oauth2_section) for key, conf in constants.CLIENT_CREDENTIALS_CONFIGS: type_ = conf['type'] saved_value = self._get_config_value(config_parser, key, type_) secure = conf.get('secure') description = conf.get('description', '') value = saved_value or conf.get('default', '') show_value = value if secure and value: show_value = secure_str(value) input_value = Prompt.ask( '请输入{} {} ([bold cyan]{}[/bold cyan])'.format( description, key, show_value ), show_default=False, default=value ) if input_value: config_parser.set(oauth2_section, key, input_value) elif saved_value is None: if conf.get('default') is not None: config_parser.set( oauth2_section, key, conf.get('default')) else: config_parser.set(oauth2_section, key, '') with open(config_path, 'w') as config_file: config_parser.write(config_file) output('') output('[green]配置已保存至:[/green]{}'.format(config_path))
def config_model(self, args): if args.show: return self._read_model_project(args) config_path = self.get_model_config_path() config_parser = get_config_parser(config_path) model_id, runtime, memory_size, timeout \ = self._config_model(config_parser) self._save_model_config(model_id, runtime, memory_size, timeout)
def run_model(self, args): params = {} if args.file: filepath = args.file if not exists(filepath): output('文件不存在:{}'.format(filepath)) sys.exit(1) if isdir(filepath): output('存在文件夹:{}'.format(filepath)) sys.exit(1) with open(filepath, 'r') as fp: params = json.load(fp) elif args.args: params = dict(args.args) draft = args.draft test = args.test project = get_active_project() config_path = self.get_model_config_path() config = get_config_parser(config_path) section_name = DEFAULT_MODEL_SECTION model_id = config_get(config.get, section_name, 'model_id') oauth2_section = DEFAULT_OAUTH2_SECTION token_section = DEFAULT_TOKEN_SECTION config = read_config(project) access_token = config_get(config.get, token_section, 'access_token') endpoint = config_get(config.get, oauth2_section, 'endpoint') api = API(access_token, endpoint=endpoint, timeout=DEFAULT_MODEL_TIMEOUT) try: if draft is True: result = api.invoke_draft_model(model_id, **params) elif test is True: port = args.port api = API(access_token, endpoint='{}:{}'.format(TEST_SERVER_ENDPOINT, port), timeout=DEFAULT_MODEL_TIMEOUT) result = api.invoke_model(model_id, **params) else: result = api.invoke_model(model_id, **params) except APIError as e: output('[red]模型运行失败:[/red]') output_json(e.result) sys.exit(1) output('[green]模型返回值:[/green]') output_json(result.json())
def _write_token_config(self, project, token_result): access_token = token_result['access_token'] token_type = token_result['token_type'] expires_in = str(token_result['expires_in']) scope = token_result['scope'] config_path = get_config_path(project) config_parser = get_config_parser(config_path) section_name = constants.DEFAULT_TOKEN_SECTION if section_name not in config_parser.sections(): config_parser.add_section(section_name) config_parser.set(section_name, 'access_token', access_token) config_parser.set(section_name, 'token_type', token_type) config_parser.set(section_name, 'expires_in', expires_in) config_parser.set(section_name, 'scope', scope) with open(config_path, 'w') as config_file: config_parser.write(config_file) output('[green]令牌已保存至:[/green]{}'.format(config_path))
def _install_sdk(self): with console.status("正在安装 bge-python-sdk", spinner="earth"): config_path = self.get_model_config_path() config = get_config_parser(config_path) section_name = DEFAULT_MODEL_SECTION model_id = config_get(config.get, section_name, 'model_id') runtime = config_get(config.get, section_name, 'runtime') client = self._get_docker_client() command = ('pip install --cache-dir /tmp/.cache/ --no-deps ' 'bge-python-sdk pimento requests_toolbelt -t /code/lib') image_name = RUNTIMES[runtime] self._get_or_pull_image(client, image_name) output('开始安装模型依赖包...') command = ('sh -c "{}"').format(command) container_name = generate_container_name(model_id) self._force_remove_container(client, container_name) self._run_by_container(client, image_name, command, container_name) output('[green]安装完成[/green]')
def start_model(self, args): port = args.port home = get_home() config_path = self.get_model_config_path() config = get_config_parser(config_path) section_name = DEFAULT_MODEL_SECTION model_id = config_get(config.get, section_name, 'model_id') runtime = config_get(config.get, section_name, 'runtime') client = self._get_docker_client() image_name = RUNTIMES[runtime] self._get_or_pull_image(client, image_name) command = 'python -u /server/app.py' user = get_sys_user() container_name = generate_container_name(model_id) self._force_remove_container(client, container_name) container = client.containers.run( image_name, command=command, name=container_name, volumes={home: { 'bind': WORKDIR, 'mode': 'rw' }}, stop_signal='SIGINT', ports={TEST_SERVER_PORT: port}, user=user, detach=True, stream=True, auto_remove=True) output('Model debug server is starting at {}...'.format(port)) output('Model {} was registered'.format(model_id)) output('\n\tURL: {}:{}/model/{}'.format(TEST_SERVER_ENDPOINT, port, model_id)) output('\tMethod: GET\n') try: logs = container.logs(stream=True, follow=True) for log in logs: output(log.strip().decode('utf-8')) finally: if container.status != 'exited': try: container.remove(force=True) except Exception: pass
def model_versions(self, args): limit = args.limit next_page = args.next_page project = get_active_project() config_path = self.get_model_config_path() config = get_config_parser(config_path) section_name = DEFAULT_MODEL_SECTION model_id = config_get(config.get, section_name, 'model_id') oauth2_section = DEFAULT_OAUTH2_SECTION token_section = DEFAULT_TOKEN_SECTION config = read_config(project) access_token = config_get(config.get, token_section, 'access_token') endpoint = config_get(config.get, oauth2_section, 'endpoint') api = API(access_token, endpoint=endpoint, timeout=DEFAULT_MODEL_TIMEOUT) try: result = api.model_versions(model_id, limit=limit, next_page=next_page) except APIError as e: output('[red]获取模型版本列表失败:[/red]') output_json(e.result) sys.exit(1) output('第 {} 页,模型 {} 已发布版本:'.format(next_page or 1, model_id)) output('...') length = None items = result['result'] for item in items: version = item['version'] message = item['message'] create_time = datetime.fromtimestamp(item['create_time']) if length is None: length = len(str(version)) output('发布时间:{},版本号:{},发布记录:{}'.format(create_time, str(version).rjust(length), message)) output('...') size = len(items) if size == 0 or size < limit or next_page == result['next_page']: output('下一页:无') else: output('下一页:{}'.format(result['next_page']))
def handler(self, args): project = get_active_project() config_path = get_config_path(project) config_parser = get_config_parser(config_path) oauth2_section = constants.DEFAULT_OAUTH2_SECTION client_id = config_get(config_parser.get, oauth2_section, 'client_id') client_secret = config_get(config_parser.get, oauth2_section, 'client_secret') endpoint = config_get(config_parser.get, oauth2_section, 'endpoint') redirect_uri = args.redirect_uri oauth2 = OAuth2(client_id, client_secret, endpoint=endpoint, timeout=60.) authorization_url = oauth2.get_authorization_url(redirect_uri, state=args.state, scopes=args.scopes) output('授权页面地址为:{}'.format(authorization_url)) output('浏览器启动中...') webbrowser.open(authorization_url, new=1, autoraise=False) output('请在浏览器登录并完成授权,复制跳转后页面链接的 code 参数并关闭浏览器。\n') while True: code = Prompt.ask('请输入浏览器回调地址携带的参数 [cyan]code[/cyan]') code = code.strip() if code: break output('[red]参数 code 不能为空[/red]') try: token_result = oauth2.exchange_authorization_code( code, redirect_uri=redirect_uri) except APIError as e: output('[red]令牌获取出错:[/red]') output_json(e.result) sys.exit(1) self._write_token_config(project, token_result) output('[green]令牌内容如下[/green]') content = [] for key in [ 'access_token', 'token_type', 'expires_in', 'scope', 'refresh_token' ]: content.append('{} = {}'.format(key, token_result[key])) output_syntax('\n'.join(content), lexer='INI')
def _save_model_config(self, model_id, runtime, memory_size, timeout, home=None): # save config_path = self.get_model_config_path(home=home) config = get_config_parser(config_path) section_name = DEFAULT_MODEL_SECTION if section_name not in config.sections(): config.add_section(section_name) output('开始保存解读模型配置...') output('配置文件 {}'.format(config_path)) config.set(section_name, 'model_id', model_id) config.set(section_name, 'runtime', runtime) config.set(section_name, 'memory_size', str(memory_size)) config.set(section_name, 'timeout', str(timeout)) with open(config_path, 'w') as config_file: config.write(config_file) output('') output('[green]解读模型配置已保存至[/green] {}'.format(config_path))
def handler(self, args): project = get_active_project() config_path = get_config_path(project) config_parser = get_config_parser(config_path) oauth2_section = constants.DEFAULT_OAUTH2_SECTION client_id = config_get(config_parser.get, oauth2_section, 'client_id') client_secret = config_get( config_parser.get, oauth2_section, 'client_secret') endpoint = config_get(config_parser.get, oauth2_section, 'endpoint') oauth2 = OAuth2( client_id, client_secret, endpoint=endpoint, timeout=60.) try: token_result = oauth2.get_credentials_token() except APIError as e: output('[red]令牌获取出错:[/red]') output_json(e.result) sys.exit(1) self._write_token_config(project, token_result) console.rule('[green]令牌内容如下[/green]') content = [] for key in ['access_token', 'token_type', 'expires_in', 'scope']: content.append('{} = {}'.format(key, token_result[key])) output_syntax('\n'.join(content), lexer='INI')
def rollback_model(self, args): version = args.version home = get_home() project = get_active_project() config_path = self.get_model_config_path() config = get_config_parser(config_path) section_name = DEFAULT_MODEL_SECTION model_id = config_get(config.get, section_name, 'model_id') oauth2_section = DEFAULT_OAUTH2_SECTION token_section = DEFAULT_TOKEN_SECTION config = read_config(project) access_token = config_get(config.get, token_section, 'access_token') endpoint = config_get(config.get, oauth2_section, 'endpoint') api = API(access_token, endpoint=endpoint, timeout=DEFAULT_MODEL_TIMEOUT) try: result = api.rollback_model(model_id, version) except APIError as e: output('[red]模型回滚失败:[/red]') output_json(e.result) sys.exit(1) task_id = result.task_id output('模型回滚任务:{}'.format(task_id)) task_path = join(home, '.bge', 'task_id') with open(task_path, 'w') as f: f.write(task_id) progress = self._wait_model_task(api, task_id, task_path) if 'SUCCESS' == progress: output('[green]模型 {} 灰度版已成功回滚至版本 {}。[/green]'.format( model_id, version)) elif 'FAILURE' == progress: output('[red]模型 {} 灰度回滚至版本 {} 失败。任务结果:{}[/red]'.format( model_id, version, result)) elif 'REVOKED' == progress: output('[white]模型 {} 灰度回滚至版本 {} 任务已被撤销失败。[/white]'.format( model_id, version))
def publish_model(self, args): """发布模型到稳定版""" message = args.message project = get_active_project() config_path = self.get_model_config_path() config = get_config_parser(config_path) section_name = DEFAULT_MODEL_SECTION model_id = config_get(config.get, section_name, 'model_id') oauth2_section = DEFAULT_OAUTH2_SECTION token_section = DEFAULT_TOKEN_SECTION config = read_config(project) access_token = config_get(config.get, token_section, 'access_token') endpoint = config_get(config.get, oauth2_section, 'endpoint') api = API(access_token, endpoint=endpoint, timeout=DEFAULT_MODEL_TIMEOUT) try: result = api.publish_model(model_id, message) except APIError as e: output('[red]模型发布失败:[/red]') output_json(e.result) sys.exit(1) output('[green]模型 {} 稳定版已成功发布。\n版本号:{}。[/green]'.format( model_id, result['version']))
def deploy_model(self, args): """部署模型""" ignore_source = args.ignore_source home = get_home() project = get_active_project() config_path = self.get_model_config_path() config = get_config_parser(config_path) section_name = DEFAULT_MODEL_SECTION model_id = config_get(config.get, section_name, 'model_id') timestr = datetime.now().strftime('%Y%m%d%H%M%S%f') randstr = uuid4().hex zip_filename = '{}.{}.{}.zip'.format(model_id, timestr, randstr) params = {} params['runtime'] = config_get(config.get, section_name, 'runtime') params['memory_size'] = config_get(config.getint, section_name, 'memory_size') params['timeout'] = config_get(config.getint, section_name, 'timeout') oauth2_section = DEFAULT_OAUTH2_SECTION token_section = DEFAULT_TOKEN_SECTION config = read_config(project) access_token = config_get(config.get, token_section, 'access_token') endpoint = config_get(config.get, oauth2_section, 'endpoint') api = API(access_token, endpoint=endpoint, timeout=DEFAULT_MODEL_TIMEOUT) object_name = None if not ignore_source: ignore_path = join(home, BGE_IGNORE_FILE) if not exists(ignore_path): output('未发现 .bgeignore 文件,初始化 {} ...'.format(ignore_path)) open(ignore_path, 'w').write(BGEIGNORE_TEMPLATE) minify_path = join(home, BGE_MINIFY_FILE) if not exists(minify_path): output('未发现 .bgeminify 文件,初始化 {} ...'.format(minify_path)) open(minify_path, 'w').write(BGEMINIFY_TEMPLATE) output('开始打包模型源码...') zip_tmpdir = join(home, '.bge', 'tmp') if not exists(zip_tmpdir): os.makedirs(zip_tmpdir) with tempfile.NamedTemporaryFile(suffix='.zip', prefix='model-', dir=zip_tmpdir, delete=False) as tmp: with zipfile.ZipFile(tmp.name, 'w', ZIP_COMPRESSION) as zf: self._zip_codedir(home, zf) tmp.flush() tmp.seek(0, 2) size = tmp.tell() tmp.seek(0) human_size = human_byte(size) if size > 100 * 1024 * 1024: output('打包后 zip 文件大小为 {},最大限制 100MB'.format(human_size)) exit(1) output('打包成功:{}'.format(tmp.name)) output('文件大小:{}'.format(human_size)) output('开始上传模型源码...') try: object_name = api.upload(zip_filename, tmp) except APIError as e: output('[red]上传模型源码失败:[/red]') output_json(e.result) sys.exit(1) output('[green]上传成功[green]') with console.status('模型部署中...', spinner='earth'): try: result = api.deploy_model(model_id, object_name=object_name, **params) except APIError as e: output('[red]部署模型失败:[/red]') output_json(e.result) sys.exit(1) task_id = result.task_id output('模型部署任务:{}'.format(task_id)) task_path = join(home, '.bge', 'task_id') with open(task_path, 'w') as f: f.write(task_id) output('模型部署任务返回结果:') progress = self._wait_model_task(api, task_id, task_path) if 'SUCCESS' == progress: output('[green]模型 {} 灰度部署成功。'.format(model_id)) elif 'FAILURE' == progress: output('[red]模型 {} 灰度部署失败。任务结果:{}'.format(model_id, result)) elif 'REVOKED' == progress: output('[white]模型 {} 灰度部署任务已被撤销。'.format(model_id))