Source code for nvflare.dashboard.cli

# Copyright (c) 2022, NVIDIA CORPORATION.  All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import os
import signal
import sys

import docker
from nvflare.apis.utils.format_check import name_check
from nvflare.lighter.utils import generate_password


[docs]def start(args): cwd = os.getcwd() if not args.folder: folder = cwd else: folder = os.path.join(cwd, args.folder) environment = dict() env_vars = args.env if env_vars: for e in env_vars: splitted = e.split("=") environment[splitted[0]] = splitted[1] passphrase = args.passphrase if passphrase: environment["NVFL_DASHBOARD_PP"] = passphrase if not os.path.exists(os.path.join(folder, "db.sqlite")): need_email = True while need_email: answer = input( "Please provide project admin email address. This person will be the super user of the dashboard and this project.\n" ) error, reason = name_check(answer, "email") if error: print(f"Expecting an email address, but got one in an invalid format. Reason: {reason}") else: need_email = False print("generating random password") pwd = generate_password(8) print(f"Project admin credential is {answer} and the password is {pwd}") environment.update({"NVFL_CREDENTIAL": f"{answer}:{pwd}"}) try: client = docker.from_env() except docker.errors.DockerException: print("Unable to communicate to docker daemon/socket. Please make sure your docker is up and running.") exit(0) dashboard_image = "nvflare/nvflare" try: print(f"Pulling {dashboard_image}, may take some time to finish.") _ = client.images.pull(dashboard_image) except docker.errors.APIError: print(f"unable to pull {dashboard_image}") exit(1) print(f"Launching {dashboard_image}") print(f"Dashboard will listen to port {args.port}") print(f"{folder} on host mounted to /var/tmp/nvflare/dashboard in container") if environment: print(f"environment vars set to {environment}") else: print("No additional environment variables set to the launched container.") try: container_obj = client.containers.run( "nvflare/nvflare", entrypoint=["/usr/local/bin/python3", "nvflare/dashboard/wsgi.py"], detach=True, auto_remove=True, name="nvflare-dashboard", ports={8443: args.port}, volumes={folder: {"bind": "/var/tmp/nvflare/dashboard", "model": "rw"}}, environment=environment, ) except docker.errors.APIError as e: print(f"Either {dashboard_image} image does not exist or another nvflare-dashboard instance is still running.") print("Please either provide an existing container image or stop the running container instance.") print(e) exit(1) if container_obj: print("Dashboard container started") print("Container name nvflare-dashboard") print(f"id is {container_obj.id}") else: print("Container failed to start")
[docs]def stop(): try: client = docker.from_env() except docker.errors.DockerException: print("Unable to communicate to docker daemon/socket. Please make sure your docker is up and running.") exit(0) try: container_obj = client.containers.get("nvflare-dashboard") except docker.errors.NotFound: print("No nvflare-dashboard container found") exit(0) container_obj.kill(signal=signal.SIGINT) print("nvflare-dashboard exited")
[docs]def has_no_arguments() -> bool: last_item = sys.argv[-1] return ( last_item.endswith("dashboard.cli") or last_item.endswith("dashboard/cli.py") or last_item.endswith("dashboard") )
[docs]def main(): parser = argparse.ArgumentParser() define_dashboard_parser(parser) args = parser.parse_args() handle_dashboard(args)
[docs]def define_dashboard_parser(parser): parser.add_argument("--start", action="store_true", help="start dashboard") parser.add_argument("--stop", action="store_true", help="stop dashboard") parser.add_argument("-p", "--port", type=str, default="443", help="port to listen") parser.add_argument( "-f", "--folder", type=str, help="folder containing necessary info (default: current working directory)" ) parser.add_argument( "--passphrase", help="Passphrase to encrypt/decrypt root CA private key. !!! Do not share it with others. !!!" ) parser.add_argument("-e", "--env", action="append", help="additonal environment variables: var1=value1")
[docs]def handle_dashboard(args): if has_no_arguments(): print("Add -h option to see usage") exit(0) if args.stop: stop() elif args.start: start(args)
if __name__ == "__main__": main()