From a7ec41992057d75af79bb0bb78819e90b20984cf Mon Sep 17 00:00:00 2001 From: Katherine Li Date: Thu, 27 Dec 2018 20:40:58 -0800 Subject: [PATCH 001/595] Ubuntu cluster updatewq --- docs/deployment/On-Prem/Ubuntu-Machines.md | 96 ++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100755 docs/deployment/On-Prem/Ubuntu-Machines.md diff --git a/docs/deployment/On-Prem/Ubuntu-Machines.md b/docs/deployment/On-Prem/Ubuntu-Machines.md new file mode 100755 index 000000000..ae7c2b943 --- /dev/null +++ b/docs/deployment/On-Prem/Ubuntu-Machines.md @@ -0,0 +1,96 @@ +# Steps to deploy DL workspace cluster for a off-prem cluster Ubuntu. + +This document describes the procedure to deploy DL workspace cluster on a off-prem Clusters (VM or actual machine) that is already been imaged with Ubuntu OS. + +1. On dev node, +..* Enable password-less sudo, with sudo visudo +..* Generate ssh-key, and grant github access for the dev machine. +..* Install git, if not installed yet. +..* Find data partition, if any, use mkfs ext4 to make partition, and to prepare the partition for mount in fstab. +..* use ```sudo mount -a``` to remount. +..* check DNS setting. +..* many of TLS doesn't work + +1. [Run Once] Setup [development environment](../../DevEnvironment/Readme.md). + +2. [Configuration the cluster](../configuration/Readme.md), and determine important information of the cluster (e.g., cluster name, number of Etcd servers used). Please refer to [Backup/Restore](../Backup.md) on instruction to backup/restore cluster configuration. + +3. Configure and setup the [databased](../database/Readme.md) used in the cluster. + +4. Config shared file system to be used in the cluster, following instructions in [Storage](../Storage/Readme.md) and the [configuration](../Storage/configure.md). + +5. Configure the information of the servers used in the cluster. Please write the following entries in config.yaml. + + ``` + network: + domain: <> + container-network-iprange: "<>" + + platform-scripts : ubuntu + + machines: + <>: + role: infrastructure + <>: + role: worker + <>: + role: worker + .... + ``` + If you are building a high availability cluster, please include multiple infrastructure nodes. The number of infrastructure nodes should be odd, e.g., 1, 3, 5. 3 infrastructure nodes tolerate 1 failure. 5 infrastructure nodes tolerate 2 failures. + +6. Build Ubuntu PXE-server via: + ``` + ./deploy.py -y build + ./deploy.py build pxe-ubuntu + ``` + +7. Start Ubuntu PXE-server. You will need to point DHCP server to the Ubuntu PXE-server. + ``` + ./deploy.py docker run pxe-ubuntu + ``` + Reboot each machine to be deployed. In each boot screen, select to install Ubuntu 16.04. + +8. After the machines is reimaged to Ubuntu, install sshkey. (optional: If you ignore step 2,3 and choose to use an existing ubuntu cluster, you may put root username and password to files: ./deploy/sshkey/rootuser and ./deploy/sshkey/rootpasswd. In this case, the root user should be able to run "sudo" without password.) + ``` + ./deploy.py sshkey install + ``` + +9. Setup basic tools on the Ubuntu image. + ``` + ./deploy.py runscriptonall ./scripts/prepare_ubuntu.sh + ./deploy.py execonall sudo usermod -aG docker core + ``` + +10. Partition hard drive, if necessary. Please refer to section [Partition](Repartition.md) for details. + +11. Setup kubernetes + ``` + ./deploy.py -y deploy + ./deploy.py -y updateworker + ./deploy.py -y kubernetes labels + ``` + If you are running a small cluster, and need to run workload on the Kubernete master node (this choice may affect cluster stability), please use: + ``` + ./deploy.py -y kubernetes uncordon + ``` + Works now will be scheduled on the master node. If you stop here, you will have a fully functional kubernete cluster. Thus, part of DL Workspace setup can be considered automatic procedure to setup a kubernete cluster. You don't need shared file system or database for kubernete cluster operation. + +12. [optional] Configure, setup and mount [GlusterFS](../Storage/GlusterFS.md) +13. [Optional] Configure, setup and mount [HDFS](../Storage/hdfs.md) +14. [Optional] Setup [Spark](../Storage/spark.md) + +15. Mount shared file system + ``` + ./deploy.py mount + ``` + +16. Build and deploy jobmanager, restfulapi, and webportal. Mount storage. + ``` + ./deploy.py webui + ./deploy.py docker push restfulapi + ./deploy.py docker push webui + ./deploy.py kubernetes start jobmanager + ./deploy.py kubernetes start restfulapi + ./deploy.py kubernetes start webportal + ``` From 3c30d717a9db91238c30ddfee08825054c173333 Mon Sep 17 00:00:00 2001 From: Jin Li Date: Fri, 28 Dec 2018 17:49:44 -0800 Subject: [PATCH 002/595] Document related to independent ubuntu cluster setupt --- docs/deployment/On-Prem/Ubuntu-Machines.md | 56 +++++++++++++++------- docs/deployment/Storage/nfs.md | 35 ++++++++++++++ docs/deployment/network/dns.md | 12 +++++ sample.output | 0 4 files changed, 87 insertions(+), 16 deletions(-) create mode 100755 docs/deployment/Storage/nfs.md create mode 100644 docs/deployment/network/dns.md mode change 100644 => 100755 sample.output diff --git a/docs/deployment/On-Prem/Ubuntu-Machines.md b/docs/deployment/On-Prem/Ubuntu-Machines.md index ae7c2b943..b7954e505 100755 --- a/docs/deployment/On-Prem/Ubuntu-Machines.md +++ b/docs/deployment/On-Prem/Ubuntu-Machines.md @@ -11,15 +11,23 @@ This document describes the procedure to deploy DL workspace cluster on a off-pr ..* check DNS setting. ..* many of TLS doesn't work -1. [Run Once] Setup [development environment](../../DevEnvironment/Readme.md). +2. [Run Once] Setup [development environment](../../DevEnvironment/Readme.md). -2. [Configuration the cluster](../configuration/Readme.md), and determine important information of the cluster (e.g., cluster name, number of Etcd servers used). Please refer to [Backup/Restore](../Backup.md) on instruction to backup/restore cluster configuration. +3. [Configuration the cluster](../configuration/Readme.md), and determine important information of the cluster (e.g., cluster name, number of Etcd servers used). Please refer to [Backup/Restore](../Backup.md) on instruction to backup/restore cluster configuration. -3. Configure and setup the [databased](../database/Readme.md) used in the cluster. +4. Configure and setup the [databased](../database/Readme.md) used in the cluster. -4. Config shared file system to be used in the cluster, following instructions in [Storage](../Storage/Readme.md) and the [configuration](../Storage/configure.md). +5. Config shared file system to be used in the cluster, following instructions in [Storage](../Storage/Readme.md) and the [configuration](../Storage/nfs.md). -5. Configure the information of the servers used in the cluster. Please write the following entries in config.yaml. +6. Install sshkey to all nodes. +..* in config.yaml, insert entry of the admin_username of cluster + admin_username: +..* insert admin password to + ./deploy/sshkey/rootpasswd +..* install sshkey via: + ./deploy.py sshkey install + +7. Configure the information of the servers used in the cluster. Please write the following entries in config.yaml. ``` network: @@ -39,32 +47,48 @@ This document describes the procedure to deploy DL workspace cluster on a off-pr ``` If you are building a high availability cluster, please include multiple infrastructure nodes. The number of infrastructure nodes should be odd, e.g., 1, 3, 5. 3 infrastructure nodes tolerate 1 failure. 5 infrastructure nodes tolerate 2 failures. -6. Build Ubuntu PXE-server via: +8. Build Ubuntu PXE-server via: ``` ./deploy.py -y build ./deploy.py build pxe-ubuntu ``` -7. Start Ubuntu PXE-server. You will need to point DHCP server to the Ubuntu PXE-server. +9. Start Ubuntu PXE-server. You will need to point DHCP server to the Ubuntu PXE-server. ``` ./deploy.py docker run pxe-ubuntu ``` Reboot each machine to be deployed. In each boot screen, select to install Ubuntu 16.04. -8. After the machines is reimaged to Ubuntu, install sshkey. (optional: If you ignore step 2,3 and choose to use an existing ubuntu cluster, you may put root username and password to files: ./deploy/sshkey/rootuser and ./deploy/sshkey/rootpasswd. In this case, the root user should be able to run "sudo" without password.) +10. After the machines is reimaged to Ubuntu, install sshkey. (optional: If you ignore step 2,3 and choose to use an existing ubuntu cluster, you may put root username and password to files: ./deploy/sshkey/rootuser and ./deploy/sshkey/rootpasswd. In this case, the root user should be able to run "sudo" without password.) ``` ./deploy.py sshkey install ``` -9. Setup basic tools on the Ubuntu image. +11. Enable password less sudo priviledge, by adding following entry to `sudo visudo` + ``` + ALL=(ALL) NOPASSWD:ALL + ``` + Please verify if password less sudo works on the remote machine. e.g., via, + ``` + ./deploy.py execonall sudo ls -al + ``` + +12. If apt-get gives a crash error, the issue is caused by: + https://askubuntu.com/questions/942895/e-problem-executing-scripts-aptupdatepost-invoke-success + ``` + ./deploy.py execonall sudo apt-get remove libappstream3 + ``` + + +12. Setup basic tools on the Ubuntu image. ``` ./deploy.py runscriptonall ./scripts/prepare_ubuntu.sh ./deploy.py execonall sudo usermod -aG docker core ``` -10. Partition hard drive, if necessary. Please refer to section [Partition](Repartition.md) for details. +13. Partition hard drive, if necessary. Please refer to section [Partition](Repartiuion.md) for details. -11. Setup kubernetes +14. Setup kubernetes ``` ./deploy.py -y deploy ./deploy.py -y updateworker @@ -76,16 +100,16 @@ This document describes the procedure to deploy DL workspace cluster on a off-pr ``` Works now will be scheduled on the master node. If you stop here, you will have a fully functional kubernete cluster. Thus, part of DL Workspace setup can be considered automatic procedure to setup a kubernete cluster. You don't need shared file system or database for kubernete cluster operation. -12. [optional] Configure, setup and mount [GlusterFS](../Storage/GlusterFS.md) -13. [Optional] Configure, setup and mount [HDFS](../Storage/hdfs.md) -14. [Optional] Setup [Spark](../Storage/spark.md) +15. [optional] Configure, setup and mount [GlusterFS](../Storage/GlusterFS.md) +16. [Optional] Configure, setup and mount [HDFS](../Storage/hdfs.md) +17. [Optional] Setup [Spark](../Storage/spark.md) -15. Mount shared file system +18. Mount shared file system ``` ./deploy.py mount ``` -16. Build and deploy jobmanager, restfulapi, and webportal. Mount storage. +19. Build and deploy jobmanager, restfulapi, and webportal. Mount storage. ``` ./deploy.py webui ./deploy.py docker push restfulapi diff --git a/docs/deployment/Storage/nfs.md b/docs/deployment/Storage/nfs.md new file mode 100755 index 000000000..dc3e00119 --- /dev/null +++ b/docs/deployment/Storage/nfs.md @@ -0,0 +1,35 @@ +# Deployment NFS server + +The document describes the procedure to setup NFS server. We follow the procedure in https://www.digitalocean.com/community/tutorials/how-to-set-up-an-nfs-mount-on-ubuntu-16-04 + +1. Install nfs kernel. + ``` + sudo apt-get update + sudo apt-get install nfs-kernel-server + ``` + +2. Format ext4 partition, and mount the partition to a particular mount point + ``` + sudo mkdir -p /mnt/dlwsdata + # nfs use nobody:nogroup to visit + sudo chown nobody:nogroup /mnt/dlwsdata + # discover the UUID information of the block device. + sudo lsblk -o Name,FSTYPE,UUID + # edit /etc/fstab, and add entry, which mounts a particular UUID storage device to the mount point. Last number is the fsck order. + UUID=e2c91cb7-c97d-46f7-a51b-001a06a14e08 /mnt/dlwsdata ext4 errors=remount-ro 0 2 + # causes all filesystems mentioned in fstab (of the proper type and/or having or not having the proper options) to be mounted as indicated, except for those whose line contains the noauto keyword. + sudo mount -a + ``` + +3. Modify /etc/exports + /mnt/dlwsdata *(rw,sync,no_root_squash,no_subtree_check) + +4. Check firewall status, if any. + ``` + sudo ufw status + ``` + +5. Start NFS server. + ``` + sudo systemctl restart nfs-kernel-server + ``` diff --git a/docs/deployment/network/dns.md b/docs/deployment/network/dns.md new file mode 100644 index 000000000..4d7639d20 --- /dev/null +++ b/docs/deployment/network/dns.md @@ -0,0 +1,12 @@ +# Add DNS server. + +# https://unix.stackexchange.com/questions/128220/how-do-i-set-my-dns-when-resolv-conf-is-being-overwritten + +sudo vim /etc/resolvconf/resolv.conf.d/base + +Then put your nameserver list in like so: + +nameserver 8.8.8.8 +nameserver 8.8.4.4 + +sudo resolvconf -u diff --git a/sample.output b/sample.output old mode 100644 new mode 100755 From 16ac03fd916182c7e09b6dfb3784b3f397137cf3 Mon Sep 17 00:00:00 2001 From: Jin Li Date: Sat, 29 Dec 2018 15:14:00 +0800 Subject: [PATCH 003/595] Update Deployment, enable empty domain. --- src/ClusterBootstrap/deploy.py | 4 ++-- src/ClusterBootstrap/install_prerequisites.sh | 21 ++++++++++++++----- .../scripts/prepare_ubuntu.sh | 9 +++++++- .../scripts/setup_nfs_server.sh | 20 +++++++++--------- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 5b127f1e9..67ccbfc83 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -516,7 +516,7 @@ def is_cur_on_same_domain(): # Get domain of the node def get_domain(): - if "network" in config and "domain" in config["network"]: + if "network" in config and "domain" in config["network"] and len(config["network"]["domain"]) > 0 : if is_cur_on_same_domain(): domain = "" else: @@ -531,6 +531,7 @@ def get_nodes_from_config(machinerole): return [] else: domain = get_domain() + # print ("Doamin = %s " % domain ) Nodes = [] for nodename in config["machines"]: nodeInfo = config["machines"][nodename] @@ -3752,4 +3753,3 @@ def run_script_blocks( verbose, script_collection ): print "Error: Unknown scriptblocks " + nargs[0] else: run_command( args, command, nargs, parser) - diff --git a/src/ClusterBootstrap/install_prerequisites.sh b/src/ClusterBootstrap/install_prerequisites.sh index 314628915..38ef3f909 100755 --- a/src/ClusterBootstrap/install_prerequisites.sh +++ b/src/ClusterBootstrap/install_prerequisites.sh @@ -15,14 +15,25 @@ sudo apt-get install -y --no-install-recommends \ ca-certificates # Install docker -curl -fsSL https://yum.dockerproject.org/gpg | apt-key add - +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - + sudo add-apt-repository \ - "deb https://apt.dockerproject.org/repo/ \ - ubuntu-$(lsb_release -cs) \ - main" + "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) \ + stable" + docker-ce + +AZ_REPO=$(lsb_release -cs) +echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $AZ_REPO main" | \ + sudo tee /etc/apt/sources.list.d/azure-cli.list + +sudo apt-key --keyring /etc/apt/trusted.gpg.d/Microsoft.gpg adv \ + --keyserver packages.microsoft.com \ + --recv-keys BC528686B50D79E339D3721CEB3E94ADBE1229CF + sudo apt-get update sudo apt-get install -y --no-install-recommends \ - docker-engine +sudo apt-get install azure-cli pip install --upgrade pip pip install setuptools && pip install pyyaml && pip install jinja2 diff --git a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh index 99dea6c33..0fa7644cf 100755 --- a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh +++ b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh @@ -33,7 +33,14 @@ then docker --version ## docker already installed else -curl -q https://get.docker.com/ | sudo bash +sudo apt-get remove docker docker-engine docker.io +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - +sudo add-apt-repository \ + "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) \ + stable" +sudo apt-get update +sudo apt-get install -y docker-ce fi sudo pip install --upgrade pip diff --git a/src/ClusterBootstrap/scripts/setup_nfs_server.sh b/src/ClusterBootstrap/scripts/setup_nfs_server.sh index 0bcd0ddcc..6abc0d455 100755 --- a/src/ClusterBootstrap/scripts/setup_nfs_server.sh +++ b/src/ClusterBootstrap/scripts/setup_nfs_server.sh @@ -1,10 +1,10 @@ -sudo apt-get update -sudo apt-get install -y nfs-kernel-server - -sudo mkdir -p /mnt/share -sudo chown nobody:nogroup /mnt/share - -echo "/mnt/share {{cnf["cloud_config"]["vnet_range"]}}(rw,sync,no_subtree_check,no_root_squash)" | sudo tee /etc/exports -sudo systemctl restart nfs-kernel-server - - +sudo apt-get update +sudo apt-get install -y nfs-kernel-server + +sudo mkdir -p /mnt/share +sudo chown nobody:nogroup /mnt/share + +echo "/mnt/share {{cnf["cloud_config"]["vnet_range"]}}(rw,sync,no_subtree_check,no_root_squash)" | sudo tee /etc/exports +sudo systemctl restart nfs-kernel-server + + From 469e06f5979db3037274d8abe8b077ddd02eccc3 Mon Sep 17 00:00:00 2001 From: Jin Li Date: Fri, 28 Dec 2018 23:26:42 -0800 Subject: [PATCH 004/595] Observe scp --- src/ClusterBootstrap/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClusterBootstrap/utils.py b/src/ClusterBootstrap/utils.py index d96bebab3..4c5e7a7c1 100755 --- a/src/ClusterBootstrap/utils.py +++ b/src/ClusterBootstrap/utils.py @@ -166,7 +166,7 @@ def sudo_scp (identity_file, source, target, user, host,changePermission=False, cmd += " ; sudo chmod +x %s" % target if verbose: print cmd - SSH_exec_cmd(identity_file, user, host, cmd, False) + SSH_exec_cmd(identity_file, user, host, cmd, verbose) # Execute a remote SSH cmd with identity file (private SSH key), user, host # Return the output of the remote command to local From e159e44df6177d3fd10ced432e68dbd2bffaad05 Mon Sep 17 00:00:00 2001 From: Jin Li Date: Sat, 29 Dec 2018 13:25:07 -0800 Subject: [PATCH 005/595] generate kubernetes cert migrate to use github easy-rsa --- src/ClusterBootstrap/template/ssl/gencerts_aggregator.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ClusterBootstrap/template/ssl/gencerts_aggregator.sh b/src/ClusterBootstrap/template/ssl/gencerts_aggregator.sh index fb5011685..d8c9d8e66 100755 --- a/src/ClusterBootstrap/template/ssl/gencerts_aggregator.sh +++ b/src/ClusterBootstrap/template/ssl/gencerts_aggregator.sh @@ -126,8 +126,11 @@ function generate-aggregator-certs { function setup-easyrsa { (set -x cd "${KUBE_TEMP}" - curl -L -O --connect-timeout 20 --retry 6 --retry-delay 2 https://storage.googleapis.com/kubernetes-release/easy-rsa/easy-rsa.tar.gz - tar xzf easy-rsa.tar.gz + # change away from using googleapis + curl -L -O --connect-timeout 20 --retry 6 --retry-delay 2 https://github.com/OpenVPN/easy-rsa/archive/v3.0.5.tar.gz + # tar to easy-rsa-v3.0.5 + tar xzf v3.0.5.tar.gz + mv easy-rsa-3.0.5 easy-rsa-master mkdir easy-rsa-master/kubelet cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/kubelet mkdir easy-rsa-master/aggregator From 0e6e8b71a756959e41b6bf926ca733b3c79835fd Mon Sep 17 00:00:00 2001 From: Jin Li Date: Sat, 29 Dec 2018 14:33:35 -0800 Subject: [PATCH 006/595] Update, not using gcr.io/pause --- src/ClusterBootstrap/scripts/prepare_ubuntu.sh | 3 +++ src/ClusterBootstrap/template/kubelet/kubelet.service.template | 2 +- src/ClusterBootstrap/template/master/kubelet.service | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh index 0fa7644cf..fc0233955 100755 --- a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh +++ b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh @@ -103,3 +103,6 @@ if lspci | grep -qE "[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F].[0-9] (3D|VG NV_DRIVER=/opt/nvidia-driver/$NVIDIA_VERSION sudo ln -s $NV_DRIVER /opt/nvidia-driver/current fi + +# https://github.com/kubernetes/kubeadm/issues/610 +sudo swapoff -a \ No newline at end of file diff --git a/src/ClusterBootstrap/template/kubelet/kubelet.service.template b/src/ClusterBootstrap/template/kubelet/kubelet.service.template index 0640a367f..efc78ae5b 100755 --- a/src/ClusterBootstrap/template/kubelet/kubelet.service.template +++ b/src/ClusterBootstrap/template/kubelet/kubelet.service.template @@ -28,7 +28,7 @@ ExecStart=/opt/bin/kubelet \ --feature-gates="Accelerators=true" \ --allow-privileged=true \ --pod-manifest-path=/etc/kubernetes/manifests \ - --pod-infra-container-image {{cnf["dockers"]["container"]["podinfra"]["fullname"]}} \ + --pod-infra-container-image={{cnf["dockers"]["container"]["podinfra"]["fullname"]}} \ --network-plugin=cni \ --cluster_dns={{cnf["dns-server-ip"]}} \ --docker-disable-shared-pid \ diff --git a/src/ClusterBootstrap/template/master/kubelet.service b/src/ClusterBootstrap/template/master/kubelet.service index b7953cc70..ac6facdfc 100755 --- a/src/ClusterBootstrap/template/master/kubelet.service +++ b/src/ClusterBootstrap/template/master/kubelet.service @@ -17,7 +17,7 @@ ExecStart=/opt/bin/kubelet \ --kubeconfig=/etc/kubernetes/worker-kubeconfig.yaml \ --require-kubeconfig=true \ --register-with-taints=node-role.kubernetes.io/master=:NoSchedule \ - --pod-infra-container-image {{cnf["dockers"]["container"]["podinfra"]["fullname"]}} \ + --pod-infra-container-image={{cnf["dockers"]["container"]["podinfra"]["fullname"]}} \ --container-runtime=docker \ --allow-privileged=true \ --feature-gates="Accelerators=true" \ From 9bb9024caf71182d4ea1c9b9ea5e2acf4ac7e445 Mon Sep 17 00:00:00 2001 From: Jin Li Date: Sun, 30 Dec 2018 14:43:02 -0800 Subject: [PATCH 007/595] Remove nvidia-docker1 --- .gitignore | 1 + docs/deployment/On-Prem/Ubuntu-Machines.md | 11 +++++++++++ src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh | 2 ++ 3 files changed, 14 insertions(+) create mode 100755 src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh diff --git a/.gitignore b/.gitignore index 6544b994b..baebbd25c 100755 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ src/WebUI/dotnet/WebPortal/userconfig.json src/WebUI/dotnet/WebPortal/configAuth.json src/WebUI/dotnet/WebPortal/Master-Templates.json src/WebUI/dotnet/WebPortal/hosting.json +**package-lock.json **/wwwroot/* **/bin/Release/* **/bin/Debug/* diff --git a/docs/deployment/On-Prem/Ubuntu-Machines.md b/docs/deployment/On-Prem/Ubuntu-Machines.md index b7954e505..a25bc39a4 100755 --- a/docs/deployment/On-Prem/Ubuntu-Machines.md +++ b/docs/deployment/On-Prem/Ubuntu-Machines.md @@ -89,6 +89,11 @@ This document describes the procedure to deploy DL workspace cluster on a off-pr 13. Partition hard drive, if necessary. Please refer to section [Partition](Repartiuion.md) for details. 14. Setup kubernetes + If setup in the cluster in China, we tried to use dlws/pause-amd64:3.0 as --pod-infra-container-image, however, for some reason, this doesn't work. Temporary, please do: + ``` + ./deploy.py execonall docker pull dlws/pause-amd64:3.0 + ./deploy.py execonall docker tag dlws/pause-amd64:3.0 gcr.io/google_containers/pause-amd64:3.0 + ``` ``` ./deploy.py -y deploy ./deploy.py -y updateworker @@ -114,7 +119,13 @@ This document describes the procedure to deploy DL workspace cluster on a off-pr ./deploy.py webui ./deploy.py docker push restfulapi ./deploy.py docker push webui + ./deploy.py nginx fqdn + ./deploy.py nginx config + ./deploy.py kubernetes start mysql ./deploy.py kubernetes start jobmanager ./deploy.py kubernetes start restfulapi ./deploy.py kubernetes start webportal + ./deploy.py kubernetes start cloudmonitor + ./deploy.py kubernetes start nginx + ./deploy.py kubernetes start custommetrics ``` diff --git a/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh b/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh new file mode 100755 index 000000000..3a949a4fd --- /dev/null +++ b/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh @@ -0,0 +1,2 @@ +docker volume ls -q -f driver=nvidia-docker | xargs -r -I{} -n1 docker ps -q -a -f volume={} | xargs -r docker rm -f +sudo apt-get purge nvidia-docker From 6082c62d6b4cfd07520e281b8f8f7187af66a896 Mon Sep 17 00:00:00 2001 From: Jin Li Date: Sun, 30 Dec 2018 14:47:59 -0800 Subject: [PATCH 008/595] install nvidia-docker2 --- src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh b/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh index 3a949a4fd..ebf8d0343 100755 --- a/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh +++ b/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh @@ -1,2 +1,11 @@ docker volume ls -q -f driver=nvidia-docker | xargs -r -I{} -n1 docker ps -q -a -f volume={} | xargs -r docker rm -f sudo apt-get purge nvidia-docker + +curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - +distribution=$(. /etc/os-release;echo $ID$VERSION_ID) +curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \ + sudo tee /etc/apt/sources.list.d/nvidia-docker.list +sudo apt-get update + +sudo apt-get install nvidia-docker2 +sudo pkill -SIGHUP dockerd \ No newline at end of file From f18e21420e769414de5476c38776f9638487704a Mon Sep 17 00:00:00 2001 From: Jin Li Date: Sun, 30 Dec 2018 14:49:09 -0800 Subject: [PATCH 009/595] Update nvidia-docker installation. --- src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh b/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh index ebf8d0343..2aba45c1f 100755 --- a/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh +++ b/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh @@ -3,8 +3,7 @@ sudo apt-get purge nvidia-docker curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - distribution=$(. /etc/os-release;echo $ID$VERSION_ID) -curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \ - sudo tee /etc/apt/sources.list.d/nvidia-docker.list +curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list sudo apt-get update sudo apt-get install nvidia-docker2 From 1c23195c41225670235ea0c6aa3e092aeb2ea609 Mon Sep 17 00:00:00 2001 From: Jin Li Date: Sun, 30 Dec 2018 14:55:59 -0800 Subject: [PATCH 010/595] Install nvidia-docker2 --- .../scripts/remove_nvidia_docker1.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh b/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh index 2aba45c1f..966b3ee3d 100755 --- a/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh +++ b/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh @@ -1,10 +1,10 @@ -docker volume ls -q -f driver=nvidia-docker | xargs -r -I{} -n1 docker ps -q -a -f volume={} | xargs -r docker rm -f -sudo apt-get purge nvidia-docker - -curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - -distribution=$(. /etc/os-release;echo $ID$VERSION_ID) -curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list -sudo apt-get update - -sudo apt-get install nvidia-docker2 +docker volume ls -q -f driver=nvidia-docker | xargs -r -I{} -n1 docker ps -q -a -f volume={} | xargs -r docker rm -f +sudo apt-get purge nvidia-docker + +curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - +distribution=$(. /etc/os-release;echo $ID$VERSION_ID) +curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list +sudo apt-get update + +sudo apt-get install nvidia-docker2 sudo pkill -SIGHUP dockerd \ No newline at end of file From 4778aa62592e50bddfdb4d430195dfda200fe32a Mon Sep 17 00:00:00 2001 From: Jin Li Date: Sun, 30 Dec 2018 17:28:29 -0800 Subject: [PATCH 011/595] Update docker to nvidia-docker2 --- src/ClusterBootstrap/scripts/prepare_ubuntu.sh | 16 ++++++++++++---- .../scripts/remove_nvidia_docker1.sh | 4 ++-- .../services/monitor/collectd.yaml | 10 +++++----- zjlab.tar.gz | Bin 0 -> 20962 bytes 4 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 zjlab.tar.gz diff --git a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh index fc0233955..43dc3bf62 100755 --- a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh +++ b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh @@ -88,10 +88,18 @@ if lspci | grep -qE "[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F].[0-9] (3D|VG sudo rm -r /opt/nvidia-driver || true - # Install nvidia-docker and nvidia-docker-plugin - rm /tmp/nvidia-docker*.deb - wget -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.1/nvidia-docker_1.0.1-1_amd64.deb - sudo dpkg -i /tmp/nvidia-docker*.deb && rm /tmp/nvidia-docker*.deb + # Install nvidia-docker and nvidia-docker-plugin ( Upgrade to nvidia-docker2) + # rm /tmp/nvidia-docker*.deb + # wget -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.1/nvidia-docker_1.0.1-1_amd64.deb + # sudo dpkg -i /tmp/nvidia-docker*.deb && rm /tmp/nvidia-docker*.deb + + curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - + distribution=$(. /etc/os-release;echo $ID$VERSION_ID) + curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list + sudo apt-get update + + sudo apt-get install -y nvidia-docker2 + sudo pkill -SIGHUP dockerd # Test nvidia-smi sudo nvidia-docker run --rm dlws/cuda nvidia-smi diff --git a/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh b/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh index 966b3ee3d..d0cc8210f 100755 --- a/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh +++ b/src/ClusterBootstrap/scripts/remove_nvidia_docker1.sh @@ -1,10 +1,10 @@ docker volume ls -q -f driver=nvidia-docker | xargs -r -I{} -n1 docker ps -q -a -f volume={} | xargs -r docker rm -f -sudo apt-get purge nvidia-docker +sudo apt-get purge -y nvidia-docker curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - distribution=$(. /etc/os-release;echo $ID$VERSION_ID) curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list sudo apt-get update -sudo apt-get install nvidia-docker2 +sudo apt-get install -y nvidia-docker2 sudo pkill -SIGHUP dockerd \ No newline at end of file diff --git a/src/ClusterBootstrap/services/monitor/collectd.yaml b/src/ClusterBootstrap/services/monitor/collectd.yaml index 9784fe88f..bca285b91 100755 --- a/src/ClusterBootstrap/services/monitor/collectd.yaml +++ b/src/ClusterBootstrap/services/monitor/collectd.yaml @@ -60,8 +60,8 @@ spec: readOnly: true - name: run mountPath: /var/run/docker.sock - - name: nvidia-driver - mountPath: /usr/local/nvidia + # - name: nvidia-driver + # mountPath: /usr/local/nvidia volumes: #- name: collectd-config # configMap: @@ -90,9 +90,9 @@ spec: - name: run hostPath: path: /var/run/docker.sock - - name: nvidia-driver - hostPath: - path: {{cnf["nvidia-driver-path"]}} + # - name: nvidia-driver + # hostPath: + # path: {{cnf["nvidia-driver-path"]}} tolerations: - key: CriticalAddonsOnly operator: Exists diff --git a/zjlab.tar.gz b/zjlab.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..b6b1c6c7d4f95d6cbd61b0ea43cd744687d31d7e GIT binary patch literal 20962 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 Date: Mon, 31 Dec 2018 12:06:20 -0800 Subject: [PATCH 012/595] Use device plug in --- docs/deployment/Storage/nfs.md | 1 + .../template/kubelet/kubelet.service.template | 5 +++-- src/ClusterBootstrap/template/master/kubelet.service | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/deployment/Storage/nfs.md b/docs/deployment/Storage/nfs.md index dc3e00119..86faaf6e2 100755 --- a/docs/deployment/Storage/nfs.md +++ b/docs/deployment/Storage/nfs.md @@ -17,6 +17,7 @@ The document describes the procedure to setup NFS server. We follow the procedur sudo lsblk -o Name,FSTYPE,UUID # edit /etc/fstab, and add entry, which mounts a particular UUID storage device to the mount point. Last number is the fsck order. UUID=e2c91cb7-c97d-46f7-a51b-001a06a14e08 /mnt/dlwsdata ext4 errors=remount-ro 0 2 + # Comment any swap entry, as kubelet doesn't run with swap on # causes all filesystems mentioned in fstab (of the proper type and/or having or not having the proper options) to be mounted as indicated, except for those whose line contains the noauto keyword. sudo mount -a ``` diff --git a/src/ClusterBootstrap/template/kubelet/kubelet.service.template b/src/ClusterBootstrap/template/kubelet/kubelet.service.template index efc78ae5b..3d555d0c3 100755 --- a/src/ClusterBootstrap/template/kubelet/kubelet.service.template +++ b/src/ClusterBootstrap/template/kubelet/kubelet.service.template @@ -19,13 +19,14 @@ ExecStartPre=/bin/bash -c 'if lspci | grep -qE "[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA # # https://github.com/kubernetes/kubernetes/issues/48937 # Glusterfs currently need docker-disable-shared-pid option, will evaluate in future kubernete release -# +# +# https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/ ExecStart=/opt/bin/kubelet \ --require-kubeconfig=true \ --container-runtime={{'remote' if cnf["kube_custom_cri"] else 'docker'}} \ --enable-server=true \ --register-node=true \ - --feature-gates="Accelerators=true" \ + --feature-gates="Accelerators=true,DevicePlugins=true" \ --allow-privileged=true \ --pod-manifest-path=/etc/kubernetes/manifests \ --pod-infra-container-image={{cnf["dockers"]["container"]["podinfra"]["fullname"]}} \ diff --git a/src/ClusterBootstrap/template/master/kubelet.service b/src/ClusterBootstrap/template/master/kubelet.service index ac6facdfc..62b721255 100755 --- a/src/ClusterBootstrap/template/master/kubelet.service +++ b/src/ClusterBootstrap/template/master/kubelet.service @@ -13,6 +13,7 @@ ExecStartPre=/bin/bash -c 'if lspci | grep -qE "[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA- # https://github.com/kubernetes/kubernetes/issues/48937 # Glusterfs currently need docker-disable-shared-pid option, will evaluate in future kubernete release # +# https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/ ExecStart=/opt/bin/kubelet \ --kubeconfig=/etc/kubernetes/worker-kubeconfig.yaml \ --require-kubeconfig=true \ @@ -20,7 +21,7 @@ ExecStart=/opt/bin/kubelet \ --pod-infra-container-image={{cnf["dockers"]["container"]["podinfra"]["fullname"]}} \ --container-runtime=docker \ --allow-privileged=true \ - --feature-gates="Accelerators=true" \ + --feature-gates="Accelerators=true,DevicePlugins=true" \ --pod-manifest-path=/etc/kubernetes/manifests \ --network-plugin=cni \ --cluster_dns={{cnf["dns-server-ip"]}} \ From 4cf8bb15f2bfc46e78eba306c29d2bc4483df17a Mon Sep 17 00:00:00 2001 From: Jin Li Date: Mon, 31 Dec 2018 13:04:18 -0800 Subject: [PATCH 013/595] Use nvidia docker runtime. --- docs/deployment/On-Prem/Ubuntu-Machines.md | 4 ++-- .../services/cloudmonitor/collectd.yaml | 10 +++++----- src/ClusterBootstrap/template/kubelet/daemon.json | 9 +++++++++ src/ClusterBootstrap/template/kubelet/deploy.list | 3 ++- .../template/kubelet/ubuntu/deploy.list | 3 ++- 5 files changed, 20 insertions(+), 9 deletions(-) create mode 100755 src/ClusterBootstrap/template/kubelet/daemon.json diff --git a/docs/deployment/On-Prem/Ubuntu-Machines.md b/docs/deployment/On-Prem/Ubuntu-Machines.md index a25bc39a4..a05e8c521 100755 --- a/docs/deployment/On-Prem/Ubuntu-Machines.md +++ b/docs/deployment/On-Prem/Ubuntu-Machines.md @@ -8,8 +8,7 @@ This document describes the procedure to deploy DL workspace cluster on a off-pr ..* Install git, if not installed yet. ..* Find data partition, if any, use mkfs ext4 to make partition, and to prepare the partition for mount in fstab. ..* use ```sudo mount -a``` to remount. -..* check DNS setting. -..* many of TLS doesn't work +..* check DNS setting, check if network is working. If shows TLS error message, network may not be stable. 2. [Run Once] Setup [development environment](../../DevEnvironment/Readme.md). @@ -85,6 +84,7 @@ This document describes the procedure to deploy DL workspace cluster on a off-pr ./deploy.py runscriptonall ./scripts/prepare_ubuntu.sh ./deploy.py execonall sudo usermod -aG docker core ``` + Use nvidia-docker as default docker run time, as shown in 13. Partition hard drive, if necessary. Please refer to section [Partition](Repartiuion.md) for details. diff --git a/src/ClusterBootstrap/services/cloudmonitor/collectd.yaml b/src/ClusterBootstrap/services/cloudmonitor/collectd.yaml index 8ccc78808..983c3aac4 100755 --- a/src/ClusterBootstrap/services/cloudmonitor/collectd.yaml +++ b/src/ClusterBootstrap/services/cloudmonitor/collectd.yaml @@ -57,8 +57,8 @@ spec: readOnly: true - name: run mountPath: /var/run/docker.sock - - name: nvidia-driver - mountPath: /usr/local/nvidia + #- name: nvidia-driver + # mountPath: /usr/local/nvidia volumes: #- name: collectd-config # configMap: @@ -84,9 +84,9 @@ spec: - name: run hostPath: path: /var/run/docker.sock - - name: nvidia-driver - hostPath: - path: {{cnf["nvidia-driver-path"]}} + #- name: nvidia-driver + # hostPath: + # path: {{cnf["nvidia-driver-path"]}} tolerations: - key: CriticalAddonsOnly operator: Exists diff --git a/src/ClusterBootstrap/template/kubelet/daemon.json b/src/ClusterBootstrap/template/kubelet/daemon.json new file mode 100755 index 000000000..f938c1b50 --- /dev/null +++ b/src/ClusterBootstrap/template/kubelet/daemon.json @@ -0,0 +1,9 @@ +{ + "default-runtime": "nvidia", + "runtimes": { + "nvidia": { + "path": "nvidia-container-runtime", + "runtimeArgs": [] + } + } +} \ No newline at end of file diff --git a/src/ClusterBootstrap/template/kubelet/deploy.list b/src/ClusterBootstrap/template/kubelet/deploy.list index 9e982cd36..b45bb68a0 100755 --- a/src/ClusterBootstrap/template/kubelet/deploy.list +++ b/src/ClusterBootstrap/template/kubelet/deploy.list @@ -11,4 +11,5 @@ ./deploy/ssl/kubelet/apiserver-key.pem,/etc/kubernetes/ssl/worker-key.pem ./deploy/kubelet/report.sh,/opt/report.sh ./deploy/kubelet/reportcluster.service,/etc/systemd/system/reportcluster.service -./deploy/kubelet/nodelist.yaml,/etc/kubernetes/nodes/nodelist.yaml \ No newline at end of file +./deploy/kubelet/nodelist.yaml,/etc/kubernetes/nodes/nodelist.yaml +./deploy/kubelet/daemon.json,/etc/docker/daemon.json \ No newline at end of file diff --git a/src/ClusterBootstrap/template/kubelet/ubuntu/deploy.list b/src/ClusterBootstrap/template/kubelet/ubuntu/deploy.list index 5b1ca6cb1..71ca7de92 100755 --- a/src/ClusterBootstrap/template/kubelet/ubuntu/deploy.list +++ b/src/ClusterBootstrap/template/kubelet/ubuntu/deploy.list @@ -23,4 +23,5 @@ ./deploy/bin/noop,/opt/cni/bin/noop ./deploy/bin/host-local,/opt/cni/bin/host-local ./deploy/bin/cnitool,/opt/cni/bin/cnitool -./deploy/bin/flannel,/opt/cni/bin/flannel \ No newline at end of file +./deploy/bin/flannel,/opt/cni/bin/flannel +./deploy/kubelet/daemon.json,/etc/docker/daemon.json \ No newline at end of file From a9d18a3f190e7381e8f935394ba2c0655294210d Mon Sep 17 00:00:00 2001 From: Jin Li Date: Mon, 31 Dec 2018 21:02:27 -0800 Subject: [PATCH 014/595] Change to use nvidia-driver2 --- docs/deployment/On-Prem/Ubuntu-Machines.md | 8 ++++++-- .../template/kubelet/ubuntu/pre-worker-deploy.sh | 1 + src/ClusterManager/job_manager.py | 8 ++++---- src/Jobs_Templete/DistJob.yaml.template | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/deployment/On-Prem/Ubuntu-Machines.md b/docs/deployment/On-Prem/Ubuntu-Machines.md index a05e8c521..9e0dd3d85 100755 --- a/docs/deployment/On-Prem/Ubuntu-Machines.md +++ b/docs/deployment/On-Prem/Ubuntu-Machines.md @@ -84,7 +84,7 @@ This document describes the procedure to deploy DL workspace cluster on a off-pr ./deploy.py runscriptonall ./scripts/prepare_ubuntu.sh ./deploy.py execonall sudo usermod -aG docker core ``` - Use nvidia-docker as default docker run time, as shown in + Use nvidia-docker as default docker run time (included in kubelet startup) 13. Partition hard drive, if necessary. Please refer to section [Partition](Repartiuion.md) for details. @@ -113,8 +113,12 @@ This document describes the procedure to deploy DL workspace cluster on a off-pr ``` ./deploy.py mount ``` +19. Deploy nvidia-device plugin. + ``` + ./deploy.py kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v1.9/nvidia-device-plugin.yml + ``` -19. Build and deploy jobmanager, restfulapi, and webportal. Mount storage. +20. Build and deploy jobmanager, restfulapi, and webportal. Mount storage. ``` ./deploy.py webui ./deploy.py docker push restfulapi diff --git a/src/ClusterBootstrap/template/kubelet/ubuntu/pre-worker-deploy.sh b/src/ClusterBootstrap/template/kubelet/ubuntu/pre-worker-deploy.sh index 01fe7403a..3523fdbac 100755 --- a/src/ClusterBootstrap/template/kubelet/ubuntu/pre-worker-deploy.sh +++ b/src/ClusterBootstrap/template/kubelet/ubuntu/pre-worker-deploy.sh @@ -1,5 +1,6 @@ sudo systemctl stop kubelet sudo systemctl stop kubecri +sudo systemctl restart docker sudo docker rm -f $(docker ps -a | grep 'k8s_kube\|k8s_POD' | awk '{print $1}') sudo mkdir -p /etc/kubernetes diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 2a7fb83d5..0efa07e28 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -148,9 +148,9 @@ def SubmitRegularJob(job): for onemount in jobParams["mountpoints"]: onemount["name"] = onemount["containerPath"].replace("/","") - mp = {"name":"nvidia-driver","containerPath":"/usr/local/nvidia","hostPath":nvidiaDriverPath, "enabled":True} - if CheckMountPoints(jobParams["mountpoints"],mp): - jobParams["mountpoints"].append(mp) + # mp = {"name":"nvidia-driver","containerPath":"/usr/local/nvidia","hostPath":nvidiaDriverPath, "enabled":True} + # if CheckMountPoints(jobParams["mountpoints"],mp): + # jobParams["mountpoints"].append(mp) mp = {"name":"job","containerPath":"/job","hostPath":jobParams["hostjobPath"], "enabled":True} if CheckMountPoints(jobParams["mountpoints"],mp): @@ -418,7 +418,7 @@ def SubmitPSDistJob(job): if "mountpoints" not in distJobParam: distJobParam["mountpoints"] = [] - distJobParam["mountpoints"].append({"name":"nvidia-driver","containerPath":"/usr/local/nvidia","hostPath":nvidiaDriverPath}) + # distJobParam["mountpoints"].append({"name":"nvidia-driver","containerPath":"/usr/local/nvidia","hostPath":nvidiaDriverPath}) distJobParam["mountpoints"].append({"name":"job","containerPath":"/job","hostPath":distJobParam["hostjobPath"]}) distJobParam["mountpoints"].append({"name":"work","containerPath":"/work","hostPath":distJobParam["hostworkPath"]}) distJobParam["mountpoints"].append({"name":"data","containerPath":"/data","hostPath":distJobParam["hostdataPath"]}) diff --git a/src/Jobs_Templete/DistJob.yaml.template b/src/Jobs_Templete/DistJob.yaml.template index be2022f0b..5b584ea66 100755 --- a/src/Jobs_Templete/DistJob.yaml.template +++ b/src/Jobs_Templete/DistJob.yaml.template @@ -41,7 +41,7 @@ spec: {% if job["distRole"] =="worker" %} resources: limits: - alpha.kubernetes.io/nvidia-gpu: {{ job["resourcegpu"] }} + nvidia.com/gpu: {{ job["resourcegpu"] }} {% if not job["cpurequest"] %} requests: cpu: 1.0 From 337de763eef25bf8db3e9e50ed9ee2c906ee92cd Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Fri, 4 Jan 2019 08:59:03 -0800 Subject: [PATCH 015/595] fix a few issues to use docker nvidia runtime --- src/ClusterBootstrap/params.py | 5 +++ .../scripts/prepare_ubuntu.sh | 4 ++- .../nvidia-device-plugin.yaml | 35 +++++++++++++++++++ .../kubelet/ubuntu/post-worker-deploy.sh | 1 + src/Jobs_Templete/RegularJob.yaml.template | 2 +- 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100755 src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 178995008..3ddb90dc5 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -601,6 +601,7 @@ "-y deploy", "-y updateworker", "-y kubernetes labels", + "kubernetes start nvidia-device-plugin", "webui", "docker push restfulapi", "docker push webui", @@ -626,6 +627,7 @@ "-y updateworker", "kubernetes uncordon", "-y kubernetes labels", + "kubernetes start nvidia-device-plugin", "webui", "docker push restfulapi", "docker push webui", @@ -646,6 +648,7 @@ "-y deploy", "-y updateworker", "-y kubernetes labels", + "kubernetes start nvidia-device-plugin", "kubernetes uncordon", "mount", "webui", @@ -663,6 +666,7 @@ "runscriptonall ./scripts/prepare_ubuntu.sh", "-y deploy", "-y kubernetes labels", + "kubernetes start nvidia-device-plugin", "kubernetes uncordon", "sleep 60", "-y updateworker", @@ -729,6 +733,7 @@ "-y deploy", "-y updateworker", "-y kubernetes labels", + "kubernetes start nvidia-device-plugin", "mount", "webui", "docker push restfulapi", diff --git a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh index 43dc3bf62..fde2d9659 100755 --- a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh +++ b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh @@ -79,8 +79,10 @@ if lspci | grep -qE "[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F].[0-9] (3D|VG # chmod +x /tmp/NVIDIA-Linux-x86_64-$NVIDIA_VERSION.run # sudo bash /tmp/NVIDIA-Linux-x86_64-$NVIDIA_VERSION.run -a -s + sudo add-apt-repository -y ppa:graphics-drivers/ppa sudo apt-get purge -y nvidia* - sudo apt-get install -y nvidia-384 + sudo apt-get update + sudo apt-get install -y nvidia-410 diff --git a/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml b/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml new file mode 100755 index 000000000..e201c211e --- /dev/null +++ b/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml @@ -0,0 +1,35 @@ +apiVersion: extensions/v1beta1 +kind: DaemonSet +metadata: + name: nvidia-device-plugin-daemonset + namespace: kube-system +spec: + template: + metadata: + # Mark this pod as a critical add-on; when enabled, the critical add-on scheduler + # reserves resources for critical add-on pods so that they can be rescheduled after + # a failure. This annotation works in tandem with the toleration below. + annotations: + scheduler.alpha.kubernetes.io/critical-pod: "" + labels: + name: nvidia-device-plugin-ds + spec: + tolerations: + # Allow this pod to be rescheduled while the node is in "critical add-ons only" mode. + # This, along with the annotation above marks this pod as a critical add-on. + - key: CriticalAddonsOnly + operator: Exists + containers: + - image: nvidia/k8s-device-plugin:1.9 + name: nvidia-device-plugin-ctr + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + volumeMounts: + - name: device-plugin + mountPath: /var/lib/kubelet/device-plugins + volumes: + - name: device-plugin + hostPath: + path: /var/lib/kubelet/device-plugins diff --git a/src/ClusterBootstrap/template/kubelet/ubuntu/post-worker-deploy.sh b/src/ClusterBootstrap/template/kubelet/ubuntu/post-worker-deploy.sh index f7196fe0d..3622dc7dc 100755 --- a/src/ClusterBootstrap/template/kubelet/ubuntu/post-worker-deploy.sh +++ b/src/ClusterBootstrap/template/kubelet/ubuntu/post-worker-deploy.sh @@ -6,6 +6,7 @@ sudo chmod +x /opt/bin/kubelet sudo systemctl daemon-reload sudo systemctl stop kubelet sudo systemctl stop kubecri +sudo systemctl restart docker {{'sudo systemctl start kubecri' if cnf["kube_custom_cri"]}} sudo systemctl start kubelet sudo systemctl start rpc-statd diff --git a/src/Jobs_Templete/RegularJob.yaml.template b/src/Jobs_Templete/RegularJob.yaml.template index b2ece2cb7..f03c4dd74 100755 --- a/src/Jobs_Templete/RegularJob.yaml.template +++ b/src/Jobs_Templete/RegularJob.yaml.template @@ -40,7 +40,7 @@ spec: {% endif %} resources: limits: - alpha.kubernetes.io/nvidia-gpu: {{ job["resourcegpu"] }} + nvidia.com/gpu: {{ job["resourcegpu"] }} {% if not job["cpurequest"] %} requests: cpu: 1.0 From d0bf1e6db938e208aaed63284ad71f7c431a471c Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Thu, 31 Jan 2019 10:24:06 -0800 Subject: [PATCH 016/595] use mpi for distributd training. note: tensorflow dist func is not longer working in this branch --- src/ClusterBootstrap/params.py | 2 +- src/ClusterManager/job_manager.py | 124 +++++++++++++++--- src/Jobs_Templete/DistJob.yaml.template | 16 ++- src/Jobs_Templete/RegularJob.yaml.template | 2 + .../WebPortal/Views/Home/JobSubmission.cshtml | 2 +- 5 files changed, 128 insertions(+), 18 deletions(-) diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 178995008..97488a803 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -553,7 +553,7 @@ "external": { # These dockers are to be built by additional add ons. "hyperkube": {"fullname":"dlws/hyperkube:v1.9.0"}, - "freeflow": {"fullname":"dlws/freeflow:0.16"}, + "freeflow": {"fullname":"dlws/freeflow:0.18"}, "podinfra": {"fullname":"dlws/pause-amd64:3.0"}, "nvidiadriver": {"fullname":"dlws/nvidia_driver:375.20"}, "weave":{"fullname":"dlws/weave:2.2.0"}, diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 2a7fb83d5..b45783a62 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -170,6 +170,10 @@ def SubmitRegularJob(job): if CheckMountPoints(jobParams["mountpoints"],mp): jobParams["mountpoints"].append(mp) + for idx in range(len(jobParams["mountpoints"])): + if "name" not in jobParams["mountpoints"][idx]: + jobParams["mountpoints"][idx]["name"] = str(uuid.uuid4()).replace("-","") + jobParams["pod_ip_range"] = config["pod_ip_range"] if "usefreeflow" in config: @@ -382,10 +386,16 @@ def SubmitPSDistJob(job): #fi #""" - - launchCMD = """ + if role == "ps": + launchCMD = """ #!/bin/bash mkdir -p /opt +cp -r /sshkey/.ssh /root +chown -R root:root /root/.ssh +echo export LD_PRELOAD=$LD_PRELOAD >> /etc/default/ssh +echo export VNET_PREFIX=$VNET_PREFIX >> /etc/default/ssh +service ssh restart + echo "[DLWorkspace System]: Waiting for all containers are ready..." while [ ! -f /opt/run_dist_job ] || [ ! -f /opt/run_dist_job.sh ]; do sleep 3 @@ -394,12 +404,25 @@ def SubmitPSDistJob(job): chmod +x /opt/run_dist_job.sh /opt/run_dist_job.sh """ + else: + launchCMD = """ +#!/bin/bash +mkdir -p /opt +cp -r /sshkey/.ssh /root +chown -R root:root /root/.ssh +echo export LD_PRELOAD=$LD_PRELOAD >> /etc/default/ssh +echo export VNET_PREFIX=$VNET_PREFIX >> /etc/default/ssh +service ssh restart +sleep infinity +""" + + - launchScriptPath = os.path.join(localJobPath,"launch-%s.sh" % distJobParam["jobId"]) + launchScriptPath = os.path.join(localJobPath,"launch-%s-%s%d.sh" % (distJobParam["jobId"],role,i)) with open(launchScriptPath, 'w') as f: f.write(launchCMD) f.close() - distJobParam["LaunchCMD"] = "[\"bash\", \"/job/launch-%s.sh\"]" % distJobParam["jobId"] + distJobParam["LaunchCMD"] = "[\"bash\", \"/job/launch-%s-%s%d.sh\"]" % (distJobParam["jobId"],role,i) @@ -422,12 +445,26 @@ def SubmitPSDistJob(job): distJobParam["mountpoints"].append({"name":"job","containerPath":"/job","hostPath":distJobParam["hostjobPath"]}) distJobParam["mountpoints"].append({"name":"work","containerPath":"/work","hostPath":distJobParam["hostworkPath"]}) distJobParam["mountpoints"].append({"name":"data","containerPath":"/data","hostPath":distJobParam["hostdataPath"]}) + + userAlias = getAlias(jobParams["userName"]) + distJobParam["mountpoints"].append({"name":"rootsshkey","containerPath":"/sshkey/.ssh","hostPath":os.path.join(config["storage-mount-path"], GetWorkPath(userAlias)+"/.ssh"), "readOnly":True, "enabled":True}) + + + for idx in range(len(distJobParam["mountpoints"])): + if "name" not in distJobParam["mountpoints"][idx]: + distJobParam["mountpoints"][idx]["name"] = str(uuid.uuid4()).replace("-","") + + distJobParam["pod_ip_range"] = config["pod_ip_range"] - if "usefreeflow" in config and config["usefreeflow"] == "True": + if "usefreeflow" in config: distJobParam["usefreeflow"] = config["usefreeflow"] else: distJobParam["usefreeflow"] = False + distJobParam["numworker"] = int(jobParams["numpsworker"]) + distJobParam["numps"] = int(jobParams["numps"]) + + random.seed(datetime.datetime.now()) distJobParam["containerPort"] = int(random.random()*1000+3000) @@ -727,6 +764,8 @@ def launch_ps_dist_job(jobParams): ps_pod_ips = [pod["status"]["podIP"] for pod in psPodInfo["items"]] worker_pod_ips = [pod["status"]["podIP"] for pod in workerPodInfo["items"]] + worker_gpu_num = [pod["spec"]["containers"][0]["resources"]["requests"]["alpha.kubernetes.io/nvidia-gpu"] for pod in workerPodInfo["items"]] + ps_num = len(psPodInfo["items"]) worker_num = len(workerPodInfo["items"]) @@ -745,33 +784,88 @@ def launch_ps_dist_job(jobParams): ps_files = ["/tmp/" + str(uuid.uuid4()) for i in range(ps_num)] worker_files = ["/tmp/" + str(uuid.uuid4()) for i in range(worker_num)] - ps_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=ps --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,ps_files[i]) for i in range(ps_num)] - worker_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=worker --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,worker_files[i]) for i in range(worker_num)] + #ps_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=ps --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,ps_files[i]) for i in range(ps_num)] + #worker_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=worker --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,worker_files[i]) for i in range(worker_num)] + + ps_cmd = ["%s 2>&1 | tee %s" % (jobParams["cmd"], ps_files[i]) for i in range(ps_num)] + worker_cmd = ["%s 2>&1 | tee %s" % (jobParams["cmd"], worker_files[i]) for i in range(worker_num)] + + + hostfilecontent = "" + for workerip,workergpu in zip(worker_pod_ips,worker_gpu_num): + hostfilecontent += "%s slots=%s\n" %(workerip,workergpu) for i in range(ps_num): os.system("mkdir -p %s" % ps_files[i]) - ps_files[i] = os.path.join(ps_files[i],"run_dist_job.sh") - with open(ps_files[i], 'w') as f: + psfile = os.path.join(ps_files[i],"run_dist_job.sh") + with open(psfile, 'w') as f: f.write(ps_cmd[i] + "\n") f.close() if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], ps_files[i])) - remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (ps_files[i],ps_pod_names[i]) + os.system("chown -R %s %s" % (jobParams["userId"], psfile)) + remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (psfile,ps_pod_names[i]) k8sUtils.kubectl_exec(remotecmd) + + + os.system("mkdir -p %s" % ps_files[i]) + psfile = os.path.join(ps_files[i],"hostfile") + with open(psfile, 'w') as f: + f.write(hostfilecontent + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], psfile)) + remotecmd = "cp %s %s:/opt/hostfile" % (psfile,ps_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + os.system("mkdir -p %s" % ps_files[i]) + psfile = os.path.join(ps_files[i],"taskindex") + with open(psfile, 'w') as f: + f.write(str(i) + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], psfile)) + remotecmd = "cp %s %s:/opt/taskindex" % (psfile,ps_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + k8sUtils.kubectl_exec("exec %s touch /opt/run_dist_job" % ps_pod_names[i]) for i in range(worker_num): os.system("mkdir -p %s" % worker_files[i]) - worker_files[i] = os.path.join(worker_files[i],"run_dist_job.sh") - with open(worker_files[i], 'w') as f: + workerfile = os.path.join(worker_files[i],"run_dist_job.sh") + with open(workerfile, 'w') as f: f.write(worker_cmd[i] + "\n") f.close() if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], worker_files[i])) - remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (worker_files[i],worker_pod_names[i]) + os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) + remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (workerfile,worker_pod_names[i]) k8sUtils.kubectl_exec(remotecmd) + + + os.system("mkdir -p %s" % worker_files[i]) + workerfile = os.path.join(worker_files[i],"hostfile") + with open(workerfile, 'w') as f: + f.write(hostfilecontent + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) + remotecmd = "cp %s %s:/opt/hostfile" % (workerfile,worker_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + + os.system("mkdir -p %s" % worker_files[i]) + workerfile = os.path.join(worker_files[i],"taskindex") + with open(workerfile, 'w') as f: + f.write(str(i) + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) + remotecmd = "cp %s %s:/opt/taskindex" % (workerfile,worker_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + k8sUtils.kubectl_exec("exec %s touch /opt/run_dist_job" % worker_pod_names[i]) dataHandler = DataHandler() diff --git a/src/Jobs_Templete/DistJob.yaml.template b/src/Jobs_Templete/DistJob.yaml.template index be2022f0b..e3a6f3b97 100755 --- a/src/Jobs_Templete/DistJob.yaml.template +++ b/src/Jobs_Templete/DistJob.yaml.template @@ -55,8 +55,10 @@ spec: {% endif %} {% endif %} volumeMounts: + {% if job["usefreeflow"] %} - mountPath: /freeflow name: freeflow + {% endif %} {% for mp in job["mountpoints"] %} - mountPath: {{ mp.containerPath }} name: {{ mp.name }} @@ -70,10 +72,20 @@ spec: value: {{ job["familyToken"] }} - name: DLWS_REST_API value: {{ job["rest-api"] }} + - name: DLWS_JOB_ID + value: {{ job["jobId"] }} + - name: DLWS_NUM_PS + value: "{{ job["numps"] }}" + - name: DLWS_NUM_WORK + value: "{{ job["numworker"] }}" + - name: DLWS_NUM_GPU_PER_WORKER + value: "{{ job["resourcegpu"] }}" + {% if job["usefreeflow"] %} - name: VNET_PREFIX value: {{ job["pod_ip_range"] }} - name: LD_PRELOAD - value: "/freeflow/libfsocket.so" + value: "/freeflow/libfsocket.so" + {% endif %} - name: POD_NAME valueFrom: fieldRef: @@ -89,9 +101,11 @@ spec: restartPolicy: Never volumes: + {% if job["usefreeflow"] %} - name: freeflow hostPath: path: /freeflow + {% endif %} {% if not job["dnsPolicy"] %} - name: resolv hostPath: diff --git a/src/Jobs_Templete/RegularJob.yaml.template b/src/Jobs_Templete/RegularJob.yaml.template index b2ece2cb7..e00284a62 100755 --- a/src/Jobs_Templete/RegularJob.yaml.template +++ b/src/Jobs_Templete/RegularJob.yaml.template @@ -77,6 +77,8 @@ spec: value: {{ job["familyToken"] }} - name: DLWS_REST_API value: {{ job["rest-api"] }} + - name: JOB_ID + value: {{ job["jobId"] }} {% if job["usefreeflow"] %} - name: VNET_PREFIX value: {{ job["pod_ip_range"] }} diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 2ee925a7a..5c0684a2c 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -162,7 +162,7 @@ }; }; if (mounthomefolder) { - mounts.unshift({ "containerPath": "/home/" + username, "hostPath": "/dlwsdata/work/" + username, "description": homefolderDescription, "enabled": true }); + mounts.unshift({"name":"homefolder", "containerPath": "/home/" + username, "hostPath": "/dlwsdata/work/" + username, "description": homefolderDescription, "enabled": true }); } return mounts; }; From 212ac093f1bdfd8dc03d78af699d1330c9a4421c Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Tue, 12 Mar 2019 10:54:44 -0700 Subject: [PATCH 017/595] add vc control --- src/ClusterBootstrap/deploy.py | 23 +++++++++++++++++++ src/ClusterBootstrap/params.py | 3 +++ src/Jobs_Templete/DistJob.yaml.template | 3 ++- src/Jobs_Templete/RegularJob.yaml.template | 8 ++++++- src/RestAPI/dlwsrestapi.py | 7 +++++- .../WebPortal/Views/Home/JobSubmission.cshtml | 16 +++++++++++-- src/WebUI/dotnet/WebPortal/web.config | 4 +++- 7 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 5b127f1e9..c618c17f4 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -2770,6 +2770,27 @@ def kubernetes_label_nodes( verb, servicelists, force ): elif verb == "remove": kubernetes_label_node(cmdoptions, nodename, label+"-") + +# Label kubernete nodes according to a service. +# A service (usually a Kubernete daemon service) can request to be run on: +# all: all nodes +# etcd_node: all etcd node +# etcd_node_n: a particular etcd node +# worker_node: all worker node +# The kubernete node will be marked accordingly to facilitate the running of daemon service. +def kubernetes_label_vc(): + nodes = get_nodes(config["clusterId"]) + vc_config = fetch_config(config, ["vc_config"]) + for vc_name, nodes in vc_config: + for node in nodes: + nodename = kubernetes_get_node_name(node) + if nodename in nodes or "*" in nodes: + kubernetes_label_node("--overwrite", nodename, vc_name+"=active") + else: + kubernetes_label_node("--overwrite", nodename, vc_name+"=inactive") + + + def kubernetes_patch_nodes_provider (provider, scaledOnly): nodes = [] if scaledOnly: @@ -3490,6 +3511,8 @@ def run_command( args, command, nargs, parser ): kubernetes_mark_nodes( nargs[1:], False) elif nargs[0] == "cordon" or nargs[0] == "uncordon": run_kube_command_on_nodes(nargs) + elif nargs[0] == "labelvc": + kubernetes_label_vc(True) else: parser.print_help() print "Error: Unknown kubernetes subcommand " + nargs[0] diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 97488a803..f3a5a88ea 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -590,6 +590,9 @@ # "source_addresses_prefixes": [ "52.151.0.0/16"] } }, + "vc_config":{ + "VC-Default":["*"], + } } # These are super scripts diff --git a/src/Jobs_Templete/DistJob.yaml.template b/src/Jobs_Templete/DistJob.yaml.template index e3a6f3b97..dfd378cb1 100755 --- a/src/Jobs_Templete/DistJob.yaml.template +++ b/src/Jobs_Templete/DistJob.yaml.template @@ -10,8 +10,9 @@ metadata: userName: {{ job["userNameLabel"] }} spec: #hostNetwork: true - {% if job["nodeSelector"]|length > 0 %} nodeSelector: + worker: active + {% if job["nodeSelector"]|length > 0 %} {% for key, value in job["nodeSelector"].items() %} {{key}}: {{value}} {% endfor %} diff --git a/src/Jobs_Templete/RegularJob.yaml.template b/src/Jobs_Templete/RegularJob.yaml.template index e00284a62..31c1dcda1 100755 --- a/src/Jobs_Templete/RegularJob.yaml.template +++ b/src/Jobs_Templete/RegularJob.yaml.template @@ -15,8 +15,14 @@ metadata: {% endfor %} {% endif %} spec: - {% if job["resourcegpu"]|int < 8 %} nodeSelector: + worker: active + {% if job["nodeSelector"]|length > 0 %} + {% for key, value in job["nodeSelector"].items() %} + {{key}}: {{value}} + {% endfor %} + {% endif %} + {% if job["resourcegpu"]|int < 8 %} FragmentGPUJob: active {% endif %} {% if job["dnsPolicy"] %} diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index a3cc6be68..c82aefff9 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -77,7 +77,8 @@ def get(self): parser.add_argument('familyToken') parser.add_argument('isParent') parser.add_argument('jobType') - + parser.add_argument('nodeSelector') + parser.add_argument('jobtrainingtype') parser.add_argument('numps') @@ -134,6 +135,10 @@ def get(self): # !! note: if userId is not provided, the container will be running as root. There shouldn't be any security concern since all the resources in docker container should be user's own property. Also, we plan to allow user to choose "run as root". params["userId"] = "0" + if args["nodeSelector"] is not None and len(args["nodeSelector"].strip()) > 0: + params["nodeSelector"] = {args["nodeSelector"]:"active"} + + if args["interactivePort"] is not None and len(args["interactivePort"].strip()) > 0: params["interactivePort"] = args["interactivePort"] diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 5c0684a2c..5a852120a 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -524,7 +524,7 @@
- + {{ g.Text }} @@ -625,7 +625,19 @@
- +
+ + + + Default VC + V100 + P100 + P40 + TitanXP + 1080Ti + + +
diff --git a/src/WebUI/dotnet/WebPortal/web.config b/src/WebUI/dotnet/WebPortal/web.config index 1f48560c5..c7029f6a1 100755 --- a/src/WebUI/dotnet/WebPortal/web.config +++ b/src/WebUI/dotnet/WebPortal/web.config @@ -7,6 +7,8 @@ - + + + \ No newline at end of file From dbf9ff8d78c8ba8230a82e9597942dda8c49bf30 Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Fri, 15 Mar 2019 22:23:58 -0700 Subject: [PATCH 018/595] fixed a few bugs --- src/ClusterBootstrap/az_params.py | 4 +- src/ClusterBootstrap/az_tools.py | 3 + src/ClusterBootstrap/params.py | 1 + src/ClusterBootstrap/scripts/dns.sh | 8 + .../scripts/prepare_ubuntu.sh | 4 +- .../scripts/prepare_vm_disk.sh | 30 +- .../scripts/setup_nfs_server.sh | 6 +- src/ClusterManager/job_manager.py | 266 +++++++++--------- 8 files changed, 175 insertions(+), 147 deletions(-) create mode 100644 src/ClusterBootstrap/scripts/dns.sh diff --git a/src/ClusterBootstrap/az_params.py b/src/ClusterBootstrap/az_params.py index 27849210b..790ad805e 100755 --- a/src/ClusterBootstrap/az_params.py +++ b/src/ClusterBootstrap/az_params.py @@ -6,9 +6,9 @@ "infra_vm_size" : "Standard_D1_v2", "worker_vm_size": "Standard_NC6", "vm_image" : "UbuntuLTS", - "vm_storage_sku" : "Standard_LRS", + "vm_storage_sku" : "Premium_LRS", # "udp_port_ranges": "" # Use file_share_name to create Azure file share # "file_share_name" : "files", }, -} \ No newline at end of file +} diff --git a/src/ClusterBootstrap/az_tools.py b/src/ClusterBootstrap/az_tools.py index addd1a366..2b1bb0b3c 100755 --- a/src/ClusterBootstrap/az_tools.py +++ b/src/ClusterBootstrap/az_tools.py @@ -125,6 +125,7 @@ def create_vm_pwd(vmname, vm_ip, vm_size, use_private_ip, pwd): --nsg %s \ --admin-username %s \ --storage-sku %s \ + --data-disk-sizes-gb 2047 \ %s \ """ % (config["azure_cluster"]["resource_group_name"], vmname, @@ -159,6 +160,7 @@ def create_vm(vmname, vm_ip, bIsWorker, vm_size): --nsg %s \ --admin-username %s \ --storage-sku %s \ + --data-disk-sizes-gb 2047 \ --ssh-key-value "%s" """ % (config["azure_cluster"]["resource_group_name"], vmname, @@ -184,6 +186,7 @@ def create_vm(vmname, vm_ip, bIsWorker, vm_size): --nsg %s \ --admin-username %s \ --storage-sku %s \ + --data-disk-sizes-gb 2047 \ --ssh-key-value "%s" """ % (config["azure_cluster"]["resource_group_name"], vmname, diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 602615945..84944c2be 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -598,6 +598,7 @@ "runscriptonall ./scripts/prepare_vm_disk.sh", "nfs-server create", "runscriptonall ./scripts/prepare_ubuntu.sh", + "runscriptonall ./scripts/dns.sh", "-y deploy", "-y updateworker", "-y kubernetes labels", diff --git a/src/ClusterBootstrap/scripts/dns.sh b/src/ClusterBootstrap/scripts/dns.sh new file mode 100644 index 000000000..743bf2caa --- /dev/null +++ b/src/ClusterBootstrap/scripts/dns.sh @@ -0,0 +1,8 @@ +sudo systemctl disable systemd-resolved.service +sudo systemctl stop systemd-resolved +echo "dns=default" | sudo tee -a /etc/NetworkManager/NetworkManager.conf +sudo rm /etc/resolv.conf +echo "nameserver 8.8.8.8" | sudo tee -a /etc/resolv.conf +#echo 'search {{cnf["network"]["domain"]}}' | sudo tee -a /etc/resolv.conf +echo "search eastus.cloudapp.azure.com" | sudo tee -a /etc/resolv.conf +sudo service network-manager restart diff --git a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh index fde2d9659..5f41b2ae8 100755 --- a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh +++ b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh @@ -82,7 +82,7 @@ if lspci | grep -qE "[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F].[0-9] (3D|VG sudo add-apt-repository -y ppa:graphics-drivers/ppa sudo apt-get purge -y nvidia* sudo apt-get update - sudo apt-get install -y nvidia-410 + sudo apt-get install -y nvidia-driver-415 @@ -115,4 +115,4 @@ if lspci | grep -qE "[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F].[0-9] (3D|VG fi # https://github.com/kubernetes/kubeadm/issues/610 -sudo swapoff -a \ No newline at end of file +sudo swapoff -a diff --git a/src/ClusterBootstrap/scripts/prepare_vm_disk.sh b/src/ClusterBootstrap/scripts/prepare_vm_disk.sh index 5fc33f144..47de44cf9 100755 --- a/src/ClusterBootstrap/scripts/prepare_vm_disk.sh +++ b/src/ClusterBootstrap/scripts/prepare_vm_disk.sh @@ -1,21 +1,31 @@ #!/bin/bash + +printf "o\nn\np\n1\n\n\nw\n" | sudo fdisk /dev/sdc +sudo mkfs.ext4 /dev/sdc1 +sleep 10 +sudo mkdir /data +uuid=$(ls -l /dev/disk/by-uuid/ | grep sdc1 | awk '{print $9}') +echo "UUID=$uuid /data ext4 defaults,discard 0 0" | sudo tee -a /etc/fstab +sudo mount /data + sudo mv /var/log /var/log.bak -sudo mkdir -p /mnt/log -sudo rm -r /var/log ; sudo ln -s /mnt/log /var/log -sudo mv /var/log.bak/* /mnt/log +sudo mkdir -p /data/log +sudo rm -r /var/log ; sudo ln -s /data/log /var/log +sudo mv /var/log.bak/* /data/log sudo rm -r /var/log.bak -sudo mkdir -p /mnt/lib/docker -sudo mkdir -p /mnt/lib/mysql -sudo mkdir -p /mnt/lib/influxdb +sudo mkdir -p /data/lib/docker +sudo mkdir -p /data/lib/mysql +sudo mkdir -p /data/lib/influxdb if [ ! -L /var/lib/docker ]; then - sudo ln -s /mnt/lib/docker /var/lib/docker + sudo ln -s /data/lib/docker /var/lib/docker fi if [ ! -L /var/lib/mysql ]; then # It is a symlink! # Symbolic link specific commands go here. - sudo ln -s /mnt/lib/mysql /var/lib/mysql + sudo ln -s /data/lib/mysql /var/lib/mysql fi -if [ ! -L /var/lib/influxdb ]; then - sudo ln -s /mnt/lib/influxdb /var/lib/influxdb +if [ ! -L /var/lib/influxdb ]; then + sudo ln -s /data/lib/influxdb /var/lib/influxdb fi + diff --git a/src/ClusterBootstrap/scripts/setup_nfs_server.sh b/src/ClusterBootstrap/scripts/setup_nfs_server.sh index 6abc0d455..48cc90586 100755 --- a/src/ClusterBootstrap/scripts/setup_nfs_server.sh +++ b/src/ClusterBootstrap/scripts/setup_nfs_server.sh @@ -1,10 +1,10 @@ sudo apt-get update sudo apt-get install -y nfs-kernel-server -sudo mkdir -p /mnt/share -sudo chown nobody:nogroup /mnt/share +sudo mkdir -p /data/share +sudo chown nobody:nogroup /data/share -echo "/mnt/share {{cnf["cloud_config"]["vnet_range"]}}(rw,sync,no_subtree_check,no_root_squash)" | sudo tee /etc/exports +echo "/data/share {{cnf["cloud_config"]["vnet_range"]}}(rw,sync,no_subtree_check,no_root_squash)" | sudo tee /etc/exports sudo systemctl restart nfs-kernel-server diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index d28f7e333..3bbcab954 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -591,7 +591,7 @@ def UpdateJobStatus(job): if job["jobStatus"] == "scheduling" and jobParams["jobtrainingtype"] == "PSDistJob": launch_ps_dist_job(jobParams) - + return jobPath,workPath,dataPath = GetStoragePath(jobParams["jobPath"],jobParams["workPath"],jobParams["dataPath"]) localJobPath = os.path.join(config["storage-mount-path"],jobPath) @@ -752,144 +752,150 @@ def run(self): def launch_ps_dist_job(jobParams): - jobId = jobParams["jobId"] - workerPodInfo = k8sUtils.GetPod("distRole=worker,run=" + jobId) - psPodInfo = k8sUtils.GetPod("distRole=ps,run=" + jobId) - if "items" in workerPodInfo and len(workerPodInfo["items"]) == int(jobParams["numpsworker"]) and "items" in psPodInfo and len(psPodInfo["items"]) == int(jobParams["numps"]): - podStatus = [k8sUtils.check_pod_status(pod) for pod in workerPodInfo["items"] + psPodInfo["items"] ] - if all([status == "Running" for status in podStatus]): - ps_pod_names = [pod["metadata"]["name"] for pod in psPodInfo["items"]] - worker_pod_names = [pod["metadata"]["name"] for pod in workerPodInfo["items"]] - - ps_pod_ips = [pod["status"]["podIP"] for pod in psPodInfo["items"]] - worker_pod_ips = [pod["status"]["podIP"] for pod in workerPodInfo["items"]] - - worker_gpu_num = [pod["spec"]["containers"][0]["resources"]["requests"]["alpha.kubernetes.io/nvidia-gpu"] for pod in workerPodInfo["items"]] - - ps_num = len(psPodInfo["items"]) - worker_num = len(workerPodInfo["items"]) - - ps_ports = [int(item["metadata"]["labels"]["distPort"]) for item in psPodInfo["items"]] - worker_ports = [int(item["metadata"]["labels"]["distPort"]) for item in workerPodInfo["items"]] - - #port range: 30000~31000 - #rndList = range(max(1000,ps_num + worker_num)) - #random.shuffle(rndList) - #ps_ports = [rndList[i] + 30000 for i in range(ps_num)] - #worker_ports = [rndList[i + ps_num] + 30000 for i in range(worker_num)] - - ps_hosts = ",".join(["%s:%s" % (ps_pod_ips[i],ps_ports[i]) for i in range(ps_num)]) - worker_hosts = ",".join(["%s:%s" % (worker_pod_ips[i],worker_ports[i]) for i in range(worker_num)]) - - ps_files = ["/tmp/" + str(uuid.uuid4()) for i in range(ps_num)] - worker_files = ["/tmp/" + str(uuid.uuid4()) for i in range(worker_num)] - - #ps_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=ps --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,ps_files[i]) for i in range(ps_num)] - #worker_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=worker --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,worker_files[i]) for i in range(worker_num)] - - ps_cmd = ["%s 2>&1 | tee %s" % (jobParams["cmd"], ps_files[i]) for i in range(ps_num)] - worker_cmd = ["%s 2>&1 | tee %s" % (jobParams["cmd"], worker_files[i]) for i in range(worker_num)] - - - hostfilecontent = "" - for workerip,workergpu in zip(worker_pod_ips,worker_gpu_num): - hostfilecontent += "%s slots=%s\n" %(workerip,workergpu) - - - for i in range(ps_num): - os.system("mkdir -p %s" % ps_files[i]) - psfile = os.path.join(ps_files[i],"run_dist_job.sh") - with open(psfile, 'w') as f: - f.write(ps_cmd[i] + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], psfile)) - remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (psfile,ps_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - - - os.system("mkdir -p %s" % ps_files[i]) - psfile = os.path.join(ps_files[i],"hostfile") - with open(psfile, 'w') as f: - f.write(hostfilecontent + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], psfile)) - remotecmd = "cp %s %s:/opt/hostfile" % (psfile,ps_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - - os.system("mkdir -p %s" % ps_files[i]) - psfile = os.path.join(ps_files[i],"taskindex") - with open(psfile, 'w') as f: - f.write(str(i) + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], psfile)) - remotecmd = "cp %s %s:/opt/taskindex" % (psfile,ps_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - - - k8sUtils.kubectl_exec("exec %s touch /opt/run_dist_job" % ps_pod_names[i]) - - - for i in range(worker_num): - os.system("mkdir -p %s" % worker_files[i]) - workerfile = os.path.join(worker_files[i],"run_dist_job.sh") - with open(workerfile, 'w') as f: - f.write(worker_cmd[i] + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) - remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (workerfile,worker_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - - - os.system("mkdir -p %s" % worker_files[i]) - workerfile = os.path.join(worker_files[i],"hostfile") - with open(workerfile, 'w') as f: - f.write(hostfilecontent + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) - remotecmd = "cp %s %s:/opt/hostfile" % (workerfile,worker_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - - - os.system("mkdir -p %s" % worker_files[i]) - workerfile = os.path.join(worker_files[i],"taskindex") - with open(workerfile, 'w') as f: - f.write(str(i) + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) - remotecmd = "cp %s %s:/opt/taskindex" % (workerfile,worker_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - + try: + jobId = jobParams["jobId"] + workerPodInfo = k8sUtils.GetPod("distRole=worker,run=" + jobId) + psPodInfo = k8sUtils.GetPod("distRole=ps,run=" + jobId) + if "items" in workerPodInfo and len(workerPodInfo["items"]) == int(jobParams["numpsworker"]) and "items" in psPodInfo and len(psPodInfo["items"]) == int(jobParams["numps"]): + podStatus = [k8sUtils.check_pod_status(pod) for pod in workerPodInfo["items"] + psPodInfo["items"] ] + if all([status == "Running" for status in podStatus]): + ps_pod_names = [pod["metadata"]["name"] for pod in psPodInfo["items"]] + worker_pod_names = [pod["metadata"]["name"] for pod in workerPodInfo["items"]] - k8sUtils.kubectl_exec("exec %s touch /opt/run_dist_job" % worker_pod_names[i]) + ps_pod_ips = [pod["status"]["podIP"] for pod in psPodInfo["items"]] + worker_pod_ips = [pod["status"]["podIP"] for pod in workerPodInfo["items"]] - dataHandler = DataHandler() - dataHandler.UpdateJobTextField(jobParams["jobId"],"jobStatus","running") + worker_gpu_num = [pod["spec"]["containers"][0]["resources"]["requests"]["nvidia.com/gpu"] for pod in workerPodInfo["items"]] - #ps_threads = [Kube_RemoteCMD_Thread(jobId,ps_pod_names[i],ps_cmd[i],ps_logfiles[i]) for i in range(ps_num)] - #worker_threads = [Kube_RemoteCMD_Thread(jobId,worker_pod_names[i],worker_cmd[i],worker_logfiles[i]) for i in range(worker_num)] - - #for t in ps_threads: - # t.start() + ps_num = len(psPodInfo["items"]) + worker_num = len(workerPodInfo["items"]) + + ps_ports = [int(item["metadata"]["labels"]["distPort"]) for item in psPodInfo["items"]] + worker_ports = [int(item["metadata"]["labels"]["distPort"]) for item in workerPodInfo["items"]] + + #port range: 30000~31000 + #rndList = range(max(1000,ps_num + worker_num)) + #random.shuffle(rndList) + #ps_ports = [rndList[i] + 30000 for i in range(ps_num)] + #worker_ports = [rndList[i + ps_num] + 30000 for i in range(worker_num)] + + ps_hosts = ",".join(["%s:%s" % (ps_pod_ips[i],ps_ports[i]) for i in range(ps_num)]) + worker_hosts = ",".join(["%s:%s" % (worker_pod_ips[i],worker_ports[i]) for i in range(worker_num)]) + + ps_files = ["/tmp/" + str(uuid.uuid4()) for i in range(ps_num)] + worker_files = ["/tmp/" + str(uuid.uuid4()) for i in range(worker_num)] - #for t in worker_threads: - # t.start() + #ps_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=ps --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,ps_files[i]) for i in range(ps_num)] + #worker_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=worker --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,worker_files[i]) for i in range(worker_num)] + ps_cmd = ["%s 2>&1 | tee %s" % (jobParams["cmd"], ps_files[i]) for i in range(ps_num)] + worker_cmd = ["%s 2>&1 | tee %s" % (jobParams["cmd"], worker_files[i]) for i in range(worker_num)] - #while (True): + + hostfilecontent = "" + for workerip,workergpu in zip(worker_pod_ips,worker_gpu_num): + hostfilecontent += "%s slots=%s\n" %(workerip,workergpu) + + error_flag = False + for i in range(ps_num): + os.system("mkdir -p %s" % ps_files[i]) + psfile = os.path.join(ps_files[i],"run_dist_job.sh") + with open(psfile, 'w') as f: + f.write(ps_cmd[i] + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], psfile)) + remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (psfile,ps_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + + os.system("mkdir -p %s" % ps_files[i]) + psfile = os.path.join(ps_files[i],"hostfile") + with open(psfile, 'w') as f: + f.write(hostfilecontent + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], psfile)) + remotecmd = "cp %s %s:/opt/hostfile" % (psfile,ps_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + os.system("mkdir -p %s" % ps_files[i]) + psfile = os.path.join(ps_files[i],"taskindex") + with open(psfile, 'w') as f: + f.write(str(i) + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], psfile)) + remotecmd = "cp %s %s:/opt/taskindex" % (psfile,ps_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + k8sUtils.kubectl_exec("exec %s touch /opt/run_dist_job" % ps_pod_names[i]) + output = k8sUtils.kubectl_exec("exec %s ls /opt/run_dist_job" % ps_pod_names[i]) + if (output == ""): + error_flag = True + + for i in range(worker_num): + os.system("mkdir -p %s" % worker_files[i]) + workerfile = os.path.join(worker_files[i],"run_dist_job.sh") + with open(workerfile, 'w') as f: + f.write(worker_cmd[i] + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) + remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (workerfile,worker_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + + os.system("mkdir -p %s" % worker_files[i]) + workerfile = os.path.join(worker_files[i],"hostfile") + with open(workerfile, 'w') as f: + f.write(hostfilecontent + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) + remotecmd = "cp %s %s:/opt/hostfile" % (workerfile,worker_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + + os.system("mkdir -p %s" % worker_files[i]) + workerfile = os.path.join(worker_files[i],"taskindex") + with open(workerfile, 'w') as f: + f.write(str(i) + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) + remotecmd = "cp %s %s:/opt/taskindex" % (workerfile,worker_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + + k8sUtils.kubectl_exec("exec %s touch /opt/run_dist_job" % worker_pod_names[i]) + output = k8sUtils.kubectl_exec("exec %s ls /opt/run_dist_job" % worker_pod_names[i]) + if (output == ""): + error_flag = True + if not error_flag: + dataHandler = DataHandler() + dataHandler.UpdateJobTextField(jobParams["jobId"],"jobStatus","running") + + #ps_threads = [Kube_RemoteCMD_Thread(jobId,ps_pod_names[i],ps_cmd[i],ps_logfiles[i]) for i in range(ps_num)] + #worker_threads = [Kube_RemoteCMD_Thread(jobId,worker_pod_names[i],worker_cmd[i],worker_logfiles[i]) for i in range(worker_num)] + #for t in ps_threads: - # print t.isAlive() - #time.sleep(5) + # t.start() + + #for t in worker_threads: + # t.start() + - #cmd = "test" - #thread.start_new_thread( run_dist_cmd_on_pod, - #(workerPodInfo["items"][0]["metadata"]["name"], cmd) ) + #while (True): + #for t in ps_threads: + # print t.isAlive() + #time.sleep(5) + #cmd = "test" + #thread.start_new_thread( run_dist_cmd_on_pod, + #(workerPodInfo["items"][0]["metadata"]["name"], cmd) ) + except Exception as e: + print e From eb201c7b6e255c30ec18bb58828f6936443b2266 Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Fri, 15 Mar 2019 22:30:17 -0700 Subject: [PATCH 019/595] update alpha.kubernetes.io/nvidia-gpu to nvidia/gpu --- src/ClusterBootstrap/services/detectron/detectron.yaml | 2 +- src/ClusterManager/node_manager.py | 4 ++-- src/docker-images/collectd/kubernetes_collectd.py | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ClusterBootstrap/services/detectron/detectron.yaml b/src/ClusterBootstrap/services/detectron/detectron.yaml index e464c0bee..b3295f850 100755 --- a/src/ClusterBootstrap/services/detectron/detectron.yaml +++ b/src/ClusterBootstrap/services/detectron/detectron.yaml @@ -25,7 +25,7 @@ spec: image: {{cnf["dockers"]["container"]["tutorial-caffe2"]["fullname"]}} resources: limits: - alpha.kubernetes.io/nvidia-gpu: 1 + nvidia.com/gpu: 1 imagePullPolicy: Always command: ["/run.sh"] livenessProbe: diff --git a/src/ClusterManager/node_manager.py b/src/ClusterManager/node_manager.py index 592d3cdab..a5639af32 100755 --- a/src/ClusterManager/node_manager.py +++ b/src/ClusterManager/node_manager.py @@ -88,7 +88,7 @@ def get_job_gpu_usage(jobId): def get_cluster_status(): cluster_status={} - gpuStr = "alpha.kubernetes.io/nvidia-gpu" + gpuStr = "nvidia.com/gpu" try: output = k8sUtils.kubectl_exec(" get nodes -o yaml") nodeInfo = yaml.load(output) @@ -248,4 +248,4 @@ def Run(): time.sleep(30) if __name__ == '__main__': - Run() \ No newline at end of file + Run() diff --git a/src/docker-images/collectd/kubernetes_collectd.py b/src/docker-images/collectd/kubernetes_collectd.py index a3b4ce53b..8c4932f95 100755 --- a/src/docker-images/collectd/kubernetes_collectd.py +++ b/src/docker-images/collectd/kubernetes_collectd.py @@ -150,8 +150,8 @@ def read(data=None): if "spec" in item and "containers" in item["spec"]: if "status" in item and "phase" in item["status"] and item["status"]["phase"] == "Running": for container in item["spec"]["containers"]: - if "resources" in container and "requests" in container["resources"] and "alpha.kubernetes.io/nvidia-gpu" in container["resources"]["requests"]: - used_gpus += int(container["resources"]["requests"]["alpha.kubernetes.io/nvidia-gpu"]) + if "resources" in container and "requests" in container["resources"] and "nvidia.com/gpu" in container["resources"]["requests"]: + used_gpus += int(container["resources"]["requests"]["nvidia.com/gpu"]) vl = collectd.Values(type='gauge') vl.plugin = 'gpu' vl.plugin_instance = "usedgpu" @@ -171,8 +171,8 @@ def read(data=None): nodes = json.loads( curl_get(os.environ['K8SAPI']+"/api/v1/nodes")) if "items" in nodes: for item in nodes["items"]: - if "status" in item and "capacity" in item["status"] and "alpha.kubernetes.io/nvidia-gpu" in item["status"]["capacity"]: - total_gpus += int(item["status"]["capacity"]["alpha.kubernetes.io/nvidia-gpu"]) + if "status" in item and "capacity" in item["status"] and "nvidia.com/gpu" in item["status"]["capacity"]: + total_gpus += int(item["status"]["capacity"]["nvidia.com/gpu"]) vl = collectd.Values(type='gauge') vl.plugin = 'gpu' vl.plugin_instance = "totalgpu" From 829484ab1a453b2ac83bf09bfb2be250e1e82229 Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Mon, 18 Mar 2019 14:48:20 -0700 Subject: [PATCH 020/595] update a few default options --- src/ClusterManager/job_manager.py | 2 +- src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 3bbcab954..a38d24cb2 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -578,7 +578,7 @@ def AutoApproveJob(job): if user["userName"] == jobUser: currentGPU = int(user["userGPU"]) - if currentGPU == 0 or currentGPU + jobGPU <= 4: + if True || currentGPU == 0 or currentGPU + jobGPU <= 4: ApproveJob(job) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 5c0684a2c..ab3ba9de1 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -550,7 +550,7 @@
- + @@ -816,7 +816,7 @@
-

Priviledged Docker

+

Privileged Docker

From 06e2b89d4d1b27e3ae8a6ffcfb821e30b3e8da66 Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Mon, 18 Mar 2019 16:22:25 -0700 Subject: [PATCH 021/595] temp adding docker registry cred --- src/ClusterManager/job_manager.py | 2 +- src/Jobs_Templete/DistJob.yaml.template | 3 +++ src/Jobs_Templete/RegularJob.yaml.template | 7 +++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index a38d24cb2..2cffc2d94 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -578,7 +578,7 @@ def AutoApproveJob(job): if user["userName"] == jobUser: currentGPU = int(user["userGPU"]) - if True || currentGPU == 0 or currentGPU + jobGPU <= 4: + if True or currentGPU == 0 or currentGPU + jobGPU <= 4: ApproveJob(job) diff --git a/src/Jobs_Templete/DistJob.yaml.template b/src/Jobs_Templete/DistJob.yaml.template index 40ef91f92..233bc9f20 100755 --- a/src/Jobs_Templete/DistJob.yaml.template +++ b/src/Jobs_Templete/DistJob.yaml.template @@ -98,6 +98,9 @@ spec: - name: {{ env.name }} value: {{ env.value }} {% endfor %} + + imagePullSecrets: + - name: regcred restartPolicy: Never volumes: diff --git a/src/Jobs_Templete/RegularJob.yaml.template b/src/Jobs_Templete/RegularJob.yaml.template index 2740f57ed..b56265969 100755 --- a/src/Jobs_Templete/RegularJob.yaml.template +++ b/src/Jobs_Templete/RegularJob.yaml.template @@ -30,7 +30,7 @@ spec: {% endif %} containers: - name: {{ job["podName"] }} - image: {{ job["image"] }} + image: {{ job["image"] }} imagePullPolicy: Always command: {{ job["LaunchCMD"] }} securityContext: @@ -97,7 +97,10 @@ spec: - name: {{ env.name }} value: "{{ env.value }}" {% endfor %} - + + imagePullSecrets: + - name: regcred + restartPolicy: Never volumes: {% if not job["dnsPolicy"] %} From 4cc0c45f44a562d9d3000b3458a62d1d3a7e46ed Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Tue, 19 Mar 2019 18:10:23 -0700 Subject: [PATCH 022/595] changes for dist jobs --- src/ClusterManager/job_manager.py | 2 ++ src/Jobs_Templete/DistJob.yaml.template | 2 +- src/Jobs_Templete/RegularJob.yaml.template | 8 +++++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 2cffc2d94..1e4026894 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -118,6 +118,8 @@ def SubmitRegularJob(job): launchScriptPath = os.path.join(localJobPath,"launch-%s.sh" % jobParams["jobId"]) with open(launchScriptPath, 'w') as f: f.write("#!/bin/bash -x\n") + f.write("mkdir /opt; \n") + f.write("echo 'localhost slots=%s' | tee -a /opt/hostfile; \n" % jobParams["resourcegpu"]) f.write(jobParams["cmd"] + "\n") f.close() if "userId" in jobParams: diff --git a/src/Jobs_Templete/DistJob.yaml.template b/src/Jobs_Templete/DistJob.yaml.template index 233bc9f20..51a1ba8f4 100755 --- a/src/Jobs_Templete/DistJob.yaml.template +++ b/src/Jobs_Templete/DistJob.yaml.template @@ -76,7 +76,7 @@ spec: value: {{ job["jobId"] }} - name: DLWS_NUM_PS value: "{{ job["numps"] }}" - - name: DLWS_NUM_WORK + - name: DLWS_NUM_WORKER value: "{{ job["numworker"] }}" - name: DLWS_NUM_GPU_PER_WORKER value: "{{ job["resourcegpu"] }}" diff --git a/src/Jobs_Templete/RegularJob.yaml.template b/src/Jobs_Templete/RegularJob.yaml.template index b56265969..6fd8c28f6 100755 --- a/src/Jobs_Templete/RegularJob.yaml.template +++ b/src/Jobs_Templete/RegularJob.yaml.template @@ -78,7 +78,13 @@ spec: - name: DLWS_REST_API value: {{ job["rest-api"] }} - name: JOB_ID - value: {{ job["jobId"] }} + value: {{ job["jobId"] }} + - name: DLWS_JOB_ID + value: {{ job["jobId"] }} + - name: DLWS_NUM_WORKER + value: "1" + - name: DLWS_NUM_GPU_PER_WORKER + value: "{{ job["resourcegpu"] }}" {% if job["usefreeflow"] %} - name: VNET_PREFIX value: {{ job["pod_ip_range"] }} From 479188625e757e3f0bd2815e9efab57a4905fb78 Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Wed, 20 Mar 2019 13:33:48 -0700 Subject: [PATCH 023/595] allow interactive ports in dist jobs --- src/ClusterManager/job_manager.py | 19 +++++++++++++++++++ src/Jobs_Templete/DistJob.yaml.template | 1 + src/utils/k8sUtils.py | 1 - 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 1e4026894..ca219bef5 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -483,6 +483,25 @@ def SubmitPSDistJob(job): distJobParams[role].append(distJobParam) + + if (role == "ps" and "interactivePort" in distJobParam and len(distJobParam["interactivePort"].strip()) > 0): + ports = [p.strip() for p in re.split(",|;",distJobParam["interactivePort"]) if len(p.strip()) > 0 and p.strip().isdigit()] + + distJobParam["podName"] = distJobParam["jobId"]+"-"+distJobParam["distId"] + + for portNum in ports: + distJobParam["serviceId"] = "interactive-" + distJobParam["podName"] + "-" + portNum + distJobParam["port"] = portNum + distJobParam["port-name"] = "interactive" + distJobParam["port-type"] = "TCP" + + serviceTemplate = ENV.get_template(os.path.join(jobTempDir,"KubeSvc.yaml.template")) + + stemplate = ENV.get_template(serviceTemplate) + interactiveMeta = stemplate.render(svc=distJobParam) + jobDescriptionList.append(interactiveMeta) + + jobParams["jobDescriptionPath"] = "jobfiles/" + time.strftime("%y%m%d") + "/" + jobParams["jobId"] + "/" + jobParams["jobId"] + ".yaml" jobDescription = "\n---\n".join(jobDescriptionList) diff --git a/src/Jobs_Templete/DistJob.yaml.template b/src/Jobs_Templete/DistJob.yaml.template index 51a1ba8f4..3d0be1899 100755 --- a/src/Jobs_Templete/DistJob.yaml.template +++ b/src/Jobs_Templete/DistJob.yaml.template @@ -4,6 +4,7 @@ metadata: name: {{ job["jobId"] }}-{{ job["distId"] }} labels: run: {{ job["jobId"] }} + podName: {{ job["jobId"] }}-{{ job["distId"] }} jobName: {{ job["jobNameLabel"] }} distRole: {{ job["distRole"] }} distPort: "{{job["containerPort"]}}" diff --git a/src/utils/k8sUtils.py b/src/utils/k8sUtils.py index eb9967291..3e158d0b9 100755 --- a/src/utils/k8sUtils.py +++ b/src/utils/k8sUtils.py @@ -131,7 +131,6 @@ def GetServiceAddress(jobId): selector += "," selector += "{0}={1}".format(label, svc["spec"]["selector"][label]) labelIndex += 1 - if selector is not None: podInfo = GetPod(selector) if podInfo is not None and "items" in podInfo: From 04b5bddac55beb86c24abb6a59b38e1d741b2926 Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Wed, 20 Mar 2019 16:17:35 -0700 Subject: [PATCH 024/595] host networking --- src/ClusterManager/job_manager.py | 67 +++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index ca219bef5..d05b17504 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -403,8 +403,10 @@ def SubmitPSDistJob(job): sleep 3 done echo "[DLWorkspace System]: All containers are ready, launching training job..." +chown root /root/.ssh/config chmod +x /opt/run_dist_job.sh /opt/run_dist_job.sh +touch /job/finish """ else: launchCMD = """ @@ -414,8 +416,11 @@ def SubmitPSDistJob(job): chown -R root:root /root/.ssh echo export LD_PRELOAD=$LD_PRELOAD >> /etc/default/ssh echo export VNET_PREFIX=$VNET_PREFIX >> /etc/default/ssh +chown root /root/.ssh/config service ssh restart -sleep infinity +while [ ! -f /job/finish ] ; do + sleep 3 +done """ @@ -809,13 +814,39 @@ def launch_ps_dist_job(jobParams): #ps_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=ps --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,ps_files[i]) for i in range(ps_num)] #worker_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=worker --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,worker_files[i]) for i in range(worker_num)] + + ssh_config = """ +Host %s + HostName %s + Port %s + User root + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + """ + + ps_cmd = ["%s 2>&1 | tee %s" % (jobParams["cmd"], ps_files[i]) for i in range(ps_num)] worker_cmd = ["%s 2>&1 | tee %s" % (jobParams["cmd"], worker_files[i]) for i in range(worker_num)] + #hostfilecontent = "" + #for workerip,workergpu in zip(worker_pod_ips,worker_gpu_num): + # hostfilecontent += "%s slots=%s\n" %(workerip,workergpu) + + + hostfilecontent = "" - for workerip,workergpu in zip(worker_pod_ips,worker_gpu_num): - hostfilecontent += "%s slots=%s\n" %(workerip,workergpu) + for i in range(len(worker_gpu_num)): + hostfilecontent += "%s slots=%s\n" %("worker-"+str(i),worker_gpu_num[i]) + + + sshconfigstr = "" + for i in range(ps_num): + sshconfigstr += (ssh_config %("master-"+str(i), ps_pod_ips[i] ,"22222") +"\n") + + for i in range(worker_num): + sshconfigstr += (ssh_config %("worker-"+str(i), worker_pod_ips[i] ,"22222") +"\n") + error_flag = False for i in range(ps_num): @@ -840,6 +871,22 @@ def launch_ps_dist_job(jobParams): remotecmd = "cp %s %s:/opt/hostfile" % (psfile,ps_pod_names[i]) k8sUtils.kubectl_exec(remotecmd) + + os.system("mkdir -p %s" % ps_files[i]) + psfile = os.path.join(ps_files[i],"config") + with open(psfile, 'w') as f: + f.write(sshconfigstr + "\n") + f.close() + #if "userId" in jobParams: + # os.system("chown -R %s %s" % (jobParams["userId"], psfile)) + k8sUtils.kubectl_exec("exec %s mkdir -p /root/.ssh" % ps_pod_names[i]) + remotecmd = "cp %s %s:/root/.ssh/config" % (psfile,ps_pod_names[i]) + k8sUtils.kubectl_exec("exec %s chmod 600 /root/.ssh/config" % ps_pod_names[i]) + k8sUtils.kubectl_exec("exec %s chown root /root/.ssh/config" % ps_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + + os.system("mkdir -p %s" % ps_files[i]) psfile = os.path.join(ps_files[i],"taskindex") with open(psfile, 'w') as f: @@ -878,6 +925,20 @@ def launch_ps_dist_job(jobParams): k8sUtils.kubectl_exec(remotecmd) + os.system("mkdir -p %s" % worker_files[i]) + psfile = os.path.join(worker_files[i],"config") + with open(psfile, 'w') as f: + f.write(sshconfigstr + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], psfile)) + k8sUtils.kubectl_exec("exec %s mkdir -p /root/.ssh" % worker_pod_names[i]) + remotecmd = "cp %s %s:/root/.ssh/config" % (psfile,worker_pod_names[i]) + k8sUtils.kubectl_exec("exec %s chmod 600 /root/.ssh/config" % worker_pod_names[i]) + k8sUtils.kubectl_exec("exec %s chown root /root/.ssh/config" % worker_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + os.system("mkdir -p %s" % worker_files[i]) workerfile = os.path.join(worker_files[i],"taskindex") with open(workerfile, 'w') as f: From c62d0a5f80d77522d7c5d2084bf8e63314e4f464 Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Thu, 21 Mar 2019 14:30:55 -0700 Subject: [PATCH 025/595] update --- zjlab.tar.gz | Bin 20962 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 zjlab.tar.gz diff --git a/zjlab.tar.gz b/zjlab.tar.gz deleted file mode 100644 index b6b1c6c7d4f95d6cbd61b0ea43cd744687d31d7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20962 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 Date: Thu, 21 Mar 2019 14:31:42 -0700 Subject: [PATCH 026/595] update --- zjlab.tar.gz | Bin 20962 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 zjlab.tar.gz diff --git a/zjlab.tar.gz b/zjlab.tar.gz deleted file mode 100644 index b6b1c6c7d4f95d6cbd61b0ea43cd744687d31d7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20962 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 Date: Wed, 27 Mar 2019 07:27:21 +0000 Subject: [PATCH 027/595] fixed a few bugs --- src/ClusterManager/job_manager.py | 191 +++++++++++++----- .../WebPortal/Views/Home/JobDetail.cshtml | 7 +- 2 files changed, 148 insertions(+), 50 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index d05b17504..7853d7edb 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -328,6 +328,19 @@ def SubmitPSDistJob(job): assignedRack = None if len(config["racks"]) > 0: assignedRack = random.choice(config["racks"]) + + # in distributed job and host network is using, we will randomly assign a SSH port to each container. + ssh_ports = {} + if "hostNetwork" in jobParams and jobParams["hostNetwork"]: + ps_num = int(jobParams["numps"]) + worker_num = int(jobParams["numpsworker"]) + #port range: 30000~31000 + rndList = range(max(1000,ps_num + worker_num)) + random.shuffle(rndList) + ssh_ports["ps"] = [rndList[i] + 30000 for i in range(ps_num)] + ssh_ports["worker"] = [rndList[i + ps_num] + 30000 for i in range(worker_num)] + + if jobParams["jobtrainingtype"] == "PSDistJob": jobDescriptionList = [] nums = {"ps":int(jobParams["numps"]),"worker":int(jobParams["numpsworker"])} @@ -387,15 +400,28 @@ def SubmitPSDistJob(job): # /opt/run_dist_job.sh #fi #""" + if "hostNetwork" in jobParams and jobParams["hostNetwork"]: + change_ssh_port_CMD = "cat /etc/ssh/sshd_config | grep -v 'Port\\ [0-9]*' > /tmp/sshd_config && echo 'Port "+str(ssh_ports[role][i])+"' | tee -a /tmp/sshd_config && sudo mv /tmp/sshd_config /etc/ssh/sshd_config ; " + else: + change_ssh_port_CMD = "" if role == "ps": launchCMD = """ #!/bin/bash mkdir -p /opt -cp -r /sshkey/.ssh /root -chown -R root:root /root/.ssh +mkdir -p /root/.ssh +cp -r /sshkey/.ssh/id_rsa /root/.ssh +cp -r /sshkey/.ssh/id_rsa.pub /root/.ssh +cp -r /sshkey/.ssh/authorized_keys /root/.ssh +chown root:root /root/.ssh +chown root:root /root/.ssh/id_rsa +chown root:root /root/.ssh/id_rsa.pub +chown root:root /root/.ssh/authorized_keys echo export LD_PRELOAD=$LD_PRELOAD >> /etc/default/ssh echo export VNET_PREFIX=$VNET_PREFIX >> /etc/default/ssh + +%s + service ssh restart echo "[DLWorkspace System]: Waiting for all containers are ready..." @@ -403,25 +429,35 @@ def SubmitPSDistJob(job): sleep 3 done echo "[DLWorkspace System]: All containers are ready, launching training job..." -chown root /root/.ssh/config chmod +x /opt/run_dist_job.sh +sleep 10 +chown root:root /root/.ssh/config /opt/run_dist_job.sh -touch /job/finish -""" +""" % change_ssh_port_CMD else: launchCMD = """ #!/bin/bash mkdir -p /opt -cp -r /sshkey/.ssh /root -chown -R root:root /root/.ssh +mkdir -p /root/.ssh +cp -r /sshkey/.ssh/id_rsa /root/.ssh +cp -r /sshkey/.ssh/id_rsa.pub /root/.ssh +cp -r /sshkey/.ssh/authorized_keys /root/.ssh +chown root:root /root/.ssh +chown root:root /root/.ssh/id_rsa +chown root:root /root/.ssh/id_rsa.pub +chown root:root /root/.ssh/authorized_keys echo export LD_PRELOAD=$LD_PRELOAD >> /etc/default/ssh echo export VNET_PREFIX=$VNET_PREFIX >> /etc/default/ssh -chown root /root/.ssh/config + +%s + service ssh restart -while [ ! -f /job/finish ] ; do +while [ ! -f /opt/run_dist_job ] || [ ! -f /opt/run_dist_job.sh ]; do sleep 3 done -""" +chown root:root /root/.ssh/config +sleep infinity +""" % change_ssh_port_CMD @@ -474,8 +510,11 @@ def SubmitPSDistJob(job): random.seed(datetime.datetime.now()) - distJobParam["containerPort"] = int(random.random()*1000+3000) - + if "hostNetwork" in jobParams and jobParams["hostNetwork"]: + distJobParam["containerPort"] = ssh_ports[role][i] + else: + distJobParam["containerPort"] = int(random.random()*1000+3000) + if assignedRack is not None: if "nodeSelector" not in distJobParam: distJobParam["nodeSelector"] = {} @@ -642,7 +681,7 @@ def UpdateJobStatus(job): if job["jobStatus"] != "running": dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","running") - if "interactivePort" in jobParams: + if "interactivePort" in jobParams and ("hostNetwork" not in jobParams or not jobParams["hostNetwork"]): serviceAddress = k8sUtils.GetServiceAddress(job["jobId"]) serviceAddress = base64.b64encode(json.dumps(serviceAddress)) dataHandler.UpdateJobTextField(job["jobId"],"endpoints",serviceAddress) @@ -782,6 +821,7 @@ def launch_ps_dist_job(jobParams): jobId = jobParams["jobId"] workerPodInfo = k8sUtils.GetPod("distRole=worker,run=" + jobId) psPodInfo = k8sUtils.GetPod("distRole=ps,run=" + jobId) + ssh_endpoints =[] if "items" in workerPodInfo and len(workerPodInfo["items"]) == int(jobParams["numpsworker"]) and "items" in psPodInfo and len(psPodInfo["items"]) == int(jobParams["numps"]): podStatus = [k8sUtils.check_pod_status(pod) for pod in workerPodInfo["items"] + psPodInfo["items"] ] if all([status == "Running" for status in podStatus]): @@ -808,6 +848,42 @@ def launch_ps_dist_job(jobParams): ps_hosts = ",".join(["%s:%s" % (ps_pod_ips[i],ps_ports[i]) for i in range(ps_num)]) worker_hosts = ",".join(["%s:%s" % (worker_pod_ips[i],worker_ports[i]) for i in range(worker_num)]) + ps_hostnames = [pod["spec"]["nodeName"] for pod in psPodInfo["items"]] + worker_hostnames = [pod["spec"]["nodeName"] for pod in workerPodInfo["items"]] + + for h,ip,p in zip(ps_hostnames,ps_pod_ips,ps_ports): + hostName = h + if "." not in hostName and "domain" in config and (not config["domain"] is None) and len(config["domain"].strip()) >0: + hostName += "."+config["domain"] + ssh_endpoints.append( + { + "hostName":hostName, + "hostIP":ip, + "containerPort":22, + "hostPort":p, + "user":"root", + } + ) + + + for h,ip,p in zip(worker_hostnames,worker_pod_ips,worker_ports): + hostName = h + if "." not in hostName and "domain" in config and (not config["domain"] is None) and len(config["domain"].strip()) >0: + hostName += "."+config["domain"] + ssh_endpoints.append( + { + "hostName":hostName, + "hostIP":ip, + "containerPort":22, + "hostPort":p, + "user":"root", + } + ) + if "hostNetwork" in jobParams and jobParams["hostNetwork"]: + dataHandler = DataHandler() + serviceAddress = base64.b64encode(json.dumps(ssh_endpoints)) + dataHandler.UpdateJobTextField(jobParams["jobId"],"endpoints",serviceAddress) + ps_files = ["/tmp/" + str(uuid.uuid4()) for i in range(ps_num)] worker_files = ["/tmp/" + str(uuid.uuid4()) for i in range(worker_num)] @@ -815,6 +891,7 @@ def launch_ps_dist_job(jobParams): #worker_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=worker --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,worker_files[i]) for i in range(worker_num)] + ssh_config = """ Host %s HostName %s @@ -825,13 +902,11 @@ def launch_ps_dist_job(jobParams): """ + ps_cmd = ["%s 2>&1 | tee %s" % (jobParams["cmd"], ps_files[i]) for i in range(ps_num)] worker_cmd = ["%s 2>&1 | tee %s" % (jobParams["cmd"], worker_files[i]) for i in range(worker_num)] - #hostfilecontent = "" - #for workerip,workergpu in zip(worker_pod_ips,worker_gpu_num): - # hostfilecontent += "%s slots=%s\n" %(workerip,workergpu) @@ -842,24 +917,20 @@ def launch_ps_dist_job(jobParams): sshconfigstr = "" for i in range(ps_num): - sshconfigstr += (ssh_config %("master-"+str(i), ps_pod_ips[i] ,"22222") +"\n") + if "hostNetwork" in jobParams and jobParams["hostNetwork"]: + sshconfigstr += (ssh_config %("master-"+str(i), ps_pod_ips[i] ,str(ps_ports[i])) +"\n") + else: + sshconfigstr += (ssh_config %("master-"+str(i), ps_pod_ips[i] ,str(22)) +"\n") for i in range(worker_num): - sshconfigstr += (ssh_config %("worker-"+str(i), worker_pod_ips[i] ,"22222") +"\n") + if "hostNetwork" in jobParams and jobParams["hostNetwork"]: + sshconfigstr += (ssh_config %("worker-"+str(i), worker_pod_ips[i] ,str(worker_ports[i])) +"\n") + else: + sshconfigstr += (ssh_config %("worker-"+str(i), worker_pod_ips[i] ,str(22)) +"\n") error_flag = False for i in range(ps_num): - os.system("mkdir -p %s" % ps_files[i]) - psfile = os.path.join(ps_files[i],"run_dist_job.sh") - with open(psfile, 'w') as f: - f.write(ps_cmd[i] + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], psfile)) - remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (psfile,ps_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - os.system("mkdir -p %s" % ps_files[i]) psfile = os.path.join(ps_files[i],"hostfile") @@ -879,11 +950,11 @@ def launch_ps_dist_job(jobParams): f.close() #if "userId" in jobParams: # os.system("chown -R %s %s" % (jobParams["userId"], psfile)) - k8sUtils.kubectl_exec("exec %s mkdir -p /root/.ssh" % ps_pod_names[i]) + k8sUtils.kubectl_exec("exec %s 'mkdir -p /root/.ssh'" % ps_pod_names[i]) remotecmd = "cp %s %s:/root/.ssh/config" % (psfile,ps_pod_names[i]) - k8sUtils.kubectl_exec("exec %s chmod 600 /root/.ssh/config" % ps_pod_names[i]) - k8sUtils.kubectl_exec("exec %s chown root /root/.ssh/config" % ps_pod_names[i]) k8sUtils.kubectl_exec(remotecmd) + k8sUtils.kubectl_exec("exec %s 'chmod 400 /root/.ssh/config'" % ps_pod_names[i]) + k8sUtils.kubectl_exec("exec %s 'chown root:root /root/.ssh/config'" % ps_pod_names[i]) @@ -897,21 +968,10 @@ def launch_ps_dist_job(jobParams): remotecmd = "cp %s %s:/opt/taskindex" % (psfile,ps_pod_names[i]) k8sUtils.kubectl_exec(remotecmd) - k8sUtils.kubectl_exec("exec %s touch /opt/run_dist_job" % ps_pod_names[i]) - output = k8sUtils.kubectl_exec("exec %s ls /opt/run_dist_job" % ps_pod_names[i]) - if (output == ""): - error_flag = True + + for i in range(worker_num): - os.system("mkdir -p %s" % worker_files[i]) - workerfile = os.path.join(worker_files[i],"run_dist_job.sh") - with open(workerfile, 'w') as f: - f.write(worker_cmd[i] + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) - remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (workerfile,worker_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) os.system("mkdir -p %s" % worker_files[i]) @@ -932,11 +992,11 @@ def launch_ps_dist_job(jobParams): f.close() if "userId" in jobParams: os.system("chown -R %s %s" % (jobParams["userId"], psfile)) - k8sUtils.kubectl_exec("exec %s mkdir -p /root/.ssh" % worker_pod_names[i]) + k8sUtils.kubectl_exec("exec %s 'mkdir -p /root/.ssh'" % worker_pod_names[i]) remotecmd = "cp %s %s:/root/.ssh/config" % (psfile,worker_pod_names[i]) - k8sUtils.kubectl_exec("exec %s chmod 600 /root/.ssh/config" % worker_pod_names[i]) - k8sUtils.kubectl_exec("exec %s chown root /root/.ssh/config" % worker_pod_names[i]) k8sUtils.kubectl_exec(remotecmd) + k8sUtils.kubectl_exec("exec %s 'chmod 400 /root/.ssh/config'" % worker_pod_names[i]) + k8sUtils.kubectl_exec("exec %s 'chown root:root /root/.ssh/config'" % worker_pod_names[i]) os.system("mkdir -p %s" % worker_files[i]) @@ -949,11 +1009,44 @@ def launch_ps_dist_job(jobParams): remotecmd = "cp %s %s:/opt/taskindex" % (workerfile,worker_pod_names[i]) k8sUtils.kubectl_exec(remotecmd) - + + for i in range(worker_num): + os.system("mkdir -p %s" % worker_files[i]) + workerfile = os.path.join(worker_files[i],"run_dist_job.sh") + with open(workerfile, 'w') as f: + f.write(worker_cmd[i] + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) + remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (workerfile,worker_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + k8sUtils.kubectl_exec("exec %s touch /opt/run_dist_job" % worker_pod_names[i]) output = k8sUtils.kubectl_exec("exec %s ls /opt/run_dist_job" % worker_pod_names[i]) if (output == ""): - error_flag = True + error_flag = True + + + for i in range(ps_num): + os.system("mkdir -p %s" % ps_files[i]) + psfile = os.path.join(ps_files[i],"run_dist_job.sh") + with open(psfile, 'w') as f: + f.write(ps_cmd[i] + "\n") + f.close() + if "userId" in jobParams: + os.system("chown -R %s %s" % (jobParams["userId"], psfile)) + remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (psfile,ps_pod_names[i]) + k8sUtils.kubectl_exec(remotecmd) + + + k8sUtils.kubectl_exec("exec %s touch /opt/run_dist_job" % ps_pod_names[i]) + output = k8sUtils.kubectl_exec("exec %s ls /opt/run_dist_job" % ps_pod_names[i]) + if (output == ""): + error_flag = True + + + + if not error_flag: dataHandler = DataHandler() dataHandler.UpdateJobTextField(jobParams["jobId"],"jobStatus","running") diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobDetail.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobDetail.cshtml index 51b3907fa..8beb495eb 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobDetail.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobDetail.cshtml @@ -217,7 +217,12 @@ for (var i = 0; i < payload.endpoints.length; i++) { if (payload.endpoints[i].containerPort == 22) { - endpointsStr += "To access the container via SSH, use command: ssh -i @ViewData["workPath"]" + ".ssh/id_rsa -p " + payload.endpoints[i].hostPort + " @ViewData["Username"]" + "@@" + payload.endpoints[i].hostName + "\n"; + if (payload.endpoints[i].user) { + endpointsStr += "To access the container via SSH, use command: ssh -i @ViewData["workPath"]" + ".ssh/id_rsa -p " + payload.endpoints[i].hostPort + " " + payload.endpoints[i].user + "@@" + payload.endpoints[i].hostName + "\n"; + } + else { + endpointsStr += "To access the container via SSH, use command: ssh -i @ViewData["workPath"]" + ".ssh/id_rsa -p " + payload.endpoints[i].hostPort + " @ViewData["Username"]" + "@@" + payload.endpoints[i].hostName + "\n"; + } } else { var svcurl = "http://" + payload.endpoints[i].hostName + ":" + payload.endpoints[i].hostPort; From 2b802c0c80d89839e380db3beca573f3e27e6041 Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Wed, 27 Mar 2019 22:50:30 +0000 Subject: [PATCH 028/595] show the correct gpu# in distributed jobs --- src/WebUI/dotnet/WebPortal/Views/Home/ViewJobs.cshtml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/ViewJobs.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/ViewJobs.cshtml index 21cab9775..4108f9531 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/ViewJobs.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/ViewJobs.cshtml @@ -56,7 +56,12 @@ tr.append("" + job.jobStatus + " "); //GPU - tr.append("" + job.jobParams.resourcegpu + ""); + if (job.jobParams.numpsworker && job.jobParams.numpsworker > 0) { + tr.append("" + job.jobParams.resourcegpu * job.jobParams.numpsworker+ ""); + } + else { + tr.append("" + job.jobParams.resourcegpu + ""); + } //Username tr.append("" + job.userName + ""); //Submited Time From ebafb116f40fcab89da76d7a74879cbd2e6090e9 Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Fri, 29 Mar 2019 05:06:22 +0000 Subject: [PATCH 029/595] fixed a bug in mysql database, allow cluster to have large number of nodes --- src/utils/MySQLDataHandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index f41d7581a..2b96264e5 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -104,7 +104,7 @@ def CreateTable(self): CREATE TABLE IF NOT EXISTS `%s` ( `id` INT NOT NULL AUTO_INCREMENT, - `status` TEXT NOT NULL, + `status` LONGTEXT NOT NULL, `time` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, PRIMARY KEY (`id`) ) From 7a7d70d15459c5f61d1e73984f361b094a32db33 Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Fri, 29 Mar 2019 17:43:21 +0000 Subject: [PATCH 030/595] fixed a bug to show #GPU --- src/WebUI/dotnet/WebPortal/Views/Home/ViewJobs.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/ViewJobs.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/ViewJobs.cshtml index 4108f9531..248a1c443 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/ViewJobs.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/ViewJobs.cshtml @@ -56,7 +56,7 @@ tr.append("" + job.jobStatus + " "); //GPU - if (job.jobParams.numpsworker && job.jobParams.numpsworker > 0) { + if (job.jobParams.numpsworker && job.jobParams.numpsworker > 0 && job.jobParams.jobtrainingtype =="PSDistJob") { tr.append("" + job.jobParams.resourcegpu * job.jobParams.numpsworker+ ""); } else { From 331b52513b1fcbab9bc992777f82a6b19f2b61e8 Mon Sep 17 00:00:00 2001 From: Deepak Bansal Date: Tue, 2 Apr 2019 16:00:48 -0700 Subject: [PATCH 031/595] vc_storage --- src/ClusterBootstrap/deploy.py | 31 ++ .../template/RestfulAPI/config.yaml | 1 + src/RestAPI/dlwsrestapi.py | 414 +++++++++++++++- src/docker-images/RestfulAPI/Dockerfile | 52 +- src/utils/JobRestAPIUtils.py | 225 +++++++-- src/utils/MySQLDataHandler.py | 451 ++++++++++++++++-- src/utils/SQLDataHandler.py | 448 +++++++++++++++-- src/utils/authorization.py | 222 +++++++++ 8 files changed, 1721 insertions(+), 123 deletions(-) create mode 100644 src/utils/authorization.py diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index c618c17f4..5140e8bfe 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -17,6 +17,7 @@ import glob import copy import numbers +import requests from os.path import expanduser @@ -3130,6 +3131,36 @@ def run_command( args, command, nargs, parser ): print "Error: scan need one parameter with format x.x.x.x/n. " exit() + elif command == "admin": + if len(nargs) >= 1: + if nargs[0] == "vc": + if len(nargs) >= 2: + if nargs[1] == "add": + url = "http://%s:%s/AddVC?vcName=%s&resources=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3]) + response = requests.get(url) + print(response) + elif nargs[1] == "update": + url = "http://%s:%s/UpdateVC?vcName=%s&resources=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3]) + response = requests.get(url) + print(response) + elif nargs[1] == "delete": + url = "http://%s:%s/DeleteVC?vcName=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2]) + response = requests.get(url) + print(response) + elif nargs[1] == "get": + url = "http://%s:%s/GetVC?vcName=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2]) + response = requests.get(url) + print(response) + if nargs[0] == "acl": + if len(nargs) >= 2: + if nargs[1] == "update": + url = "http://%s:%s/UpdateAce?identityName=%s&resourceType=%s&resourceName=%s&permissions=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3], nargs[4], nargs[5]) + response = requests.get(url) + print(response) + elif nargs[1] == "get": + url = "http://%s:%s/GetACL?userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"]) + response = requests.get(url) + print(response) elif command == "updateworker": response = raw_input_with_default("Deploy Worker Nodes (y/n)?") diff --git a/src/ClusterBootstrap/template/RestfulAPI/config.yaml b/src/ClusterBootstrap/template/RestfulAPI/config.yaml index 384b3ab86..1624912fb 100755 --- a/src/ClusterBootstrap/template/RestfulAPI/config.yaml +++ b/src/ClusterBootstrap/template/RestfulAPI/config.yaml @@ -30,3 +30,4 @@ default-storage-folders : {{cnf["default-storage-folders"]}} webportal_node: {{cnf["webportal_node"]}} datasource : {{cnf["datasource"]}} kube_custom_scheduler: {{cnf["kube_custom_scheduler"]}} +WinbindServers: {{cnf["WinbindServers"]}} diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index c82aefff9..d7f7949f7 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -14,6 +14,7 @@ sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),"../utils")) #from JobRestAPIUtils import SubmitDistJob, GetJobList, GetJobStatus, DeleteJob, GetTensorboard, GetServiceAddress, GetLog, GetJob import JobRestAPIUtils +from authorization import ResourceType, Permission, AuthorizationManager from config import config from config import global_vars @@ -28,9 +29,15 @@ api = Api(app) verbose = True logger.info( "------------------- Restful API started ------------------------------------- ") - logger.info("%s" % config ) +if "initAdminAccess" not in global_vars or not global_vars["initAdminAccess"]: + logger.info("===========Init Admin Access===============") + global_vars["initAdminAccess"] = True + logger.info('setting admin access!') + AuthorizationManager.UpdateAce("Administrator", AuthorizationManager.GetResourceAclPath("", ResourceType.Cluster), Permission.Admin, False) + logger.info('admin access given!') + parser = reqparse.RequestParser() @@ -70,6 +77,7 @@ def get(self): parser.add_argument('logDir') parser.add_argument('interactivePort') parser.add_argument('userName') + parser.add_argument('vcName') parser.add_argument('userId') parser.add_argument('runningasroot') parser.add_argument('containerUserId') @@ -97,6 +105,8 @@ def get(self): if args["jobName"] is None or len(args["jobName"].strip()) == 0: ret["error"] = "job name cannot be empty" + elif args["vcName"] is None or len(args["vcName"].strip()) == 0: + ret["error"] = "vc name cannot be empty" elif args["resourcegpu"] is None or len(args["resourcegpu"].strip()) == 0: ret["error"] = "Number of GPU cannot be empty" elif args["dataPath"] is None or len(args["dataPath"].strip()) == 0: @@ -107,6 +117,7 @@ def get(self): ret["error"] = "jobType cannot be empty" else: params["jobName"] = args["jobName"] + params["vcName"] = args["vcName"] params["resourcegpu"] = args["resourcegpu"] params["workPath"] = args["workPath"] params["dataPath"] = args["dataPath"] @@ -231,6 +242,8 @@ def get(self): ## api.add_resource(SubmitJob, '/SubmitJob') + + class PostJob(Resource): def post(self): params = request.get_json(force=True) @@ -265,6 +278,8 @@ class ListJobs(Resource): def get(self): parser.add_argument('userName') parser.add_argument('num') + parser.add_argument('vcName') + parser.add_argument('jobOwner') args = parser.parse_args() num = None if args["num"] is not None: @@ -272,10 +287,8 @@ def get(self): num = int(args["num"]) except: pass - if args["userName"] is not None and len(args["userName"].strip()) > 0: - jobs = JobRestAPIUtils.GetJobList(args["userName"],num) - else: - jobs = [] + jobs = JobRestAPIUtils.GetJobList(args["userName"], args["vcName"], args["jobOwner"], num) + jobList = [] queuedJobs = [] runningJobs = [] @@ -310,7 +323,6 @@ def get(self): else: finishedJobs.append(job) - ret = {} ret["queuedJobs"] = queuedJobs ret["runningJobs"] = runningJobs @@ -322,7 +334,6 @@ def get(self): resp.headers["dataType"] = "json" return resp - ## ## Actually setup the Api resource routing here ## @@ -333,9 +344,11 @@ def get(self): class KillJob(Resource): def get(self): parser.add_argument('jobId') + parser.add_argument('userName') args = parser.parse_args() jobId = args["jobId"] - result = JobRestAPIUtils.KillJob(jobId) + userName = args["userName"] + result = JobRestAPIUtils.KillJob(userName, jobId) ret = {} if result: ret["result"] = "Success, the job is scheduled to be terminated." @@ -346,7 +359,6 @@ def get(self): resp.headers["Access-Control-Allow-Origin"] = "*" resp.headers["dataType"] = "json" - return resp ## ## Actually setup the Api resource routing here @@ -355,12 +367,92 @@ def get(self): +class PauseJob(Resource): + def get(self): + parser.add_argument('jobId') + parser.add_argument('userName') + args = parser.parse_args() + jobId = args["jobId"] + userName = args["userName"] + result = JobRestAPIUtils.PauseJob(userName, jobId) + ret = {} + if result: + ret["result"] = "Success, the job is scheduled to be paused." + else: + ret["result"] = "Cannot pause the job. Job ID:" + jobId + + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(PauseJob, '/PauseJob') + + + +class ResumeJob(Resource): + def get(self): + parser.add_argument('jobId') + parser.add_argument('userName') + args = parser.parse_args() + jobId = args["jobId"] + userName = args["userName"] + result = JobRestAPIUtils.ResumeJob(userName, jobId) + ret = {} + if result: + ret["result"] = "Success, the job is scheduled to be resumed." + else: + ret["result"] = "Cannot resume the job. Job ID:" + jobId + + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(ResumeJob, '/ResumeJob') + + + +class CloneJob(Resource): + def get(self): + parser.add_argument('jobId') + parser.add_argument('userName') + args = parser.parse_args() + jobId = args["jobId"] + userName = args["userName"] + result = JobRestAPIUtils.CloneJob(userName, jobId) + ret = {} + if result: + ret["result"] = "Success, the job is scheduled to be cloned." + else: + ret["result"] = "Cannot clone the job. Job ID:" + jobId + + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(CloneJob, '/CloneJob') + + + class ApproveJob(Resource): def get(self): parser.add_argument('jobId') + parser.add_argument('userName') args = parser.parse_args() jobId = args["jobId"] - result = JobRestAPIUtils.ApproveJob(jobId) + userName = args["userName"] + result = JobRestAPIUtils.ApproveJob(userName, jobId) ret = {} if result: ret["result"] = "Success, the job has been approved." @@ -371,7 +463,6 @@ def get(self): resp.headers["Access-Control-Allow-Origin"] = "*" resp.headers["dataType"] = "json" - return resp ## ## Actually setup the Api resource routing here @@ -383,9 +474,11 @@ def get(self): class GetCommands(Resource): def get(self): parser.add_argument('jobId') + parser.add_argument('userName') args = parser.parse_args() jobId = args["jobId"] - commands = JobRestAPIUtils.GetCommands(jobId) + userName = args["userName"] + commands = JobRestAPIUtils.GetCommands(userName, jobId) resp = jsonify(commands) resp.headers["Access-Control-Allow-Origin"] = "*" resp.headers["dataType"] = "json" @@ -401,9 +494,11 @@ def get(self): class GetJobDetail(Resource): def get(self): parser.add_argument('jobId') + parser.add_argument('userName') args = parser.parse_args() jobId = args["jobId"] - job = JobRestAPIUtils.GetJobDetail(jobId) + userName = args["userName"] + job = JobRestAPIUtils.GetJobDetail(userName, jobId) job["jobParams"] = json.loads(base64.b64decode(job["jobParams"])) if "endpoints" in job and job["endpoints"] is not None and (job["endpoints"].strip()) > 0: job["endpoints"] = json.loads(base64.b64decode(job["endpoints"])) @@ -428,6 +523,9 @@ def get(self): class GetClusterStatus(Resource): def get(self): + parser.add_argument('userName') + args = parser.parse_args() + userName = args["userName"] cluster_status, last_updated_time = JobRestAPIUtils.GetClusterStatus() cluster_status["last_updated_time"] = last_updated_time resp = jsonify(cluster_status) @@ -441,15 +539,36 @@ def get(self): api.add_resource(GetClusterStatus, '/GetClusterStatus') +class GetVCStatus(Resource): # Todo + def get(self): + parser.add_argument('vcName') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + userName = args["userName"] + vc_status, last_updated_time = JobRestAPIUtils.GetVCStatus(userName, vcName) + vc_status["last_updated_time"] = last_updated_time + resp = jsonify(vc_status) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(GetVCStatus, '/GetVCStatus') + class AddCommand(Resource): def get(self): parser.add_argument('jobId') parser.add_argument('command') + parser.add_argument('userName') args = parser.parse_args() + userName = args["userName"] jobId = args["jobId"] command = args["command"] - result = JobRestAPIUtils.AddCommand(jobId, command) + result = JobRestAPIUtils.AddCommand(userName, jobId, command) ret = {} if result: ret["result"] = "Success, the command is scheduled to be run." @@ -488,7 +607,274 @@ def get(self): api.add_resource(AddUser, '/AddUser') +class UpdateAce(Resource): + def get(self): + parser.add_argument('userName') + parser.add_argument('identityName') + parser.add_argument('resourceType') + parser.add_argument('resourceName') + parser.add_argument('permissions') + args = parser.parse_args() + username = args["userName"] + identityName = str(args["identityName"]) + resourceType = int(args["resourceType"]) + resourceName = str(args["resourceName"]) + permissions = int(args["permissions"]) + ret = {} + ret["result"] = JobRestAPIUtils.UpdateAce(username, identityName, resourceType, resourceName, permissions) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(UpdateAce, '/UpdateAce') + + +class DeleteAce(Resource): + def get(self): + parser.add_argument('userName') + parser.add_argument('identityName') + parser.add_argument('resourceType') + parser.add_argument('resourceName') + args = parser.parse_args() + username = args["userName"] + identityName = str(args["identityName"]) + resourceType = int(args["resourceType"]) + resourceName = str(args["resourceName"]) + ret = {} + ret["result"] = JobRestAPIUtils.DeleteAce(username, identityName, resourceType, resourceName) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(DeleteAce, '/DeleteAce') + + +class IsClusterAdmin(Resource): + def get(self): + parser.add_argument('userName') + args = parser.parse_args() + username = args["userName"] + ret = {} + ret["result"] = AuthorizationManager.IsClusterAdmin(username) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(IsClusterAdmin, '/IsClusterAdmin') + + +class GetACL(Resource): + def get(self): + parser.add_argument('userName') + args = parser.parse_args() + username = args["userName"] + ret = {} + ret["result"] = AuthorizationManager.GetAcl(username) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(GetACL, '/GetACL') + + +class ListVCs(Resource): + def get(self): + parser.add_argument('userName') + args = parser.parse_args() + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.ListVCs(userName) + + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp + +## +## Actually setup the Api resource routing here +## +api.add_resource(ListVCs, '/ListVCs') + + +class AddVC(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('resources') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + resources = args["resources"] + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.AddVC(userName, vcName, resources) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(AddVC, '/AddVC') + + +class DeleteVC(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.DeleteVC(userName, vcName) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(DeleteVC, '/DeleteVC') + + +class UpdateVC(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('resources') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + resources = args["resources"] + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.UpdateVC(userName, vcName, resources) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(UpdateVC, '/UpdateVC') + + +class ListStorages(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.ListStorages(userName, vcName) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(ListStorages, '/ListStorages') + + +class AddStorage(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('storageType') + parser.add_argument('url') + parser.add_argument('description') + parser.add_argument('defaultMountPath') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + storageType = args["storageType"] + url = args["url"] + description = args["description"] + defaultMountPath = args["defaultMountPath"] + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.AddStorage(userName, vcName, url, storageType, description, defaultMountPath) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(AddStorage, '/AddStorage') + + +class DeleteStorage(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('userName') + parser.add_argument('url') + args = parser.parse_args() + vcName = args["vcName"] + userName = args["userName"] + url = args["url"] + ret = {} + ret["result"] = JobRestAPIUtils.DeleteStorage(userName, vcName, url) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(DeleteStorage, '/DeleteStorage') + + +class UpdateStorage(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('storageType') + parser.add_argument('url') + parser.add_argument('description') + parser.add_argument('defaultMountPath') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + storageType = args["storageType"] + url = args["url"] + description = args["description"] + defaultMountPath = args["defaultMountPath"] + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.UpdateStorage(userName, vcName, url, storageType, description, defaultMountPath) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(UpdateStorage, '/UpdateStorage') if __name__ == '__main__': app.run(debug=False,host="0.0.0.0",threaded=True) + diff --git a/src/docker-images/RestfulAPI/Dockerfile b/src/docker-images/RestfulAPI/Dockerfile index f80ab76f5..5af0e0e98 100755 --- a/src/docker-images/RestfulAPI/Dockerfile +++ b/src/docker-images/RestfulAPI/Dockerfile @@ -1,6 +1,56 @@ -FROM dlws/restfulapi:v1.4 +FROM ubuntu:16.04 MAINTAINER Hongzhi Li +# See https://stackoverflow.com/questions/37706635/in-docker-apt-get-install-fails-with-failed-to-fetch-http-archive-ubuntu-com +# It is a good practice to merge apt-get update with the following apt-get install +RUN apt-get update; +RUN apt-get update; apt-get install -y --no-install-recommends apt-transport-https \ + build-essential \ + cmake \ + git \ + wget \ + vim \ + python-dev \ + python-pip \ + python-yaml \ + locales \ + python-pycurl \ + bison \ + curl \ + nfs-common \ + apt-utils + + +RUN pip install --upgrade pip; + +RUN pip install setuptools; +RUN locale-gen en_US.UTF-8 +RUN update-locale LANG=en_US.UTF-8 + +RUN pip install flask +RUN pip install flask.restful +RUN pip install requests + +RUN wget http://ccsdatarepo.westus.cloudapp.azure.com/data/tools/mysql-connector-python_2.1.7-1ubuntu16.04_all.deb -O /mysql-connector-python_2.1.7-1ubuntu16.04_all.deb +RUN dpkg -i /mysql-connector-python_2.1.7-1ubuntu16.04_all.deb +RUN apt-get install -y libmysqlclient-dev mysql-connector-python + + +# Install python for Azure SQL + +RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - + +RUN curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list > /etc/apt/sources.list.d/mssql-release.list + +RUN apt-get update; ACCEPT_EULA=Y apt-get install -y msodbcsql=13.1.4.0-1 unixodbc-dev + + +RUN pip install pyodbc +RUN pip install tzlocal +RUN apt-get update; apt-get install -y --no-install-recommends ssh apache2 libapache2-mod-wsgi sudo +RUN usermod -a -G sudo www-data +RUN echo "\nwww-data ALL=(ALL) NOPASSWD:ALL\n" > /etc/sudoers + COPY kubectl /usr/local/bin/kubectl RUN chmod +x /usr/local/bin/kubectl #COPY gittoken /root/.ssh/id_rsa diff --git a/src/utils/JobRestAPIUtils.py b/src/utils/JobRestAPIUtils.py index 38d6d920a..42b17fa9b 100755 --- a/src/utils/JobRestAPIUtils.py +++ b/src/utils/JobRestAPIUtils.py @@ -18,6 +18,7 @@ from config import global_vars from MyLogger import MyLogger +from authorization import ResourceType, Permission, AuthorizationManager import copy @@ -36,7 +37,9 @@ def SubmitJob(jobParamsJsonStr): if "jobName" not in jobParams or len(jobParams["jobName"].strip()) == 0: ret["error"] = "ERROR: Job name cannot be empty" return ret - + if "vcName" not in jobParams or len(jobParams["vcName"].strip()) == 0: + ret["error"] = "ERROR: VC name cannot be empty" + return ret if "jobId" not in jobParams or jobParams["jobId"] == "": #jobParams["jobId"] = jobParams["jobName"] + "-" + str(uuid.uuid4()) @@ -65,6 +68,10 @@ def SubmitJob(jobParamsJsonStr): if "/" in userName: userName = userName.split("/")[1].strip() + if not AuthorizationManager.HasAccess(jobParams["userName"], ResourceType.VC, jobParams["vcName"].strip(), Permission.User): + ret["error"] = "Access Denied!" + return ret + if "cmd" not in jobParams: jobParams["cmd"] = "" @@ -176,70 +183,80 @@ def SubmitJob(jobParamsJsonStr): -def GetJobList(userName,num=None): +def GetJobList(userName, vcName, jobOwner, num=None): try: dataHandler = DataHandler() jobs = [] + hasAccessOnAllJobs = False + + if AuthorizationManager.HasAccess(userName, ResourceType.VC, vcName, Permission.Collaborator): + hasAccessOnAllJobs = True - if userName != "all": - jobs = jobs + dataHandler.GetJobList(userName,None, "running,queued,scheduling,unapproved", ("=","or")) - jobs = jobs + dataHandler.GetJobList(userName,num, "running,queued,scheduling,unapproved", ("<>","and")) + if jobOwner != "all" or not hasAccessOnAllJobs: + jobs = jobs + dataHandler.GetJobList(userName,vcName,None, "running,queued,scheduling,unapproved", ("=","or")) + jobs = jobs + dataHandler.GetJobList(userName,vcName,num, "running,queued,scheduling,unapproved", ("<>","and")) else: - jobs = dataHandler.GetJobList(userName,None, "error,failed,finished,killed", ("<>","and")) + jobs = dataHandler.GetJobList(jobOwner,vcName,None, "error,failed,finished,killed", ("<>","and")) for job in jobs: job.pop('jobMeta', None) dataHandler.Close() return jobs - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) logger.warn("Fail to get job list for user %s, return empty list" % userName) return [] -def GetCommands(jobId): +def GetCommands(userName, jobId): + commands = [] dataHandler = DataHandler() - commands = dataHandler.GetCommands(jobId=jobId); + jobs = dataHandler.GetJob(jobId=jobId) + if jobs[0]["userName"] == userName or AuthorizationManager.HasAccess(userName, ResourceType.VC, jobs[0]["vcName"], Permission.Collaborator): + commands = dataHandler.GetCommands(jobId=jobId) dataHandler.Close() return commands -def KillJob(jobId): - ret = True +def KillJob(userName, jobId): + ret = False dataHandler = DataHandler() jobs = dataHandler.GetJob(jobId=jobId) if len(jobs) == 1: job = jobs[0] - if job["isParent"] == 1: - for currJob in dataHandler.GetJob(familyToken=job["familyToken"]): - ret = ret and dataHandler.KillJob(currJob["jobId"]) - else: - ret = dataHandler.KillJob(jobId) - else: - ret = False + if job["userName"] == userName or AuthorizationManager.HasAccess(userName, ResourceType.VC, job["vcName"], Permission.Admin): + if job["isParent"] == 1: + ret = True + for currJob in dataHandler.GetJob(familyToken=job["familyToken"]): + ret = ret and dataHandler.KillJob(currJob["jobId"]) + else: + ret = dataHandler.KillJob(jobId) dataHandler.Close() return ret -def AddCommand(jobId,command): +def AddCommand(userName, jobId,command): dataHandler = DataHandler() ret = False jobs = dataHandler.GetJob(jobId=jobId) if len(jobs) == 1: - ret = dataHandler.AddCommand(jobId,command) + if jobs[0]["userName"] == userName or AuthorizationManager.HasAccess(userName, ResourceType.VC, jobs[0]["vcName"], Permission.Collaborator): + ret = dataHandler.AddCommand(jobId,command) dataHandler.Close() return ret -def ApproveJob(jobId): +def ApproveJob(userName, jobId): dataHandler = DataHandler() ret = False jobs = dataHandler.GetJob(jobId=jobId) if len(jobs) == 1: - ret = dataHandler.ApproveJob(jobId) + if AuthorizationManager.HasAccess(userName, ResourceType.VC, jobs[0]["vcName"], Permission.Admin): + ret = dataHandler.ApproveJob(jobId) dataHandler.Close() return ret + def isBase64(s): try: if base64.b64encode(base64.b64decode(s)) == s: @@ -248,41 +265,43 @@ def isBase64(s): pass return False -def GetJobDetail(jobId): + +def GetJobDetail(userName, jobId): job = None dataHandler = DataHandler() jobs = dataHandler.GetJob(jobId=jobId) if len(jobs) == 1: - job = jobs[0] - job["log"] = "" - #jobParams = json.loads(base64.b64decode(job["jobMeta"])) - #jobPath,workPath,dataPath = GetStoragePath(jobParams["jobPath"],jobParams["workPath"],jobParams["dataPath"]) - #localJobPath = os.path.join(config["storage-mount-path"],jobPath) - #logPath = os.path.join(localJobPath,"joblog.txt") - #print logPath - #if os.path.isfile(logPath): - # with open(logPath, 'r') as f: - # log = f.read() - # job["log"] = log - # f.close() - if "jobDescription" in job: - job.pop("jobDescription",None) - try: - log = dataHandler.GetJobTextField(jobId,"jobLog") + if jobs[0]["userName"] == userName or AuthorizationManager.HasAccess(userName, ResourceType.VC, jobs[0]["vcName"], Permission.Collaborator): + job = jobs[0] + job["log"] = "" + #jobParams = json.loads(base64.b64decode(job["jobMeta"])) + #jobPath,workPath,dataPath = GetStoragePath(jobParams["jobPath"],jobParams["workPath"],jobParams["dataPath"]) + #localJobPath = os.path.join(config["storage-mount-path"],jobPath) + #logPath = os.path.join(localJobPath,"joblog.txt") + #print logPath + #if os.path.isfile(logPath): + # with open(logPath, 'r') as f: + # log = f.read() + # job["log"] = log + # f.close() + if "jobDescription" in job: + job.pop("jobDescription",None) try: - if isBase64(log): - log = base64.b64decode(log) - except Exception: - pass - if log is not None: - job["log"] = log - except: - job["log"] = "fail-to-get-logs" + log = dataHandler.GetJobTextField(jobId,"jobLog") + try: + if isBase64(log): + log = base64.b64decode(log) + except Exception: + pass + if log is not None: + job["log"] = log + except: + job["log"] = "fail-to-get-logs" dataHandler.Close() return job -def GetClusterStatus(): +def GetClusterStatus(): #todo : access check job = None dataHandler = DataHandler() cluster_status,last_update_time = dataHandler.GetClusterStatus() @@ -290,7 +309,7 @@ def GetClusterStatus(): return cluster_status,last_update_time -def AddUser(username,userId): +def AddUser(username,userId): # todo : access check? ret = None dataHandler = DataHandler() ret = dataHandler.AddUser(username,userId) @@ -298,6 +317,114 @@ def AddUser(username,userId): return ret +def UpdateAce(userName, identityName, resourceType, resourceName, permissions): + ret = None + resourceAclPath = AuthorizationManager.GetResourceAclPath(resourceName, resourceType) + if AuthorizationManager.HasAccess(userName, resourceType, resourceName, Permission.Admin): + ret = AuthorizationManager.UpdateAce(identityName, resourceAclPath, permissions, False) + else: + ret = "Access Denied!" + return ret + + +def DeleteAce(userName, identityName, resourceType, resourceName): + ret = None + resourceAclPath = AuthorizationManager.GetResourceAclPath(resourceName, resourceType) + if AuthorizationManager.HasAccess(userName, resourceType, resourceName, Permission.Admin): + ret = AuthorizationManager.DeleteAce(identityName, resourceAclPath) + else: + ret = "Access Denied!" + return ret + + +def AddStorage(userName, vcName, url, storageType, description, defaultMountPath): + ret = None + dataHandler = DataHandler() + if AuthorizationManager.IsClusterAdmin(userName): + ret = dataHandler.AddStorage(vcName, url, storageType, description, defaultMountPath) + else: + ret = "Access Denied!" + dataHandler.Close() + return ret + + +def ListStorages(userName, vcName): + ret = [] + dataHandler = DataHandler() + if AuthorizationManager.HasAccess(userName, ResourceType.VC, vcName, Permission.User): + ret = dataHandler.ListStorages(vcName) + dataHandler.Close() + return ret + + +def DeleteStorage(userName, vcName, url): + ret = None + dataHandler = DataHandler() + if AuthorizationManager.HasAccess(userName, ResourceType.VC, vcName, Permission.Admin): + ret = dataHandler.DeleteStorage(vcName, url) + else: + ret = "Access Denied!" + dataHandler.Close() + return ret + + +def UpdateStorage(userName, vcName, url, storageType, description, defaultMountPath): + ret = None + dataHandler = DataHandler() + if AuthorizationManager.HasAccess(userName, ResourceType.VC, vcName, Permission.Admin): + ret = dataHandler.UpdateStorage(vcName, url, storageType, description, defaultMountPath) + else: + ret = "Access Denied!" + dataHandler.Close() + return ret + + +def AddVC(userName, vcName, resources): + ret = None + dataHandler = DataHandler() + if AuthorizationManager.IsClusterAdmin(userName): + ret = dataHandler.AddVC(vcName, resources) + else: + ret = "Access Denied!" + dataHandler.Close() + return ret + + +def ListVCs(userName): + ret = [] + dataHandler = DataHandler() + vcList = dataHandler.ListVCs() + for vc in vcList: + if AuthorizationManager.HasAccess(userName, ResourceType.VC, vc["vcName"], Permission.User): + # todo : get other info (resource consumption, quota etc.) about VC? + ret.append(vc) + # web portal (client) can filter out Default VC + dataHandler.Close() + return ret + + +def DeleteVC(userName, vcName): + ret = None + dataHandler = DataHandler() + if AuthorizationManager.IsClusterAdmin(userName): + ret = dataHandler.DeleteVC(vcName) + else: + ret = "Access Denied!" + dataHandler.Close() + return ret + + +def UpdateVC(userName, vcName, resources): + ret = None + dataHandler = DataHandler() + if AuthorizationManager.IsClusterAdmin(userName): + ret = dataHandler.UpdateVC(vcName, resources) + else: + ret = "Access Denied!" + dataHandler.Close() + return ret + + if __name__ == '__main__': TEST_SUB_REG_JOB = False TEST_JOB_STATUS = True diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index f41d7581a..13aaf279a 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -19,6 +19,8 @@ class DataHandler: def __init__(self): start_time = timeit.default_timer() + + logger.info ("DataHandler::Init") self.CreateDatabase() server = config["mysql"]["hostname"] @@ -28,17 +30,21 @@ def __init__(self): self.conn = mysql.connector.connect(user=username, password=password, host=server,database=database) - logger.debug ("Get database connection %s" % str(self.conn)) + logger.info ("Get database connection %s" % str(self.conn)) #print "Connecting to server ..." self.jobtablename = "jobs" self.usertablename = "users" + self.identitytablename = "identity" + self.acltablename = "acl" + self.vctablename = "vc" + self.storagetablename = "storage" self.clusterstatustablename = "clusterstatus" self.commandtablename = "commands" self.CreateTable() elapsed = timeit.default_timer() - start_time - logger.debug ("DataHandler initialization, time elapsed %f s" % elapsed) + logger.info ("DataHandler initialization, time elapsed %f s" % elapsed) @@ -53,7 +59,7 @@ def CreateDatabase(self): password = config["mysql"]["password"] conn = mysql.connector.connect(user=username, password=password, - host=server) + host=server) sql = " CREATE DATABASE IF NOT EXISTS `%s` DEFAULT CHARACTER SET 'utf8' " % (database) cursor = conn.cursor() cursor.execute(sql) @@ -74,6 +80,7 @@ def CreateTable(self): `isParent` INT NOT NULL, `jobName` varchar(1024) NOT NULL, `userName` varchar(255) NOT NULL, + `vcName` varchar(255) NOT NULL, `jobStatus` varchar(255) NOT NULL DEFAULT 'unapproved', `jobStatusDetail` LONGTEXT NULL, `jobType` varchar(255) NOT NULL, @@ -87,6 +94,7 @@ def CreateTable(self): `jobLog` LONGTEXT NULL, `retries` int NULL DEFAULT 0, PRIMARY KEY (`id`), + UNIQUE(`jobId`), INDEX (`userName`), INDEX (`jobTime`), INDEX (`jobId`), @@ -152,42 +160,423 @@ def CreateTable(self): cursor.close() + sql = """ + CREATE TABLE IF NOT EXISTS `%s` + ( + `id` INT NOT NULL AUTO_INCREMENT, + `storageType` varchar(255) NOT NULL, + `url` varchar(255) NOT NULL, + `description` TEXT NOT NULL, + `vcName` varchar(255) NOT NULL, + `defaultMountPath` varchar(255) NOT NULL, + `time` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT vc_url UNIQUE(`vcName`,`url`), + CONSTRAINT vc_mountPath UNIQUE(`vcName`,`defaultMountPath`) + ) + """ % (self.storagetablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + sql = """ + CREATE TABLE IF NOT EXISTS `%s` + ( + `id` INT NOT NULL AUTO_INCREMENT, + `vcName` varchar(255) NOT NULL UNIQUE, + `resources` varchar(255) NOT NULL, + `time` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + PRIMARY KEY (`id`) + ) + """ % (self.vctablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + sql = """ + CREATE TABLE IF NOT EXISTS `%s` + ( + `id` INT NOT NULL AUTO_INCREMENT, + `identityName` varchar(255) NOT NULL UNIQUE, + `identityId` INT NOT NULL, + `groups` MEDIUMTEXT NOT NULL, + `time` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + PRIMARY KEY (`id`) + ) + """ % (self.identitytablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + sql = """ + CREATE TABLE IF NOT EXISTS `%s` + ( + `id` INT NOT NULL AUTO_INCREMENT, + `identityName` varchar(255) NOT NULL, + `identityId` INT NOT NULL, + `resource` varchar(255) NOT NULL, + `permissions` INT NOT NULL, + `isDeny` INT NOT NULL, + `time` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT identityName_resource UNIQUE(`identityName`,`resource`) + ) + """ % (self.acltablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + def AddStorage(self, vcName, url, storageType, description, defaultMountPath): + try: + start_time = timeit.default_timer() + sql = "INSERT INTO `"+self.storagetablename+"` (storageType, url, description, vcName, defaultMountPath) VALUES (%s,%s,%s,%s,%s)" + cursor = self.conn.cursor() + cursor.execute(sql, (storageType, url, description, vcName, defaultMountPath)) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: AddStorage to DB: url : %s, vcName: %s , time elapsed %f s" % (url, vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def DeleteStorage(self, url, vcName): + try: + start_time = timeit.default_timer() + sql = "DELETE FROM `%s` WHERE url = '%s' and vcName = '%s'" % (self.storagetablename, url, vcName) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteStorage: url:%s, vcName:%s, time elapsed %f s" % (url, vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def ListStorages(self, vcName): + start_time = timeit.default_timer() + cursor = self.conn.cursor(dictionary=True) + query = "SELECT `vcName`,`url`,`storageType`,`description`,`defaultMountPath` FROM `%s` WHERE vcName = '%s' " % (self.storagetablename, vcName) + ret = [] + try: + cursor.execute(query) + for (vcName,url,storageType,description,defaultMountPath) in cursor: + record = {} + record["vcName"] = vcName + record["url"] = url + record["storageType"] = storageType + record["description"] = description + record["defaultMountPath"] = defaultMountPath + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: ListStorages time elapsed %f s" % (elapsed)) + return ret + + + def UpdateStorage(self, vcName, url, storageType, description, defaultMountPath): + try: + start_time = timeit.default_timer() + sql = """update `%s` set storageType = '%s', description = '%s', defaultMountPath = '%s' where vcName = '%s' and url = '%s' """ % (self.storagetablename, storageType, description, defaultMountPath, vcName, url) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateStorage: vcName: %s, url: %s, time elapsed %f s" % (vcName, url, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def AddVC(self, vcName, resources): + try: + start_time = timeit.default_timer() + sql = "INSERT INTO `"+self.vctablename+"` (vcName, resources) VALUES (%s,%s)" + cursor = self.conn.cursor() + cursor.execute(sql, (vcName, resources)) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: AddVC to DB: vcName: %s , time elapsed %f s" % (vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def ListVCs(self): + start_time = timeit.default_timer() + cursor = self.conn.cursor(dictionary=True) + query = "SELECT `vcName`,`resources` FROM `%s`" % (self.vctablename) + ret = [] + try: + cursor.execute(query) + for (vcName,resources) in cursor: + record = {} + record["vcName"] = vcName + record["resources"] = resources + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: ListVCs time elapsed %f s" % (elapsed)) + return ret + + + def DeleteVC(self, vcName): + try: + start_time = timeit.default_timer() + sql = "DELETE FROM `%s` WHERE vcName = '%s'" % (self.vctablename, vcName) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteVC: vcName: %s , time elapsed %f s" % (vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def UpdateVC(self, vcName, resources): + try: + start_time = timeit.default_timer() + sql = """update `%s` set resources = '%s' where vcName = '%s' """ % (self.vctablename, resources, vcName) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateVC: vcName: %s , time elapsed %f s" % (vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def GetIdentityInfo(self, identityName): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT `identityName`,`identityId`,`groups` FROM `%s` where `identityName` = '%s'" % (self.identitytablename, identityName) + ret = [] + try: + cursor.execute(query) + for (identityName,identityId,groups) in cursor: + record = {} + record["identityName"] = identityName + record["identityId"] = identityId + record["groups"] = groups + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetIdentityInfo time elapsed %f s" % (elapsed)) + return ret + + + def UpdateIdentityInfo(self, identityName, identityId, groups): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + if len(self.GetIdentityInfo(identityName)) == 0: + sql = "INSERT INTO `"+self.identitytablename+"` (identityName,identityId,groups) VALUES (%s,%s,%s)" + cursor.execute(sql, (identityName, identityId, groups)) + else: + sql = """update `%s` set groups = '%s' where `identityName` = '%s' and `identityId` = '%s' """ % (self.identitytablename, groups, identityName, identityId) + cursor.execute(sql) + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateIdentityInfo %s to database , time elapsed %f s" % (identityName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def GetAceCount(self, identityName, resource): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT count(ALL id) as c FROM `%s` where `identityName` = '%s' and `resource` = '%s'" % (self.acltablename,identityName, resource) + cursor.execute(query) + ret = 0 + for c in cursor: + ret = c[0] + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetAceCount time elapsed %f s" % ( elapsed)) + return ret + + + def UpdateAce(self, identityName, identityId, resource, permissions, isDeny): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + existingAceCount = self.GetAceCount(identityName, resource) + logger.info(existingAceCount) + + if existingAceCount == 0: + sql = "INSERT INTO `"+self.acltablename+"` (identityName,identityId,resource,permissions,isDeny) VALUES (%s,%s,%s,%s,%s)" + cursor.execute(sql, (identityName, identityId, resource, permissions, isDeny)) + else: + sql = """update `%s` set permissions = '%s' where `identityName` = '%s' and `resource` = '%s' """ % (self.acltablename, permissions, identityName, resource) + cursor.execute(sql) + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateAce %s - %s to database , time elapsed %f s" % (identityName, resource, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def DeleteResourceAcl(self, resource): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + sql = "DELETE FROM `%s` WHERE `resource` = '%s'" % (self.acltablename, resource) + cursor.execute(sql) + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteResourceAcl %s, time elapsed %f s" % (resource, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def DeleteAce(self, identityName, resource): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + sql = "DELETE FROM `%s` WHERE `identityName` = '%s' and `resource` = '%s'" % (self.acltablename, identityName, resource) + cursor.execute(sql) + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteAce %s : %s time elapsed %f s" % (resource, identityName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def GetAcl(self): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT `identityName`,`identityId`,`resource`,`permissions`,`isDeny` FROM `%s`" % (self.acltablename) + ret = [] + try: + cursor.execute(query) + for (identityName,identityId,resource,permissions,isDeny) in cursor: + record = {} + record["identityName"] = identityName + record["identityId"] = identityId + record["resource"] = resource + record["permissions"] = permissions + record["isDeny"] = isDeny + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetAcl time elapsed %f s" % ( elapsed)) + return ret + + + def GetResourceAcl(self, resource): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT `identityName`,`identityId`,`resource`,`permissions`,`isDeny` FROM `%s` where `resource` = '%s'" % (self.acltablename, resource) + ret = [] + try: + cursor.execute(query) + for (identityName,identityId,resource,permissions,isDeny) in cursor: + record = {} + record["identityName"] = identityName + record["identityId"] = identityId + record["resource"] = resource + record["permissions"] = permissions + record["isDeny"] = isDeny + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetResourceAcl time elapsed %f s" % ( elapsed)) + return ret + + def AddJob(self, jobParams): try: start_time = timeit.default_timer() - sql = "INSERT INTO `"+self.jobtablename+"` (jobId, familyToken, isParent, jobName, userName, jobType,jobParams ) VALUES (%s,%s,%s,%s,%s,%s,%s)" + sql = "INSERT INTO `"+self.jobtablename+"` (jobId, familyToken, isParent, jobName, userName, vcName, jobType,jobParams ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)" cursor = self.conn.cursor() jobParam = base64.b64encode(json.dumps(jobParams)) - cursor.execute(sql, (jobParams["jobId"], jobParams["familyToken"], jobParams["isParent"], jobParams["jobName"], jobParams["userName"], jobParams["jobType"],jobParam)) + cursor.execute(sql, (jobParams["jobId"], jobParams["familyToken"], jobParams["isParent"], jobParams["jobName"], jobParams["userName"], jobParams["vcName"], jobParams["jobType"],jobParam)) self.conn.commit() cursor.close() elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: added job %s to database, time elapsed %f s" % (jobParams["jobId"],elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False - def GetJobList(self, userName, num = None, status = None, op = ("=","or")): + def GetJobList(self, userName, vcName, num = None, status = None, op = ("=","or")): start_time = timeit.default_timer() ret = [] cursor = self.conn.cursor() try: - query = "SELECT `jobId`,`jobName`,`userName`, `jobStatus`, `jobStatusDetail`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s`" % (self.jobtablename) + query = "SELECT `jobId`,`jobName`,`userName`, 'vcName', `jobStatus`, `jobStatusDetail`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `vcName` = '%s'" % (self.jobtablename, vcName) if userName != "all": - query += " where `userName` = '%s'" % userName - else: - query += " where `id` > -1 " + query += " and `userName` = '%s'" % userName if status is not None: if "," not in status: query += " and `jobStatus` %s '%s'" % (op[0],status) else: status_list = [ " `jobStatus` %s '%s' " % (op[0],s) for s in status.split(',')] status_statement = (" "+op[1]+" ").join(status_list) - query += " and ( %s ) " % status_statement - - + query += " and ( %s ) " % status_statement query += " order by `jobTime` Desc" @@ -201,11 +590,12 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): data = cursor.fetchall() elapsed2 = timeit.default_timer() - start_time2 logger.info ("(fetchall time: %f)" % (elapsed2)) - for (jobId,jobName,userName, jobStatus,jobStatusDetail, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in data: + for (jobId,jobName,userName, vcName, jobStatus,jobStatusDetail, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in data: record = {} record["jobId"] = jobId record["jobName"] = jobName record["userName"] = userName + record["vcName"] = vcName record["jobStatus"] = jobStatus record["jobStatusDetail"] = jobStatusDetail record["jobType"] = jobType @@ -217,7 +607,7 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): record["errorMsg"] = errorMsg record["jobMeta"] = jobMeta ret.append(record) - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) pass cursor.close() @@ -228,14 +618,14 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): def GetJob(self, **kwargs): start_time = timeit.default_timer() - valid_keys = ["jobId", "familyToken", "isParent", "jobName", "userName", "jobStatus", "jobType", "jobTime"] + valid_keys = ["jobId", "familyToken", "isParent", "jobName", "userName", "vcName", "jobStatus", "jobType", "jobTime"] if len(kwargs) != 1: return [] key, expected = kwargs.popitem() if key not in valid_keys: logger.error("DataHandler_GetJob: key is not in valid keys list...") return [] cursor = self.conn.cursor() - query = "SELECT `jobId`,`familyToken`,`isParent`,`jobName`,`userName`, `jobStatus`, `jobStatusDetail`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `%s` = '%s' " % (self.jobtablename,key,expected) + query = "SELECT `jobId`,`familyToken`,`isParent`,`jobName`,`userName`, 'vcName', `jobStatus`, `jobStatusDetail`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `%s` = '%s' " % (self.jobtablename,key,expected) cursor.execute(query) columns = [column[0] for column in cursor.description] ret = [dict(zip(columns, row)) for row in cursor.fetchall()] @@ -256,7 +646,7 @@ def AddCommand(self,jobId,command): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: add command to database, jobId: %s , time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -290,7 +680,7 @@ def FinishCommand(self,commandId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: set command %s as finished , time elapsed %f s" % (commandId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -325,7 +715,7 @@ def KillJob(self,jobId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: mark job %s to be killed in database, time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -341,7 +731,7 @@ def ApproveJob(self,jobId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: approved job %s , time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -349,14 +739,15 @@ def ApproveJob(self,jobId): def GetPendingJobs(self): start_time = timeit.default_timer() cursor = self.conn.cursor() - query = "SELECT `jobId`,`jobName`,`userName`, `jobStatus`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `jobStatus` <> 'error' and `jobStatus` <> 'failed' and `jobStatus` <> 'finished' and `jobStatus` <> 'killed' order by `jobTime` DESC" % (self.jobtablename) + query = "SELECT `jobId`,`jobName`,`userName`, 'vcName', `jobStatus`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `jobStatus` <> 'error' and `jobStatus` <> 'failed' and `jobStatus` <> 'finished' and `jobStatus` <> 'killed' order by `jobTime` DESC" % (self.jobtablename) cursor.execute(query) ret = [] - for (jobId,jobName,userName, jobStatus, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in cursor: + for (jobId,jobName,userName,vcName, jobStatus, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in cursor: record = {} record["jobId"] = jobId record["jobName"] = jobName record["userName"] = userName + record["vcName"] = vcName record["jobStatus"] = jobStatus record["jobType"] = jobType record["jobDescriptionPath"] = jobDescriptionPath @@ -384,7 +775,7 @@ def SetJobError(self,jobId,errorMsg): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: set job %s error status in database, time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -400,7 +791,7 @@ def UpdateJobTextField(self,jobId,field,value): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: update job %s, field %s , time elapsed %f s" % (jobId, field, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -414,7 +805,7 @@ def GetJobTextField(self,jobId,field): cursor.execute(query) for (jobId, value) in cursor: ret = value - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) pass cursor.close() @@ -456,7 +847,7 @@ def UpdateClusterStatus(self,clusterStatus): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: update cluster status, time elapsed %f s" % (elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -472,7 +863,7 @@ def GetClusterStatus(self): for (t, value) in cursor: ret = json.loads(base64.b64decode(value)) time = t - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) pass cursor.close() @@ -505,7 +896,7 @@ def AddUser(self, username,userId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: add user %s to database , time elapsed %f s" % (username, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False diff --git a/src/utils/SQLDataHandler.py b/src/utils/SQLDataHandler.py index 6b0b5043b..ec729f3ad 100755 --- a/src/utils/SQLDataHandler.py +++ b/src/utils/SQLDataHandler.py @@ -44,7 +44,7 @@ def TestSQLConnection(conn): c.execute(sql) c.close() connected = True - except Exception, e: + except Exception as e: logger.error ("Exception: %s" % str(e) ) connected = False return connected @@ -58,12 +58,12 @@ def GetConnection(): logger.debug("current connection pool size %d" %(global_vars["sql_connections"].qsize())) try: conn = global_vars["sql_connections"].get(block = False) - except Exception, e: + except Exception as e: logger.error ("Exception: %s" % str(e) ) if conn is not None: logger.debug("Get a database connection from connection pool, current pool size %d: connection Id: %s" %(global_vars["sql_connections"].qsize(), str(conn))) - except Exception, e: + except Exception as e: logger.error ("Exception: %s" % str(e) ) finally: if acquired: @@ -74,7 +74,7 @@ def GetConnection(): if conn is None and global_vars["sql_connection_num"] > 0: try: conn = global_vars["sql_connections"].get(timeout = 1) - except Exception, e: + except Exception as e: pass if conn is not None: @@ -94,7 +94,7 @@ def GetConnection(): acquired = global_vars["sql_lock"].acquire() try: global_vars["sql_connection_num"] += 1 - except Exception, e: + except Exception as e: logger.error ("Exception: %s" % str(e) ) finally: if acquired: @@ -129,7 +129,7 @@ def ReturnConnection(conn): conn.close() except: pass - except Exception, e: + except Exception as e: logger.error ("Exception: %s" % str(e) ) finally: if not returnedConn: @@ -154,6 +154,10 @@ def __init__(self): #print "Connecting to server ..." self.jobtablename = "jobs-%s" % config["clusterId"] self.usertablename = "users-%s" % config["clusterId"] + self.acltablename = "acl-%s" % config["clusterId"] + self.identitytablename = "identity-%s" % config["clusterId"] + self.vctablename = "vc-%s" % config["clusterId"] + self.storagetablename = "storage-%s" % config["clusterId"] self.clusterstatustablename = "clusterstatus-%s" % config["clusterId"] self.commandtablename = "commands-%s" % config["clusterId"] @@ -191,11 +195,12 @@ def CreateTable(self): CREATE TABLE [dbo].[%s] ( [id] INT IDENTITY (1, 1) NOT NULL, - [jobId] varchar(50) NOT NULL, + [jobId] varchar(50) NOT NULL UNIQUE, [familyToken] varchar(50) NOT NULL, [isParent] INT NOT NULL, [jobName] varchar(max) NOT NULL, [userName] varchar(255) NOT NULL, + [vcName] varchar(255) NOT NULL, [jobStatus] varchar(255) NOT NULL DEFAULT 'unapproved', [jobStatusDetail] varchar(max) NULL, [jobType] varchar(max) NOT NULL, @@ -278,24 +283,411 @@ def CreateTable(self): cursor.close() + sql = """ + if not exists (select * from sysobjects where name='%s' and xtype='U') + CREATE TABLE [dbo].[%s] + ( + [id] INT IDENTITY (1, 1) NOT NULL, + [storageType] varchar(255) NOT NULL, + [url] varchar(255) NOT NULL, + [description] varchar(max) NOT NULL, + [vcName] varchar(255) NOT NULL, + [defaultMountPath] varchar(255) NOT NULL, + [time] DATETIME DEFAULT (getdate()) NOT NULL, + PRIMARY KEY CLUSTERED ([id] ASC), + CONSTRAINT vc_url UNIQUE (vcName,url), + CONSTRAINT vc_mountPath UNIQUE (vcName,defaultMountPath) + ) + """ % (self.storagetablename,self.storagetablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + sql = """ + if not exists (select * from sysobjects where name='%s' and xtype='U') + CREATE TABLE [dbo].[%s] + ( + [id] INT IDENTITY (1, 1) NOT NULL, + [vcName] varchar(255) NOT NULL UNIQUE, + [resources] varchar(255) NOT NULL, + [time] DATETIME DEFAULT (getdate()) NOT NULL, + PRIMARY KEY CLUSTERED ([id] ASC) + ) + """ % (self.vctablename,self.vctablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + sql = """ + if not exists (select * from sysobjects where name='%s' and xtype='U') + CREATE TABLE [dbo].[%s] + ( + [id] INT IDENTITY (1, 1) NOT NULL, + [identityName] varchar(255) NOT NULL UNIQUE, + [identityId] INT NOT NULL, + [groups] varchar(max) NOT NULL, + [time] DATETIME DEFAULT (getdate()) NOT NULL, + PRIMARY KEY CLUSTERED ([id] ASC) + ) + """ % (self.identitytablename,self.identitytablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + sql = """ + if not exists (select * from sysobjects where name='%s' and xtype='U') + CREATE TABLE [dbo].[%s] + ( + [id] INT IDENTITY (1, 1) NOT NULL, + [identityName] varchar(255) NOT NULL, + [identityId] INT NOT NULL, + [resource] varchar(255) NOT NULL, + [permissions] INT NOT NULL, + [isDeny] INT NOT NULL, + [time] DATETIME DEFAULT (getdate()) NOT NULL, + PRIMARY KEY CLUSTERED ([id] ASC), + CONSTRAINT identityName_resource UNIQUE (identityName,resource) + ) + """ % (self.acltablename,self.acltablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + def AddStorage(self, vcName, url, storageType, description, defaultMountPath): + try: + start_time = timeit.default_timer() + sql = "INSERT INTO [%s] (storageType, url, description, vcName, defaultMountPath) VALUES (?,?,?,?,?)""" % self.storagetablename + cursor = self.conn.cursor() + cursor.execute(sql, (storageType, url, description, vcName, defaultMountPath)) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: AddStorage to DB: url : %s, vcName: %s , time elapsed %f s" % (url, vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def DeleteStorage(self, url, vcName): + try: + start_time = timeit.default_timer() + sql = "DELETE FROM [%s] WHERE [url] = '%s' and [vcName] = '%s'" % (self.storagetablename, url, vcName) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteStorage: url:%s, vcName:%s, time elapsed %f s" % (url, vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def ListStorages(self, vcName): + start_time = timeit.default_timer() + cursor = self.conn.cursor(dictionary=True) + query = "SELECT [vcName],[url],[storageType],[description],[defaultMountPath] FROM [%s] WHERE [vcName] = '%s' " % (self.storagetablename, vcName) + ret = [] + try: + cursor.execute(query) + for (vcName,url,storageType,description,defaultMountPath) in cursor: + record = {} + record["vcName"] = vcName + record["url"] = url + record["storageType"] = storageType + record["description"] = description + record["defaultMountPath"] = defaultMountPath + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: ListStorages time elapsed %f s" % (elapsed)) + return ret + + + def UpdateStorage(self, vcName, url, storageType, description, defaultMountPath): + try: + start_time = timeit.default_timer() + sql = """update [%s] set storageType = '%s', description = '%s', defaultMountPath = '%s' where [vcName] = '%s' and [url] = '%s' """ % (self.storagetablename, storageType, description, defaultMountPath, vcName, url) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateStorage: vcName: %s, url: %s, time elapsed %f s" % (vcName, url, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def AddVC(self, vcName, resources): + try: + start_time = timeit.default_timer() + sql = """INSERT INTO [%s] (vcName, resources) VALUES (?,?)""" % self.vctablename + cursor = self.conn.cursor() + cursor.execute(sql, vcName, resources) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: AddVC to DB: vcName: %s , time elapsed %f s" % (vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def ListVCs(self, vcName): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT [vcName],[resources] FROM [%s]" % (self.vctablename) + ret = [] + try: + cursor.execute(query) + for (vcName,resources) in cursor: + record = {} + record["vcName"] = vcName + record["resources"] = resources + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: ListVCs time elapsed %f s" % ( elapsed)) + return ret + + + def DeleteVC(self, vcName): + try: + start_time = timeit.default_timer() + sql = "DELETE FROM [%s] WHERE [vcName] = '%s'" % (self.vctablename, vcName) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteVC: vcName: %s , time elapsed %f s" % (vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def UpdateVC(self, vcName, resources): + try: + start_time = timeit.default_timer() + sql = """update [%s] set resources = '%s' where [vcName] = '%s'""" % (self.vctablename, resources, vcName) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateVC: vcName: %s , time elapsed %f s" % (vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def GetIdentityInfo(self, identityName): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT [identityName],[identityId],[groups] FROM [%s] where [identityName] = '%s'" % (self.identitytablename, identityName) + ret = [] + try: + cursor.execute(query) + for (identityName,identityId,groups) in cursor: + record = {} + record["identityName"] = identityName + record["identityId"] = identityId + record["groups"] = groups + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetIdentityInfo time elapsed %f s" % (elapsed)) + return ret + + + def UpdateIdentityInfo(self, identityName, identityId, groups): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + if len(self.GetIdentityInfo(identityName)) == 0: + sql = """INSERT INTO [%s] (identityName,identityId,groups) VALUES (?,?,?)""" % self.identitytablename + cursor.execute(sql, identityName, identityId, groups) + else: + sql = """update [%s] set groups = '%s' where [identityName] = '%s' and [identityId] = '%s' """ % (self.identitytablename, groups, identityName, identityId) + cursor.execute(sql) + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateIdentityInfo %s to database , time elapsed %f s" % (identityName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def GetAceCount(self, identityId, resource): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT count(ALL id) as c FROM [%s] where [identityId] = '%s' and [resource] = '%s'" % (self.acltablename,identityId, resource) + cursor.execute(query) + ret = 0 + for c in cursor: + ret = c[0] + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetAceCount time elapsed %f s" % ( elapsed)) + return ret + + + def UpdateAce(self, identityName, identityId, resource, permissions, isDeny): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + if self.GetAceCount(identityId, resource) == 0: + sql = """INSERT INTO [%s] (identityName,identityId,resource,permissions,isDeny) VALUES (?,?,?,?,?)""" % self.acltablename + cursor.execute(sql, identityName, identityId, resource, permissions, isDeny) + else: + sql = """update [%s] set permissions = '%s' where [identityId] = '%s' and [resource] = '%s' """ % (self.acltablename, permissions, identityId, resource) + cursor.execute(sql) + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateAce %s - %s to database , time elapsed %f s" % (identityName, resource, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def DeleteResourceAcl(self, resource): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + sql = "DELETE FROM [%s] WHERE [resource] = '%s'" % (self.acltablename, resource) + cursor = self.conn.cursor() + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteResourceAcl %s, time elapsed %f s" % (resource, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def DeleteAce(self, identityName, resource): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + sql = "DELETE FROM [%s] WHERE [identityName] = '%s' and [resource] = '%s'" % (self.acltablename, identityName, resource) + cursor = self.conn.cursor() + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteAce %s : %s, time elapsed %f s" % (resource, identityName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def GetAcl(self): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT [identityName],[identityId],[resource],[permissions],[isDeny] FROM [%s]" % (self.acltablename) + ret = [] + try: + cursor.execute(query) + for (identityName,identityId,resource,permissions,isDeny) in cursor: + record = {} + record["identityName"] = identityName + record["identityId"] = identityId + record["resource"] = resource + record["permissions"] = permissions + record["isDeny"] = isDeny + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetAcl time elapsed %f s" % ( elapsed)) + return ret + + + def GetResourceAcl(self, resource): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT [identityName],[identityId],[resource],[permissions],[isDeny] FROM [%s] where [resource] = '%s'" % (self.acltablename, resource) + ret = [] + try: + cursor.execute(query) + for (identityName,identityId,resource,permissions,isDeny) in cursor: + record = {} + record["identityName"] = identityName + record["identityId"] = identityId + record["resource"] = resource + record["permissions"] = permissions + record["isDeny"] = isDeny + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetResourceAcl time elapsed %f s" % ( elapsed)) + return ret + + def AddJob(self, jobParams): try: start_time = timeit.default_timer() - sql = """INSERT INTO [%s] (jobId, familyToken, isParent, jobName, userName, jobType,jobParams ) VALUES (?,?,?,?,?,?,?)""" % self.jobtablename + sql = """INSERT INTO [%s] (jobId, familyToken, isParent, jobName, userName, vcName, jobType,jobParams ) VALUES (?,?,?,?,?,?,?)""" % self.jobtablename cursor = self.conn.cursor() jobParam = base64.b64encode(json.dumps(jobParams)) - cursor.execute(sql, jobParams["jobId"], jobParams["familyToken"], jobParams["isParent"], jobParams["jobName"], jobParams["userName"], jobParams["jobType"],jobParam) + cursor.execute(sql, jobParams["jobId"], jobParams["familyToken"], jobParams["isParent"], jobParams["jobName"], jobParams["userName"], jobParams["vcName"], jobParams["jobType"],jobParam) self.conn.commit() cursor.close() elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: added job %s to database, time elapsed %f s" % (jobParams["jobId"],elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False - def GetJobList(self, userName, num = None, status = None, op = ("=","or")): + def GetJobList(self, userName, vcName, num = None, status = None, op = ("=","or")): start_time = timeit.default_timer() ret = [] cursor = self.conn.cursor() @@ -304,20 +696,16 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): selectNum = "" else: selectNum = " TOP %s " % str(num) - query = "SELECT %s [jobId],[jobName],[userName], [jobStatus], [jobStatusDetail], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s]" % (selectNum, self.jobtablename) + query = "SELECT %s [jobId],[jobName],[userName], [vcName], [jobStatus], [jobStatusDetail], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s] where [vcName] = '%s'" % (selectNum, self.jobtablename, vcName) if userName != "all": - query += " where [userName] = '%s'" % userName - else: - query += " where [id] > -1 " + query += " and [userName] = '%s'" % userName if status is not None: if "," not in status: query += " and [jobStatus] %s '%s'" % (op[0],status) else: status_list = [ " [jobStatus] %s '%s' " % (op[0],s) for s in status.split(',')] status_statement = (" "+op[1]+" ").join(status_list) - query += " and ( %s ) " % status_statement - - + query += " and ( %s ) " % status_statement query += " order by [jobTime] Desc" start_time1 = timeit.default_timer() @@ -327,11 +715,12 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): data = cursor.fetchall() elapsed2 = timeit.default_timer() - start_time2 logger.info ("(fetchall time: %f)" % (elapsed2)) - for (jobId,jobName,userName, jobStatus,jobStatusDetail, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in data: + for (jobId,jobName,userName, vcName,jobStatus,jobStatusDetail, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in data: record = {} record["jobId"] = jobId record["jobName"] = jobName record["userName"] = userName + record["vcName"] = vcName record["jobStatus"] = jobStatus record["jobStatusDetail"] = jobStatusDetail record["jobType"] = jobType @@ -343,7 +732,7 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): record["errorMsg"] = errorMsg record["jobMeta"] = jobMeta ret.append(record) - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) pass cursor.close() @@ -354,14 +743,14 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): def GetJob(self, **kwargs): start_time = timeit.default_timer() - valid_keys = ["jobId", "familyToken", "isParent", "jobName", "userName", "jobStatus", "jobType", "jobTime"] + valid_keys = ["jobId", "familyToken", "isParent", "jobName", "userName", "vcName", "jobStatus", "jobType", "jobTime"] if len(kwargs) != 1: return [] key, expected = kwargs.popitem() if key not in valid_keys: logger.error("DataHandler_GetJob: key is not in valid keys list...") return [] cursor = self.conn.cursor() - query = "SELECT [jobId],[familyToken],[isParent],[jobName],[userName], [jobStatus], [jobStatusDetail], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s] where [%s] = '%s' " % (self.jobtablename,key,expected) + query = "SELECT [jobId],[familyToken],[isParent],[jobName],[userName],[vcName], [jobStatus], [jobStatusDetail], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s] where [%s] = '%s' " % (self.jobtablename,key,expected) cursor.execute(query) columns = [column[0] for column in cursor.description] ret = [dict(zip(columns, row)) for row in cursor.fetchall()] @@ -382,7 +771,7 @@ def AddCommand(self,jobId,command): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: add command to database, jobId: %s , time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -416,7 +805,7 @@ def FinishCommand(self,commandId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: set command %s as finished , time elapsed %f s" % (commandId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -451,7 +840,7 @@ def KillJob(self,jobId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: mark job %s to be killed in database, time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -467,7 +856,7 @@ def ApproveJob(self,jobId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: approved job %s , time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -475,14 +864,15 @@ def ApproveJob(self,jobId): def GetPendingJobs(self): start_time = timeit.default_timer() cursor = self.conn.cursor() - query = "SELECT [jobId],[jobName],[userName], [jobStatus], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s] where [jobStatus] <> 'error' and [jobStatus] <> 'failed' and [jobStatus] <> 'finished' and [jobStatus] <> 'killed' order by [jobTime] DESC" % (self.jobtablename) + query = "SELECT [jobId],[jobName],[userName], [vcName], [jobStatus], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s] where [jobStatus] <> 'error' and [jobStatus] <> 'failed' and [jobStatus] <> 'finished' and [jobStatus] <> 'killed' order by [jobTime] DESC" % (self.jobtablename) cursor.execute(query) ret = [] - for (jobId,jobName,userName, jobStatus, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in cursor: + for (jobId,jobName,userName, vcName, jobStatus, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in cursor: record = {} record["jobId"] = jobId record["jobName"] = jobName record["userName"] = userName + record["vcName"] = vcName record["jobStatus"] = jobStatus record["jobType"] = jobType record["jobDescriptionPath"] = jobDescriptionPath @@ -510,7 +900,7 @@ def SetJobError(self,jobId,errorMsg): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: set job %s error status in database, time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False diff --git a/src/utils/authorization.py b/src/utils/authorization.py new file mode 100644 index 000000000..0a1278ddc --- /dev/null +++ b/src/utils/authorization.py @@ -0,0 +1,222 @@ +from DataHandler import DataHandler +from MyLogger import MyLogger +import json +import requests + +logger = MyLogger() + +def enum(*sequential, **named): + enums = dict(zip(sequential, range(len(sequential))), **named) + reverse = dict((value, key) for key, value in enums.items()) + enums['reverse_mapping'] = reverse + return type('Enum', (), enums) + +Permission = enum(Unauthorized=0, User=1, Collaborator=3, Admin=7) +ResourceType = enum(Cluster=1, VC=2, Job=3) +''' + +import enum +class Permission(enum.Enum): + Unauthorized = 0 + User = 1 + Collaborator=3 + Admin = 7 + + +class ResourceType(enum.Enum): + Cluster = 1 + NFS = 2 + VC = 3 + Job = 4 + Template = 5 +''' + + +class AuthorizationManager: + #TODO : Add Cache to aovid frequent DB calls + + CLUSTER_ACL_PATH = "Cluster" + ACL_DELIMITER = "/" + TYPE_NAME_DELIMITER = ":" + WinBindUrl = "http://onenet40.redmond.corp.microsoft.com/domaininfo/GetUserId?userName={0}" #TODO:read form config + + # Check if user has requested access (based on effective ACL) on the specified resource. + @staticmethod + def _HasAccess(identityName, resourceAclPath, permissions): + dataHandler = DataHandler() + try: + logger.info('HasAccess invoked!') + identities = [] + identities.append(IdentityManager(AuthorizationManager.WinBindUrl).GetIdentityInfo(identityName)["groups"]) + + logger.info('initial resourceAclPath ' + resourceAclPath) + #TODO: handle isDeny + while resourceAclPath: + logger.info('resourceAclPath ' + resourceAclPath) + acl = dataHandler.GetResourceAcl(resourceAclPath) + for ace in acl: + for identity in identities: + if ace["identityId"] == identity or ace["identityName"] == identityName: + permissions = permissions & (~ace["permissions"]) + if not permissions: + logger.info('access : Yes') + return True + + resourceAclPath = AuthorizationManager.__GetParentPath(resourceAclPath) + logger.info('access : No') + return False + + except Exception as e: + logger.error('Exception: '+ str(e)) + logger.warn("HasAccess failed for user %s" % identityName) + logger.info('access : No (exception)') + return False + + finally: + dataHandler.Close() + + + @staticmethod + def HasAccess(identityName, resourceType, resourceName, permissions): + resourceAclPath = AuthorizationManager.GetResourceAclPath(resourceName, resourceType) + return AuthorizationManager._HasAccess(identityName, resourceAclPath, permissions) + + + # Add/Update a specific access control entry. + @staticmethod + def UpdateAce(identityName, resourceAclPath, permissions, isDeny): + dataHandler = DataHandler() + try: + identityId = IdentityManager(AuthorizationManager.WinBindUrl).GetIdentityInfo(identityName)["uid"] + if identityId == -1: + identityId = 0 + return dataHandler.UpdateAce(identityName, identityId, resourceAclPath, permissions, isDeny) + + except Exception as e: + logger.error('Exception: '+ str(e)) + logger.warn("Fail to Update Ace for user %s" % identityName) + return False + + finally: + dataHandler.Close() + + + @staticmethod + def DeleteAce(identityName, resourceAclPath): + dataHandler = DataHandler() + try: + return dataHandler.DeleteAce(identityName, resourceAclPath) + + except Exception as e: + logger.error('Exception: '+ str(e)) + logger.warn("Fail to Delete Ace for user %s" % identityName) + return False + + finally: + dataHandler.Close() + + + @staticmethod + def DeleteResourceAcl(resourceAclPath): + dataHandler = DataHandler() + try: + return dataHandler.DeleteResourceAcl(resourceAclPath) + + except Exception as e: + logger.error('Exception: '+ str(e)) + logger.warn("DeleteResourceAcl failed for %s" % resourceAclPath) + return False + + finally: + dataHandler.Close() + + + # Return all access control entries (for resources on which user has read access). + @staticmethod + def __GetAccessibleAcl(userName, permissions): + dataHandler = DataHandler() + try: + acl = dataHandler.GetAcl() + ret = [] + + for ace in acl: + if AuthorizationManager.HasAccess(userName, ace["resource"], permissions): #resource + ret.append(ace) + + return ret + + except Exception as e: + logger.error('Exception: '+ str(e)) + logger.warn("Fail to get ACL for user %s, return empty list" % userName) + return [] + + finally: + dataHandler.Close() + + + @staticmethod + def GetAcl(username): + return AuthorizationManager.__GetAccessibleAcl(username, Permission.User) + + + @staticmethod + def IsClusterAdmin(userName): + return AuthorizationManager.HasAccess(userName, AuthorizationManager.CLUSTER_ACL_PATH, Permission.Admin) + + + @staticmethod + def __GetParentPath(aclPath): + if AuthorizationManager.ACL_DELIMITER in aclPath: + return aclPath.rsplit(AuthorizationManager.ACL_DELIMITER, 1)[0] + else: + return "" + + + @staticmethod + def GetResourceAclPath(resourceIdentifier, resourceType): + if (resourceType == ResourceType.VC): + #return AuthorizationManager.CLUSTER_ACL_PATH + AuthorizationManager.ACL_DELIMITER + ResourceType(resourceType).name + AuthorizationManager.TYPE_NAME_DELIMITER + resourceIdentifier.strip(AuthorizationManager.ACL_DELIMITER) + return AuthorizationManager.CLUSTER_ACL_PATH + AuthorizationManager.ACL_DELIMITER + ResourceType.reverse_mapping[resourceType] + AuthorizationManager.TYPE_NAME_DELIMITER + resourceIdentifier.strip(AuthorizationManager.ACL_DELIMITER) + elif resourceType == ResourceType.Cluster: + return AuthorizationManager.CLUSTER_ACL_PATH + + + +class IdentityManager: + serverUrl = "" + #TODO: do we need to cache? + + def __init__(self, winbindServerUrl): + self.serverUrl = winbindServerUrl + + + def GetIdentityInfo(self, identityName): + dataHandler = DataHandler() + try: + # winbind (depending on configs) handles nested groups for userIds + response = requests.get(self.serverUrl.format(identityName)) + info = json.loads(response.text) + + dataHandler.UpdateIdentityInfo(identityName, info["uid"], info["groups"]) + + return info + except Exception as e: + logger.error('Exception: '+ str(e)) + logger.warn("GetIdentityInfo from winbind failed for identity %s" % identityName) + + info = {} + info["uid"] = -1 + info["gid"] = -1 + info["groups"] = [-1] + + lst = dataHandler.GetIdentityInfo(identityName) + if lst: + info["uid"] = lst[0][1] + info["gid"] = lst[0][1] + info["groups"] = lst[0][2] + else: + logger.warn("GetIdentityInfo : Identity %s not found in DB" % identityName) + return info + + finally: + dataHandler.Close() \ No newline at end of file From 45ff7e73d27477da5dc864326963cce8571c3f7d Mon Sep 17 00:00:00 2001 From: Deepak Bansal Date: Tue, 2 Apr 2019 16:00:48 -0700 Subject: [PATCH 032/595] vc_storage --- src/ClusterBootstrap/deploy.py | 50 ++ .../template/RestfulAPI/config.yaml | 1 + src/RestAPI/dlwsrestapi.py | 418 +++++++++++++++- src/docker-images/RestfulAPI/Dockerfile | 52 +- src/utils/JobRestAPIUtils.py | 225 +++++++-- src/utils/MySQLDataHandler.py | 456 ++++++++++++++++-- src/utils/SQLDataHandler.py | 450 +++++++++++++++-- src/utils/authorization.py | 221 +++++++++ 8 files changed, 1749 insertions(+), 124 deletions(-) create mode 100644 src/utils/authorization.py diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index c618c17f4..808ed3181 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -17,6 +17,7 @@ import glob import copy import numbers +import requests from os.path import expanduser @@ -3130,6 +3131,55 @@ def run_command( args, command, nargs, parser ): print "Error: scan need one parameter with format x.x.x.x/n. " exit() + elif command == "admin": + if len(nargs) >= 1: + if nargs[0] == "vc": + if len(nargs) >= 2: + if nargs[1] == "add": + url = "http://%s:%s/AddVC?vcName=%s"a=%s&metadata=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3], nargs[4]) + response = requests.get(url) + print(response) + elif nargs[1] == "update": + url = "http://%s:%s/UpdateVC?vcName=%s"a=%s&metadata=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3], nargs[4]) + response = requests.get(url) + print(response) + elif nargs[1] == "delete": + url = "http://%s:%s/DeleteVC?vcName=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2]) + response = requests.get(url) + print(response) + elif nargs[1] == "list": + url = "http://%s:%s/ListVCs?userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"]) + response = requests.get(url) + print(response.text) + elif nargs[0] == "storage": + if len(nargs) >= 2: + if nargs[1] == "add": + url = "http://%s:%s/AddStorage?vcName=%s&url=%s&storageType=%s&metadata=%s&defaultMountPath=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3], nargs[4], nargs[5], nargs[6]) + response = requests.get(url) + print(response) + elif nargs[1] == "update": + url = "http://%s:%s/UpdateStorage?vcName=%s&url=%s&storageType=%s&metadata=%s&defaultMountPath=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3], nargs[4], nargs[5], nargs[6]) + response = requests.get(url) + print(response) + elif nargs[1] == "delete": + url = "http://%s:%s/DeleteStorage?vcName=%s&url=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3]) + response = requests.get(url) + print(response) + elif nargs[1] == "list": + url = "http://%s:%s/ListStorages?vcName=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2]) + response = requests.get(url) + print(response.text) + elif nargs[0] == "acl": + if len(nargs) >= 2: + if nargs[1] == "update": + url = "http://%s:%s/UpdateAce?identityName=%s&resourceType=%s&resourceName=%s&permissions=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3], nargs[4], nargs[5]) + response = requests.get(url) + print(response) + elif nargs[1] == "get": + url = "http://%s:%s/GetACL?userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"]) + response = requests.get(url) + print(response) + elif command == "updateworker": response = raw_input_with_default("Deploy Worker Nodes (y/n)?") diff --git a/src/ClusterBootstrap/template/RestfulAPI/config.yaml b/src/ClusterBootstrap/template/RestfulAPI/config.yaml index 384b3ab86..1624912fb 100755 --- a/src/ClusterBootstrap/template/RestfulAPI/config.yaml +++ b/src/ClusterBootstrap/template/RestfulAPI/config.yaml @@ -30,3 +30,4 @@ default-storage-folders : {{cnf["default-storage-folders"]}} webportal_node: {{cnf["webportal_node"]}} datasource : {{cnf["datasource"]}} kube_custom_scheduler: {{cnf["kube_custom_scheduler"]}} +WinbindServers: {{cnf["WinbindServers"]}} diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index c82aefff9..3f24edf99 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -14,6 +14,7 @@ sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),"../utils")) #from JobRestAPIUtils import SubmitDistJob, GetJobList, GetJobStatus, DeleteJob, GetTensorboard, GetServiceAddress, GetLog, GetJob import JobRestAPIUtils +from authorization import ResourceType, Permission, AuthorizationManager from config import config from config import global_vars @@ -28,9 +29,15 @@ api = Api(app) verbose = True logger.info( "------------------- Restful API started ------------------------------------- ") - logger.info("%s" % config ) +if "initAdminAccess" not in global_vars or not global_vars["initAdminAccess"]: + logger.info("===========Init Admin Access===============") + global_vars["initAdminAccess"] = True + logger.info('setting admin access!') + AuthorizationManager.UpdateAce("Administrator", AuthorizationManager.GetResourceAclPath("", ResourceType.Cluster), Permission.Admin, False) + logger.info('admin access given!') + parser = reqparse.RequestParser() @@ -70,6 +77,7 @@ def get(self): parser.add_argument('logDir') parser.add_argument('interactivePort') parser.add_argument('userName') + parser.add_argument('vcName') parser.add_argument('userId') parser.add_argument('runningasroot') parser.add_argument('containerUserId') @@ -97,6 +105,8 @@ def get(self): if args["jobName"] is None or len(args["jobName"].strip()) == 0: ret["error"] = "job name cannot be empty" + elif args["vcName"] is None or len(args["vcName"].strip()) == 0: + ret["error"] = "vc name cannot be empty" elif args["resourcegpu"] is None or len(args["resourcegpu"].strip()) == 0: ret["error"] = "Number of GPU cannot be empty" elif args["dataPath"] is None or len(args["dataPath"].strip()) == 0: @@ -107,6 +117,7 @@ def get(self): ret["error"] = "jobType cannot be empty" else: params["jobName"] = args["jobName"] + params["vcName"] = args["vcName"] params["resourcegpu"] = args["resourcegpu"] params["workPath"] = args["workPath"] params["dataPath"] = args["dataPath"] @@ -231,6 +242,8 @@ def get(self): ## api.add_resource(SubmitJob, '/SubmitJob') + + class PostJob(Resource): def post(self): params = request.get_json(force=True) @@ -265,6 +278,8 @@ class ListJobs(Resource): def get(self): parser.add_argument('userName') parser.add_argument('num') + parser.add_argument('vcName') + parser.add_argument('jobOwner') args = parser.parse_args() num = None if args["num"] is not None: @@ -272,10 +287,8 @@ def get(self): num = int(args["num"]) except: pass - if args["userName"] is not None and len(args["userName"].strip()) > 0: - jobs = JobRestAPIUtils.GetJobList(args["userName"],num) - else: - jobs = [] + jobs = JobRestAPIUtils.GetJobList(args["userName"], args["vcName"], args["jobOwner"], num) + jobList = [] queuedJobs = [] runningJobs = [] @@ -310,7 +323,6 @@ def get(self): else: finishedJobs.append(job) - ret = {} ret["queuedJobs"] = queuedJobs ret["runningJobs"] = runningJobs @@ -322,7 +334,6 @@ def get(self): resp.headers["dataType"] = "json" return resp - ## ## Actually setup the Api resource routing here ## @@ -333,9 +344,11 @@ def get(self): class KillJob(Resource): def get(self): parser.add_argument('jobId') + parser.add_argument('userName') args = parser.parse_args() jobId = args["jobId"] - result = JobRestAPIUtils.KillJob(jobId) + userName = args["userName"] + result = JobRestAPIUtils.KillJob(userName, jobId) ret = {} if result: ret["result"] = "Success, the job is scheduled to be terminated." @@ -346,7 +359,6 @@ def get(self): resp.headers["Access-Control-Allow-Origin"] = "*" resp.headers["dataType"] = "json" - return resp ## ## Actually setup the Api resource routing here @@ -355,12 +367,92 @@ def get(self): +class PauseJob(Resource): + def get(self): + parser.add_argument('jobId') + parser.add_argument('userName') + args = parser.parse_args() + jobId = args["jobId"] + userName = args["userName"] + result = JobRestAPIUtils.PauseJob(userName, jobId) + ret = {} + if result: + ret["result"] = "Success, the job is scheduled to be paused." + else: + ret["result"] = "Cannot pause the job. Job ID:" + jobId + + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(PauseJob, '/PauseJob') + + + +class ResumeJob(Resource): + def get(self): + parser.add_argument('jobId') + parser.add_argument('userName') + args = parser.parse_args() + jobId = args["jobId"] + userName = args["userName"] + result = JobRestAPIUtils.ResumeJob(userName, jobId) + ret = {} + if result: + ret["result"] = "Success, the job is scheduled to be resumed." + else: + ret["result"] = "Cannot resume the job. Job ID:" + jobId + + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(ResumeJob, '/ResumeJob') + + + +class CloneJob(Resource): + def get(self): + parser.add_argument('jobId') + parser.add_argument('userName') + args = parser.parse_args() + jobId = args["jobId"] + userName = args["userName"] + result = JobRestAPIUtils.CloneJob(userName, jobId) + ret = {} + if result: + ret["result"] = "Success, the job is scheduled to be cloned." + else: + ret["result"] = "Cannot clone the job. Job ID:" + jobId + + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(CloneJob, '/CloneJob') + + + class ApproveJob(Resource): def get(self): parser.add_argument('jobId') + parser.add_argument('userName') args = parser.parse_args() jobId = args["jobId"] - result = JobRestAPIUtils.ApproveJob(jobId) + userName = args["userName"] + result = JobRestAPIUtils.ApproveJob(userName, jobId) ret = {} if result: ret["result"] = "Success, the job has been approved." @@ -371,7 +463,6 @@ def get(self): resp.headers["Access-Control-Allow-Origin"] = "*" resp.headers["dataType"] = "json" - return resp ## ## Actually setup the Api resource routing here @@ -383,9 +474,11 @@ def get(self): class GetCommands(Resource): def get(self): parser.add_argument('jobId') + parser.add_argument('userName') args = parser.parse_args() jobId = args["jobId"] - commands = JobRestAPIUtils.GetCommands(jobId) + userName = args["userName"] + commands = JobRestAPIUtils.GetCommands(userName, jobId) resp = jsonify(commands) resp.headers["Access-Control-Allow-Origin"] = "*" resp.headers["dataType"] = "json" @@ -401,9 +494,11 @@ def get(self): class GetJobDetail(Resource): def get(self): parser.add_argument('jobId') + parser.add_argument('userName') args = parser.parse_args() jobId = args["jobId"] - job = JobRestAPIUtils.GetJobDetail(jobId) + userName = args["userName"] + job = JobRestAPIUtils.GetJobDetail(userName, jobId) job["jobParams"] = json.loads(base64.b64decode(job["jobParams"])) if "endpoints" in job and job["endpoints"] is not None and (job["endpoints"].strip()) > 0: job["endpoints"] = json.loads(base64.b64decode(job["endpoints"])) @@ -428,6 +523,9 @@ def get(self): class GetClusterStatus(Resource): def get(self): + parser.add_argument('userName') + args = parser.parse_args() + userName = args["userName"] cluster_status, last_updated_time = JobRestAPIUtils.GetClusterStatus() cluster_status["last_updated_time"] = last_updated_time resp = jsonify(cluster_status) @@ -441,15 +539,36 @@ def get(self): api.add_resource(GetClusterStatus, '/GetClusterStatus') +class GetVCStatus(Resource): # Todo + def get(self): + parser.add_argument('vcName') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + userName = args["userName"] + vc_status, last_updated_time = JobRestAPIUtils.GetVCStatus(userName, vcName) + vc_status["last_updated_time"] = last_updated_time + resp = jsonify(vc_status) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(GetVCStatus, '/GetVCStatus') + class AddCommand(Resource): def get(self): parser.add_argument('jobId') parser.add_argument('command') + parser.add_argument('userName') args = parser.parse_args() + userName = args["userName"] jobId = args["jobId"] command = args["command"] - result = JobRestAPIUtils.AddCommand(jobId, command) + result = JobRestAPIUtils.AddCommand(userName, jobId, command) ret = {} if result: ret["result"] = "Success, the command is scheduled to be run." @@ -488,7 +607,278 @@ def get(self): api.add_resource(AddUser, '/AddUser') +class UpdateAce(Resource): + def get(self): + parser.add_argument('userName') + parser.add_argument('identityName') + parser.add_argument('resourceType') + parser.add_argument('resourceName') + parser.add_argument('permissions') + args = parser.parse_args() + username = args["userName"] + identityName = str(args["identityName"]) + resourceType = int(args["resourceType"]) + resourceName = str(args["resourceName"]) + permissions = int(args["permissions"]) + ret = {} + ret["result"] = JobRestAPIUtils.UpdateAce(username, identityName, resourceType, resourceName, permissions) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(UpdateAce, '/UpdateAce') + + +class DeleteAce(Resource): + def get(self): + parser.add_argument('userName') + parser.add_argument('identityName') + parser.add_argument('resourceType') + parser.add_argument('resourceName') + args = parser.parse_args() + username = args["userName"] + identityName = str(args["identityName"]) + resourceType = int(args["resourceType"]) + resourceName = str(args["resourceName"]) + ret = {} + ret["result"] = JobRestAPIUtils.DeleteAce(username, identityName, resourceType, resourceName) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(DeleteAce, '/DeleteAce') + + +class IsClusterAdmin(Resource): + def get(self): + parser.add_argument('userName') + args = parser.parse_args() + username = args["userName"] + ret = {} + ret["result"] = AuthorizationManager.IsClusterAdmin(username) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(IsClusterAdmin, '/IsClusterAdmin') + + +class GetACL(Resource): + def get(self): + parser.add_argument('userName') + args = parser.parse_args() + username = args["userName"] + ret = {} + ret["result"] = AuthorizationManager.GetAcl(username) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(GetACL, '/GetACL') + + +class ListVCs(Resource): + def get(self): + parser.add_argument('userName') + args = parser.parse_args() + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.ListVCs(userName) + + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp + +## +## Actually setup the Api resource routing here +## +api.add_resource(ListVCs, '/ListVCs') + + +class AddVC(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('quota') + parser.add_argument('metadata') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + quota = args["quota"] + metadata = args["metadata"] + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.AddVC(userName, vcName, quota, metadata) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(AddVC, '/AddVC') + + +class DeleteVC(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.DeleteVC(userName, vcName) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(DeleteVC, '/DeleteVC') + + +class UpdateVC(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('quota') + parser.add_argument('metadata') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + quota = args["quota"] + metadata = args["metadata"] + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.UpdateVC(userName, vcName, quota, metadata) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(UpdateVC, '/UpdateVC') + + +class ListStorages(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.ListStorages(userName, vcName) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(ListStorages, '/ListStorages') + + +class AddStorage(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('storageType') + parser.add_argument('url') + parser.add_argument('metadata') + parser.add_argument('defaultMountPath') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + storageType = args["storageType"] + url = args["url"] + metadata = args["metadata"] + defaultMountPath = args["defaultMountPath"] + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.AddStorage(userName, vcName, url, storageType, metadata, defaultMountPath) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(AddStorage, '/AddStorage') + + +class DeleteStorage(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('userName') + parser.add_argument('url') + args = parser.parse_args() + vcName = args["vcName"] + userName = args["userName"] + url = args["url"] + ret = {} + ret["result"] = JobRestAPIUtils.DeleteStorage(userName, vcName, url) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(DeleteStorage, '/DeleteStorage') + + +class UpdateStorage(Resource): + def get(self): + parser.add_argument('vcName') + parser.add_argument('storageType') + parser.add_argument('url') + parser.add_argument('metadata') + parser.add_argument('defaultMountPath') + parser.add_argument('userName') + args = parser.parse_args() + vcName = args["vcName"] + storageType = args["storageType"] + url = args["url"] + metadata = args["metadata"] + defaultMountPath = args["defaultMountPath"] + userName = args["userName"] + ret = {} + ret["result"] = JobRestAPIUtils.UpdateStorage(userName, vcName, url, storageType, metadata, defaultMountPath) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp +## +## Actually setup the Api resource routing here +## +api.add_resource(UpdateStorage, '/UpdateStorage') if __name__ == '__main__': app.run(debug=False,host="0.0.0.0",threaded=True) + diff --git a/src/docker-images/RestfulAPI/Dockerfile b/src/docker-images/RestfulAPI/Dockerfile index f80ab76f5..5af0e0e98 100755 --- a/src/docker-images/RestfulAPI/Dockerfile +++ b/src/docker-images/RestfulAPI/Dockerfile @@ -1,6 +1,56 @@ -FROM dlws/restfulapi:v1.4 +FROM ubuntu:16.04 MAINTAINER Hongzhi Li +# See https://stackoverflow.com/questions/37706635/in-docker-apt-get-install-fails-with-failed-to-fetch-http-archive-ubuntu-com +# It is a good practice to merge apt-get update with the following apt-get install +RUN apt-get update; +RUN apt-get update; apt-get install -y --no-install-recommends apt-transport-https \ + build-essential \ + cmake \ + git \ + wget \ + vim \ + python-dev \ + python-pip \ + python-yaml \ + locales \ + python-pycurl \ + bison \ + curl \ + nfs-common \ + apt-utils + + +RUN pip install --upgrade pip; + +RUN pip install setuptools; +RUN locale-gen en_US.UTF-8 +RUN update-locale LANG=en_US.UTF-8 + +RUN pip install flask +RUN pip install flask.restful +RUN pip install requests + +RUN wget http://ccsdatarepo.westus.cloudapp.azure.com/data/tools/mysql-connector-python_2.1.7-1ubuntu16.04_all.deb -O /mysql-connector-python_2.1.7-1ubuntu16.04_all.deb +RUN dpkg -i /mysql-connector-python_2.1.7-1ubuntu16.04_all.deb +RUN apt-get install -y libmysqlclient-dev mysql-connector-python + + +# Install python for Azure SQL + +RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - + +RUN curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list > /etc/apt/sources.list.d/mssql-release.list + +RUN apt-get update; ACCEPT_EULA=Y apt-get install -y msodbcsql=13.1.4.0-1 unixodbc-dev + + +RUN pip install pyodbc +RUN pip install tzlocal +RUN apt-get update; apt-get install -y --no-install-recommends ssh apache2 libapache2-mod-wsgi sudo +RUN usermod -a -G sudo www-data +RUN echo "\nwww-data ALL=(ALL) NOPASSWD:ALL\n" > /etc/sudoers + COPY kubectl /usr/local/bin/kubectl RUN chmod +x /usr/local/bin/kubectl #COPY gittoken /root/.ssh/id_rsa diff --git a/src/utils/JobRestAPIUtils.py b/src/utils/JobRestAPIUtils.py index 38d6d920a..6c13326f9 100755 --- a/src/utils/JobRestAPIUtils.py +++ b/src/utils/JobRestAPIUtils.py @@ -18,6 +18,7 @@ from config import global_vars from MyLogger import MyLogger +from authorization import ResourceType, Permission, AuthorizationManager import copy @@ -36,7 +37,9 @@ def SubmitJob(jobParamsJsonStr): if "jobName" not in jobParams or len(jobParams["jobName"].strip()) == 0: ret["error"] = "ERROR: Job name cannot be empty" return ret - + if "vcName" not in jobParams or len(jobParams["vcName"].strip()) == 0: + ret["error"] = "ERROR: VC name cannot be empty" + return ret if "jobId" not in jobParams or jobParams["jobId"] == "": #jobParams["jobId"] = jobParams["jobName"] + "-" + str(uuid.uuid4()) @@ -65,6 +68,10 @@ def SubmitJob(jobParamsJsonStr): if "/" in userName: userName = userName.split("/")[1].strip() + if not AuthorizationManager.HasAccess(jobParams["userName"], ResourceType.VC, jobParams["vcName"].strip(), Permission.User): + ret["error"] = "Access Denied!" + return ret + if "cmd" not in jobParams: jobParams["cmd"] = "" @@ -176,70 +183,80 @@ def SubmitJob(jobParamsJsonStr): -def GetJobList(userName,num=None): +def GetJobList(userName, vcName, jobOwner, num=None): try: dataHandler = DataHandler() jobs = [] + hasAccessOnAllJobs = False + + if AuthorizationManager.HasAccess(userName, ResourceType.VC, vcName, Permission.Collaborator): + hasAccessOnAllJobs = True - if userName != "all": - jobs = jobs + dataHandler.GetJobList(userName,None, "running,queued,scheduling,unapproved", ("=","or")) - jobs = jobs + dataHandler.GetJobList(userName,num, "running,queued,scheduling,unapproved", ("<>","and")) + if jobOwner != "all" or not hasAccessOnAllJobs: + jobs = jobs + dataHandler.GetJobList(userName,vcName,None, "running,queued,scheduling,unapproved", ("=","or")) + jobs = jobs + dataHandler.GetJobList(userName,vcName,num, "running,queued,scheduling,unapproved", ("<>","and")) else: - jobs = dataHandler.GetJobList(userName,None, "error,failed,finished,killed", ("<>","and")) + jobs = dataHandler.GetJobList(jobOwner,vcName,None, "error,failed,finished,killed", ("<>","and")) for job in jobs: job.pop('jobMeta', None) dataHandler.Close() return jobs - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) logger.warn("Fail to get job list for user %s, return empty list" % userName) return [] -def GetCommands(jobId): +def GetCommands(userName, jobId): + commands = [] dataHandler = DataHandler() - commands = dataHandler.GetCommands(jobId=jobId); + jobs = dataHandler.GetJob(jobId=jobId) + if jobs[0]["userName"] == userName or AuthorizationManager.HasAccess(userName, ResourceType.VC, jobs[0]["vcName"], Permission.Collaborator): + commands = dataHandler.GetCommands(jobId=jobId) dataHandler.Close() return commands -def KillJob(jobId): - ret = True +def KillJob(userName, jobId): + ret = False dataHandler = DataHandler() jobs = dataHandler.GetJob(jobId=jobId) if len(jobs) == 1: job = jobs[0] - if job["isParent"] == 1: - for currJob in dataHandler.GetJob(familyToken=job["familyToken"]): - ret = ret and dataHandler.KillJob(currJob["jobId"]) - else: - ret = dataHandler.KillJob(jobId) - else: - ret = False + if job["userName"] == userName or AuthorizationManager.HasAccess(userName, ResourceType.VC, job["vcName"], Permission.Admin): + if job["isParent"] == 1: + ret = True + for currJob in dataHandler.GetJob(familyToken=job["familyToken"]): + ret = ret and dataHandler.KillJob(currJob["jobId"]) + else: + ret = dataHandler.KillJob(jobId) dataHandler.Close() return ret -def AddCommand(jobId,command): +def AddCommand(userName, jobId,command): dataHandler = DataHandler() ret = False jobs = dataHandler.GetJob(jobId=jobId) if len(jobs) == 1: - ret = dataHandler.AddCommand(jobId,command) + if jobs[0]["userName"] == userName or AuthorizationManager.HasAccess(userName, ResourceType.VC, jobs[0]["vcName"], Permission.Collaborator): + ret = dataHandler.AddCommand(jobId,command) dataHandler.Close() return ret -def ApproveJob(jobId): +def ApproveJob(userName, jobId): dataHandler = DataHandler() ret = False jobs = dataHandler.GetJob(jobId=jobId) if len(jobs) == 1: - ret = dataHandler.ApproveJob(jobId) + if AuthorizationManager.HasAccess(userName, ResourceType.VC, jobs[0]["vcName"], Permission.Admin): + ret = dataHandler.ApproveJob(jobId) dataHandler.Close() return ret + def isBase64(s): try: if base64.b64encode(base64.b64decode(s)) == s: @@ -248,41 +265,43 @@ def isBase64(s): pass return False -def GetJobDetail(jobId): + +def GetJobDetail(userName, jobId): job = None dataHandler = DataHandler() jobs = dataHandler.GetJob(jobId=jobId) if len(jobs) == 1: - job = jobs[0] - job["log"] = "" - #jobParams = json.loads(base64.b64decode(job["jobMeta"])) - #jobPath,workPath,dataPath = GetStoragePath(jobParams["jobPath"],jobParams["workPath"],jobParams["dataPath"]) - #localJobPath = os.path.join(config["storage-mount-path"],jobPath) - #logPath = os.path.join(localJobPath,"joblog.txt") - #print logPath - #if os.path.isfile(logPath): - # with open(logPath, 'r') as f: - # log = f.read() - # job["log"] = log - # f.close() - if "jobDescription" in job: - job.pop("jobDescription",None) - try: - log = dataHandler.GetJobTextField(jobId,"jobLog") + if jobs[0]["userName"] == userName or AuthorizationManager.HasAccess(userName, ResourceType.VC, jobs[0]["vcName"], Permission.Collaborator): + job = jobs[0] + job["log"] = "" + #jobParams = json.loads(base64.b64decode(job["jobMeta"])) + #jobPath,workPath,dataPath = GetStoragePath(jobParams["jobPath"],jobParams["workPath"],jobParams["dataPath"]) + #localJobPath = os.path.join(config["storage-mount-path"],jobPath) + #logPath = os.path.join(localJobPath,"joblog.txt") + #print logPath + #if os.path.isfile(logPath): + # with open(logPath, 'r') as f: + # log = f.read() + # job["log"] = log + # f.close() + if "jobDescription" in job: + job.pop("jobDescription",None) try: - if isBase64(log): - log = base64.b64decode(log) - except Exception: - pass - if log is not None: - job["log"] = log - except: - job["log"] = "fail-to-get-logs" + log = dataHandler.GetJobTextField(jobId,"jobLog") + try: + if isBase64(log): + log = base64.b64decode(log) + except Exception: + pass + if log is not None: + job["log"] = log + except: + job["log"] = "fail-to-get-logs" dataHandler.Close() return job -def GetClusterStatus(): +def GetClusterStatus(): #todo : access check job = None dataHandler = DataHandler() cluster_status,last_update_time = dataHandler.GetClusterStatus() @@ -290,7 +309,7 @@ def GetClusterStatus(): return cluster_status,last_update_time -def AddUser(username,userId): +def AddUser(username,userId): # todo : access check? ret = None dataHandler = DataHandler() ret = dataHandler.AddUser(username,userId) @@ -298,6 +317,114 @@ def AddUser(username,userId): return ret +def UpdateAce(userName, identityName, resourceType, resourceName, permissions): + ret = None + resourceAclPath = AuthorizationManager.GetResourceAclPath(resourceName, resourceType) + if AuthorizationManager.HasAccess(userName, resourceType, resourceName, Permission.Admin): + ret = AuthorizationManager.UpdateAce(identityName, resourceAclPath, permissions, False) + else: + ret = "Access Denied!" + return ret + + +def DeleteAce(userName, identityName, resourceType, resourceName): + ret = None + resourceAclPath = AuthorizationManager.GetResourceAclPath(resourceName, resourceType) + if AuthorizationManager.HasAccess(userName, resourceType, resourceName, Permission.Admin): + ret = AuthorizationManager.DeleteAce(identityName, resourceAclPath) + else: + ret = "Access Denied!" + return ret + + +def AddStorage(userName, vcName, url, storageType, metadata, defaultMountPath): + ret = None + dataHandler = DataHandler() + if AuthorizationManager.IsClusterAdmin(userName): + ret = dataHandler.AddStorage(vcName, url, storageType, metadata, defaultMountPath) + else: + ret = "Access Denied!" + dataHandler.Close() + return ret + + +def ListStorages(userName, vcName): + ret = [] + dataHandler = DataHandler() + if AuthorizationManager.HasAccess(userName, ResourceType.VC, vcName, Permission.User): + ret = dataHandler.ListStorages(vcName) + dataHandler.Close() + return ret + + +def DeleteStorage(userName, vcName, url): + ret = None + dataHandler = DataHandler() + if AuthorizationManager.HasAccess(userName, ResourceType.VC, vcName, Permission.Admin): + ret = dataHandler.DeleteStorage(vcName, url) + else: + ret = "Access Denied!" + dataHandler.Close() + return ret + + +def UpdateStorage(userName, vcName, url, storageType, metadata, defaultMountPath): + ret = None + dataHandler = DataHandler() + if AuthorizationManager.HasAccess(userName, ResourceType.VC, vcName, Permission.Admin): + ret = dataHandler.UpdateStorage(vcName, url, storageType, metadata, defaultMountPath) + else: + ret = "Access Denied!" + dataHandler.Close() + return ret + + +def AddVC(userName, vcName, quota, metadata): + ret = None + dataHandler = DataHandler() + if AuthorizationManager.IsClusterAdmin(userName): + ret = dataHandler.AddVC(vcName, quota, metadata) + else: + ret = "Access Denied!" + dataHandler.Close() + return ret + + +def ListVCs(userName): + ret = [] + dataHandler = DataHandler() + vcList = dataHandler.ListVCs() + for vc in vcList: + if AuthorizationManager.HasAccess(userName, ResourceType.VC, vc["vcName"], Permission.User): + # todo : get other info (resource consumption, quota etc.) about VC? + ret.append(vc) + # web portal (client) can filter out Default VC + dataHandler.Close() + return ret + + +def DeleteVC(userName, vcName): + ret = None + dataHandler = DataHandler() + if AuthorizationManager.IsClusterAdmin(userName): + ret = dataHandler.DeleteVC(vcName) + else: + ret = "Access Denied!" + dataHandler.Close() + return ret + + +def UpdateVC(userName, vcName, quota, metadata): + ret = None + dataHandler = DataHandler() + if AuthorizationManager.IsClusterAdmin(userName): + ret = dataHandler.UpdateVC(vcName, quota, metadata) + else: + ret = "Access Denied!" + dataHandler.Close() + return ret + + if __name__ == '__main__': TEST_SUB_REG_JOB = False TEST_JOB_STATUS = True diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index f41d7581a..2b95bc248 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -19,6 +19,8 @@ class DataHandler: def __init__(self): start_time = timeit.default_timer() + + logger.info ("DataHandler::Init") self.CreateDatabase() server = config["mysql"]["hostname"] @@ -28,17 +30,21 @@ def __init__(self): self.conn = mysql.connector.connect(user=username, password=password, host=server,database=database) - logger.debug ("Get database connection %s" % str(self.conn)) + logger.info ("Get database connection %s" % str(self.conn)) #print "Connecting to server ..." self.jobtablename = "jobs" self.usertablename = "users" + self.identitytablename = "identity" + self.acltablename = "acl" + self.vctablename = "vc" + self.storagetablename = "storage" self.clusterstatustablename = "clusterstatus" self.commandtablename = "commands" self.CreateTable() elapsed = timeit.default_timer() - start_time - logger.debug ("DataHandler initialization, time elapsed %f s" % elapsed) + logger.info ("DataHandler initialization, time elapsed %f s" % elapsed) @@ -53,7 +59,7 @@ def CreateDatabase(self): password = config["mysql"]["password"] conn = mysql.connector.connect(user=username, password=password, - host=server) + host=server) sql = " CREATE DATABASE IF NOT EXISTS `%s` DEFAULT CHARACTER SET 'utf8' " % (database) cursor = conn.cursor() cursor.execute(sql) @@ -74,6 +80,7 @@ def CreateTable(self): `isParent` INT NOT NULL, `jobName` varchar(1024) NOT NULL, `userName` varchar(255) NOT NULL, + `vcName` varchar(255) NOT NULL, `jobStatus` varchar(255) NOT NULL DEFAULT 'unapproved', `jobStatusDetail` LONGTEXT NULL, `jobType` varchar(255) NOT NULL, @@ -87,6 +94,7 @@ def CreateTable(self): `jobLog` LONGTEXT NULL, `retries` int NULL DEFAULT 0, PRIMARY KEY (`id`), + UNIQUE(`jobId`), INDEX (`userName`), INDEX (`jobTime`), INDEX (`jobId`), @@ -152,42 +160,425 @@ def CreateTable(self): cursor.close() + sql = """ + CREATE TABLE IF NOT EXISTS `%s` + ( + `id` INT NOT NULL AUTO_INCREMENT, + `storageType` varchar(255) NOT NULL, + `url` varchar(255) NOT NULL, + `metadata` TEXT NOT NULL, + `vcName` varchar(255) NOT NULL, + `defaultMountPath` varchar(255) NOT NULL, + `time` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT vc_url UNIQUE(`vcName`,`url`), + CONSTRAINT vc_mountPath UNIQUE(`vcName`,`defaultMountPath`) + ) + """ % (self.storagetablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + sql = """ + CREATE TABLE IF NOT EXISTS `%s` + ( + `id` INT NOT NULL AUTO_INCREMENT, + `vcName` varchar(255) NOT NULL UNIQUE, + `quota` varchar(255) NOT NULL, + `metadata` TEXT NOT NULL, + `time` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + PRIMARY KEY (`id`) + ) + """ % (self.vctablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + sql = """ + CREATE TABLE IF NOT EXISTS `%s` + ( + `id` INT NOT NULL AUTO_INCREMENT, + `identityName` varchar(255) NOT NULL UNIQUE, + `identityId` INT NOT NULL, + `groups` MEDIUMTEXT NOT NULL, + `time` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + PRIMARY KEY (`id`) + ) + """ % (self.identitytablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + sql = """ + CREATE TABLE IF NOT EXISTS `%s` + ( + `id` INT NOT NULL AUTO_INCREMENT, + `identityName` varchar(255) NOT NULL, + `identityId` INT NOT NULL, + `resource` varchar(255) NOT NULL, + `permissions` INT NOT NULL, + `isDeny` INT NOT NULL, + `time` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT identityName_resource UNIQUE(`identityName`,`resource`) + ) + """ % (self.acltablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + def AddStorage(self, vcName, url, storageType, metadata, defaultMountPath): + try: + start_time = timeit.default_timer() + sql = "INSERT INTO `"+self.storagetablename+"` (storageType, url, metadata, vcName, defaultMountPath) VALUES (%s,%s,%s,%s,%s)" + cursor = self.conn.cursor() + cursor.execute(sql, (storageType, url, metadata, vcName, defaultMountPath)) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: AddStorage to DB: url : %s, vcName: %s , time elapsed %f s" % (url, vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def DeleteStorage(self, vcName, url): + try: + start_time = timeit.default_timer() + sql = "DELETE FROM `%s` WHERE url = '%s' and vcName = '%s'" % (self.storagetablename, url, vcName) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteStorage: url:%s, vcName:%s, time elapsed %f s" % (url, vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def ListStorages(self, vcName): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT `storageType`,`url`,`metadata`,`vcName`,`defaultMountPath` FROM `%s` WHERE vcName = '%s' " % (self.storagetablename, vcName) + ret = [] + try: + cursor.execute(query) + for (storageType,url,metadata,vcName,defaultMountPath) in cursor: + record = {} + record["vcName"] = vcName + record["url"] = url + record["storageType"] = storageType + record["metadata"] = metadata + record["defaultMountPath"] = defaultMountPath + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: ListStorages time elapsed %f s" % (elapsed)) + return ret + + + def UpdateStorage(self, vcName, url, storageType, metadata, defaultMountPath): + try: + start_time = timeit.default_timer() + sql = """update `%s` set storageType = '%s', metadata = '%s', defaultMountPath = '%s' where vcName = '%s' and url = '%s' """ % (self.storagetablename, storageType, metadata, defaultMountPath, vcName, url) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateStorage: vcName: %s, url: %s, time elapsed %f s" % (vcName, url, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def AddVC(self, vcName, quota, metadata): + try: + start_time = timeit.default_timer() + sql = "INSERT INTO `"+self.vctablename+"` (vcName, quota, metadata) VALUES (%s,%s,%s)" + cursor = self.conn.cursor() + cursor.execute(sql, (vcName, quota, metadata)) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: AddVC to DB: vcName: %s , time elapsed %f s" % (vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def ListVCs(self): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT `vcName`,`quota`,`metadata` FROM `%s`" % (self.vctablename) + ret = [] + try: + cursor.execute(query) + for (vcName,quota,metadata) in cursor: + record = {} + record["vcName"] = vcName + record["quota"] = quota + record["metadata"] = metadata + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: ListVCs time elapsed %f s" % (elapsed)) + return ret + + + def DeleteVC(self, vcName): + try: + start_time = timeit.default_timer() + sql = "DELETE FROM `%s` WHERE vcName = '%s'" % (self.vctablename, vcName) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteVC: vcName: %s , time elapsed %f s" % (vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def UpdateVC(self, vcName, quota, metadata): + try: + start_time = timeit.default_timer() + sql = """update `%s` set quota = '%s', metadata = '%s' where vcName = '%s' """ % (self.vctablename, quota, metadata, vcName) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateVC: vcName: %s , time elapsed %f s" % (vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def GetIdentityInfo(self, identityName): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT `identityName`,`identityId`,`groups` FROM `%s` where `identityName` = '%s'" % (self.identitytablename, identityName) + ret = [] + try: + cursor.execute(query) + for (identityName,identityId,groups) in cursor: + record = {} + record["identityName"] = identityName + record["identityId"] = identityId + record["groups"] = groups + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetIdentityInfo time elapsed %f s" % (elapsed)) + return ret + + + def UpdateIdentityInfo(self, identityName, identityId, groups): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + if len(self.GetIdentityInfo(identityName)) == 0: + sql = "INSERT INTO `"+self.identitytablename+"` (identityName,identityId,groups) VALUES (%s,%s,%s)" + cursor.execute(sql, (identityName, identityId, groups)) + else: + sql = """update `%s` set groups = '%s' where `identityName` = '%s' and `identityId` = '%s' """ % (self.identitytablename, groups, identityName, identityId) + cursor.execute(sql) + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateIdentityInfo %s to database , time elapsed %f s" % (identityName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def GetAceCount(self, identityName, resource): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT count(ALL id) as c FROM `%s` where `identityName` = '%s' and `resource` = '%s'" % (self.acltablename,identityName, resource) + cursor.execute(query) + ret = 0 + for c in cursor: + ret = c[0] + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetAceCount time elapsed %f s" % ( elapsed)) + return ret + + + def UpdateAce(self, identityName, identityId, resource, permissions, isDeny): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + existingAceCount = self.GetAceCount(identityName, resource) + logger.info(existingAceCount) + + if existingAceCount == 0: + sql = "INSERT INTO `"+self.acltablename+"` (identityName,identityId,resource,permissions,isDeny) VALUES (%s,%s,%s,%s,%s)" + cursor.execute(sql, (identityName, identityId, resource, permissions, isDeny)) + else: + sql = """update `%s` set permissions = '%s' where `identityName` = '%s' and `resource` = '%s' """ % (self.acltablename, permissions, identityName, resource) + cursor.execute(sql) + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateAce %s - %s to database , time elapsed %f s" % (identityName, resource, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def DeleteResourceAcl(self, resource): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + sql = "DELETE FROM `%s` WHERE `resource` = '%s'" % (self.acltablename, resource) + cursor.execute(sql) + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteResourceAcl %s, time elapsed %f s" % (resource, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def DeleteAce(self, identityName, resource): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + sql = "DELETE FROM `%s` WHERE `identityName` = '%s' and `resource` = '%s'" % (self.acltablename, identityName, resource) + cursor.execute(sql) + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteAce %s : %s time elapsed %f s" % (resource, identityName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def GetAcl(self): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT `identityName`,`identityId`,`resource`,`permissions`,`isDeny` FROM `%s`" % (self.acltablename) + ret = [] + try: + cursor.execute(query) + for (identityName,identityId,resource,permissions,isDeny) in cursor: + record = {} + record["identityName"] = identityName + record["identityId"] = identityId + record["resource"] = resource + record["permissions"] = permissions + record["isDeny"] = isDeny + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetAcl time elapsed %f s" % ( elapsed)) + return ret + + + def GetResourceAcl(self, resource): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT `identityName`,`identityId`,`resource`,`permissions`,`isDeny` FROM `%s` where `resource` = '%s'" % (self.acltablename, resource) + ret = [] + try: + cursor.execute(query) + for (identityName,identityId,resource,permissions,isDeny) in cursor: + record = {} + record["identityName"] = identityName + record["identityId"] = identityId + record["resource"] = resource + record["permissions"] = permissions + record["isDeny"] = isDeny + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetResourceAcl time elapsed %f s" % ( elapsed)) + return ret + + def AddJob(self, jobParams): try: start_time = timeit.default_timer() - sql = "INSERT INTO `"+self.jobtablename+"` (jobId, familyToken, isParent, jobName, userName, jobType,jobParams ) VALUES (%s,%s,%s,%s,%s,%s,%s)" + sql = "INSERT INTO `"+self.jobtablename+"` (jobId, familyToken, isParent, jobName, userName, vcName, jobType,jobParams ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)" cursor = self.conn.cursor() jobParam = base64.b64encode(json.dumps(jobParams)) - cursor.execute(sql, (jobParams["jobId"], jobParams["familyToken"], jobParams["isParent"], jobParams["jobName"], jobParams["userName"], jobParams["jobType"],jobParam)) + cursor.execute(sql, (jobParams["jobId"], jobParams["familyToken"], jobParams["isParent"], jobParams["jobName"], jobParams["userName"], jobParams["vcName"], jobParams["jobType"],jobParam)) self.conn.commit() cursor.close() elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: added job %s to database, time elapsed %f s" % (jobParams["jobId"],elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False - def GetJobList(self, userName, num = None, status = None, op = ("=","or")): + def GetJobList(self, userName, vcName, num = None, status = None, op = ("=","or")): start_time = timeit.default_timer() ret = [] cursor = self.conn.cursor() try: - query = "SELECT `jobId`,`jobName`,`userName`, `jobStatus`, `jobStatusDetail`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s`" % (self.jobtablename) + query = "SELECT `jobId`,`jobName`,`userName`, 'vcName', `jobStatus`, `jobStatusDetail`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `vcName` = '%s'" % (self.jobtablename, vcName) if userName != "all": - query += " where `userName` = '%s'" % userName - else: - query += " where `id` > -1 " + query += " and `userName` = '%s'" % userName if status is not None: if "," not in status: query += " and `jobStatus` %s '%s'" % (op[0],status) else: status_list = [ " `jobStatus` %s '%s' " % (op[0],s) for s in status.split(',')] status_statement = (" "+op[1]+" ").join(status_list) - query += " and ( %s ) " % status_statement - - + query += " and ( %s ) " % status_statement query += " order by `jobTime` Desc" @@ -201,11 +592,12 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): data = cursor.fetchall() elapsed2 = timeit.default_timer() - start_time2 logger.info ("(fetchall time: %f)" % (elapsed2)) - for (jobId,jobName,userName, jobStatus,jobStatusDetail, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in data: + for (jobId,jobName,userName, vcName, jobStatus,jobStatusDetail, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in data: record = {} record["jobId"] = jobId record["jobName"] = jobName record["userName"] = userName + record["vcName"] = vcName record["jobStatus"] = jobStatus record["jobStatusDetail"] = jobStatusDetail record["jobType"] = jobType @@ -217,7 +609,7 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): record["errorMsg"] = errorMsg record["jobMeta"] = jobMeta ret.append(record) - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) pass cursor.close() @@ -228,14 +620,14 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): def GetJob(self, **kwargs): start_time = timeit.default_timer() - valid_keys = ["jobId", "familyToken", "isParent", "jobName", "userName", "jobStatus", "jobType", "jobTime"] + valid_keys = ["jobId", "familyToken", "isParent", "jobName", "userName", "vcName", "jobStatus", "jobType", "jobTime"] if len(kwargs) != 1: return [] key, expected = kwargs.popitem() if key not in valid_keys: logger.error("DataHandler_GetJob: key is not in valid keys list...") return [] cursor = self.conn.cursor() - query = "SELECT `jobId`,`familyToken`,`isParent`,`jobName`,`userName`, `jobStatus`, `jobStatusDetail`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `%s` = '%s' " % (self.jobtablename,key,expected) + query = "SELECT `jobId`,`familyToken`,`isParent`,`jobName`,`userName`, 'vcName', `jobStatus`, `jobStatusDetail`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `%s` = '%s' " % (self.jobtablename,key,expected) cursor.execute(query) columns = [column[0] for column in cursor.description] ret = [dict(zip(columns, row)) for row in cursor.fetchall()] @@ -248,7 +640,7 @@ def GetJob(self, **kwargs): def AddCommand(self,jobId,command): try: start_time = timeit.default_timer() - sql = "INSERT INTO `"+self.jobtablename+"` (jobId, command) VALUES (%s,%s)" + sql = "INSERT INTO `"+self.commandtablename+"` (jobId, command) VALUES (%s,%s)" cursor = self.conn.cursor() cursor.execute(sql, (jobId, command)) self.conn.commit() @@ -256,7 +648,7 @@ def AddCommand(self,jobId,command): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: add command to database, jobId: %s , time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -290,7 +682,7 @@ def FinishCommand(self,commandId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: set command %s as finished , time elapsed %f s" % (commandId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -325,7 +717,7 @@ def KillJob(self,jobId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: mark job %s to be killed in database, time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -341,7 +733,7 @@ def ApproveJob(self,jobId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: approved job %s , time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -349,14 +741,15 @@ def ApproveJob(self,jobId): def GetPendingJobs(self): start_time = timeit.default_timer() cursor = self.conn.cursor() - query = "SELECT `jobId`,`jobName`,`userName`, `jobStatus`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `jobStatus` <> 'error' and `jobStatus` <> 'failed' and `jobStatus` <> 'finished' and `jobStatus` <> 'killed' order by `jobTime` DESC" % (self.jobtablename) + query = "SELECT `jobId`,`jobName`,`userName`, 'vcName', `jobStatus`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `jobStatus` <> 'error' and `jobStatus` <> 'failed' and `jobStatus` <> 'finished' and `jobStatus` <> 'killed' order by `jobTime` DESC" % (self.jobtablename) cursor.execute(query) ret = [] - for (jobId,jobName,userName, jobStatus, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in cursor: + for (jobId,jobName,userName,vcName, jobStatus, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in cursor: record = {} record["jobId"] = jobId record["jobName"] = jobName record["userName"] = userName + record["vcName"] = vcName record["jobStatus"] = jobStatus record["jobType"] = jobType record["jobDescriptionPath"] = jobDescriptionPath @@ -384,7 +777,7 @@ def SetJobError(self,jobId,errorMsg): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: set job %s error status in database, time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -400,7 +793,7 @@ def UpdateJobTextField(self,jobId,field,value): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: update job %s, field %s , time elapsed %f s" % (jobId, field, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -414,7 +807,7 @@ def GetJobTextField(self,jobId,field): cursor.execute(query) for (jobId, value) in cursor: ret = value - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) pass cursor.close() @@ -456,7 +849,7 @@ def UpdateClusterStatus(self,clusterStatus): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: update cluster status, time elapsed %f s" % (elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -472,7 +865,7 @@ def GetClusterStatus(self): for (t, value) in cursor: ret = json.loads(base64.b64decode(value)) time = t - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) pass cursor.close() @@ -480,6 +873,7 @@ def GetClusterStatus(self): logger.info ("DataHandler: get cluster status , time elapsed %f s" % (elapsed)) return ret, time + def GetUsersCount(self, username): start_time = timeit.default_timer() cursor = self.conn.cursor() @@ -505,7 +899,7 @@ def AddUser(self, username,userId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: add user %s to database , time elapsed %f s" % (username, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False diff --git a/src/utils/SQLDataHandler.py b/src/utils/SQLDataHandler.py index 6b0b5043b..317c399d5 100755 --- a/src/utils/SQLDataHandler.py +++ b/src/utils/SQLDataHandler.py @@ -44,7 +44,7 @@ def TestSQLConnection(conn): c.execute(sql) c.close() connected = True - except Exception, e: + except Exception as e: logger.error ("Exception: %s" % str(e) ) connected = False return connected @@ -58,12 +58,12 @@ def GetConnection(): logger.debug("current connection pool size %d" %(global_vars["sql_connections"].qsize())) try: conn = global_vars["sql_connections"].get(block = False) - except Exception, e: + except Exception as e: logger.error ("Exception: %s" % str(e) ) if conn is not None: logger.debug("Get a database connection from connection pool, current pool size %d: connection Id: %s" %(global_vars["sql_connections"].qsize(), str(conn))) - except Exception, e: + except Exception as e: logger.error ("Exception: %s" % str(e) ) finally: if acquired: @@ -74,7 +74,7 @@ def GetConnection(): if conn is None and global_vars["sql_connection_num"] > 0: try: conn = global_vars["sql_connections"].get(timeout = 1) - except Exception, e: + except Exception as e: pass if conn is not None: @@ -94,7 +94,7 @@ def GetConnection(): acquired = global_vars["sql_lock"].acquire() try: global_vars["sql_connection_num"] += 1 - except Exception, e: + except Exception as e: logger.error ("Exception: %s" % str(e) ) finally: if acquired: @@ -129,7 +129,7 @@ def ReturnConnection(conn): conn.close() except: pass - except Exception, e: + except Exception as e: logger.error ("Exception: %s" % str(e) ) finally: if not returnedConn: @@ -154,6 +154,10 @@ def __init__(self): #print "Connecting to server ..." self.jobtablename = "jobs-%s" % config["clusterId"] self.usertablename = "users-%s" % config["clusterId"] + self.acltablename = "acl-%s" % config["clusterId"] + self.identitytablename = "identity-%s" % config["clusterId"] + self.vctablename = "vc-%s" % config["clusterId"] + self.storagetablename = "storage-%s" % config["clusterId"] self.clusterstatustablename = "clusterstatus-%s" % config["clusterId"] self.commandtablename = "commands-%s" % config["clusterId"] @@ -191,11 +195,12 @@ def CreateTable(self): CREATE TABLE [dbo].[%s] ( [id] INT IDENTITY (1, 1) NOT NULL, - [jobId] varchar(50) NOT NULL, + [jobId] varchar(50) NOT NULL UNIQUE, [familyToken] varchar(50) NOT NULL, [isParent] INT NOT NULL, [jobName] varchar(max) NOT NULL, [userName] varchar(255) NOT NULL, + [vcName] varchar(255) NOT NULL, [jobStatus] varchar(255) NOT NULL DEFAULT 'unapproved', [jobStatusDetail] varchar(max) NULL, [jobType] varchar(max) NOT NULL, @@ -278,24 +283,413 @@ def CreateTable(self): cursor.close() + sql = """ + if not exists (select * from sysobjects where name='%s' and xtype='U') + CREATE TABLE [dbo].[%s] + ( + [id] INT IDENTITY (1, 1) NOT NULL, + [storageType] varchar(255) NOT NULL, + [url] varchar(255) NOT NULL, + [metadata] varchar(max) NOT NULL, + [vcName] varchar(255) NOT NULL, + [defaultMountPath] varchar(255) NOT NULL, + [time] DATETIME DEFAULT (getdate()) NOT NULL, + PRIMARY KEY CLUSTERED ([id] ASC), + CONSTRAINT vc_url UNIQUE (vcName,url), + CONSTRAINT vc_mountPath UNIQUE (vcName,defaultMountPath) + ) + """ % (self.storagetablename,self.storagetablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + sql = """ + if not exists (select * from sysobjects where name='%s' and xtype='U') + CREATE TABLE [dbo].[%s] + ( + [id] INT IDENTITY (1, 1) NOT NULL, + [vcName] varchar(255) NOT NULL UNIQUE, + [quota] varchar(255) NOT NULL, + [metadata] varchar(max) NOT NULL, + [time] DATETIME DEFAULT (getdate()) NOT NULL, + PRIMARY KEY CLUSTERED ([id] ASC) + ) + """ % (self.vctablename,self.vctablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + sql = """ + if not exists (select * from sysobjects where name='%s' and xtype='U') + CREATE TABLE [dbo].[%s] + ( + [id] INT IDENTITY (1, 1) NOT NULL, + [identityName] varchar(255) NOT NULL UNIQUE, + [identityId] INT NOT NULL, + [groups] varchar(max) NOT NULL, + [time] DATETIME DEFAULT (getdate()) NOT NULL, + PRIMARY KEY CLUSTERED ([id] ASC) + ) + """ % (self.identitytablename,self.identitytablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + sql = """ + if not exists (select * from sysobjects where name='%s' and xtype='U') + CREATE TABLE [dbo].[%s] + ( + [id] INT IDENTITY (1, 1) NOT NULL, + [identityName] varchar(255) NOT NULL, + [identityId] INT NOT NULL, + [resource] varchar(255) NOT NULL, + [permissions] INT NOT NULL, + [isDeny] INT NOT NULL, + [time] DATETIME DEFAULT (getdate()) NOT NULL, + PRIMARY KEY CLUSTERED ([id] ASC), + CONSTRAINT identityName_resource UNIQUE (identityName,resource) + ) + """ % (self.acltablename,self.acltablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + + + def AddStorage(self, vcName, url, storageType, metadata, defaultMountPath): + try: + start_time = timeit.default_timer() + sql = "INSERT INTO [%s] (storageType, url, metadata, vcName, defaultMountPath) VALUES (?,?,?,?,?)""" % self.storagetablename + cursor = self.conn.cursor() + cursor.execute(sql, (storageType, url, metadata, vcName, defaultMountPath)) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: AddStorage to DB: url : %s, vcName: %s , time elapsed %f s" % (url, vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def DeleteStorage(self, vcName, url): + try: + start_time = timeit.default_timer() + sql = "DELETE FROM [%s] WHERE [url] = '%s' and [vcName] = '%s'" % (self.storagetablename, url, vcName) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteStorage: url:%s, vcName:%s, time elapsed %f s" % (url, vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def ListStorages(self, vcName): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT [vcName],[url],[storageType],[metadata],[defaultMountPath] FROM [%s] WHERE [vcName] = '%s' " % (self.storagetablename, vcName) + ret = [] + try: + cursor.execute(query) + for (vcName,url,storageType,metadata,defaultMountPath) in cursor: + record = {} + record["vcName"] = vcName + record["url"] = url + record["storageType"] = storageType + record["metadata"] = metadata + record["defaultMountPath"] = defaultMountPath + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: ListStorages time elapsed %f s" % (elapsed)) + return ret + + + def UpdateStorage(self, vcName, url, storageType, metadata, defaultMountPath): + try: + start_time = timeit.default_timer() + sql = """update [%s] set storageType = '%s', metadata = '%s', defaultMountPath = '%s' where [vcName] = '%s' and [url] = '%s' """ % (self.storagetablename, storageType, metadata, defaultMountPath, vcName, url) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateStorage: vcName: %s, url: %s, time elapsed %f s" % (vcName, url, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def AddVC(self, vcName, quota, metadata): + try: + start_time = timeit.default_timer() + sql = """INSERT INTO [%s] (vcName, quota, metadata) VALUES (?,?,?)""" % self.vctablename + cursor = self.conn.cursor() + cursor.execute(sql, vcName, quota, metadata) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: AddVC to DB: vcName: %s , time elapsed %f s" % (vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def ListVCs(self, vcName): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT [vcName],[quota],[metadata] FROM [%s]" % (self.vctablename) + ret = [] + try: + cursor.execute(query) + for (vcName,quota,metadata) in cursor: + record = {} + record["vcName"] = vcName + record["quota"] = quota + record["metadata"] = metadata + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: ListVCs time elapsed %f s" % ( elapsed)) + return ret + + + def DeleteVC(self, vcName): + try: + start_time = timeit.default_timer() + sql = "DELETE FROM [%s] WHERE [vcName] = '%s'" % (self.vctablename, vcName) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteVC: vcName: %s , time elapsed %f s" % (vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def UpdateVC(self, vcName, quota, metadata): + try: + start_time = timeit.default_timer() + sql = """update [%s] set quota = '%s', metadata = '%s' where [vcName] = '%s'""" % (self.vctablename, quota, metadata, vcName) + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateVC: vcName: %s , time elapsed %f s" % (vcName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def GetIdentityInfo(self, identityName): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT [identityName],[identityId],[groups] FROM [%s] where [identityName] = '%s'" % (self.identitytablename, identityName) + ret = [] + try: + cursor.execute(query) + for (identityName,identityId,groups) in cursor: + record = {} + record["identityName"] = identityName + record["identityId"] = identityId + record["groups"] = groups + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetIdentityInfo time elapsed %f s" % (elapsed)) + return ret + + + def UpdateIdentityInfo(self, identityName, identityId, groups): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + if len(self.GetIdentityInfo(identityName)) == 0: + sql = """INSERT INTO [%s] (identityName,identityId,groups) VALUES (?,?,?)""" % self.identitytablename + cursor.execute(sql, identityName, identityId, groups) + else: + sql = """update [%s] set groups = '%s' where [identityName] = '%s' and [identityId] = '%s' """ % (self.identitytablename, groups, identityName, identityId) + cursor.execute(sql) + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateIdentityInfo %s to database , time elapsed %f s" % (identityName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def GetAceCount(self, identityId, resource): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT count(ALL id) as c FROM [%s] where [identityId] = '%s' and [resource] = '%s'" % (self.acltablename,identityId, resource) + cursor.execute(query) + ret = 0 + for c in cursor: + ret = c[0] + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetAceCount time elapsed %f s" % ( elapsed)) + return ret + + + def UpdateAce(self, identityName, identityId, resource, permissions, isDeny): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + if self.GetAceCount(identityId, resource) == 0: + sql = """INSERT INTO [%s] (identityName,identityId,resource,permissions,isDeny) VALUES (?,?,?,?,?)""" % self.acltablename + cursor.execute(sql, identityName, identityId, resource, permissions, isDeny) + else: + sql = """update [%s] set permissions = '%s' where [identityId] = '%s' and [resource] = '%s' """ % (self.acltablename, permissions, identityId, resource) + cursor.execute(sql) + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: UpdateAce %s - %s to database , time elapsed %f s" % (identityName, resource, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def DeleteResourceAcl(self, resource): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + sql = "DELETE FROM [%s] WHERE [resource] = '%s'" % (self.acltablename, resource) + cursor = self.conn.cursor() + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteResourceAcl %s, time elapsed %f s" % (resource, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def DeleteAce(self, identityName, resource): + try: + start_time = timeit.default_timer() + cursor = self.conn.cursor() + + sql = "DELETE FROM [%s] WHERE [identityName] = '%s' and [resource] = '%s'" % (self.acltablename, identityName, resource) + cursor = self.conn.cursor() + + self.conn.commit() + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: DeleteAce %s : %s, time elapsed %f s" % (resource, identityName, elapsed)) + return True + except Exception as e: + logger.error('Exception: '+ str(e)) + return False + + + def GetAcl(self): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT [identityName],[identityId],[resource],[permissions],[isDeny] FROM [%s]" % (self.acltablename) + ret = [] + try: + cursor.execute(query) + for (identityName,identityId,resource,permissions,isDeny) in cursor: + record = {} + record["identityName"] = identityName + record["identityId"] = identityId + record["resource"] = resource + record["permissions"] = permissions + record["isDeny"] = isDeny + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetAcl time elapsed %f s" % ( elapsed)) + return ret + + + def GetResourceAcl(self, resource): + start_time = timeit.default_timer() + cursor = self.conn.cursor() + query = "SELECT [identityName],[identityId],[resource],[permissions],[isDeny] FROM [%s] where [resource] = '%s'" % (self.acltablename, resource) + ret = [] + try: + cursor.execute(query) + for (identityName,identityId,resource,permissions,isDeny) in cursor: + record = {} + record["identityName"] = identityName + record["identityId"] = identityId + record["resource"] = resource + record["permissions"] = permissions + record["isDeny"] = isDeny + ret.append(record) + except Exception as e: + logger.error('Exception: '+ str(e)) + pass + cursor.close() + elapsed = timeit.default_timer() - start_time + logger.info ("DataHandler: GetResourceAcl time elapsed %f s" % ( elapsed)) + return ret + + def AddJob(self, jobParams): try: start_time = timeit.default_timer() - sql = """INSERT INTO [%s] (jobId, familyToken, isParent, jobName, userName, jobType,jobParams ) VALUES (?,?,?,?,?,?,?)""" % self.jobtablename + sql = """INSERT INTO [%s] (jobId, familyToken, isParent, jobName, userName, vcName, jobType,jobParams ) VALUES (?,?,?,?,?,?,?)""" % self.jobtablename cursor = self.conn.cursor() jobParam = base64.b64encode(json.dumps(jobParams)) - cursor.execute(sql, jobParams["jobId"], jobParams["familyToken"], jobParams["isParent"], jobParams["jobName"], jobParams["userName"], jobParams["jobType"],jobParam) + cursor.execute(sql, jobParams["jobId"], jobParams["familyToken"], jobParams["isParent"], jobParams["jobName"], jobParams["userName"], jobParams["vcName"], jobParams["jobType"],jobParam) self.conn.commit() cursor.close() elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: added job %s to database, time elapsed %f s" % (jobParams["jobId"],elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False - def GetJobList(self, userName, num = None, status = None, op = ("=","or")): + def GetJobList(self, userName, vcName, num = None, status = None, op = ("=","or")): start_time = timeit.default_timer() ret = [] cursor = self.conn.cursor() @@ -304,20 +698,16 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): selectNum = "" else: selectNum = " TOP %s " % str(num) - query = "SELECT %s [jobId],[jobName],[userName], [jobStatus], [jobStatusDetail], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s]" % (selectNum, self.jobtablename) + query = "SELECT %s [jobId],[jobName],[userName], [vcName], [jobStatus], [jobStatusDetail], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s] where [vcName] = '%s'" % (selectNum, self.jobtablename, vcName) if userName != "all": - query += " where [userName] = '%s'" % userName - else: - query += " where [id] > -1 " + query += " and [userName] = '%s'" % userName if status is not None: if "," not in status: query += " and [jobStatus] %s '%s'" % (op[0],status) else: status_list = [ " [jobStatus] %s '%s' " % (op[0],s) for s in status.split(',')] status_statement = (" "+op[1]+" ").join(status_list) - query += " and ( %s ) " % status_statement - - + query += " and ( %s ) " % status_statement query += " order by [jobTime] Desc" start_time1 = timeit.default_timer() @@ -327,11 +717,12 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): data = cursor.fetchall() elapsed2 = timeit.default_timer() - start_time2 logger.info ("(fetchall time: %f)" % (elapsed2)) - for (jobId,jobName,userName, jobStatus,jobStatusDetail, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in data: + for (jobId,jobName,userName, vcName,jobStatus,jobStatusDetail, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in data: record = {} record["jobId"] = jobId record["jobName"] = jobName record["userName"] = userName + record["vcName"] = vcName record["jobStatus"] = jobStatus record["jobStatusDetail"] = jobStatusDetail record["jobType"] = jobType @@ -343,7 +734,7 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): record["errorMsg"] = errorMsg record["jobMeta"] = jobMeta ret.append(record) - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) pass cursor.close() @@ -354,14 +745,14 @@ def GetJobList(self, userName, num = None, status = None, op = ("=","or")): def GetJob(self, **kwargs): start_time = timeit.default_timer() - valid_keys = ["jobId", "familyToken", "isParent", "jobName", "userName", "jobStatus", "jobType", "jobTime"] + valid_keys = ["jobId", "familyToken", "isParent", "jobName", "userName", "vcName", "jobStatus", "jobType", "jobTime"] if len(kwargs) != 1: return [] key, expected = kwargs.popitem() if key not in valid_keys: logger.error("DataHandler_GetJob: key is not in valid keys list...") return [] cursor = self.conn.cursor() - query = "SELECT [jobId],[familyToken],[isParent],[jobName],[userName], [jobStatus], [jobStatusDetail], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s] where [%s] = '%s' " % (self.jobtablename,key,expected) + query = "SELECT [jobId],[familyToken],[isParent],[jobName],[userName],[vcName], [jobStatus], [jobStatusDetail], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s] where [%s] = '%s' " % (self.jobtablename,key,expected) cursor.execute(query) columns = [column[0] for column in cursor.description] ret = [dict(zip(columns, row)) for row in cursor.fetchall()] @@ -382,7 +773,7 @@ def AddCommand(self,jobId,command): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: add command to database, jobId: %s , time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -416,7 +807,7 @@ def FinishCommand(self,commandId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: set command %s as finished , time elapsed %f s" % (commandId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -451,7 +842,7 @@ def KillJob(self,jobId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: mark job %s to be killed in database, time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -467,7 +858,7 @@ def ApproveJob(self,jobId): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: approved job %s , time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False @@ -475,14 +866,15 @@ def ApproveJob(self,jobId): def GetPendingJobs(self): start_time = timeit.default_timer() cursor = self.conn.cursor() - query = "SELECT [jobId],[jobName],[userName], [jobStatus], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s] where [jobStatus] <> 'error' and [jobStatus] <> 'failed' and [jobStatus] <> 'finished' and [jobStatus] <> 'killed' order by [jobTime] DESC" % (self.jobtablename) + query = "SELECT [jobId],[jobName],[userName], [vcName], [jobStatus], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s] where [jobStatus] <> 'error' and [jobStatus] <> 'failed' and [jobStatus] <> 'finished' and [jobStatus] <> 'killed' order by [jobTime] DESC" % (self.jobtablename) cursor.execute(query) ret = [] - for (jobId,jobName,userName, jobStatus, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in cursor: + for (jobId,jobName,userName, vcName, jobStatus, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in cursor: record = {} record["jobId"] = jobId record["jobName"] = jobName record["userName"] = userName + record["vcName"] = vcName record["jobStatus"] = jobStatus record["jobType"] = jobType record["jobDescriptionPath"] = jobDescriptionPath @@ -510,7 +902,7 @@ def SetJobError(self,jobId,errorMsg): elapsed = timeit.default_timer() - start_time logger.info ("DataHandler: set job %s error status in database, time elapsed %f s" % (jobId, elapsed)) return True - except Exception, e: + except Exception as e: logger.error('Exception: '+ str(e)) return False diff --git a/src/utils/authorization.py b/src/utils/authorization.py new file mode 100644 index 000000000..4e10d769f --- /dev/null +++ b/src/utils/authorization.py @@ -0,0 +1,221 @@ +from DataHandler import DataHandler +from MyLogger import MyLogger +import json +import requests + +logger = MyLogger() + +def enum(*sequential, **named): + enums = dict(zip(sequential, range(len(sequential))), **named) + reverse = dict((value, key) for key, value in enums.items()) + enums['reverse_mapping'] = reverse + return type('Enum', (), enums) + +Permission = enum(Unauthorized=0, User=1, Collaborator=3, Admin=7) +ResourceType = enum(Cluster=1, VC=2, Job=3) +''' + +import enum +class Permission(enum.Enum): + Unauthorized = 0 + User = 1 + Collaborator=3 + Admin = 7 + + +class ResourceType(enum.Enum): + Cluster = 1 + NFS = 2 + VC = 3 + Job = 4 + Template = 5 +''' + + +class AuthorizationManager: + #TODO : Add Cache to aovid frequent DB calls + + CLUSTER_ACL_PATH = "Cluster" + ACL_DELIMITER = "/" + TYPE_NAME_DELIMITER = ":" + WinBindUrl = "http://onenet40.redmond.corp.microsoft.com/domaininfo/GetUserId?userName={0}" #TODO:read form config + + # Check if user has requested access (based on effective ACL) on the specified resource. + @staticmethod + def _HasAccess(identityName, resourceAclPath, permissions): + dataHandler = DataHandler() + try: + logger.info('HasAccess invoked!') + identities = [] + identities.append(IdentityManager(AuthorizationManager.WinBindUrl).GetIdentityInfo(identityName)["groups"]) + + logger.info('initial resourceAclPath ' + resourceAclPath) + #TODO: handle isDeny + while resourceAclPath: + logger.info('resourceAclPath ' + resourceAclPath) + acl = dataHandler.GetResourceAcl(resourceAclPath) + for ace in acl: + for identity in identities: + if ace["identityId"] == identity or ace["identityName"] == identityName: + permissions = permissions & (~ace["permissions"]) + if not permissions: + logger.info('access : Yes') + return True + + resourceAclPath = AuthorizationManager.__GetParentPath(resourceAclPath) + logger.info('access : No') + return False + + except Exception as e: + logger.error('Exception: '+ str(e)) + logger.warn("HasAccess failed for user %s" % identityName) + logger.info('access : No (exception)') + return False + + finally: + dataHandler.Close() + + + @staticmethod + def HasAccess(identityName, resourceType, resourceName, permissions): + resourceAclPath = AuthorizationManager.GetResourceAclPath(resourceName, resourceType) + return AuthorizationManager._HasAccess(identityName, resourceAclPath, permissions) + + + # Add/Update a specific access control entry. + @staticmethod + def UpdateAce(identityName, resourceAclPath, permissions, isDeny): + dataHandler = DataHandler() + try: + identityId = IdentityManager(AuthorizationManager.WinBindUrl).GetIdentityInfo(identityName)["uid"] + if identityId == -1: + identityId = 0 + return dataHandler.UpdateAce(identityName, identityId, resourceAclPath, permissions, isDeny) + + except Exception as e: + logger.error('Exception: '+ str(e)) + logger.warn("Fail to Update Ace for user %s" % identityName) + return False + + finally: + dataHandler.Close() + + + @staticmethod + def DeleteAce(identityName, resourceAclPath): + dataHandler = DataHandler() + try: + return dataHandler.DeleteAce(identityName, resourceAclPath) + + except Exception as e: + logger.error('Exception: '+ str(e)) + logger.warn("Fail to Delete Ace for user %s" % identityName) + return False + + finally: + dataHandler.Close() + + + @staticmethod + def DeleteResourceAcl(resourceAclPath): + dataHandler = DataHandler() + try: + return dataHandler.DeleteResourceAcl(resourceAclPath) + + except Exception as e: + logger.error('Exception: '+ str(e)) + logger.warn("DeleteResourceAcl failed for %s" % resourceAclPath) + return False + + finally: + dataHandler.Close() + + + # Return all access control entries (for resources on which user has read access). + @staticmethod + def __GetAccessibleAcl(userName, permissions): + dataHandler = DataHandler() + try: + acl = dataHandler.GetAcl() + ret = [] + + for ace in acl: + if AuthorizationManager._HasAccess(userName, ace["resource"], permissions): #resource + ret.append(ace) + + return ret + + except Exception as e: + logger.error('Exception: '+ str(e)) + logger.warn("Fail to get ACL for user %s, return empty list" % userName) + return [] + + finally: + dataHandler.Close() + + + @staticmethod + def GetAcl(username): + return AuthorizationManager.__GetAccessibleAcl(username, Permission.User) + + + @staticmethod + def IsClusterAdmin(userName): + return AuthorizationManager._HasAccess(userName, AuthorizationManager.CLUSTER_ACL_PATH, Permission.Admin) + + + @staticmethod + def __GetParentPath(aclPath): + if AuthorizationManager.ACL_DELIMITER in aclPath: + return aclPath.rsplit(AuthorizationManager.ACL_DELIMITER, 1)[0] + else: + return "" + + + @staticmethod + def GetResourceAclPath(resourceIdentifier, resourceType): + if (resourceType == ResourceType.VC): + return AuthorizationManager.CLUSTER_ACL_PATH + AuthorizationManager.ACL_DELIMITER + ResourceType.reverse_mapping[resourceType] + AuthorizationManager.TYPE_NAME_DELIMITER + resourceIdentifier.strip(AuthorizationManager.ACL_DELIMITER) + elif resourceType == ResourceType.Cluster: + return AuthorizationManager.CLUSTER_ACL_PATH + + + +class IdentityManager: + serverUrl = "" + #TODO: do we need to cache? + + def __init__(self, winbindServerUrl): + self.serverUrl = winbindServerUrl + + + def GetIdentityInfo(self, identityName): + dataHandler = DataHandler() + try: + # winbind (depending on configs) handles nested groups for userIds + response = requests.get(self.serverUrl.format(identityName)) + info = json.loads(response.text) + + dataHandler.UpdateIdentityInfo(identityName, info["uid"], info["groups"]) + + return info + except Exception as e: + logger.error('Exception: '+ str(e)) + logger.warn("GetIdentityInfo from winbind failed for identity %s" % identityName) + + info = {} + info["uid"] = -1 + info["gid"] = -1 + info["groups"] = [-1] + + lst = dataHandler.GetIdentityInfo(identityName) + if lst: + info["uid"] = lst[0][1] + info["gid"] = lst[0][1] + info["groups"] = lst[0][2] + else: + logger.warn("GetIdentityInfo : Identity %s not found in DB" % identityName) + return info + + finally: + dataHandler.Close() \ No newline at end of file From 644521e456f3b9e3bf6b94c426aed58883c7ea5e Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Mon, 8 Apr 2019 05:56:53 +0000 Subject: [PATCH 033/595] fixed a few bugs related to port mapping --- src/ClusterManager/job_manager.py | 16 ++++++++++++---- src/Jobs_Templete/DistJob.yaml.template | 7 ++++++- src/Jobs_Templete/RegularJob.yaml.template | 7 ++++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 7853d7edb..0ce7aeb7f 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -681,10 +681,18 @@ def UpdateJobStatus(job): if job["jobStatus"] != "running": dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","running") - if "interactivePort" in jobParams and ("hostNetwork" not in jobParams or not jobParams["hostNetwork"]): - serviceAddress = k8sUtils.GetServiceAddress(job["jobId"]) - serviceAddress = base64.b64encode(json.dumps(serviceAddress)) - dataHandler.UpdateJobTextField(job["jobId"],"endpoints",serviceAddress) + if "interactivePort" in jobParams and jobParams["jobtrainingtype"] != "PSDistJob": + if "hostNetwork" not in jobParams or not jobParams["hostNetwork"]: + serviceAddress = k8sUtils.GetServiceAddress(job["jobId"]) + serviceAddress = base64.b64encode(json.dumps(serviceAddress)) + dataHandler.UpdateJobTextField(job["jobId"],"endpoints",serviceAddress) + else: + serviceAddress = k8sUtils.GetServiceAddress(job["jobId"]) + for sidx in range(len(serviceAddress)): + serviceAddress[sidx]["hostPort"] = serviceAddress[sidx]["containerPort"] + serviceAddress = base64.b64encode(json.dumps(serviceAddress)) + dataHandler.UpdateJobTextField(job["jobId"],"endpoints",serviceAddress) + elif result.strip() == "Failed": printlog("Job %s fails, cleaning..." % job["jobId"]) diff --git a/src/Jobs_Templete/DistJob.yaml.template b/src/Jobs_Templete/DistJob.yaml.template index 3d0be1899..289ae4404 100755 --- a/src/Jobs_Templete/DistJob.yaml.template +++ b/src/Jobs_Templete/DistJob.yaml.template @@ -68,6 +68,8 @@ spec: - mountPath: /etc/resolv.conf name: resolv {% endif %} + - mountPath: /dev/shm + name: dshm env: - name: FAMILY_TOKEN value: {{ job["familyToken"] }} @@ -127,4 +129,7 @@ spec: type: {{ mp.type }} {% endif %} {% endif %} - {% endfor %} \ No newline at end of file + {% endfor %} + - name: dshm + emptyDir: + medium: Memory \ No newline at end of file diff --git a/src/Jobs_Templete/RegularJob.yaml.template b/src/Jobs_Templete/RegularJob.yaml.template index 6fd8c28f6..08a4ba55d 100755 --- a/src/Jobs_Templete/RegularJob.yaml.template +++ b/src/Jobs_Templete/RegularJob.yaml.template @@ -72,6 +72,8 @@ spec: - mountPath: /freeflow name: freeflow {% endif %} + - mountPath: /dev/shm + name: dshm env: - name: FAMILY_TOKEN value: {{ job["familyToken"] }} @@ -132,4 +134,7 @@ spec: - name: freeflow hostPath: path: /freeflow - {% endif %} \ No newline at end of file + {% endif %} + - name: dshm + emptyDir: + medium: Memory \ No newline at end of file From 3375ceac9f239d02c4603263799c3fe6caca4996 Mon Sep 17 00:00:00 2001 From: Deepak Bansal Date: Tue, 9 Apr 2019 12:15:19 -0700 Subject: [PATCH 034/595] deploy commands --- src/ClusterBootstrap/deploy.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 808ed3181..dab300a82 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -3140,7 +3140,8 @@ def run_command( args, command, nargs, parser ): response = requests.get(url) print(response) elif nargs[1] == "update": - url = "http://%s:%s/UpdateVC?vcName=%s"a=%s&metadata=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3], nargs[4]) + url = "http://%s:%s/UpdateVC?vcName=%s"a=%s&metadata=%s&userName=Administrator" \ + % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3], nargs[4]) response = requests.get(url) print(response) elif nargs[1] == "delete": @@ -3175,10 +3176,29 @@ def run_command( args, command, nargs, parser ): url = "http://%s:%s/UpdateAce?identityName=%s&resourceType=%s&resourceName=%s&permissions=%s&userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3], nargs[4], nargs[5]) response = requests.get(url) print(response) - elif nargs[1] == "get": + elif nargs[1] == "list": url = "http://%s:%s/GetACL?userName=Administrator" % (config["kubernetes_master_node"][0],config["restfulapiport"]) response = requests.get(url) - print(response) + print(response.text) + elif nargs[0] == "job": + if len(nargs) >= 2: + if nargs[1] == "add": + url = "http://%s:%s/SubmitJob?jobName=%s&vcName=%s&resourcegpu=%s&gpuType=%s&dataPath=%s&workPath=%s&image=%s&jobType=%s&preemptionAllowed=%s&userName=Administrator" \ + % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3], nargs[4], nargs[5], nargs[6], nargs[7], nargs[8], nargs[9], nargs[10]) + response = requests.get(url) + print(response.text) + elif nargs[1] == "delete": + url = "http://%s:%s/KillJob?jobId=%s&userName=Administrator" \ + % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2]) + response = requests.get(url) + print(response.text) + elif nargs[1] == "list": + url = "http://%s:%s/ListJobs?vcName=%s&jobOwner=%s&num=%s&userName=Administrator" \ + % (config["kubernetes_master_node"][0],config["restfulapiport"], nargs[2], nargs[3], nargs[4]) + response = requests.get(url) + print(response.text) + + elif command == "updateworker": From 406762fdbf064fc5e5ee4d67d4cef508182810c3 Mon Sep 17 00:00:00 2001 From: Deepak Bansal Date: Tue, 9 Apr 2019 12:17:24 -0700 Subject: [PATCH 035/595] job scheduling and other misc fixes --- src/ClusterManager/job_manager.py | 191 ++++++++++++++++-- src/Jobs_Templete/DistJob.yaml.template | 5 +- src/Jobs_Templete/RegularJob.yaml.template | 3 + src/RestAPI/dlwsrestapi.py | 9 +- .../WebPortal/Controllers/dlwsController.cs | 17 +- src/utils/JobRestAPIUtils.py | 34 +++- src/utils/MySQLDataHandler.py | 42 +--- src/utils/SQLDataHandler.py | 38 +--- 8 files changed, 241 insertions(+), 98 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index b45783a62..e30009097 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -40,7 +40,7 @@ def printlog(msg): - print "%s - %s" % (datetime.datetime.utcnow().strftime("%x %X"),msg) + print("%s - %s" % (datetime.datetime.utcnow().strftime("%x %X"),msg)) def LoadJobParams(jobParamsJsonStr): return json.loads(jobParamsJsonStr) @@ -49,7 +49,7 @@ def cmd_exec(cmdStr): try: output = subprocess.check_output(["bash","-c", cmdStr]) except Exception as e: - print e + print(e) output = "" return output @@ -246,6 +246,11 @@ def SubmitRegularJob(job): jobParams["annotations"]["pod.alpha/DeviceInformation"] = "'" + json.dumps(podInfo) + "'" jobParams["resourcegpu"] = 0 # gpu requests specified through annotation + if "gpuType" in jobParams: + if "nodeSelector" not in jobParams: + jobParams["nodeSelector"] = {} + jobParams["nodeSelector"]["gpuType"] = jobParams["gpuType"] + template = ENV.get_template(os.path.abspath(jobTemp)) job_description = template.render(job=jobParams) jobDescriptionList.append(job_description) @@ -302,7 +307,7 @@ def SubmitRegularJob(job): jobMetaStr = base64.b64encode(json.dumps(jobMeta)) dataHandler.UpdateJobTextField(jobParams["jobId"],"jobMeta",jobMetaStr) except Exception as e: - print e + print(e) ret["error"] = str(e) retries = dataHandler.AddandGetJobRetries(jobParams["jobId"]) if retries >= 5: @@ -474,6 +479,11 @@ def SubmitPSDistJob(job): distJobParam["nodeSelector"] = {} distJobParam["nodeSelector"]["rack"] = assignedRack + if "gpuType" in distJobParam: + if "nodeSelector" not in distJobParam: + distJobParam["nodeSelector"] = {} + distJobParam["nodeSelector"]["gpuType"] = distJobParam["gpuType"] + template = ENV.get_template(os.path.abspath(jobTemp)) job_description = template.render(job=distJobParam) @@ -520,7 +530,7 @@ def SubmitPSDistJob(job): jobMetaStr = base64.b64encode(json.dumps(jobMeta)) dataHandler.UpdateJobTextField(jobParams["jobId"],"jobMeta",jobMetaStr) except Exception as e: - print e + print(e) ret["error"] = str(e) retries = dataHandler.AddandGetJobRetries(jobParams["jobId"]) if retries >= 5: @@ -529,7 +539,7 @@ def SubmitPSDistJob(job): return ret -def KillJob(job): +def KillJob(job, desiredState): dataHandler = DataHandler() result, detail = k8sUtils.GetJobStatus(job["jobId"]) dataHandler.UpdateJobTextField(job["jobId"],"jobStatusDetail",base64.b64encode(json.dumps(detail))) @@ -538,7 +548,7 @@ def KillJob(job): jobDescriptionPath = os.path.join(config["storage-mount-path"], job["jobDescriptionPath"]) if os.path.isfile(jobDescriptionPath): if k8sUtils.kubectl_delete(jobDescriptionPath) == 0: - dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","killed") + dataHandler.UpdateJobTextField(job["jobId"],"jobStatus", desiredState) return True else: dataHandler.UpdateJobTextField(job["jobId"],"errorMsg","Cannot delete job from Kubernetes Cluster!") @@ -561,7 +571,7 @@ def getAlias(username): def ApproveJob(job): dataHandler = DataHandler() - dataHandler.ApproveJob(job["jobId"]) + dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "queued") dataHandler.Close() return True @@ -735,7 +745,7 @@ def UpdateDistJobStatus(job): def run_dist_cmd_on_pod(podId, cmd, outputfile): remotecmd = "exec %s -- %s" % (podId,cmd) - print remotecmd + print(remotecmd) k8sUtils.kubectl_exec_output_to_file(remotecmd,outputfile) @@ -903,6 +913,147 @@ def create_log( logdir = '/var/log/dlworkspace' ): logging.config.dictConfig(logging_config) +''' +unapproved -> (after approval) queued -> (after submission) scheduling -> running () +queued +scheduling +running +killing + +pausing/paused +resuming -> queued (explicit action by user) + +pre-empting/pre-empted -> queued (implicit; probably we can directly move to queued when pre-empting) + + +if there are queued jobs: + get all queued,scheduling,running jobs info + prioritize (rank) them (for each group) + find desired state for each job + find jobs with current and desired state differing + take_action(current->desired) + + + group = vc+processingResourceType + +''' + +class ResourceInfo: + def __init__(self, tag = "", res = {}): + self.CategoryToCountMap = {} + self.BlockedCategories = set() + for key in res: + self.CategoryToCountMap[tag + "_" + key] = int(res[key]) + + @classmethod + def FromTypeAndCount(cls, tag, gpuType, gpucount): + resources = {} + resources[gpuType] = gpucount + return cls(tag, resources) + + def Add(self, otherResourceInfo): + for key in otherResourceInfo.CategoryToCountMap: + if key not in self.CategoryToCountMap: + self.CategoryToCountMap[key] = 0 + self.CategoryToCountMap[key] += otherResourceInfo.CategoryToCountMap[key] + + def CanSatisfy(self, otherResourceInfo): + for key in otherResourceInfo.CategoryToCountMap: + if (key in self.BlockedCategories) or (key not in self.CategoryToCountMap) or (self.CategoryToCountMap[key] < otherResourceInfo.CategoryToCountMap[key]): + return False + return True + + def Subtract(self, otherResourceInfo): + for key in otherResourceInfo.CategoryToCountMap: + self.CategoryToCountMap[key] -= otherResourceInfo.CategoryToCountMap[key] + + def BlockResourceCategory(self, resourceInfo): + for key in resourceInfo.CategoryToCountMap: + self.BlockedCategories.add(key) + + def UnblockResourceCategory(self, resourceInfo): + for key in resourceInfo.CategoryToCountMap: + if key in self.BlockedCategories: + self.BlockedCategories.remove(key) + + def TotalGPUs(self): + return sum(self.CategoryToCountMap.values()) #TODO : Modify when supporting CPUs too. + + + +def JobInfoSorter(elem): + return elem["sortKey"] + + +def TakeJobActions(jobs): + dataHandler = DataHandler() + vcList = dataHandler.ListVCs() + dataHandler.Close() + + localResInfo = ResourceInfo() + globalResInfo = ResourceInfo() + + for vc in vcList: + localResInfo.Add(ResourceInfo(vc["vcName"], json.loads(vc["quota"]))) + globalResInfo.Add(ResourceInfo("", json.loads(vc["quota"]))) + + jobsInfo = [] + for job in jobs: + if job["jobStatus"] == "queued" or job["jobStatus"] == "scheduling" or job["jobStatus"] == "running": + singleJobInfo = {} + singleJobInfo["job"] = job + singleJobInfo["jobParams"] = json.loads(base64.b64decode(job["jobParams"])) + singleJobInfo["localResInfo"] = ResourceInfo.FromTypeAndCount(job["vcName"], singleJobInfo["jobParams"]["gpuType"], singleJobInfo["jobParams"]["resourcegpu"]) + singleJobInfo["globalResInfo"] = ResourceInfo.FromTypeAndCount("", singleJobInfo["jobParams"]["gpuType"], singleJobInfo["jobParams"]["resourcegpu"]) + singleJobInfo["sortKey"] = str(job["jobTime"]) + if singleJobInfo["jobParams"]["preemptionAllowed"]: + singleJobInfo["sortKey"] = "1_" + singleJobInfo["sortKey"] + else: + singleJobInfo["sortKey"] = "0_" + singleJobInfo["sortKey"] + singleJobInfo["allowed"] = False + jobsInfo.append(singleJobInfo) + + jobsInfo.sort(key=JobInfoSorter) + + logging.info("TakeJobActions : local resources : %s" % (localResInfo.CategoryToCountMap)) + logging.info("TakeJobActions : global resources : %s" % (globalResInfo.CategoryToCountMap)) + + for sji in jobsInfo: + logging.info("TakeJobActions : job : %s : %s" % (sji["jobParams"]["jobName"], sji["localResInfo"].CategoryToCountMap)) + if sji["jobParams"]["preemptionAllowed"]: + localResInfo.UnblockResourceCategory(sji["localResInfo"]) + + if (localResInfo.CanSatisfy(sji["localResInfo"])): + localResInfo.Subtract(sji["localResInfo"]) + globalResInfo.Subtract(sji["globalResInfo"]) + sji["allowed"] = True + logging.info("TakeJobActions : local assignment : %s : %s" % (sji["jobParams"]["jobName"], sji["localResInfo"].CategoryToCountMap)) + elif not sji["jobParams"]["preemptionAllowed"]: + localResInfo.BlockResourceCategory(sji["localResInfo"]) #FIFO scheduling + + #logging.info("TakeJobActions : local resources : %s" % (localResInfo.CategoryToCountMap)) + #logging.info("TakeJobActions : global resources : %s" % (globalResInfo.CategoryToCountMap)) + + for sji in jobsInfo: + if (sji["jobParams"]["preemptionAllowed"] and sji["allowed"] == False): + if globalResInfo.CanSatisfy(sji["globalResInfo"]): + logging.info("TakeJobActions : job : %s : %s" % (sji["jobParams"]["jobName"], sji["globalResInfo"].CategoryToCountMap)) + # Strict FIFO policy not required for global (bonus) tokens since these jobs are anyway pre-emptible. + globalResInfo.Subtract(sji["globalResInfo"]) + sji["allowed"] = True + logging.info("TakeJobActions : global assignment : %s : %s" % (sji["jobParams"]["jobName"], sji["globalResInfo"].CategoryToCountMap)) + + logging.info("TakeJobActions : global resources : %s" % (globalResInfo.CategoryToCountMap)) + + for sji in jobsInfo: + if sji["job"]["jobStatus"] == "queued" and sji["allowed"] == True: + SubmitJob(sji["job"]) + elif (sji["job"]["jobStatus"] == "scheduling" or sji["job"]["jobStatus"] == "running") and sji["allowed"] == False: + KillJob(sji["job"], "queued") + + logging.info("TakeJobActions : job desired actions taken") + + def Run(): while True: @@ -911,29 +1062,33 @@ def Run(): config["racks"] = k8sUtils.get_node_labels("rack") config["skus"] = k8sUtils.get_node_labels("sku") except Exception as e: - print e + logging.info(e) try: dataHandler = DataHandler() pendingJobs = dataHandler.GetPendingJobs() - printlog("updating status for %d jobs" % len(pendingJobs)) + TakeJobActions(pendingJobs) + + pendingJobs = dataHandler.GetPendingJobs() + logging.info("Updating status for %d jobs" % len(pendingJobs)) for job in pendingJobs: try: - print "Processing job: %s, status: %s" % (job["jobId"], job["jobStatus"]) - if job["jobStatus"] == "queued": - SubmitJob(job) - elif job["jobStatus"] == "killing": - KillJob(job) + logging.info("Processing job: %s, status: %s" % (job["jobId"], job["jobStatus"])) + if job["jobStatus"] == "killing": + KillJob(job, "killed") + elif job["jobStatus"] == "pausing": + KillJob(job, "paused") elif job["jobStatus"] == "scheduling" or job["jobStatus"] == "running" : UpdateJobStatus(job) elif job["jobStatus"] == "unapproved" : AutoApproveJob(job) except Exception as e: - print e + logging.info(e) except Exception as e: - print e + logging.info(str(e)) + + time.sleep(60) - time.sleep(1) if __name__ == '__main__': Run() diff --git a/src/Jobs_Templete/DistJob.yaml.template b/src/Jobs_Templete/DistJob.yaml.template index dfd378cb1..15ec2bce3 100755 --- a/src/Jobs_Templete/DistJob.yaml.template +++ b/src/Jobs_Templete/DistJob.yaml.template @@ -7,7 +7,10 @@ metadata: jobName: {{ job["jobNameLabel"] }} distRole: {{ job["distRole"] }} distPort: "{{job["containerPort"]}}" - userName: {{ job["userNameLabel"] }} + userName: {{ job["userNameLabel"] }} + vcName: {{ job["vcName"] }} + gpuType: {{ job["gpuType"] }} + preemptionAllowed: {{ job["preemptionAllowed"] }} spec: #hostNetwork: true nodeSelector: diff --git a/src/Jobs_Templete/RegularJob.yaml.template b/src/Jobs_Templete/RegularJob.yaml.template index 31c1dcda1..c95e26dd9 100755 --- a/src/Jobs_Templete/RegularJob.yaml.template +++ b/src/Jobs_Templete/RegularJob.yaml.template @@ -8,6 +8,9 @@ metadata: jobName: {{ job["jobNameLabel"] }} jobId: {{job["jobId"]}} userName: {{ job["userNameLabel"] }} + vcName: {{ job["vcName"] }} + gpuType: {{ job["gpuType"] }} + preemptionAllowed: {{ job["preemptionAllowed"] }} {% if "annotations" in job %} annotations: {% for annotationKey,annotationVal in job["annotations"].items() %} diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index 3f24edf99..f8421ae15 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -7,6 +7,7 @@ from flask import request, jsonify import base64 import yaml +import uuid import logging from logging.config import dictConfig @@ -69,6 +70,7 @@ class SubmitJob(Resource): def get(self): parser.add_argument('jobName') parser.add_argument('resourcegpu') + parser.add_argument('gpuType') parser.add_argument('workPath') parser.add_argument('dataPath') parser.add_argument('jobPath') @@ -78,6 +80,7 @@ def get(self): parser.add_argument('interactivePort') parser.add_argument('userName') parser.add_argument('vcName') + parser.add_argument('preemptionAllowed') parser.add_argument('userId') parser.add_argument('runningasroot') parser.add_argument('containerUserId') @@ -108,7 +111,9 @@ def get(self): elif args["vcName"] is None or len(args["vcName"].strip()) == 0: ret["error"] = "vc name cannot be empty" elif args["resourcegpu"] is None or len(args["resourcegpu"].strip()) == 0: - ret["error"] = "Number of GPU cannot be empty" + ret["error"] = "Number of GPU cannot be empty" + elif args["gpuType"] is None or len(args["gpuType"].strip()) == 0: + ret["error"] = "GPU Type cannot be empty" elif args["dataPath"] is None or len(args["dataPath"].strip()) == 0: ret["error"] = "datapath cannot be empty" elif args["image"] is None or len(args["image"].strip()) == 0: @@ -119,11 +124,13 @@ def get(self): params["jobName"] = args["jobName"] params["vcName"] = args["vcName"] params["resourcegpu"] = args["resourcegpu"] + params["gpuType"] = args["gpuType"] params["workPath"] = args["workPath"] params["dataPath"] = args["dataPath"] params["image"] = args["image"] params["cmd"] = args["cmd"] params["jobType"] = args["jobType"] + params["preemptionAllowed"] = args["preemptionAllowed"] params["jobtrainingtype"] = args["jobtrainingtype"] diff --git a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs index 5a9e45cd5..60f2204e6 100755 --- a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs @@ -178,8 +178,11 @@ public async Task Get(string op) switch (op) { + case "GetVCs": + url = restapi + "/ListVCs?userName=" + HttpContext.Session.GetString("Email"); + break; case "ListJobs": - url = restapi + "/ListJobs?userName=" + HttpContext.Session.GetString("Email"); + url = restapi + "/ListJobs?vcName=vc1&jobOwner="+HttpContext.Session.GetString("Email") + "&userName=" + HttpContext.Session.GetString("Email"); if (HttpContext.Request.Query.ContainsKey("num")) { url += "&num=" + HttpContext.Request.Query["num"]; @@ -188,7 +191,7 @@ public async Task Get(string op) case "ListAllJobs": if (HttpContext.Session.GetString("isAdmin").Equals("true")) { - url = restapi + "/ListJobs?userName=all"; + url = restapi + "/ListJobs?vcName=vc1&jobOwner=all&userName="+HttpContext.Session.GetString("Email"); } break; case "KillJob": @@ -206,7 +209,7 @@ public async Task Get(string op) case "JobDetail": if (HttpContext.Request.Query.ContainsKey("jobId")) { - url = restapi + "/GetJobDetail?jobId=" + HttpContext.Request.Query["jobId"]; + url = restapi + "/GetJobDetail?jobId=" + HttpContext.Request.Query["jobId"] + "&userName=" + HttpContext.Session.GetString("Email"); } break; case "SubmitJob": @@ -264,13 +267,14 @@ public async Task Get(string op) case "RunCommand": if (HttpContext.Request.Query.ContainsKey("jobId") && HttpContext.Request.Query.ContainsKey("command")) { - url = restapi + "/AddCommand?jobId=" + HttpContext.Request.Query["jobId"] + "&command=" + HttpContext.Request.Query["command"]; + url = restapi + "/AddCommand?jobId=" + HttpContext.Request.Query["jobId"] + "&command=" + HttpContext.Request.Query["command"] + + "&userName=" + HttpContext.Session.GetString("Email"); } break; case "GetCommands": if (HttpContext.Request.Query.ContainsKey("jobId")) { - url = restapi + "/GetCommands?jobId=" + HttpContext.Request.Query["jobId"]; + url = restapi + "/GetCommands?jobId=" + HttpContext.Request.Query["jobId"] + "&userName=" + HttpContext.Session.GetString("Email"); } break; } @@ -394,6 +398,9 @@ public async Task postJob(TemplateParams templateParams) jobObject["userName"] = HttpContext.Session.GetString("Email"); jobObject["userId"] = uid; jobObject["jobType"] = "training"; + jobObject["vcName"] = "vc1"; + jobObject["gpuType"] = "any"; + jobObject["preemptionAllowed"] = "False"; var runningasroot = jobObject["runningasroot"]; if (!(Object.ReferenceEquals(runningasroot, null)) && (runningasroot.ToString() == "1") || (runningasroot.ToString() == true.ToString())) { diff --git a/src/utils/JobRestAPIUtils.py b/src/utils/JobRestAPIUtils.py index 6c13326f9..f144591b7 100755 --- a/src/utils/JobRestAPIUtils.py +++ b/src/utils/JobRestAPIUtils.py @@ -33,6 +33,7 @@ def SubmitJob(jobParamsJsonStr): ret = {} jobParams = LoadJobParams(jobParamsJsonStr) + print(jobParams) #TODO if "jobName" not in jobParams or len(jobParams["jobName"].strip()) == 0: ret["error"] = "ERROR: Job name cannot be empty" @@ -41,6 +42,9 @@ def SubmitJob(jobParamsJsonStr): ret["error"] = "ERROR: VC name cannot be empty" return ret + if "preemptionAllowed" not in jobParams: + jobParams["preemptionAllowed"] = False + if "jobId" not in jobParams or jobParams["jobId"] == "": #jobParams["jobId"] = jobParams["jobName"] + "-" + str(uuid.uuid4()) #jobParams["jobId"] = jobParams["jobName"] + "-" + str(time.time()) @@ -136,6 +140,8 @@ def SubmitJob(jobParamsJsonStr): jobParams["workPath"] = os.path.realpath(os.path.join("/",jobParams["workPath"]))[1:] jobParams["jobPath"] = os.path.realpath(os.path.join("/",jobParams["jobPath"]))[1:] + print(jobParams) #TODO + dataHandler = DataHandler() if "logDir" in jobParams and len(jobParams["logDir"].strip()) > 0: tensorboardParams = jobParams.copy() @@ -228,9 +234,9 @@ def KillJob(userName, jobId): if job["isParent"] == 1: ret = True for currJob in dataHandler.GetJob(familyToken=job["familyToken"]): - ret = ret and dataHandler.KillJob(currJob["jobId"]) + ret = ret and dataHandler.UpdateJobTextField(currJob["jobId"],"jobStatus","killing") else: - ret = dataHandler.KillJob(jobId) + ret = dataHandler.UpdateJobTextField(jobId,"jobStatus","killing") dataHandler.Close() return ret @@ -252,7 +258,29 @@ def ApproveJob(userName, jobId): jobs = dataHandler.GetJob(jobId=jobId) if len(jobs) == 1: if AuthorizationManager.HasAccess(userName, ResourceType.VC, jobs[0]["vcName"], Permission.Admin): - ret = dataHandler.ApproveJob(jobId) + ret = dataHandler.UpdateJobTextField(jobId,"jobStatus","queued") + dataHandler.Close() + return ret + + +def ResumeJob(userName, jobId): + dataHandler = DataHandler() + ret = False + jobs = dataHandler.GetJob(jobId=jobId) + if len(jobs) == 1: + if jobs[0]["userName"] == userName or AuthorizationManager.HasAccess(userName, ResourceType.VC, jobs[0]["vcName"], Permission.Collaborator): + ret = dataHandler.UpdateJobTextField(jobId,"jobStatus","queued") + dataHandler.Close() + return ret + + +def PauseJob(userName, jobId): + dataHandler = DataHandler() + ret = False + jobs = dataHandler.GetJob(jobId=jobId) + if len(jobs) == 1: + if jobs[0]["userName"] == userName or AuthorizationManager.HasAccess(userName, ResourceType.VC, jobs[0]["vcName"], Permission.Admin): + ret = dataHandler.UpdateJobTextField(jobId,"jobStatus","pausing") dataHandler.Close() return ret diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index 2b95bc248..1d362cdd7 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -187,12 +187,14 @@ def CreateTable(self): ( `id` INT NOT NULL AUTO_INCREMENT, `vcName` varchar(255) NOT NULL UNIQUE, + `parent` varchar(255) DEFAULT NULL, `quota` varchar(255) NOT NULL, `metadata` TEXT NOT NULL, `time` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, - PRIMARY KEY (`id`) + PRIMARY KEY (`id`), + CONSTRAINT `hierarchy` FOREIGN KEY (`parent`) REFERENCES `%s` (`vcName`) ) - """ % (self.vctablename) + """ % (self.vctablename, self.vctablename) cursor = self.conn.cursor() cursor.execute(sql) @@ -706,42 +708,10 @@ def GetCommands(self, jobId): return ret - def KillJob(self,jobId): - try: - start_time = timeit.default_timer() - sql = """update `%s` set jobStatus = 'killing' where `jobId` = '%s' """ % (self.jobtablename,jobId) - cursor = self.conn.cursor() - cursor.execute(sql) - self.conn.commit() - cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: mark job %s to be killed in database, time elapsed %f s" % (jobId, elapsed)) - return True - except Exception as e: - logger.error('Exception: '+ str(e)) - return False - - - def ApproveJob(self,jobId): - try: - start_time = timeit.default_timer() - sql = """update `%s` set jobStatus = 'queued' where `jobId` = '%s' """ % (self.jobtablename,jobId) - cursor = self.conn.cursor() - cursor.execute(sql) - self.conn.commit() - cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: approved job %s , time elapsed %f s" % (jobId, elapsed)) - return True - except Exception as e: - logger.error('Exception: '+ str(e)) - return False - - def GetPendingJobs(self): start_time = timeit.default_timer() cursor = self.conn.cursor() - query = "SELECT `jobId`,`jobName`,`userName`, 'vcName', `jobStatus`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `jobStatus` <> 'error' and `jobStatus` <> 'failed' and `jobStatus` <> 'finished' and `jobStatus` <> 'killed' order by `jobTime` DESC" % (self.jobtablename) + query = "SELECT `jobId`,`jobName`,`userName`, `vcName`, `jobStatus`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `jobStatus` <> 'error' and `jobStatus` <> 'failed' and `jobStatus` <> 'finished' and `jobStatus` <> 'killed' order by `jobTime` DESC" % (self.jobtablename) cursor.execute(query) ret = [] for (jobId,jobName,userName,vcName, jobStatus, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in cursor: @@ -791,7 +761,7 @@ def UpdateJobTextField(self,jobId,field,value): self.conn.commit() cursor.close() elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: update job %s, field %s , time elapsed %f s" % (jobId, field, elapsed)) + logger.info ("DataHandler: update job %s, field %s to %s, time elapsed %f s" % (jobId, field, value, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) diff --git a/src/utils/SQLDataHandler.py b/src/utils/SQLDataHandler.py index 317c399d5..50019b299 100755 --- a/src/utils/SQLDataHandler.py +++ b/src/utils/SQLDataHandler.py @@ -312,12 +312,14 @@ def CreateTable(self): ( [id] INT IDENTITY (1, 1) NOT NULL, [vcName] varchar(255) NOT NULL UNIQUE, + [parent] varchar(255) DEFAULT NULL, [quota] varchar(255) NOT NULL, [metadata] varchar(max) NOT NULL, [time] DATETIME DEFAULT (getdate()) NOT NULL, - PRIMARY KEY CLUSTERED ([id] ASC) + PRIMARY KEY CLUSTERED ([id] ASC), + FOREIGN KEY(parent) REFERENCES [dbo].[%s](vcName), ) - """ % (self.vctablename,self.vctablename) + """ % (self.vctablename,self.vctablename, self.vctablename) cursor = self.conn.cursor() cursor.execute(sql) @@ -831,38 +833,6 @@ def GetCommands(self, jobId): return ret - def KillJob(self,jobId): - try: - start_time = timeit.default_timer() - sql = """update [%s] set jobStatus = 'killing' where [jobId] = '%s' """ % (self.jobtablename,jobId) - cursor = self.conn.cursor() - cursor.execute(sql) - self.conn.commit() - cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: mark job %s to be killed in database, time elapsed %f s" % (jobId, elapsed)) - return True - except Exception as e: - logger.error('Exception: '+ str(e)) - return False - - - def ApproveJob(self,jobId): - try: - start_time = timeit.default_timer() - sql = """update [%s] set jobStatus = 'queued' where [jobId] = '%s' """ % (self.jobtablename,jobId) - cursor = self.conn.cursor() - cursor.execute(sql) - self.conn.commit() - cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: approved job %s , time elapsed %f s" % (jobId, elapsed)) - return True - except Exception as e: - logger.error('Exception: '+ str(e)) - return False - - def GetPendingJobs(self): start_time = timeit.default_timer() cursor = self.conn.cursor() From 22a3336a40d5becdaf7c294283aa10254976e4e7 Mon Sep 17 00:00:00 2001 From: Deepak Bansal Date: Tue, 9 Apr 2019 12:36:00 -0700 Subject: [PATCH 036/595] remove unused comments --- src/ClusterManager/job_manager.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index e30009097..f9f8944d7 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -913,30 +913,6 @@ def create_log( logdir = '/var/log/dlworkspace' ): logging.config.dictConfig(logging_config) -''' -unapproved -> (after approval) queued -> (after submission) scheduling -> running () -queued -scheduling -running -killing - -pausing/paused -resuming -> queued (explicit action by user) - -pre-empting/pre-empted -> queued (implicit; probably we can directly move to queued when pre-empting) - - -if there are queued jobs: - get all queued,scheduling,running jobs info - prioritize (rank) them (for each group) - find desired state for each job - find jobs with current and desired state differing - take_action(current->desired) - - - group = vc+processingResourceType - -''' class ResourceInfo: def __init__(self, tag = "", res = {}): From a44004fd55fd7cf9d80776eed36989bc11ee3123 Mon Sep 17 00:00:00 2001 From: Deepak Bansal Date: Wed, 10 Apr 2019 16:31:29 -0700 Subject: [PATCH 037/595] handling comments --- src/ClusterManager/ResourceInfo.py | 37 +++++++++++++ src/ClusterManager/job_manager.py | 55 +++---------------- .../WebPortal/Controllers/dlwsController.cs | 10 +++- src/docker-images/RestfulAPI/Dockerfile | 51 +---------------- src/docker-images/RestfulAPI/base/Dockerfile | 1 + src/utils/JobRestAPIUtils.py | 3 - 6 files changed, 53 insertions(+), 104 deletions(-) create mode 100644 src/ClusterManager/ResourceInfo.py diff --git a/src/ClusterManager/ResourceInfo.py b/src/ClusterManager/ResourceInfo.py new file mode 100644 index 000000000..63cc81110 --- /dev/null +++ b/src/ClusterManager/ResourceInfo.py @@ -0,0 +1,37 @@ +class ResourceInfo: + def __init__(self, tag = "", res = {}): + self.CategoryToCountMap = {} + self.BlockedCategories = set() + for key in res: + self.CategoryToCountMap[tag + "_" + key] = int(res[key]) + + @classmethod + def FromTypeAndCount(cls, tag, gpuType, gpucount): + resources = {} + resources[gpuType] = gpucount + return cls(tag, resources) + + def Add(self, otherResourceInfo): + for key in otherResourceInfo.CategoryToCountMap: + if key not in self.CategoryToCountMap: + self.CategoryToCountMap[key] = 0 + self.CategoryToCountMap[key] += otherResourceInfo.CategoryToCountMap[key] + + def CanSatisfy(self, otherResourceInfo): + for key in otherResourceInfo.CategoryToCountMap: + if (key in self.BlockedCategories) or (key not in self.CategoryToCountMap) or (self.CategoryToCountMap[key] < otherResourceInfo.CategoryToCountMap[key]): + return False + return True + + def Subtract(self, otherResourceInfo): + for key in otherResourceInfo.CategoryToCountMap: + self.CategoryToCountMap[key] -= otherResourceInfo.CategoryToCountMap[key] + + def BlockResourceCategory(self, resourceInfo): + for key in resourceInfo.CategoryToCountMap: + self.BlockedCategories.add(key) + + def UnblockResourceCategory(self, resourceInfo): + for key in resourceInfo.CategoryToCountMap: + if key in self.BlockedCategories: + self.BlockedCategories.remove(key) \ No newline at end of file diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index f9f8944d7..3ffeadb2f 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -24,6 +24,7 @@ from node_manager import create_log from node_manager import get_cluster_status import base64 +from ResourceInfo import ResourceInfo import re @@ -539,7 +540,7 @@ def SubmitPSDistJob(job): return ret -def KillJob(job, desiredState): +def KillJob(job, desiredState="killed"): dataHandler = DataHandler() result, detail = k8sUtils.GetJobStatus(job["jobId"]) dataHandler.UpdateJobTextField(job["jobId"],"jobStatusDetail",base64.b64encode(json.dumps(detail))) @@ -913,50 +914,6 @@ def create_log( logdir = '/var/log/dlworkspace' ): logging.config.dictConfig(logging_config) - -class ResourceInfo: - def __init__(self, tag = "", res = {}): - self.CategoryToCountMap = {} - self.BlockedCategories = set() - for key in res: - self.CategoryToCountMap[tag + "_" + key] = int(res[key]) - - @classmethod - def FromTypeAndCount(cls, tag, gpuType, gpucount): - resources = {} - resources[gpuType] = gpucount - return cls(tag, resources) - - def Add(self, otherResourceInfo): - for key in otherResourceInfo.CategoryToCountMap: - if key not in self.CategoryToCountMap: - self.CategoryToCountMap[key] = 0 - self.CategoryToCountMap[key] += otherResourceInfo.CategoryToCountMap[key] - - def CanSatisfy(self, otherResourceInfo): - for key in otherResourceInfo.CategoryToCountMap: - if (key in self.BlockedCategories) or (key not in self.CategoryToCountMap) or (self.CategoryToCountMap[key] < otherResourceInfo.CategoryToCountMap[key]): - return False - return True - - def Subtract(self, otherResourceInfo): - for key in otherResourceInfo.CategoryToCountMap: - self.CategoryToCountMap[key] -= otherResourceInfo.CategoryToCountMap[key] - - def BlockResourceCategory(self, resourceInfo): - for key in resourceInfo.CategoryToCountMap: - self.BlockedCategories.add(key) - - def UnblockResourceCategory(self, resourceInfo): - for key in resourceInfo.CategoryToCountMap: - if key in self.BlockedCategories: - self.BlockedCategories.remove(key) - - def TotalGPUs(self): - return sum(self.CategoryToCountMap.values()) #TODO : Modify when supporting CPUs too. - - - def JobInfoSorter(elem): return elem["sortKey"] @@ -1024,8 +981,10 @@ def TakeJobActions(jobs): for sji in jobsInfo: if sji["job"]["jobStatus"] == "queued" and sji["allowed"] == True: SubmitJob(sji["job"]) + logging.info("TakeJobActions : submitting job : %s : %s" % (sji["jobParams"]["jobId"], sji["sortKey"])) elif (sji["job"]["jobStatus"] == "scheduling" or sji["job"]["jobStatus"] == "running") and sji["allowed"] == False: KillJob(sji["job"], "queued") + logging.info("TakeJobActions : pre-empting job : %s : %s" % (sji["jobParams"]["jobId"], sji["sortKey"])) logging.info("TakeJobActions : job desired actions taken") @@ -1038,7 +997,7 @@ def Run(): config["racks"] = k8sUtils.get_node_labels("rack") config["skus"] = k8sUtils.get_node_labels("sku") except Exception as e: - logging.info(e) + print(e) try: dataHandler = DataHandler() @@ -1061,9 +1020,9 @@ def Run(): except Exception as e: logging.info(e) except Exception as e: - logging.info(str(e)) + print(str(e)) - time.sleep(60) + time.sleep(1) if __name__ == '__main__': diff --git a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs index 60f2204e6..3e60ed7f6 100755 --- a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs @@ -181,7 +181,7 @@ public async Task Get(string op) case "GetVCs": url = restapi + "/ListVCs?userName=" + HttpContext.Session.GetString("Email"); break; - case "ListJobs": + case "ListJobs": //TODO:update vc name url = restapi + "/ListJobs?vcName=vc1&jobOwner="+HttpContext.Session.GetString("Email") + "&userName=" + HttpContext.Session.GetString("Email"); if (HttpContext.Request.Query.ContainsKey("num")) { @@ -190,7 +190,7 @@ public async Task Get(string op) break; case "ListAllJobs": if (HttpContext.Session.GetString("isAdmin").Equals("true")) - { + { //TODO:update vc name url = restapi + "/ListJobs?vcName=vc1&jobOwner=all&userName="+HttpContext.Session.GetString("Email"); } break; @@ -398,9 +398,13 @@ public async Task postJob(TemplateParams templateParams) jobObject["userName"] = HttpContext.Session.GetString("Email"); jobObject["userId"] = uid; jobObject["jobType"] = "training"; - jobObject["vcName"] = "vc1"; + + //TODO:update below params + jobObject["vcName"] = "vc1"; jobObject["gpuType"] = "any"; jobObject["preemptionAllowed"] = "False"; + /////////////////////// + var runningasroot = jobObject["runningasroot"]; if (!(Object.ReferenceEquals(runningasroot, null)) && (runningasroot.ToString() == "1") || (runningasroot.ToString() == true.ToString())) { diff --git a/src/docker-images/RestfulAPI/Dockerfile b/src/docker-images/RestfulAPI/Dockerfile index 5af0e0e98..40954d417 100755 --- a/src/docker-images/RestfulAPI/Dockerfile +++ b/src/docker-images/RestfulAPI/Dockerfile @@ -1,55 +1,6 @@ -FROM ubuntu:16.04 +FROM dlws/restfulapi:v1.4 MAINTAINER Hongzhi Li -# See https://stackoverflow.com/questions/37706635/in-docker-apt-get-install-fails-with-failed-to-fetch-http-archive-ubuntu-com -# It is a good practice to merge apt-get update with the following apt-get install -RUN apt-get update; -RUN apt-get update; apt-get install -y --no-install-recommends apt-transport-https \ - build-essential \ - cmake \ - git \ - wget \ - vim \ - python-dev \ - python-pip \ - python-yaml \ - locales \ - python-pycurl \ - bison \ - curl \ - nfs-common \ - apt-utils - - -RUN pip install --upgrade pip; - -RUN pip install setuptools; -RUN locale-gen en_US.UTF-8 -RUN update-locale LANG=en_US.UTF-8 - -RUN pip install flask -RUN pip install flask.restful -RUN pip install requests - -RUN wget http://ccsdatarepo.westus.cloudapp.azure.com/data/tools/mysql-connector-python_2.1.7-1ubuntu16.04_all.deb -O /mysql-connector-python_2.1.7-1ubuntu16.04_all.deb -RUN dpkg -i /mysql-connector-python_2.1.7-1ubuntu16.04_all.deb -RUN apt-get install -y libmysqlclient-dev mysql-connector-python - - -# Install python for Azure SQL - -RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - - -RUN curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list > /etc/apt/sources.list.d/mssql-release.list - -RUN apt-get update; ACCEPT_EULA=Y apt-get install -y msodbcsql=13.1.4.0-1 unixodbc-dev - - -RUN pip install pyodbc -RUN pip install tzlocal -RUN apt-get update; apt-get install -y --no-install-recommends ssh apache2 libapache2-mod-wsgi sudo -RUN usermod -a -G sudo www-data -RUN echo "\nwww-data ALL=(ALL) NOPASSWD:ALL\n" > /etc/sudoers COPY kubectl /usr/local/bin/kubectl RUN chmod +x /usr/local/bin/kubectl diff --git a/src/docker-images/RestfulAPI/base/Dockerfile b/src/docker-images/RestfulAPI/base/Dockerfile index 58f630f53..3bd2332b3 100755 --- a/src/docker-images/RestfulAPI/base/Dockerfile +++ b/src/docker-images/RestfulAPI/base/Dockerfile @@ -29,6 +29,7 @@ RUN update-locale LANG=en_US.UTF-8 RUN pip install flask RUN pip install flask.restful +RUN pip install requests RUN wget http://ccsdatarepo.westus.cloudapp.azure.com/data/tools/mysql-connector-python_2.1.7-1ubuntu16.04_all.deb -O /mysql-connector-python_2.1.7-1ubuntu16.04_all.deb RUN dpkg -i /mysql-connector-python_2.1.7-1ubuntu16.04_all.deb diff --git a/src/utils/JobRestAPIUtils.py b/src/utils/JobRestAPIUtils.py index f144591b7..646129653 100755 --- a/src/utils/JobRestAPIUtils.py +++ b/src/utils/JobRestAPIUtils.py @@ -33,7 +33,6 @@ def SubmitJob(jobParamsJsonStr): ret = {} jobParams = LoadJobParams(jobParamsJsonStr) - print(jobParams) #TODO if "jobName" not in jobParams or len(jobParams["jobName"].strip()) == 0: ret["error"] = "ERROR: Job name cannot be empty" @@ -140,8 +139,6 @@ def SubmitJob(jobParamsJsonStr): jobParams["workPath"] = os.path.realpath(os.path.join("/",jobParams["workPath"]))[1:] jobParams["jobPath"] = os.path.realpath(os.path.join("/",jobParams["jobPath"]))[1:] - print(jobParams) #TODO - dataHandler = DataHandler() if "logDir" in jobParams and len(jobParams["logDir"].strip()) > 0: tensorboardParams = jobParams.copy() From 805a1e76497dfdbf0cf242a8045c2cda7f8ae915 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Thu, 11 Apr 2019 09:44:06 +0000 Subject: [PATCH 038/595] Distributed job: remove master count & hard code to 1 --- src/RestAPI/dlwsrestapi.py | 3 +-- .../dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index a3cc6be68..563389d99 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -80,7 +80,6 @@ def get(self): parser.add_argument('jobtrainingtype') - parser.add_argument('numps') parser.add_argument('numpsworker') parser.add_argument('nummpiworker') @@ -116,7 +115,7 @@ def get(self): params["jobtrainingtype"] = args["jobtrainingtype"] if args["jobtrainingtype"] == "PSDistJob": - params["numps"] = args["numps"] + params["numps"] = 1 params["numpsworker"] = args["numpsworker"] if args["jobtrainingtype"] == "MPIDistJob": diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index ab3ba9de1..602820200 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -542,7 +542,7 @@ Regular Job - Distributed Job + Distributed MPI Job
@@ -550,18 +550,18 @@
- - + + - - + +
- +
From a82928767533bbd0b1e06c5a67b1da27e7c2e6e8 Mon Sep 17 00:00:00 2001 From: Deepak Bansal Date: Thu, 11 Apr 2019 14:06:06 -0700 Subject: [PATCH 039/595] label nodes with gpuTypes and read winbind url from config --- src/ClusterBootstrap/deploy.py | 20 +++++-------------- src/ClusterManager/job_manager.py | 4 ++-- .../WebPortal/Views/Home/JobSubmission.cshtml | 14 ------------- src/utils/authorization.py | 14 ++++++++++--- 4 files changed, 18 insertions(+), 34 deletions(-) diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 4f4edb779..8dc4d19ee 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -2772,24 +2772,14 @@ def kubernetes_label_nodes( verb, servicelists, force ): kubernetes_label_node(cmdoptions, nodename, label+"-") -# Label kubernete nodes according to a service. -# A service (usually a Kubernete daemon service) can request to be run on: -# all: all nodes -# etcd_node: all etcd node -# etcd_node_n: a particular etcd node -# worker_node: all worker node -# The kubernete node will be marked accordingly to facilitate the running of daemon service. -def kubernetes_label_vc(): +# Label kubernete nodes with gpu types. +def kubernetes_label_GpuTypes(): nodes = get_nodes(config["clusterId"]) - vc_config = fetch_config(config, ["vc_config"]) - for vc_name, nodes in vc_config: + gpuType_config = fetch_config(config, ["gpuType_config"]) + for gpuType, nodes in gpuType_config: for node in nodes: nodename = kubernetes_get_node_name(node) - if nodename in nodes or "*" in nodes: - kubernetes_label_node("--overwrite", nodename, vc_name+"=active") - else: - kubernetes_label_node("--overwrite", nodename, vc_name+"=inactive") - + kubernetes_label_node("--overwrite", nodename, "gpuType="+gpuType) def kubernetes_patch_nodes_provider (provider, scaledOnly): diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 3ffeadb2f..b17eca714 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -981,10 +981,10 @@ def TakeJobActions(jobs): for sji in jobsInfo: if sji["job"]["jobStatus"] == "queued" and sji["allowed"] == True: SubmitJob(sji["job"]) - logging.info("TakeJobActions : submitting job : %s : %s" % (sji["jobParams"]["jobId"], sji["sortKey"])) + logging.info("TakeJobActions : submitting job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["jobParams"]["jobId"], sji["sortKey"])) elif (sji["job"]["jobStatus"] == "scheduling" or sji["job"]["jobStatus"] == "running") and sji["allowed"] == False: KillJob(sji["job"], "queued") - logging.info("TakeJobActions : pre-empting job : %s : %s" % (sji["jobParams"]["jobId"], sji["sortKey"])) + logging.info("TakeJobActions : pre-empting job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["jobParams"]["jobId"], sji["sortKey"])) logging.info("TakeJobActions : job desired actions taken") diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 5a852120a..854d03eac 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -625,20 +625,6 @@
-
- - - - Default VC - V100 - P100 - P40 - TitanXP - 1080Ti - - -
-
+ @RenderSection("scripts", required: false) diff --git a/src/WebUI/dotnet/WebPortal/Views/Shared/_LoginPartial.cshtml b/src/WebUI/dotnet/WebPortal/Views/Shared/_LoginPartial.cshtml index ffdde447e..3f6845952 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Shared/_LoginPartial.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Shared/_LoginPartial.cshtml @@ -1,32 +1,29 @@ -@using System.Security.Principal -@using Microsoft.AspNetCore.Authentication.OpenIdConnect -@using WindowsAuth - - -@if (User.Identity.IsAuthenticated) -{ - -} -else -{ - -} +@using Microsoft.AspNetCore.Http; +@using System.Security.Principal +@using Microsoft.AspNetCore.Authentication.OpenIdConnect +@using WindowsAuth + +@if (User.Identity.IsAuthenticated) +{ + +} +else +{ + +} From 5c463e567a78e29a0038782b383058637ebcc26c Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Sun, 28 Apr 2019 18:20:30 +0800 Subject: [PATCH 049/595] add vcName in PostJob resftful api --- src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs index 0c052a3cb..758803b33 100755 --- a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs @@ -435,7 +435,9 @@ public async Task postJob(TemplateParams templateParams) using (var httpClient = new HttpClient()) { httpClient.BaseAddress = new Uri(restapi); - var response = await httpClient.PostAsync("/PostJob", new StringContent(jobObject.ToString(), System.Text.Encoding.UTF8, "application/json")); + var response = await httpClient.PostAsync( + "/PostJob?vcName=" + HttpContext.Session.GetString("Team"), + new StringContent(jobObject.ToString(), System.Text.Encoding.UTF8, "application/json")); var returnInfo = await response.Content.ReadAsStringAsync(); return returnInfo; } From c5836ab6ba39b324ce455d3e2e1c51585fe14a57 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Mon, 29 Apr 2019 18:48:55 +0800 Subject: [PATCH 050/595] Convert data job in browser side --- .../WebPortal/Controllers/dlwsController.cs | 69 ------------------- .../WebPortal/Views/Home/DataJob.cshtml | 66 ++++++++++++++++-- 2 files changed, 60 insertions(+), 75 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs index 35a64a6e1..3e60ed7f6 100755 --- a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs @@ -440,75 +440,6 @@ public async Task postJob(TemplateParams templateParams) } } - // POST api/dlws/dataJob - [HttpPost("dataJob")] - public async Task postDataJob(TemplateParams templateParams) - { - var ret = "invalid API call!"; - var tuple = await processRestfulAPICommon(); - var passwdLogin = tuple.Item1; - if (!String.IsNullOrEmpty(tuple.Item2)) - return tuple.Item2; - - - if (!User.Identity.IsAuthenticated && !passwdLogin) - { - ret = "Unauthorized User, Please login!"; - return ret; - } - var username = HttpContext.Session.GetString("Username"); - ViewData["Username"] = username; - var uid = HttpContext.Session.GetString("uid"); - var gid = HttpContext.Session.GetString("gid"); - var restapi = HttpContext.Session.GetString("Restapi"); - templateParams.Json = templateParams.Json.Replace("$$username$$", username).Replace("$$uid$$", uid).Replace("$$gid$$", gid); - var jobObject = JObject.Parse(templateParams.Json); - jobObject["userName"] = HttpContext.Session.GetString("Email"); - jobObject["userId"] = uid; - //jobObject["jobType"] = "training"; - - ////TODO:update below params - //jobObject["vcName"] = "vc1"; - //jobObject["gpuType"] = "any"; - //jobObject["preemptionAllowed"] = "False"; - ///////////////////////// - - //var runningasroot = jobObject["runningasroot"]; - //if (!(Object.ReferenceEquals(runningasroot, null)) && (runningasroot.ToString() == "1") || (runningasroot.ToString() == true.ToString())) - //{ - // jobObject["containerUserId"] = "0"; - //} - //else - //{ - // jobObject["containerUserId"] = uid; - //} - - //// ToDo: Need to be included in a database, - //var familyToken = Guid.NewGuid(); - //var newKey = _familyModel.Families.TryAdd(familyToken, new FamilyModel.FamilyData - //{ - // ApiPath = HttpContext.Session.GetString("Restapi"), - // Email = HttpContext.Session.GetString("Email"), - // UID = HttpContext.Session.GetString("uid") - //}); - //if (!newKey) - //{ - // ret = "Only 1 parent is allowed per family (maybe you tried to submit the same job on two threads?)"; - //} - //jobObject["familyToken"] = String.Format("{0:N}", familyToken); - //jobObject["isParent"] = 1; - - - //using (var httpClient = new HttpClient()) - //{ - // httpClient.BaseAddress = new Uri(restapi); - // var response = await httpClient.PostAsync("/PostJob", new StringContent(jobObject.ToString(), System.Text.Encoding.UTF8, "application/json")); - // var returnInfo = await response.Content.ReadAsStringAsync(); - // return returnInfo; - //} - return ""; - } - //Helper Methods private async Task DownloadDatabase(HttpRequest httpContextRequest) { diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/DataJob.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/DataJob.cshtml index 8864c8039..255a4fcb2 100644 --- a/src/WebUI/dotnet/WebPortal/Views/Home/DataJob.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/DataJob.cshtml @@ -21,13 +21,21 @@ editableOptions.theme = 'bs3'; }); - app.controller('dataJobCtrl', function ($scope, $http) { + app.controller('dataJobCtrl', function ($scope, $http, $httpParamSerializer) { $scope.busy = false; $scope.current = {}; $scope.submit = function () { - $http.post("/api/dlws/dataJob", $scope.current) - .then(function (response) { + $scope.busy = true; + $http.post("/api/dlws/postJob", + $httpParamSerializer({ Json: JSON.stringify(convert($scope.current)) }), + { + headers: { + "Content-Type": "application/x-www-form-urlencoded" + } + } + ).then(function (response) { + $scope.busy = false; var data = response.data; if (data.jobId) { layer.alert("Job Submitted!
Job ID:" + data.jobId, { @@ -41,19 +49,65 @@ $("#submit").attr("disabled", false); } }, function () { + $scope.busy = false; layer.alert("RestfulAPI error!", { icon: 5, btn: 'Ok', title: 'alert' }) }); }; }); + function convertURI(type, folder) { + if (type === "adls") { + if (folder.match(/^adl:\/\//)) { + // adl://example.com/file + return folder; + } else if (folder.match(/^\/\//i)) { + // //example.com/file + return "adl:" + folder; + } else if (folder.match(/^\//i)) { + // /example.com/file + return "adl:/" + folder; + } else { + // example.com/file + return "adl://" + folder; + } + } else if (type === "nfs") { + if (folder.match(/^\//)) { + // /dir/file + return folder; + } else { + // dir/file + return "/" + folder; + } + } + return folder; + } + + /** Convert data job to general job */ + function convert(dataJob) { + var job = {}; + job.jobName = "Data Job @@ " + new Date().toISOString(); + job.jobtrainingtype = "RegularJob"; + job.resourcegpu = 1; + job.image = "indexserveregistry.azurecr.io/dlts-data-transfer-image"; + job.runningasroot = "1"; + + job.cmd = [ + "cd /data/DataUtils && ./copy_data.sh", + convertURI(dataJob.fromType, dataJob.fromFolder), + convertURI(dataJob.toType, dataJob.toFolder), + "False 33554432 4 8 2>/dev/null" + ].join(" "); + + return job; + } From a144a5129302dd1e3bce6cbfaa143e619b782606 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Tue, 7 May 2019 18:57:42 +0800 Subject: [PATCH 072/595] Set teams only if user has clusters --- .../WebPortal/Controllers/HomeController.cs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs b/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs index f64b3a9aa..7166c1a7d 100755 --- a/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs @@ -726,19 +726,21 @@ public async Task Index() UserUnauthorized(); _logger.LogInformation("User {0} is not authorized for any cluster ... ", email); } - - // Set Teams - var teams = await GetTeams(); - if (teams.Length == 0) - { - // Mark user as unauthorized. - UserUnauthorized(); - _logger.LogInformation("User {0} is not authorized for any virtual cluster ... ", email); - } else { - HttpContext.Session.SetString("Teams", JsonConvert.SerializeObject(teams)); - HttpContext.Session.SetString("Team", teams[0]); + // Set Teams + var teams = await GetTeams(); + if (teams.Length == 0) + { + // Mark user as unauthorized. + UserUnauthorized(); + _logger.LogInformation("User {0} is not authorized for any virtual cluster ... ", email); + } + else + { + HttpContext.Session.SetString("Teams", JsonConvert.SerializeObject(teams)); + HttpContext.Session.SetString("Team", teams[0]); + } } } } From 909da5c43b5bb1d6aac37268f26c0aa568f75dda Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Tue, 7 May 2019 19:24:09 +0800 Subject: [PATCH 073/595] Configurable GPU type --- .../WebPortal/Controllers/dlwsController.cs | 4 --- .../WebPortal/Views/Home/JobSubmission.cshtml | 36 ++++++++++++++++--- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs index f236ccb26..00dd07e65 100755 --- a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs @@ -405,11 +405,7 @@ public async Task postJob(TemplateParams templateParams) jobObject["userName"] = HttpContext.Session.GetString("Email"); jobObject["userId"] = uid; jobObject["jobType"] = "training"; - - //TODO:update below params jobObject["vcName"] = HttpContext.Session.GetString("Team"); - jobObject["gpuType"] = "any"; - /////////////////////// var runningasroot = jobObject["runningasroot"]; if (!(Object.ReferenceEquals(runningasroot, null)) && (runningasroot.ToString() == "1") || (runningasroot.ToString() == true.ToString())) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 046fe9cba..99837695e 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -1,4 +1,6 @@ -@{ +@using Microsoft.AspNetCore.Http; + +@{ ViewData["Title"] = "Submit Your Job"; } @@ -8,8 +10,6 @@ a:link { color: #1F45FC } - - @@ -77,6 +77,8 @@ $scope.current.runningasroot = "0"; if (!$scope.current.preemptionAllowed) $scope.current.preemptionAllowed = "False"; + if (!$scope.current.gpuType && $scope.gpus) + $scope.current.gpuType = Object.keys($scope.gpus)[0]; tobool($scope.current, "is_interactive", false); if ($scope.current.mountpoints == null) { @@ -211,7 +213,7 @@ var username = $scope.joblist[i].Username; if ($scope.joblist[i].Database.trim() == "Master") $scope.joblist[i].Text = name + "(master)"; - else + else $scope.joblist[i].Text = name + "(" + username.trim().split('@@')[0] + ")"; } @@ -221,6 +223,17 @@ $scope.loadTemplate(); //$log.log($scope.joblist); }); + + $http.get("/api/dlws/GetVCs").then(function (response) { + var currentTeam = @Html.Raw(Json.Serialize(@Context.Session.GetString("Team"))); + var teams = response.data.result; + if (!Array.isArray(teams)) return; + var team = teams.filter(function (team) { return team.vcName === currentTeam })[0]; + if (!team) return; + + $scope.gpus = JSON.parse(team.quota); + $scope.current.gpuType = Object.keys($scope.gpus)[0]; + }) $scope.loadTemplate = function () { $scope.curtemplate = $scope.joblist[$scope.curtemplateValue]; @@ -523,6 +536,21 @@ @@ -83,44 +83,47 @@ - + }(window.jQuery); + + } @RenderSection("scripts", required: false) From 8a2f254f9fbb4c2d3e899590d64803006bf05913 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Fri, 24 May 2019 11:55:42 +0800 Subject: [PATCH 140/595] Hide carousel in signed in Index page --- .../dotnet/WebPortal/Views/Home/Index.cshtml | 259 +++++++++--------- 1 file changed, 131 insertions(+), 128 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml index 98e3255b5..793b54542 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml @@ -12,37 +12,9 @@ } - - @if (ViewData["isAuthorized"] != null && (bool)ViewData["isAuthorized"]) { -
-

Welcome to Deep Learning Training System, @ViewData["Username"] !

- - -

Cluster Status:

@@ -60,6 +32,37 @@
+
+

Welcome to Deep Learning Training System, @ViewData["Username"] !

+ + +
+} +else +{ + + } } - + + + + --> From 5e0bedbf58792792d1f19421e106b990c4b6b5b0 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Fri, 24 May 2019 12:32:00 +0800 Subject: [PATCH 141/595] Make SSH support multiple cluster --- .../dotnet/WebPortal/Controllers/HomeController.cs | 3 +++ .../dotnet/WebPortal/Controllers/dlwsController.cs | 8 +++++++- .../dotnet/WebPortal/Views/Home/JobDetail.cshtml | 14 ++++++++------ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs b/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs index d54180349..b90502776 100755 --- a/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs @@ -913,7 +913,10 @@ public IActionResult JobDetail() { return RedirectToAction("Index", "Home"); } + var cluster = HttpContext.Request.Query["cluster"]; + ViewData["cluster"] = cluster; ViewData["jobid"] = HttpContext.Request.Query["jobId"]; + ViewData["clusterEndpoint"] = new Uri(Startup.Clusters[cluster].Restapi).Host; var workFolderAccessPoint = Startup.Clusters[HttpContext.Request.Query["cluster"]].WorkFolderAccessPoint; diff --git a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs index cef496dd6..8d02ba275 100755 --- a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs @@ -468,7 +468,13 @@ public async Task postJob(TemplateParams templateParams) [HttpPost("endpoints")] public async Task PostEndpoints() { - var restapi = HttpContext.Session.GetString("Restapi"); + var cluster = HttpContext.Request.Query["cluster"]; + var authorizedClusters = JsonConvert.DeserializeObject>(HttpContext.Session.GetString("AuthorizedClusters")); + if (!authorizedClusters.Contains(cluster)) + { + return "Invalid cluster"; + } + var restapi = Startup.Clusters[cluster].Restapi; using (var httpClient = new HttpClient()) { httpClient.BaseAddress = new Uri(restapi); diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobDetail.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobDetail.cshtml index 79dc11588..cd0819ef1 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobDetail.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobDetail.cshtml @@ -3,7 +3,8 @@ *@ - - - - - - - -

Job Details:

-
-
- -

Job Folder:

-
-
- - - - -
- SSH -
- -

Job Console Output:

-
-
-
- - -
- - - - - -
- - - - -
- - - - -
-

Job analytics and monitoring:

-

GPU Usage:

- -

System Resource Usage:

- -
- - - - +@* + For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 +*@ + +@{ + ViewData["Title"] = "Job"; +} + + + + + +

Job Details:

+
+
+

Job Folder:

+
+
+ + +
+ SSH +
+

Job Console Output:

+
+
+
+ +
+ + + + +
+ + +
+ + +
+

Job analytics and monitoring:

+

GPU Usage:

+ +

System Resource Usage:

+ +
+ + diff --git a/src/WebUI/dotnet/WebPortal/Views/Shared/_Layout.cshtml b/src/WebUI/dotnet/WebPortal/Views/Shared/_Layout.cshtml index 1bd2a654c..99e4d3f1b 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Shared/_Layout.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Shared/_Layout.cshtml @@ -5,7 +5,7 @@ - @ViewData["Title"] - WebPortal + @ViewData["Title"] - WebPortal - Deep Learning Training System @@ -79,7 +79,7 @@ @RenderBody()
-

© 2017 - Deep Learning Workspace - WebPortal

+

© 2017 - Deep Learning Training System - WebPortal

From b21501c96768203df0c2a9dca363e48ad45b07bb Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Mon, 27 May 2019 18:54:48 +0800 Subject: [PATCH 148/595] Cluster Status: move user status top of node status --- .../WebPortal/Views/Home/ViewCluster.cshtml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml index 5b009aa7e..dac1d3466 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml @@ -87,7 +87,7 @@ if (json.node_status[i].unschedulable) { tr.append(" Unschedulable "); } - else + else { tr.append(" OK "); } @@ -127,7 +127,7 @@ $('#user_status').append(tr); } - + }); } @@ -183,37 +183,37 @@ +

- Node Status: + User Status:

- - +
- - - + - - - -
Node NameNode IPGPU CapacityUser Name Used GPUAvaliable GPUStatusServicesPods
-

- User Status: + Node Status:

- + +
- + + + + + + + From 51d9d326857ff7573b5cc86f532307a0f67392f2 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Mon, 27 May 2019 18:57:13 +0800 Subject: [PATCH 149/595] Job submission: refine label of gpu selector --- src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 64f58c439..76432c895 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -564,7 +564,7 @@ - {{ name }} ({{ count }}) + {{ name }} ({{ count }} available to use) From b034f719b2ebc75cbb8a2d147ef7f91589ef945a Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Mon, 27 May 2019 18:59:27 +0800 Subject: [PATCH 150/595] Job submission: lock running as root as true. --- .../WebPortal/Views/Home/JobSubmission.cshtml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 76432c895..9a0b82a53 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -65,16 +65,7 @@ enablejobpath: true, }; tobool($scope.current, "do_log", false); - if (!$scope.current.hasOwnProperty("runningasroot")) - $scope.current.runningasroot = "0"; - if (typeof ($scope.current.runningasroot) == "boolean") { - if ($scope.current.runningasroot) - $scope.current.runningasroot = "1"; - else - $scope.current.runningasroot = "0"; - }; - if ($scope.current.runningasroot != "0" && $scope.current.runningasroot != "1") - $scope.current.runningasroot = "0"; + $scope.current.runningasroot = "1"; tobool($scope.current, "is_interactive", false); if ($scope.current.mountpoints == null) { @@ -660,11 +651,6 @@ Enable Tensorboard - - - Launch container as root user. - -
From f6a735c33580e9497184c25880b4b5b940ba2e39 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Mon, 27 May 2019 17:58:22 +0800 Subject: [PATCH 151/595] refine dist job submit --- src/ClusterManager/job_manager.py | 111 +----------------------------- 1 file changed, 3 insertions(+), 108 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 42fafa21d..9ea01a3d8 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -327,17 +327,6 @@ def SubmitPSDistJob(job): if len(config["racks"]) > 0: assignedRack = random.choice(config["racks"]) - # in distributed job and host network is using, we will randomly assign a SSH port to each container. - ssh_ports = {} - if "hostNetwork" in jobParams and jobParams["hostNetwork"]: - ps_num = int(jobParams["numps"]) - worker_num = int(jobParams["numpsworker"]) - #port range: 30000~31000 - rndList = range(max(1000,ps_num + worker_num)) - random.shuffle(rndList) - ssh_ports["ps"] = [rndList[i] + 30000 for i in range(ps_num)] - ssh_ports["worker"] = [rndList[i + ps_num] + 30000 for i in range(worker_num)] - userAlias = getAlias(jobParams["userName"]) if jobParams["jobtrainingtype"] == "PSDistJob": @@ -352,17 +341,13 @@ def SubmitPSDistJob(job): if "jobPath" not in distJobParam or len(distJobParam["jobPath"].strip()) == 0: dataHandler.SetJobError(distJobParam["jobId"],"ERROR: job-path does not exist") return False - - distJobParam["distJobPath"] = os.path.join(distJobParam["jobPath"],distJobParam["distId"]) - if "workPath" not in distJobParam or len(distJobParam["workPath"].strip()) == 0: dataHandler.SetJobError(distJobParam["jobId"],"ERROR: work-path does not exist") return False - if "dataPath" not in distJobParam or len(distJobParam["dataPath"].strip()) == 0: dataHandler.SetJobError(distJobParam["jobId"],"ERROR: data-path does not exist") return False - + distJobParam["distJobPath"] = os.path.join(distJobParam["jobPath"],distJobParam["distId"]) jobPath,workPath,dataPath = GetStoragePath(distJobParam["distJobPath"],distJobParam["workPath"],distJobParam["dataPath"]) localJobPath = os.path.join(config["storage-mount-path"],jobPath) @@ -372,101 +357,11 @@ def SubmitPSDistJob(job): else: mkdirsAsUser(localJobPath,0) - - distJobParam["LaunchCMD"] = "" + # TODO ??? if "cmd" not in distJobParam: distJobParam["cmd"] = "" -################One choice is that we only wait for certain time. -# launchCMD = """ -##!/bin/bash -#mkdir -p /opt -#echo "[DLWorkspace System]: Waiting for all containers are ready..." -## wait for at most 10 mins. -#for i in {1..200}; do -# if [ ! -f /opt/run_dist_job ] || [ ! -f /opt/run_dist_job.sh ]; then -# sleep 3 -# else -# break -# fi -#done -#if [ ! -f /opt/run_dist_job ] || [ ! -f /opt/run_dist_job.sh ]; then -# echo "[DLWorkspace System]: Waiting for containers: timeout! Restarting..." -# exit 1 -#else -# echo "[DLWorkspace System]: All containers are ready, launching training job..." -# chmod +x /opt/run_dist_job.sh -# /opt/run_dist_job.sh -#fi -#""" - if "hostNetwork" in jobParams and jobParams["hostNetwork"]: - change_ssh_port_CMD = "cat /etc/ssh/sshd_config | grep -v 'Port\\ [0-9]*' > /tmp/sshd_config && echo 'Port "+str(ssh_ports[role][i])+"' | tee -a /tmp/sshd_config && sudo mv /tmp/sshd_config /etc/ssh/sshd_config ; " - else: - change_ssh_port_CMD = "" - - if role == "ps": - launchCMD = """ -#!/bin/bash -mkdir -p /opt -mkdir -p /root/.ssh -cp -r /sshkey/.ssh/id_rsa /root/.ssh -cp -r /sshkey/.ssh/id_rsa.pub /root/.ssh -cp -r /sshkey/.ssh/authorized_keys /root/.ssh -chown root:root /root/.ssh -chown root:root /root/.ssh/id_rsa -chown root:root /root/.ssh/id_rsa.pub -chown root:root /root/.ssh/authorized_keys -echo export LD_PRELOAD=$LD_PRELOAD >> /etc/default/ssh -echo export VNET_PREFIX=$VNET_PREFIX >> /etc/default/ssh - -%s -env | while read line; do if [[ $line != HOME=* ]] && [[ $line != INTERACTIVE* ]] ; then echo "$line" >> /etc/environment; fi; done -service ssh restart - -echo "[DLWorkspace System]: Waiting for all containers are ready..." -while [ ! -f /opt/run_dist_job ] || [ ! -f /opt/run_dist_job.sh ]; do - sleep 3 -done -echo "[DLWorkspace System]: All containers are ready, launching training job..." -chmod +x /opt/run_dist_job.sh -sleep 10 -chown root:root /root/.ssh/config -/opt/run_dist_job.sh -""" % change_ssh_port_CMD - else: - launchCMD = """ -#!/bin/bash -mkdir -p /opt -mkdir -p /root/.ssh -cp -r /sshkey/.ssh/id_rsa /root/.ssh -cp -r /sshkey/.ssh/id_rsa.pub /root/.ssh -cp -r /sshkey/.ssh/authorized_keys /root/.ssh -chown root:root /root/.ssh -chown root:root /root/.ssh/id_rsa -chown root:root /root/.ssh/id_rsa.pub -chown root:root /root/.ssh/authorized_keys -echo export LD_PRELOAD=$LD_PRELOAD >> /etc/default/ssh -echo export VNET_PREFIX=$VNET_PREFIX >> /etc/default/ssh - -%s -env | while read line; do if [[ $line != HOME=* ]] && [[ $line != INTERACTIVE* ]] ; then echo "$line" >> /etc/environment; fi; done -service ssh restart -while [ ! -f /opt/run_dist_job ] || [ ! -f /opt/run_dist_job.sh ]; do - sleep 3 -done -chown root:root /root/.ssh/config -sleep infinity -""" % change_ssh_port_CMD - - - - launchScriptPath = os.path.join(localJobPath,"launch-%s-%s%d.sh" % (distJobParam["jobId"],role,i)) - # TODO need to set up user for distribute jobs - with open(launchScriptPath, 'w') as f: - f.write(launchCMD) - f.close() - distJobParam["LaunchCMD"] = "[\"bash\", \"/job/launch-%s-%s%d.sh\"]" % (distJobParam["jobId"],role,i) - + distJobParam["LaunchCMD"] = '["bash", "-c", "bash /dlws/init_user.sh && runuser -l ${DLWS_USER_NAME} -c \'sleep infinity\'"]' distJobParam["jobNameLabel"] = ''.join(e for e in distJobParam["jobName"] if e.isalnum()) ENV = Environment(loader=FileSystemLoader("/")) From d31a4ca4e4b2640b59c0c4d519b9de852c1341b2 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Mon, 27 May 2019 20:00:13 +0800 Subject: [PATCH 152/595] remove useless code --- src/ClusterManager/job_manager.py | 75 ------------------------------- 1 file changed, 75 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 9ea01a3d8..036f172ba 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -601,81 +601,6 @@ def UpdateJobStatus(job): if result.strip() != "Unknown" and job["jobId"] in UnusualJobs: del UnusualJobs[job["jobId"]] -def UpdateDistJobStatus(job): - dataHandler = DataHandler() - jobParams = json.loads(base64.b64decode(job["jobParams"])) - - if "userId" not in jobParams: - jobParams["userId"] = "0" - - jobPath,workPath,dataPath = GetStoragePath(jobParams["jobPath"],jobParams["workPath"],jobParams["dataPath"]) - localJobPath = os.path.join(config["storage-mount-path"],jobPath) - logPath = os.path.join(localJobPath,"logs/joblog.txt") - - - result, detail = k8sUtils.GetJobStatus(job["jobId"]) - dataHandler.UpdateJobTextField(job["jobId"],"jobStatusDetail",base64.b64encode(detail)) - - logging.info("job %s status: %s,%s" % (job["jobId"], result, json.dumps(detail))) - - jobDescriptionPath = os.path.join(config["storage-mount-path"], job["jobDescriptionPath"]) if "jobDescriptionPath" in job else None - - - jobId = jobParams["jobId"] - workerPodInfo = k8sUtils.GetPod("distRole=worker,run=" + jobId) - psPodInfo = k8sUtils.GetPod("distRole=ps,run=" + jobId) - if "items" in workerPodInfo and len(workerPodInfo["items"]) == int(jobParams["numpsworker"]) and "items" in psPodInfo and len(psPodInfo["items"]) == int(jobParams["numps"]): - if job["jobStatus"] == "scheduling" : - launch_ps_dist_job(jobParams) - if job["jobStatus"] == "running": - result, detail = GetDistJobStatus(job["jobId"]) - dataHandler.UpdateJobTextField(job["jobId"],"jobStatusDetail",base64.b64encode(detail)) - - printlog("job %s status: %s" % (job["jobId"], result)) - - jobDescriptionPath = os.path.join(config["storage-mount-path"], job["jobDescriptionPath"]) if "jobDescriptionPath" in job else None - - if result.strip() == "Succeeded": - joblog_manager.extract_job_log(job["jobId"],logPath,jobParams["userId"]) - dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","finished") - if jobDescriptionPath is not None and os.path.isfile(jobDescriptionPath): - k8sUtils.kubectl_delete(jobDescriptionPath) - - elif result.strip() == "Running": - joblog_manager.extract_job_log(job["jobId"],logPath,jobParams["userId"]) - if job["jobStatus"] != "running": - dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","running") - - elif result.strip() == "Failed": - printlog("Job %s fails, cleaning..." % job["jobId"]) - joblog_manager.extract_job_log(job["jobId"],logPath,jobParams["userId"]) - dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","failed") - dataHandler.UpdateJobTextField(job["jobId"],"errorMsg",detail) - if jobDescriptionPath is not None and os.path.isfile(jobDescriptionPath): - k8sUtils.kubectl_delete(jobDescriptionPath) - - elif result.strip() == "Unknown": - if job["jobId"] not in UnusualJobs: - UnusualJobs[job["jobId"]] = datetime.datetime.now() - elif (datetime.datetime.now() - UnusualJobs[job["jobId"]]).seconds > 300: - del UnusualJobs[job["jobId"]] - retries = dataHandler.AddandGetJobRetries(job["jobId"]) - if retries >= 5: - printlog("Job %s fails for more than 5 times, abort" % job["jobId"]) - dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","error") - dataHandler.UpdateJobTextField(job["jobId"],"errorMsg","cannot launch the job.") - if jobDescriptionPath is not None and os.path.isfile(jobDescriptionPath): - k8sUtils.kubectl_delete(jobDescriptionPath) - else: - printlog("Job %s fails in Kubernetes, delete and re-submit the job. Retries %d" % (job["jobId"] , retries)) - SubmitJob(job) - - if result.strip() != "Unknown" and job["jobId"] in UnusualJobs: - del UnusualJobs[job["jobId"]] - - pass - - def run_dist_cmd_on_pod(podId, cmd, outputfile): From 0f517a3e65319835df724936e76aa604ac7eced7 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Mon, 27 May 2019 20:55:06 +0800 Subject: [PATCH 153/595] setup user in pods for dist job --- src/ClusterManager/job_manager.py | 347 +++++++++--------------------- src/utils/k8sUtils.py | 13 ++ 2 files changed, 111 insertions(+), 249 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 036f172ba..2c02c379e 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -404,7 +404,7 @@ def SubmitPSDistJob(job): random.seed(datetime.datetime.now()) if "hostNetwork" in jobParams and jobParams["hostNetwork"]: - distJobParam["containerPort"] = ssh_ports[role][i] + distJobParam["containerPort"] = random.randint(40001, 49999) else: distJobParam["containerPort"] = int(random.random()*1000+3000) @@ -466,7 +466,7 @@ def SubmitPSDistJob(job): jobMeta["jobPath"] = jobParams["jobPath"] jobMeta["workPath"] = jobParams["workPath"] jobMeta["jobPath"] = jobParams["jobPath"] - jobMeta["LaunchCMD"] = jobParams["LaunchCMD"] + jobMeta["LaunchCMD"] = jobParams["cmd"] jobMeta["distJobParams"] = distJobParams jobMetaStr = base64.b64encode(json.dumps(jobMeta)) @@ -542,10 +542,22 @@ def UpdateJobStatus(job): dataHandler = DataHandler() jobParams = json.loads(base64.b64decode(job["jobParams"])) - if job["jobStatus"] == "scheduling" and jobParams["jobtrainingtype"] == "PSDistJob": - launch_ps_dist_job(jobParams) - return + # launch user command only all pods are ready + result, detail = k8sUtils.GetJobStatus(job["jobId"]) + if result in ["Failed", "Succeeded"]: + # TODO shoudn't be here, update status + dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", result) + pass + else: + # previously status is 'scheduling', and now all pods are ready + # TODO check all pods are ready + if k8sUtils.all_pod_ready(job["jobId"]): + try: + launch_ps_dist_job(jobParams) + except Exception as e: + print(e) + return jobPath,workPath,dataPath = GetStoragePath(jobParams["jobPath"],jobParams["workPath"],jobParams["dataPath"]) localJobPath = os.path.join(config["storage-mount-path"],jobPath) @@ -621,263 +633,100 @@ def run(self): run_dist_cmd_on_pod(self.podId, self.cmd, self.outputfile) -def launch_ps_dist_job(jobParams): - try: - jobId = jobParams["jobId"] - workerPodInfo = k8sUtils.GetPod("distRole=worker,run=" + jobId) - psPodInfo = k8sUtils.GetPod("distRole=ps,run=" + jobId) - ssh_endpoints =[] - if "items" in workerPodInfo and len(workerPodInfo["items"]) == int(jobParams["numpsworker"]) and "items" in psPodInfo and len(psPodInfo["items"]) == int(jobParams["numps"]): - podStatus = [k8sUtils.check_pod_status(pod) for pod in workerPodInfo["items"] + psPodInfo["items"] ] - if all([status == "Running" for status in podStatus]): - ps_pod_names = [pod["metadata"]["name"] for pod in psPodInfo["items"]] - worker_pod_names = [pod["metadata"]["name"] for pod in workerPodInfo["items"]] - - ps_pod_ips = [pod["status"]["podIP"] for pod in psPodInfo["items"]] - worker_pod_ips = [pod["status"]["podIP"] for pod in workerPodInfo["items"]] - - worker_gpu_num = [pod["spec"]["containers"][0]["resources"]["requests"]["nvidia.com/gpu"] for pod in workerPodInfo["items"]] - - ps_num = len(psPodInfo["items"]) - worker_num = len(workerPodInfo["items"]) - - ps_ports = [int(item["metadata"]["labels"]["distPort"]) for item in psPodInfo["items"]] - worker_ports = [int(item["metadata"]["labels"]["distPort"]) for item in workerPodInfo["items"]] - - #port range: 30000~31000 - #rndList = range(max(1000,ps_num + worker_num)) - #random.shuffle(rndList) - #ps_ports = [rndList[i] + 30000 for i in range(ps_num)] - #worker_ports = [rndList[i + ps_num] + 30000 for i in range(worker_num)] - - ps_hosts = ",".join(["%s:%s" % (ps_pod_ips[i],ps_ports[i]) for i in range(ps_num)]) - worker_hosts = ",".join(["%s:%s" % (worker_pod_ips[i],worker_ports[i]) for i in range(worker_num)]) - - ps_hostnames = [pod["spec"]["nodeName"] for pod in psPodInfo["items"]] - worker_hostnames = [pod["spec"]["nodeName"] for pod in workerPodInfo["items"]] - - for h,ip,p in zip(ps_hostnames,ps_pod_ips,ps_ports): - hostName = h - if "." not in hostName and "domain" in config and (not config["domain"] is None) and len(config["domain"].strip()) >0: - hostName += "."+config["domain"] - ssh_endpoints.append( - { - "hostName":hostName, - "hostIP":ip, - "containerPort":22, - "hostPort":p, - "user":"root", - } - ) - - - for h,ip,p in zip(worker_hostnames,worker_pod_ips,worker_ports): - hostName = h - if "." not in hostName and "domain" in config and (not config["domain"] is None) and len(config["domain"].strip()) >0: - hostName += "."+config["domain"] - ssh_endpoints.append( - { - "hostName":hostName, - "hostIP":ip, - "containerPort":22, - "hostPort":p, - "user":"root", - } - ) - if "hostNetwork" in jobParams and jobParams["hostNetwork"]: - dataHandler = DataHandler() - serviceAddress = base64.b64encode(json.dumps(ssh_endpoints)) - # TODO remove the related logic - # dataHandler.UpdateJobTextField(jobParams["jobId"],"endpoints",serviceAddress) - - ps_files = ["/tmp/" + str(uuid.uuid4()) for i in range(ps_num)] - worker_files = ["/tmp/" + str(uuid.uuid4()) for i in range(worker_num)] - - #ps_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=ps --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,ps_files[i]) for i in range(ps_num)] - #worker_cmd = ["%s --ps_hosts=%s --worker_hosts=%s --job_name=worker --task_index=%d 2>&1 | tee %s" % (jobParams["cmd"], ps_hosts,worker_hosts,i,worker_files[i]) for i in range(worker_num)] - - - - ssh_config = """ -Host %s - HostName %s - Port %s - User root - StrictHostKeyChecking no - UserKnownHostsFile /dev/null - """ - - - - ps_cmd = ["%s 2>&1 | tee %s" % (jobParams["cmd"], ps_files[i]) for i in range(ps_num)] - worker_cmd = ["%s 2>&1 | tee %s" % (jobParams["cmd"], worker_files[i]) for i in range(worker_num)] - - - - - - hostfilecontent = "" - for i in range(len(worker_gpu_num)): - hostfilecontent += "%s slots=%s\n" %("worker-"+str(i),worker_gpu_num[i]) - - - sshconfigstr = "" - for i in range(ps_num): - if "hostNetwork" in jobParams and jobParams["hostNetwork"]: - sshconfigstr += (ssh_config %("master-"+str(i), ps_pod_ips[i] ,str(ps_ports[i])) +"\n") - else: - sshconfigstr += (ssh_config %("master-"+str(i), ps_pod_ips[i] ,str(22)) +"\n") - - for i in range(worker_num): - if "hostNetwork" in jobParams and jobParams["hostNetwork"]: - sshconfigstr += (ssh_config %("worker-"+str(i), worker_pod_ips[i] ,str(worker_ports[i])) +"\n") - else: - sshconfigstr += (ssh_config %("worker-"+str(i), worker_pod_ips[i] ,str(22)) +"\n") - - - error_flag = False - for i in range(ps_num): - - os.system("mkdir -p %s" % ps_files[i]) - psfile = os.path.join(ps_files[i],"hostfile") - with open(psfile, 'w') as f: - f.write(hostfilecontent + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], psfile)) - remotecmd = "cp %s %s:/opt/hostfile" % (psfile,ps_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - - - os.system("mkdir -p %s" % ps_files[i]) - psfile = os.path.join(ps_files[i],"config") - with open(psfile, 'w') as f: - f.write(sshconfigstr + "\n") - f.close() - #if "userId" in jobParams: - # os.system("chown -R %s %s" % (jobParams["userId"], psfile)) - k8sUtils.kubectl_exec("exec %s 'mkdir -p /root/.ssh'" % ps_pod_names[i]) - remotecmd = "cp %s %s:/root/.ssh/config" % (psfile,ps_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - k8sUtils.kubectl_exec("exec %s 'chmod 400 /root/.ssh/config'" % ps_pod_names[i]) - k8sUtils.kubectl_exec("exec %s 'chown root:root /root/.ssh/config'" % ps_pod_names[i]) - - - - os.system("mkdir -p %s" % ps_files[i]) - psfile = os.path.join(ps_files[i],"taskindex") - with open(psfile, 'w') as f: - f.write(str(i) + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], psfile)) - remotecmd = "cp %s %s:/opt/taskindex" % (psfile,ps_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - - - - - for i in range(worker_num): - - - os.system("mkdir -p %s" % worker_files[i]) - workerfile = os.path.join(worker_files[i],"hostfile") - with open(workerfile, 'w') as f: - f.write(hostfilecontent + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) - remotecmd = "cp %s %s:/opt/hostfile" % (workerfile,worker_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - - - os.system("mkdir -p %s" % worker_files[i]) - psfile = os.path.join(worker_files[i],"config") - with open(psfile, 'w') as f: - f.write(sshconfigstr + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], psfile)) - k8sUtils.kubectl_exec("exec %s 'mkdir -p /root/.ssh'" % worker_pod_names[i]) - remotecmd = "cp %s %s:/root/.ssh/config" % (psfile,worker_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - k8sUtils.kubectl_exec("exec %s 'chmod 400 /root/.ssh/config'" % worker_pod_names[i]) - k8sUtils.kubectl_exec("exec %s 'chown root:root /root/.ssh/config'" % worker_pod_names[i]) - - - os.system("mkdir -p %s" % worker_files[i]) - workerfile = os.path.join(worker_files[i],"taskindex") - with open(workerfile, 'w') as f: - f.write(str(i) + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) - remotecmd = "cp %s %s:/opt/taskindex" % (workerfile,worker_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - +# TODO remove duplicate code later +def is_ssh_server_ready(pod_name): + bash_script = "sudo service ssh status" + output = k8sUtils.kubectl_exec("exec %s %s" % (pod_name, " -- " + bash_script)) + if output == "": + return False + return True - for i in range(worker_num): - os.system("mkdir -p %s" % worker_files[i]) - workerfile = os.path.join(worker_files[i],"run_dist_job.sh") - with open(workerfile, 'w') as f: - f.write(worker_cmd[i] + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], workerfile)) - remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (workerfile,worker_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) +# TODO remove duplicate code later +def query_ssh_port(pod_name): + bash_script = "grep ^Port /etc/ssh/sshd_config | cut -d' ' -f2" + ssh_port = k8sUtils.kubectl_exec("exec %s %s" % (pod_name, " -- " + bash_script)) + return int(ssh_port) - k8sUtils.kubectl_exec("exec %s touch /opt/run_dist_job" % worker_pod_names[i]) - output = k8sUtils.kubectl_exec("exec %s ls /opt/run_dist_job" % worker_pod_names[i]) - if (output == ""): - error_flag = True - - - for i in range(ps_num): - os.system("mkdir -p %s" % ps_files[i]) - psfile = os.path.join(ps_files[i],"run_dist_job.sh") - with open(psfile, 'w') as f: - f.write(ps_cmd[i] + "\n") - f.close() - if "userId" in jobParams: - os.system("chown -R %s %s" % (jobParams["userId"], psfile)) - remotecmd = "cp %s %s:/opt/run_dist_job.sh" % (psfile,ps_pod_names[i]) - k8sUtils.kubectl_exec(remotecmd) - - - k8sUtils.kubectl_exec("exec %s touch /opt/run_dist_job" % ps_pod_names[i]) - output = k8sUtils.kubectl_exec("exec %s ls /opt/run_dist_job" % ps_pod_names[i]) - if (output == ""): - error_flag = True +# TODO remove duplicate code later +def start_ssh_server(pod_name, user_name, ssh_port=22, host_network=False): + '''Setup the ssh server in container, and return the listening port.''' + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && cd /home/" + user_name + " && mkdir -p ssh && chmod 700 ssh && cat .ssh/id_rsa.pub >> ssh/authorized_keys && chmod 600 ssh/authorized_keys && sed -i \"s/^[#]*AuthorizedKeysFile.*/AuthorizedKeysFile %h\/ssh\/authorized_keys/\" /etc/ssh/sshd_config && service ssh restart'" + # ssh_port = 22 + # modify the script for HostNewtork + # if the ssh_port is default value 22, randomly choose one + if host_network and ssh_port==22: + ssh_port = random.randint(40001, 49999) + # bash_script = "sed -i '/^Port 22/c Port "+str(ssh_port)+"' /etc/ssh/sshd_config && "+bash_script + # TODO refine the script later + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/c Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && mkdir -p ssh && chmod 700 ssh && cat .ssh/id_rsa.pub >> ssh/authorized_keys && chmod 600 ssh/authorized_keys && sed -i \"s/^[#]*AuthorizedKeysFile.*/AuthorizedKeysFile %h\/ssh\/authorized_keys/\" /etc/ssh/sshd_config && service ssh restart'" + # TODO setup reasonable timeout + # output = k8sUtils.kubectl_exec("exec %s %s" % (jobId, " -- " + bash_script), 1) + output = k8sUtils.kubectl_exec("exec %s %s" % (pod_name, " -- " + bash_script)) + if output == "": + raise Exception("Failed to setup ssh server in container. JobId: %s " % pod_name) + return ssh_port - if not error_flag: - dataHandler = DataHandler() - dataHandler.UpdateJobTextField(jobParams["jobId"],"jobStatus","running") - #ps_threads = [Kube_RemoteCMD_Thread(jobId,ps_pod_names[i],ps_cmd[i],ps_logfiles[i]) for i in range(ps_num)] - #worker_threads = [Kube_RemoteCMD_Thread(jobId,worker_pod_names[i],worker_cmd[i],worker_logfiles[i]) for i in range(worker_num)] +def launch_ps_dist_job(jobParams): + job_id = jobParams["jobId"] + pods = k8sUtils.GetPod("run=" + job_id) - #for t in ps_threads: - # t.start() + # if any pod is not up, return + if "items" not in pods or len(pods["items"]) != (int(jobParams["numpsworker"]) + int(jobParams["numps"])): + return + # if any pod is not ready, return + pod_status = [k8sUtils.check_pod_status(pod) for pod in pods["items"]] + if any([status != "Running" for status in pod_status]): + return - #for t in worker_threads: - # t.start() + user_name = getAlias(jobParams["userName"]) + if "hostNetwork" in jobParams and jobParams["hostNetwork"]: + host_network = True + else: + host_network = False + # setup ssh server + for [idx, pod] in enumerate(pods["items"]): + pod_name = pod["metadata"]["name"] + dist_port = pod["metadata"]["labels"]["distPort"] + # quit if can't setup ssh server + ssh_port = start_ssh_server(pod_name, user_name, dist_port, host_network) - #while (True): - #for t in ps_threads: - # print t.isAlive() - #time.sleep(5) + # generate ssh config + ssh_config = """ +Host %s + HostName %s + Port %s + User %s + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + """ + sshconfigstr = "" + for [idx, pod] in enumerate(pods["items"]): + pod_ip = pod["status"]["podIP"] + dist_port = pod["metadata"]["labels"]["distPort"] + role = pod["metadata"]["labels"]["distRole"] + + # TODO hostNetwork + if host_network: + sshconfigstr += (ssh_config % (role + "-"+str(idx), pod_ip, str(dist_port), user_name) + "\n") + else: + sshconfigstr += (ssh_config % (role + "-"+str(idx), pod_ip, 22, user_name) + "\n") - #cmd = "test" - #thread.start_new_thread( run_dist_cmd_on_pod, - #(workerPodInfo["items"][0]["metadata"]["name"], cmd) ) - except Exception as e: - print e + # config ssh client + for [idx, pod] in enumerate(pods["items"]): + pod_name = pod["metadata"]["name"] + # TODO need to handle the config override problem + bash_script = "cat > /home/" + user_name + "/.ssh/config < Date: Tue, 28 May 2019 00:58:46 +0800 Subject: [PATCH 154/595] execute user command after all pods are ready --- src/ClusterManager/job_manager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 2c02c379e..8c765c667 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -724,6 +724,9 @@ def launch_ps_dist_job(jobParams): print("override ssh client config: %s" % bash_script) k8sUtils.kubectl_exec("exec %s -- bash -c \'%s\'" % (pod_name, bash_script)) + # execute user command + k8sUtils.kubectl_exec("exec %s -- runuser -l ${DLWS_USER_NAME} < Date: Tue, 28 May 2019 01:12:21 +0800 Subject: [PATCH 155/595] fix hostNetwork ssh server port --- src/ClusterManager/job_manager.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 8c765c667..c499cf908 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -655,9 +655,10 @@ def start_ssh_server(pod_name, user_name, ssh_port=22, host_network=False): # ssh_port = 22 # modify the script for HostNewtork - # if the ssh_port is default value 22, randomly choose one - if host_network and ssh_port==22: - ssh_port = random.randint(40001, 49999) + if host_network: + # if the ssh_port is default value 22, randomly choose one + if ssh_port== 22: + ssh_port = random.randint(40001, 49999) # bash_script = "sed -i '/^Port 22/c Port "+str(ssh_port)+"' /etc/ssh/sshd_config && "+bash_script # TODO refine the script later bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/c Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && mkdir -p ssh && chmod 700 ssh && cat .ssh/id_rsa.pub >> ssh/authorized_keys && chmod 600 ssh/authorized_keys && sed -i \"s/^[#]*AuthorizedKeysFile.*/AuthorizedKeysFile %h\/ssh\/authorized_keys/\" /etc/ssh/sshd_config && service ssh restart'" From cde34640fc354cc4a9158ecc1131499c8a9ba5bc Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 28 May 2019 11:33:17 +0800 Subject: [PATCH 156/595] fix script typo --- src/ClusterManager/endpoint_manager.py | 2 +- src/ClusterManager/job_manager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index afafe0408..4319e27e6 100644 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -39,7 +39,7 @@ def start_ssh_server(pod_name, user_name, host_network=False): ssh_port = random.randint(40001, 49999) # bash_script = "sed -i '/^Port 22/c Port "+str(ssh_port)+"' /etc/ssh/sshd_config && "+bash_script # TODO refine the script later - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/c Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && mkdir -p ssh && chmod 700 ssh && cat .ssh/id_rsa.pub >> ssh/authorized_keys && chmod 600 ssh/authorized_keys && sed -i \"s/^[#]*AuthorizedKeysFile.*/AuthorizedKeysFile %h\/ssh\/authorized_keys/\" /etc/ssh/sshd_config && service ssh restart'" + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && mkdir -p ssh && chmod 700 ssh && cat .ssh/id_rsa.pub >> ssh/authorized_keys && chmod 600 ssh/authorized_keys && sed -i \"s/^[#]*AuthorizedKeysFile.*/AuthorizedKeysFile %h\/ssh\/authorized_keys/\" /etc/ssh/sshd_config && service ssh restart'" # TODO setup reasonable timeout # output = k8sUtils.kubectl_exec("exec %s %s" % (jobId, " -- " + bash_script), 1) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index c499cf908..d814dfb6c 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -661,7 +661,7 @@ def start_ssh_server(pod_name, user_name, ssh_port=22, host_network=False): ssh_port = random.randint(40001, 49999) # bash_script = "sed -i '/^Port 22/c Port "+str(ssh_port)+"' /etc/ssh/sshd_config && "+bash_script # TODO refine the script later - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/c Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && mkdir -p ssh && chmod 700 ssh && cat .ssh/id_rsa.pub >> ssh/authorized_keys && chmod 600 ssh/authorized_keys && sed -i \"s/^[#]*AuthorizedKeysFile.*/AuthorizedKeysFile %h\/ssh\/authorized_keys/\" /etc/ssh/sshd_config && service ssh restart'" + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && mkdir -p ssh && chmod 700 ssh && cat .ssh/id_rsa.pub >> ssh/authorized_keys && chmod 600 ssh/authorized_keys && sed -i \"s/^[#]*AuthorizedKeysFile.*/AuthorizedKeysFile %h\/ssh\/authorized_keys/\" /etc/ssh/sshd_config && service ssh restart'" # TODO setup reasonable timeout # output = k8sUtils.kubectl_exec("exec %s %s" % (jobId, " -- " + bash_script), 1) From b0e298cf8d9c19dd77e931df0b9efcba2bcd8e89 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 28 May 2019 11:50:27 +0800 Subject: [PATCH 157/595] fix role index --- src/ClusterManager/job_manager.py | 6 ++++-- src/Jobs_Templete/DistJob.yaml.template | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index d814dfb6c..01f8223c7 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -337,6 +337,7 @@ def SubmitPSDistJob(job): distJobParam=copy.deepcopy(jobParams) distJobParam["distId"] = "%s%d" % (role,i) distJobParam["distRole"] = role + distJobParam["distRoleIdx"] = i if "jobPath" not in distJobParam or len(distJobParam["jobPath"].strip()) == 0: dataHandler.SetJobError(distJobParam["jobId"],"ERROR: job-path does not exist") @@ -710,12 +711,13 @@ def launch_ps_dist_job(jobParams): pod_ip = pod["status"]["podIP"] dist_port = pod["metadata"]["labels"]["distPort"] role = pod["metadata"]["labels"]["distRole"] + role_idx = pod["metadata"]["labels"]["distRoleIdx"] # TODO hostNetwork if host_network: - sshconfigstr += (ssh_config % (role + "-"+str(idx), pod_ip, str(dist_port), user_name) + "\n") + sshconfigstr += (ssh_config % (role + "-"+str(role_idx), pod_ip, str(dist_port), user_name) + "\n") else: - sshconfigstr += (ssh_config % (role + "-"+str(idx), pod_ip, 22, user_name) + "\n") + sshconfigstr += (ssh_config % (role + "-"+str(role_idx), pod_ip, 22, user_name) + "\n") # config ssh client for [idx, pod] in enumerate(pods["items"]): diff --git a/src/Jobs_Templete/DistJob.yaml.template b/src/Jobs_Templete/DistJob.yaml.template index 75118b6c7..64ea48440 100755 --- a/src/Jobs_Templete/DistJob.yaml.template +++ b/src/Jobs_Templete/DistJob.yaml.template @@ -7,6 +7,7 @@ metadata: podName: {{ job["jobId"] }}-{{ job["distId"] }} jobName: {{ job["jobNameLabel"] }} distRole: {{ job["distRole"] }} + distRoleIdx: "{{ job["distRoleIdx"] }}" distPort: "{{job["containerPort"]}}" userName: {{ job["user"] }} vcName: {{ job["vcName"] }} From f37c0bb4f06702cf57df23a88c3da942f6c6f152 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 21 May 2019 13:58:49 +0800 Subject: [PATCH 158/595] add a column "lastUpdated" for jobs --- src/utils/MySQLDataHandler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index 3d64c4e5a..a218d1593 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -91,6 +91,7 @@ def CreateTable(self): `jobMeta` LONGTEXT NULL, `jobLog` LONGTEXT NULL, `retries` int NULL DEFAULT 0, + `lastUpdated` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, PRIMARY KEY (`id`), UNIQUE(`jobId`), INDEX (`userName`), From 2c9d3b7765da6537783df495363bfe958556268d Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 28 May 2019 15:06:55 +0800 Subject: [PATCH 159/595] mount ~/.ssh as emptyDir volume --- src/ClusterManager/job_manager.py | 7 ++----- src/Jobs_Templete/DistJob.yaml.template | 22 ++++++++++++++++++++ src/Jobs_Templete/RegularJob.yaml.template | 24 +++++++++++++++++++++- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 01f8223c7..1d4305cba 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -168,8 +168,8 @@ def SubmitRegularJob(job): jobParams["mountpoints"].append(mp) userAlias = getAlias(jobParams["userName"]) + jobParams["homeFolderHostpath"] = os.path.join(config["storage-mount-path"], GetWorkPath(userAlias)) - mp = {"name":"sshkey","containerPath":"/home/%s/.ssh" % userAlias,"hostPath":os.path.join(config["storage-mount-path"], GetWorkPath(userAlias)+"/.ssh"), "readOnly":True, "enabled":True} if CheckMountPoints(jobParams["mountpoints"],mp): jobParams["mountpoints"].append(mp) @@ -328,6 +328,7 @@ def SubmitPSDistJob(job): assignedRack = random.choice(config["racks"]) userAlias = getAlias(jobParams["userName"]) + jobParams["homeFolderHostpath"] = os.path.join(config["storage-mount-path"], GetWorkPath(userAlias)) if jobParams["jobtrainingtype"] == "PSDistJob": jobDescriptionList = [] @@ -383,10 +384,6 @@ def SubmitPSDistJob(job): distJobParam["mountpoints"].append({"name":"work","containerPath":"/work","hostPath":distJobParam["hostworkPath"]}) distJobParam["mountpoints"].append({"name":"data","containerPath":"/data","hostPath":distJobParam["hostdataPath"]}) - - distJobParam["mountpoints"].append({"name":"rootsshkey","containerPath":"/sshkey/.ssh","hostPath":os.path.join(config["storage-mount-path"], GetWorkPath(userAlias)+"/.ssh"), "readOnly":True, "enabled":True}) - - for idx in range(len(distJobParam["mountpoints"])): if "name" not in distJobParam["mountpoints"][idx]: distJobParam["mountpoints"][idx]["name"] = str(uuid.uuid4()).replace("-","") diff --git a/src/Jobs_Templete/DistJob.yaml.template b/src/Jobs_Templete/DistJob.yaml.template index 64ea48440..5ea516c10 100755 --- a/src/Jobs_Templete/DistJob.yaml.template +++ b/src/Jobs_Templete/DistJob.yaml.template @@ -72,6 +72,17 @@ spec: - name: "init-user-script" mountPath: /dlws/init_user.sh subPath: init_user.sh + - name: ssh-volume + mountPath: /home/{{ job["user"] }}/.ssh + - name: id-rsa-volume + mountPath: /home/{{ job["user"] }}/.ssh/id_rsa + readOnly: true + - name: id-rsa-pub-volume + mountPath: /home/{{ job["user"] }}/.ssh/id_rsa.pub + readOnly: true + - name: authorized-keys-volume + mountPath: /home/{{ job["user"] }}/.ssh/authorized_keys + readOnly: true {% if job["usefreeflow"] %} - mountPath: /freeflow name: freeflow @@ -136,6 +147,17 @@ spec: - name: "init-user-script" configMap: name: "init-user-script" + - name: ssh-volume + emptyDir: {} + - name: id-rsa-volume + hostPath: + path: {{ job["homeFolderHostpath"] }}/.ssh/id_rsa + - name: id-rsa-pub-volume + hostPath: + path: {{ job["homeFolderHostpath"] }}/.ssh/id_rsa.pub + - name: authorized-keys-volume + hostPath: + path: {{ job["homeFolderHostpath"] }}/.ssh/authorized_keys {% if job["usefreeflow"] %} - name: freeflow hostPath: diff --git a/src/Jobs_Templete/RegularJob.yaml.template b/src/Jobs_Templete/RegularJob.yaml.template index 1673e8b2a..7eefb1e36 100755 --- a/src/Jobs_Templete/RegularJob.yaml.template +++ b/src/Jobs_Templete/RegularJob.yaml.template @@ -7,7 +7,7 @@ metadata: podName: {{ job["podName"] }} jobName: {{ job["jobNameLabel"] }} jobId: {{job["jobId"]}} - userName: {{ job["userNameLabel"] }} + userName: {{ job["user"] }} vcName: {{ job["vcName"] }} {% if "gpuType" in job %} {% if job["gpuType"]|length > 0 %} @@ -74,6 +74,17 @@ spec: - name: "init-user-script" mountPath: /dlws/init_user.sh subPath: init_user.sh + - name: ssh-volume + mountPath: /home/{{ job["user"] }}/.ssh + - name: id-rsa-volume + mountPath: /home/{{ job["user"] }}/.ssh/id_rsa + readOnly: true + - name: id-rsa-pub-volume + mountPath: /home/{{ job["user"] }}/.ssh/id_rsa.pub + readOnly: true + - name: authorized-keys-volume + mountPath: /home/{{ job["user"] }}/.ssh/authorized_keys + readOnly: true {% if not job["dnsPolicy"] %} - mountPath: /etc/resolv.conf name: resolv @@ -145,6 +156,17 @@ spec: - name: "init-user-script" configMap: name: "init-user-script" + - name: ssh-volume + emptyDir: {} + - name: id-rsa-volume + hostPath: + path: {{ job["homeFolderHostpath"] }}/.ssh/id_rsa + - name: id-rsa-pub-volume + hostPath: + path: {{ job["homeFolderHostpath"] }}/.ssh/id_rsa.pub + - name: authorized-keys-volume + hostPath: + path: {{ job["homeFolderHostpath"] }}/.ssh/authorized_keys {% if not job["dnsPolicy"] %} - name: resolv hostPath: From 5acd054d5d4e22b987de7ce609c34eba83d6449c Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 28 May 2019 16:18:21 +0800 Subject: [PATCH 160/595] fix .ssh/config, now multiple dist job works --- src/ClusterManager/endpoint_manager.py | 12 +++++++----- src/ClusterManager/job_manager.py | 11 ++++++----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index 4319e27e6..f668e94d3 100644 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -28,18 +28,20 @@ def query_ssh_port(pod_name): return int(ssh_port) -def start_ssh_server(pod_name, user_name, host_network=False): +def start_ssh_server(pod_name, user_name, host_network=False, ssh_port=22): '''Setup the ssh server in container, and return the listening port.''' - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && cd /home/" + user_name + " && mkdir -p ssh && chmod 700 ssh && cat .ssh/id_rsa.pub >> ssh/authorized_keys && chmod 600 ssh/authorized_keys && sed -i \"s/^[#]*AuthorizedKeysFile.*/AuthorizedKeysFile %h\/ssh\/authorized_keys/\" /etc/ssh/sshd_config && service ssh restart'" + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh || chmod 600 -R .ssh || true) && service ssh restart'" - ssh_port = 22 + # ssh_port = 22 # modify the script for HostNewtork if host_network: - ssh_port = random.randint(40001, 49999) + # if the ssh_port is default value 22, randomly choose one + if ssh_port == 22: + ssh_port = random.randint(40001, 49999) # bash_script = "sed -i '/^Port 22/c Port "+str(ssh_port)+"' /etc/ssh/sshd_config && "+bash_script # TODO refine the script later - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && mkdir -p ssh && chmod 700 ssh && cat .ssh/id_rsa.pub >> ssh/authorized_keys && chmod 600 ssh/authorized_keys && sed -i \"s/^[#]*AuthorizedKeysFile.*/AuthorizedKeysFile %h\/ssh\/authorized_keys/\" /etc/ssh/sshd_config && service ssh restart'" + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh || chmod 600 -R .ssh || true) && service ssh restart'" # TODO setup reasonable timeout # output = k8sUtils.kubectl_exec("exec %s %s" % (jobId, " -- " + bash_script), 1) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 1d4305cba..765ad7e56 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -646,20 +646,20 @@ def query_ssh_port(pod_name): return int(ssh_port) # TODO remove duplicate code later -def start_ssh_server(pod_name, user_name, ssh_port=22, host_network=False): +def start_ssh_server(pod_name, user_name, host_network=False, ssh_port=22): '''Setup the ssh server in container, and return the listening port.''' - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && cd /home/" + user_name + " && mkdir -p ssh && chmod 700 ssh && cat .ssh/id_rsa.pub >> ssh/authorized_keys && chmod 600 ssh/authorized_keys && sed -i \"s/^[#]*AuthorizedKeysFile.*/AuthorizedKeysFile %h\/ssh\/authorized_keys/\" /etc/ssh/sshd_config && service ssh restart'" + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh; chmod 700 .ssh; true) && service ssh restart'" # ssh_port = 22 # modify the script for HostNewtork if host_network: # if the ssh_port is default value 22, randomly choose one - if ssh_port== 22: + if ssh_port == 22: ssh_port = random.randint(40001, 49999) # bash_script = "sed -i '/^Port 22/c Port "+str(ssh_port)+"' /etc/ssh/sshd_config && "+bash_script # TODO refine the script later - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && mkdir -p ssh && chmod 700 ssh && cat .ssh/id_rsa.pub >> ssh/authorized_keys && chmod 600 ssh/authorized_keys && sed -i \"s/^[#]*AuthorizedKeysFile.*/AuthorizedKeysFile %h\/ssh\/authorized_keys/\" /etc/ssh/sshd_config && service ssh restart'" + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh; chmod 700 .ssh; true) && service ssh restart'" # TODO setup reasonable timeout # output = k8sUtils.kubectl_exec("exec %s %s" % (jobId, " -- " + bash_script), 1) @@ -692,7 +692,7 @@ def launch_ps_dist_job(jobParams): pod_name = pod["metadata"]["name"] dist_port = pod["metadata"]["labels"]["distPort"] # quit if can't setup ssh server - ssh_port = start_ssh_server(pod_name, user_name, dist_port, host_network) + ssh_port = start_ssh_server(pod_name, user_name, host_network, dist_port) # generate ssh config ssh_config = """ @@ -726,6 +726,7 @@ def launch_ps_dist_job(jobParams): # execute user command k8sUtils.kubectl_exec("exec %s -- runuser -l ${DLWS_USER_NAME} < Date: Tue, 28 May 2019 01:29:10 -0700 Subject: [PATCH 161/595] change userName: {{ job["user"] }} --- src/Jobs_Templete/RegularJob.yaml.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Jobs_Templete/RegularJob.yaml.template b/src/Jobs_Templete/RegularJob.yaml.template index 1673e8b2a..97dfe66ed 100755 --- a/src/Jobs_Templete/RegularJob.yaml.template +++ b/src/Jobs_Templete/RegularJob.yaml.template @@ -7,7 +7,7 @@ metadata: podName: {{ job["podName"] }} jobName: {{ job["jobNameLabel"] }} jobId: {{job["jobId"]}} - userName: {{ job["userNameLabel"] }} + userName: {{ job["user"] }} vcName: {{ job["vcName"] }} {% if "gpuType" in job %} {% if job["gpuType"]|length > 0 %} From f2ae84fc7a35483348489c7a56ae6a644f97ca4f Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Tue, 28 May 2019 09:35:09 +0000 Subject: [PATCH 162/595] makes acl more robust --- src/utils/authorization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 src/utils/authorization.py diff --git a/src/utils/authorization.py b/src/utils/authorization.py old mode 100644 new mode 100755 index bd4a2b335..fae56b724 --- a/src/utils/authorization.py +++ b/src/utils/authorization.py @@ -45,7 +45,7 @@ def _HasAccess(identityName, resourceAclPath, permissions): for ace in acl: for identity in identities: logger.debug('identity %s' % identity) - if ace["identityName"] == identityName or (ace["identityId"] == identity and (int(identity) < INVALID_RANGE_START or int(identity) > INVALID_RANGE_END)): + if ace["identityName"] == identityName or (str(ace["identityId"]) == str(identity) and (int(identity) < INVALID_RANGE_START or int(identity) > INVALID_RANGE_END)): permissions = permissions & (~ace["permissions"]) if not permissions: logger.info('access : Yes') From d88e9448aff42d70aef761b14bd05609aa793e6d Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 28 May 2019 10:03:47 +0000 Subject: [PATCH 163/595] add alert manager --- src/ClusterBootstrap/params.py | 8 ++ .../services/monitor/alert-manager.yaml | 82 +++++++++++++++++++ .../services/monitor/launch_order | 1 + .../services/monitor/prometheus.yaml | 19 +++++ 4 files changed, 110 insertions(+) create mode 100644 src/ClusterBootstrap/services/monitor/alert-manager.yaml diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index ab3c6c5a7..420317fe4 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -24,6 +24,13 @@ "node-exporter": { "port": 9100 }, "watchdog": { "port": 9101 }, "grafana": { "port": 3000 }, + "alert-manager": { + "port": 9093, + "configured": False + # If want to deploy with alert-manager, should config + # configured with True, and fill appropriate value to: + # smtp_url, smtp_from, smtp_auth_username, smtp_auth_password and receiver + }, "mysql_port": "3306", "mysql_username": "root", @@ -207,6 +214,7 @@ "FragmentGPUJob": "all", "grafana": "etcd_node_1", "prometheus": "etcd_node_1", + "alert-manager": "etcd_node_1", "watchdog": "etcd_node_1", "elasticsearch": "etcd_node_1", "kibana": "etcd_node_1", diff --git a/src/ClusterBootstrap/services/monitor/alert-manager.yaml b/src/ClusterBootstrap/services/monitor/alert-manager.yaml new file mode 100644 index 000000000..ec900a5fd --- /dev/null +++ b/src/ClusterBootstrap/services/monitor/alert-manager.yaml @@ -0,0 +1,82 @@ +{% if cnf["alert-manager"]["configured"] %} + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: alert-manager + namespace: kube-system +spec: + replicas: 1 + selector: + matchLabels: + app: alert-manager + template: + metadata: + name: alert-manager + labels: + app: alert-manager + annotations: + prometheus.io/alert: "true" + prometheus.io/port: "{{ cnf['alert-manager']['port'] }}" + spec: + nodeSelector: + alert-manager: active + hostNetwork: true + containers: + - name: alert-manager + image: prom/alertmanager:v0.15.1 + args: + - '--config.file=/etc/alertmanager/config.yml' + - '--storage.path=/alertmanager' + - '--web.external-url={{ cnf["api-server-ip"] }}/alert-manager/' + - '--web.route-prefix=alert-manager' + ports: + - name: alert-manager + containerPort: {{ cnf["alert-manager"]["port"] }} + volumeMounts: + - name: config-volume + mountPath: /etc/alertmanager + - name: storage + mountPath: /alertmanager + volumes: + - name: config-volume + configMap: + name: alert-manager + - name: storage + emptyDir: {} + tolerations: + - key: node.kubernetes.io/memory-pressure + operator: "Exists" + - key: node.kubernetes.io/disk-pressure + operator: "Exists" + - key: node-role.kubernetes.io/master + effect: NoSchedule +--- +{% endif %} +kind: ConfigMap +apiVersion: v1 +metadata: + name: alert-manager + namespace: kube-system +data: + config.yml: |- +{% if cnf["alert-manager"]["configured"] %} +{% set alert_info = cnf["alert-manager"] %} + global: + resolve_timeout: 5m + smtp_smarthost: {{ alert_info["smtp_url"] }} + smtp_from: {{ alert_info["smtp_from"] }} + smtp_auth_username: {{ alert_info["smtp_auth_username"] }} + smtp_auth_password: {{ alert_info["smtp_auth_password"] }} + route: + receiver: alert-email + group_wait: 30s + group_interval: 5m + group_by: [alertname] + receivers: + - name: "alert-email" + email_configs: + - to: {{ alert_info["receiver"] }} + headers: + subject: '{{ cnf["cluster_name"] }}: {{ "{{" }} template "__subject" . {{ "}}" }}' +{% endif %} diff --git a/src/ClusterBootstrap/services/monitor/launch_order b/src/ClusterBootstrap/services/monitor/launch_order index cb9a67dd5..044066b57 100755 --- a/src/ClusterBootstrap/services/monitor/launch_order +++ b/src/ClusterBootstrap/services/monitor/launch_order @@ -5,3 +5,4 @@ prometheus-alerting.yaml prometheus.yaml grafana-config.yaml grafana.yaml +alert-manager.yaml diff --git a/src/ClusterBootstrap/services/monitor/prometheus.yaml b/src/ClusterBootstrap/services/monitor/prometheus.yaml index d07df7a92..975fdc15b 100644 --- a/src/ClusterBootstrap/services/monitor/prometheus.yaml +++ b/src/ClusterBootstrap/services/monitor/prometheus.yaml @@ -34,6 +34,25 @@ data: - source_labels: [__meta_kubernetes_pod_label_app] action: replace target_label: exporter_name + alerting: + alertmanagers: + - path_prefix: alert-manager + tls_config: + ca_file: '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt' + bearer_token_file: '/var/run/secrets/kubernetes.io/serviceaccount/token' + kubernetes_sd_configs: + - role: pod + namespaces: + names: ["kube-system"] + relabel_configs: + - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_alert] + regex: true + action: keep + - source_labels: [__meta_kubernetes_pod_host_ip, __meta_kubernetes_pod_annotation_prometheus_io_port] + regex: '([^;]+);(\d+)' + replacement: ${1}:${2} + action: replace + target_label: __address__ --- apiVersion: apps/v1 kind: Deployment From 279ba456031e9f32ec2448ce0e7b743dbb97415c Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 28 May 2019 18:54:11 +0800 Subject: [PATCH 164/595] fix ~/.ssh/ permission --- src/ClusterManager/endpoint_manager.py | 4 ++-- src/ClusterManager/job_manager.py | 4 ++-- src/Jobs_Templete/init_user.sh | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index f668e94d3..244c8b029 100644 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -30,7 +30,7 @@ def query_ssh_port(pod_name): def start_ssh_server(pod_name, user_name, host_network=False, ssh_port=22): '''Setup the ssh server in container, and return the listening port.''' - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh || chmod 600 -R .ssh || true) && service ssh restart'" + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh/*; chmod 700 .ssh; true) && service ssh restart'" # ssh_port = 22 @@ -41,7 +41,7 @@ def start_ssh_server(pod_name, user_name, host_network=False, ssh_port=22): ssh_port = random.randint(40001, 49999) # bash_script = "sed -i '/^Port 22/c Port "+str(ssh_port)+"' /etc/ssh/sshd_config && "+bash_script # TODO refine the script later - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh || chmod 600 -R .ssh || true) && service ssh restart'" + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh/*; chmod 700 .ssh; true) && service ssh restart'" # TODO setup reasonable timeout # output = k8sUtils.kubectl_exec("exec %s %s" % (jobId, " -- " + bash_script), 1) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 765ad7e56..8fb96b51b 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -648,7 +648,7 @@ def query_ssh_port(pod_name): # TODO remove duplicate code later def start_ssh_server(pod_name, user_name, host_network=False, ssh_port=22): '''Setup the ssh server in container, and return the listening port.''' - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh; chmod 700 .ssh; true) && service ssh restart'" + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh/*; chmod 700 .ssh; true) && service ssh restart'" # ssh_port = 22 @@ -659,7 +659,7 @@ def start_ssh_server(pod_name, user_name, host_network=False, ssh_port=22): ssh_port = random.randint(40001, 49999) # bash_script = "sed -i '/^Port 22/c Port "+str(ssh_port)+"' /etc/ssh/sshd_config && "+bash_script # TODO refine the script later - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh; chmod 700 .ssh; true) && service ssh restart'" + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh/*; chmod 700 .ssh; true) && service ssh restart'" # TODO setup reasonable timeout # output = k8sUtils.kubectl_exec("exec %s %s" % (jobId, " -- " + bash_script), 1) diff --git a/src/Jobs_Templete/init_user.sh b/src/Jobs_Templete/init_user.sh index 08aa5471e..b7ae161fb 100644 --- a/src/Jobs_Templete/init_user.sh +++ b/src/Jobs_Templete/init_user.sh @@ -12,6 +12,8 @@ addgroup --force-badname --gid ${DLWS_GID} domainusers adduser --force-badname --home /home/${DLWS_USER_NAME} --shell /bin/bash --uid ${DLWS_UID} -gecos '' --gid ${DLWS_GID} --disabled-password ${DLWS_USER_NAME} usermod -p $(echo tryme2017 | openssl passwd -1 -stdin) ${DLWS_USER_NAME} chown -R ${DLWS_USER_NAME} /home/${DLWS_USER_NAME}/ || /bin/true +chmod -R 600 /home/${DLWS_USER_NAME}/.ssh || /bin/true +chmod 700 /home/${DLWS_USER_NAME}/.ssh || /bin/true # setup sudoers adduser $DLWS_USER_NAME sudo From c07e218c705511d5c0a1c31cb6cc004e5f427241 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Tue, 28 May 2019 19:17:51 +0800 Subject: [PATCH 165/595] Remove directory / dashboard info in Home page --- src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml index 7311f6795..71d600502 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml @@ -34,18 +34,6 @@

Welcome to Deep Learning Training System, @ViewData["Username"] !

- -
} else From 91e88b5551ae7f97dbac901688b809252f00544c Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Tue, 28 May 2019 19:18:32 +0800 Subject: [PATCH 166/595] Cluster info page: fix hostname of grafana --- .../WebPortal/Controllers/dlwsController.cs | 18 +++++++++++++++++- .../WebPortal/Views/Home/ViewCluster.cshtml | 15 ++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs index 8d02ba275..e1073aee3 100755 --- a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs @@ -83,12 +83,28 @@ public async Task GetMountPoints() } + // GET api/dlws/hostname + [HttpGet("hostname")] + public string GetHostname() + { + + var cluster = HttpContext.Request.Query["cluster"]; + var authorizedClusters = JsonConvert.DeserializeObject>(HttpContext.Session.GetString("AuthorizedClusters")); + if (!authorizedClusters.Contains(cluster)) + { + return "Invalid cluster"; + } + var restapi = Startup.Clusters[cluster].Restapi; + return new Uri(restapi).Host; + } + + // GET api/dlws/GetLog [HttpGet("GetLog/{jobId}")] public async Task GetLog(string jobId) { - string url = String.Format(@"http://"+ Request.Host + ":9200/_search?sort=time:asc&_source=log&size=100&q=kubernetes.pod_name:{0}",jobId); + string url = String.Format(@"http://" + Request.Host + ":9200/_search?sort=time:asc&_source=log&size=100&q=kubernetes.pod_name:{0}", jobId); string ret = ""; using (var httpClient = new HttpClient()) { diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml index dac1d3466..b9b6d07a1 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml @@ -68,6 +68,19 @@ }) } function buildTable(cluster) { + $.ajax({ + url: "/api/dlws/hostname?cluster=" + cluster, + dataType: "text" + }).done( + function (hostname) { + var srcTemplate = $('#usage-dashboard').data('src'); + var currentSrc = $('#usage-dashboard').attr('src'); + var src = srcTemplate.replace('{{hostname}}', hostname); + if (src !== currentSrc) { + $('#usage-dashboard').attr('src', src); + } + } + ) $.ajax({ url: "/api/dlws/GetClusterStatus?cluster=" + cluster, dataType: 'json', @@ -169,7 +182,7 @@
User NameNode NameNode IPGPU Capacity Used GPUAvaliable GPUStatusServicesPods
- + ") if response == "WIPEOUT": config["hdfsconfig"]["formatoptions"] = "--force " - # Start a kubelet service. + # Start a kubelet service. for servicename in servicenames: start_kube_service(servicename) elif nargs[0] == "stop": @@ -3588,7 +3591,7 @@ def run_command( args, command, nargs, parser ): elif nargs[0] == "cordon" or nargs[0] == "uncordon": run_kube_command_on_nodes(nargs) elif nargs[0] == "labelvc": - kubernetes_label_vc(True) + kubernetes_label_vc(True) else: parser.print_help() print "Error: Unknown kubernetes subcommand " + nargs[0] @@ -3645,7 +3648,7 @@ def run_command( args, command, nargs, parser ): push_docker_images(nargs[1:]) elif nargs[0] == "run": if len(nargs)>=2: - run_docker_image( nargs[1], args.native, sudo = args.sudo ) + run_docker_image( nargs[1], args.native, sudo = args.sudo ) else: parser.print_help() print "Error: docker run expects an image name " @@ -3688,7 +3691,7 @@ def run_script_blocks( verbose, script_collection ): run_command( args, command, nargs, parser ) if __name__ == '__main__': - # the program always run at the current directory. + # the program always run at the current directory. dirpath = os.path.dirname(os.path.abspath(os.path.realpath(__file__))) # print "Directory: " + dirpath os.chdir(dirpath) @@ -3702,133 +3705,133 @@ def run_script_blocks( verbose, script_collection ): * Metadata of deployed cluster is stored at deploy. Command: - scriptblocks Execute a block of scripts. + scriptblocks Execute a block of scripts. azure - build [arg] Build deployment environment + build [arg] Build deployment environment arg="": should be executed first, generate keys for the cluster arg=iso-coreos: build ISO image fore CoreOS deployment. - arg=pxe-coreos: build PXE server for CoreOS deployment. + arg=pxe-coreos: build PXE server for CoreOS deployment. arg=pxe-ubuntu: build PXE server for Ubuntu deployment. [We use standard Ubuntu ISO for Ubuntu ISO deployment. ] - nfs-server create: Create NFS-server. - sshkey install: [Ubuntu] install sshkey to Ubuntu cluster. + nfs-server create: Create NFS-server. + sshkey install: [Ubuntu] install sshkey to Ubuntu cluster. production [nodes] Deploy a production cluster, with tasks of: - set hostname, deploy etcd/master nodes, deploy worker nodes, uncordon master nodes. + set hostname, deploy etcd/master nodes, deploy worker nodes, uncordon master nodes. deploy Deploy DL workspace cluster. - updateworker [nodes] Update the worker nodes. If no additional node is specified, all nodes will be updated. + updateworker [nodes] Update the worker nodes. If no additional node is specified, all nodes will be updated. clean Clean away a failed deployment. - update [args] Update cluster. - config: update cloud-config of each deployed node. + update [args] Update cluster. + config: update cloud-config of each deployed node. connect [master|etcd|worker] num: Connect to either master, etcd or worker node (with an index number). hostname [args] manage hostname on the cluster set: set hostname - uncordon allow etcd/master nodes to be scheduled jobs - partition [args] Manage data partitions. - ls: show all existing partitions. + uncordon allow etcd/master nodes to be scheduled jobs + partition [args] Manage data partitions. + ls: show all existing partitions. create n: create n partitions of equal size. create s1 s2 ... sn: create n partitions; - if s_i < 0, the partition is s_i GB, - if s_i > 0, the partition is in portitional to s_i. - We use parted mkpart percentage% to create partitions. As such, the minimum partition is 1% of a disk. + if s_i < 0, the partition is s_i GB, + if s_i > 0, the partition is in portitional to s_i. + We use parted mkpart percentage% to create partitions. As such, the minimum partition is 1% of a disk. mount install | start | stop | link - start: mount all fileshares + start: mount all fileshares install: install all client components related to the fileshare stop: unmount all fileshares nolink: mount all fileshares, but doesnot symbolic link to the mount share - glusterfs [args] manage glusterFS on the cluster. - display: display lvm information on each node of the cluster. - create: formatting and create lvm for used by glusterfs. - remove: deletel and remove glusterfs volumes. + glusterfs [args] manage glusterFS on the cluster. + display: display lvm information on each node of the cluster. + create: formatting and create lvm for used by glusterfs. + remove: deletel and remove glusterfs volumes. config: generate configuration file, build and push glusterfs docker. - start: start glusterfs service and endpoints. - stop: stop glusterfs service and endpoints. - hdfs [args] manage HDFS on the cluster. - create: formatting and create local drive for use by HDFS. - mount: mount local drive for use by HDFS. + start: start glusterfs service and endpoints. + stop: stop glusterfs service and endpoints. + hdfs [args] manage HDFS on the cluster. + create: formatting and create local drive for use by HDFS. + mount: mount local drive for use by HDFS. umount: unmount local drive that is used for HDFS. download [args] Manage download kubectl: download kubelet/kubectl. kubelet: download kubelet/kubectl. - backup [fname] [key] Backup configuration & encrypt, fname is the backup file without surfix. - If key exists, the backup file will be encrypted. - restore [fname] [key] Decrypt & restore configuration, fname is the backup file with surfix. - If the backup file is encrypted, a key needs to be provided to decrypt the configuration. + backup [fname] [key] Backup configuration & encrypt, fname is the backup file without surfix. + If key exists, the backup file will be encrypted. + restore [fname] [key] Decrypt & restore configuration, fname is the backup file with surfix. + If the backup file is encrypted, a key needs to be provided to decrypt the configuration. etcd [args] manage etcd server. check: check ETCD service. - kubernetes [args] manage kubelet services on the cluster. - start: launch a certain kubelet service. - stop: stop a certain kubelet service. - restart: replace a certain kubelet service. - cordon [node]: cordon certain nodes. If no node, cordon all etcd nodes. - uncordon [node]: uncordon certain nodes. If no node, uncordon all etcd nodes. - labels verb [services]: applying labels to node according to service (usually daemon) setup. + kubernetes [args] manage kubelet services on the cluster. + start: launch a certain kubelet service. + stop: stop a certain kubelet service. + restart: replace a certain kubelet service. + cordon [node]: cordon certain nodes. If no node, cordon all etcd nodes. + uncordon [node]: uncordon certain nodes. If no node, uncordon all etcd nodes. + labels verb [services]: applying labels to node according to service (usually daemon) setup. -y: overwrite existing value verb: active, inactive, remove (default=on) services: if none, apply to all services in the service directory mark [properties]: applying labels on node according to node property (usually in cluster.yaml) unmark [properties]: removing labels on node according to node property (usually in cluster.yaml) - kubectl [args] run a native kubectl command. - docker [args] manage docker images. - build: build one or more docker images associated with the current deployment. + kubectl [args] run a native kubectl command. + docker [args] manage docker images. + build: build one or more docker images associated with the current deployment. push: build and push one or more docker images to register run [--sudo]: run a docker image (--sudo: in super user mode) nginx [args] manage nginx reverse proxy config: config nginx node, mainly install file that specify how to direct traffic fqdn: config nginx node, install FQDN for each node - execonall [cmd ... ] Execute the command on all nodes and print the output. - doonall [cmd ... ] Execute the command on all nodes. - runscriptonall [script] Execute the shell/python script on all nodes. + execonall [cmd ... ] Execute the command on all nodes and print the output. + doonall [cmd ... ] Execute the command on all nodes. + runscriptonall [script] Execute the shell/python script on all nodes. listmac display mac address of the cluster notes checkconfig display config items rendertemplate template_file target_file ''') ) - parser.add_argument("-y", "--yes", - help="Answer yes automatically for all prompt", + parser.add_argument("-y", "--yes", + help="Answer yes automatically for all prompt", action="store_true" ) - parser.add_argument("--force", - help="Force perform certain operation", + parser.add_argument("--force", + help="Force perform certain operation", action="store_true" ) - parser.add_argument("--native", - help="Run docker in native mode (in how it is built)", + parser.add_argument("--native", + help="Run docker in native mode (in how it is built)", action="store_true" ) - parser.add_argument("-p", "--public", - help="Use public IP address to deploy/connect [e.g., Azure, AWS]", + parser.add_argument("-p", "--public", + help="Use public IP address to deploy/connect [e.g., Azure, AWS]", action="store_true") - parser.add_argument("-s", "--sudo", - help = "Execute scripts in sudo", + parser.add_argument("-s", "--sudo", + help = "Execute scripts in sudo", action="store_true" ) - parser.add_argument("--discoverserver", - help = "Specify an alternative discover server, default = " + default_config_parameters["discoverserver"], - action="store", + parser.add_argument("--discoverserver", + help = "Specify an alternative discover server, default = " + default_config_parameters["discoverserver"], + action="store", default=default_config_parameters["discoverserver"]) - parser.add_argument("--homeinserver", - help = "Specify an alternative home in server, default = " + default_config_parameters["homeinserver"], - action="store", + parser.add_argument("--homeinserver", + help = "Specify an alternative home in server, default = " + default_config_parameters["homeinserver"], + action="store", default=default_config_parameters["homeinserver"]) - parser.add_argument("-v", "--verbose", - help = "verbose print", + parser.add_argument("-v", "--verbose", + help = "verbose print", action="store_true") - parser.add_argument("--nocache", - help = "Build docker without cache", + parser.add_argument("--nocache", + help = "Build docker without cache", action="store_true") - parser.add_argument("--glusterfs", + parser.add_argument("--glusterfs", help = textwrap.dedent('''"Additional glusterfs launch parameter, \ - detach: detach all glusterfs nodes (to rebuild cluster), + detach: detach all glusterfs nodes (to rebuild cluster), start: initiate cluster (all nodes need to be operative during start stage to construct the cluster), - run: continuous operation, - ''' ), - action="store", + run: continuous operation, + ''' ), + action="store", default="run" ) - parser.add_argument("--nodes", - help = "Specify an python regular expression that limit the nodes that the operation is applied.", + parser.add_argument("--nodes", + help = "Specify an python regular expression that limit the nodes that the operation is applied.", action="store", default=None ) - parser.add_argument("command", + parser.add_argument("command", help = "See above for the list of valid command" ) - parser.add_argument('nargs', nargs=argparse.REMAINDER, - help="Additional command argument", + parser.add_argument('nargs', nargs=argparse.REMAINDER, + help="Additional command argument", ) args = parser.parse_args() command = args.command From 126b8012bdb864fdbbe9e736da942d48fb9fdbaf Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Tue, 28 May 2019 19:08:41 +0000 Subject: [PATCH 173/595] update gpu number per worker --- src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 25959f9e8..c85765770 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -235,6 +235,7 @@ if (!team) return; $scope.gpus = JSON.parse(team.quota); + $scope.gpumeta = JSON.parse(team.metadata); $scope.checkExtras(); }) $scope.extras.gpuType = null; @@ -611,14 +612,14 @@ - + - +
You have to let us know the number of gpus per worker. @@ -629,7 +630,7 @@
- You can request at most {{gpus[extras.gpuType]}} GPUs per worker. + You can request at most {{gpumeta[extras.gpuType]['num_gpu_per_node']}} GPUs per worker.
From 26b7045dc213087c0fd5c7ec8b8d4c7191d6371c Mon Sep 17 00:00:00 2001 From: Hongzhi Li Date: Tue, 28 May 2019 23:23:55 +0000 Subject: [PATCH 174/595] allow to mount root data folder --- src/ClusterManager/job_manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 8fb96b51b..fa13ee0dc 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -346,9 +346,9 @@ def SubmitPSDistJob(job): if "workPath" not in distJobParam or len(distJobParam["workPath"].strip()) == 0: dataHandler.SetJobError(distJobParam["jobId"],"ERROR: work-path does not exist") return False - if "dataPath" not in distJobParam or len(distJobParam["dataPath"].strip()) == 0: - dataHandler.SetJobError(distJobParam["jobId"],"ERROR: data-path does not exist") - return False + #if "dataPath" not in distJobParam or len(distJobParam["dataPath"].strip()) == 0: + # dataHandler.SetJobError(distJobParam["jobId"],"ERROR: data-path does not exist") + # return False distJobParam["distJobPath"] = os.path.join(distJobParam["jobPath"],distJobParam["distId"]) jobPath,workPath,dataPath = GetStoragePath(distJobParam["distJobPath"],distJobParam["workPath"],distJobParam["dataPath"]) From 6851084090d6e6ca0b0e52c0881113567b08ddb5 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 29 May 2019 08:20:20 +0800 Subject: [PATCH 175/595] create init-user-script while starting jobmanager --- src/ClusterBootstrap/deploy.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index aab739f05..4f3bee96b 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -2830,8 +2830,9 @@ def start_one_kube_service(fname): except Exception as e: pass - # recreate the configmap init-user-script each time - run_kubectl( ["create configmap init-user-script --from-file=../Jobs_Templete/init_user.sh -o yaml --dry-run | ./deploy/bin/kubectl apply -f -"] ) + if fname == "./deploy/services/jobmanager/jobmanager.yaml": + # recreate the configmap init-user-script + run_kubectl( ["create configmap init-user-script --from-file=../Jobs_Templete/init_user.sh -o yaml --dry-run | ./deploy/bin/kubectl apply -f -"] ) run_kubectl( ["create", "-f", fname ] ) From 67b2714fb0af2d939d8c4e9ad71c5ccfa19db5bf Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 29 May 2019 03:29:40 +0000 Subject: [PATCH 176/595] add email templates to replace default one --- .gitignore | 1 + src/ClusterBootstrap/params.py | 2 + .../services/monitor/alert-manager.yaml | 10 +- .../monitor/alert-templates/email.tmpl | 128 ++++++++++++++++++ .../services/monitor/launch_order | 1 + .../services/monitor/pre-render.sh | 3 + 6 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/ClusterBootstrap/services/monitor/alert-templates/email.tmpl diff --git a/.gitignore b/.gitignore index b46a3ca3c..1d62c6528 100755 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,4 @@ src/WebUI/dotnet/WebPortal/hosting.json cluster-autoscaler src/ClusterBootstrap/services/monitor/grafana-config.yaml src/ClusterBootstrap/services/monitor/prometheus-alerting.yaml +src/ClusterBootstrap/services/monitor/alert-templates.yaml diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 420317fe4..26406c1ec 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -88,6 +88,7 @@ ".swf": True, ".gzip": True, ".rules": True, + ".tmpl": True, }, "render-by-copy": { # The following file will be copied (not rendered for configuration) @@ -102,6 +103,7 @@ "collectd.influxdb.conf.tpl": True, "collectd.riemann.conf.tpl": True, "prometheus-alerting.yaml": True, + "alert-templates.yaml": True, # "nginx": True, "RecogServer": True, diff --git a/src/ClusterBootstrap/services/monitor/alert-manager.yaml b/src/ClusterBootstrap/services/monitor/alert-manager.yaml index ec900a5fd..266b70c6f 100644 --- a/src/ClusterBootstrap/services/monitor/alert-manager.yaml +++ b/src/ClusterBootstrap/services/monitor/alert-manager.yaml @@ -28,7 +28,7 @@ spec: args: - '--config.file=/etc/alertmanager/config.yml' - '--storage.path=/alertmanager' - - '--web.external-url={{ cnf["api-server-ip"] }}/alert-manager/' + - '--web.external-url=http://localhost/alert-manager/' - '--web.route-prefix=alert-manager' ports: - name: alert-manager @@ -38,10 +38,15 @@ spec: mountPath: /etc/alertmanager - name: storage mountPath: /alertmanager + - name: templates-volume + mountPath: /etc/alertmanager/template volumes: - name: config-volume configMap: name: alert-manager + - name: templates-volume + configMap: + name: alert-templates - name: storage emptyDir: {} tolerations: @@ -68,6 +73,8 @@ data: smtp_from: {{ alert_info["smtp_from"] }} smtp_auth_username: {{ alert_info["smtp_auth_username"] }} smtp_auth_password: {{ alert_info["smtp_auth_password"] }} + templates: + - "/etc/alertmanager/template/*.tmpl" route: receiver: alert-email group_wait: 30s @@ -77,6 +84,7 @@ data: - name: "alert-email" email_configs: - to: {{ alert_info["receiver"] }} + html: '{{ "{{" }} template "email.html" . {{ "}}" }}' headers: subject: '{{ cnf["cluster_name"] }}: {{ "{{" }} template "__subject" . {{ "}}" }}' {% endif %} diff --git a/src/ClusterBootstrap/services/monitor/alert-templates/email.tmpl b/src/ClusterBootstrap/services/monitor/alert-templates/email.tmpl new file mode 100644 index 000000000..b9e411dde --- /dev/null +++ b/src/ClusterBootstrap/services/monitor/alert-templates/email.tmpl @@ -0,0 +1,128 @@ +{{ define "email.html" }} + + + + + + +{{ template "__subject" . }} + + + + + +
+ + + + + +
+
+ + + {{ if gt (len .Alerts.Firing) 0 }} + + + + + +
+ {{ else }} + + {{ end }} + {{ .Alerts | len }} alert{{ if gt (len .Alerts) 1 }}s{{ end }} for {{ range .GroupLabels.SortedPairs }} + {{ .Name }}={{ .Value }} + {{ end }} +
+ + + + + {{ if gt (len .Alerts.Firing) 0 }} + + + + {{ end }} + {{ range .Alerts.Firing }} + + + + {{ end }} + + {{ if gt (len .Alerts.Resolved) 0 }} + + + + {{ end }} + {{ range .Alerts.Resolved }} + + + + {{ end }} +
+ View in {{ template "__alertmanager" . }} +
+ [{{ .Alerts.Firing | len }}] Firing +
+ {{ if .Annotations.summary }} + {{ .Annotations.summary }} + {{ else }} + Labels
+ {{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}
{{ end }} + {{ if gt (len .Annotations) 0 }}Annotations
{{ end }} + {{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}
{{ end }} + {{ end }} + Source
+
+ [{{ .Alerts.Resolved | len }}] Resolved +
+ {{ if .Annotations.summary }} + {{ .Annotations.summary }} + {{ else }} + Labels
+ {{ range .Labels.SortedPairs }}{{ .Name }} = {{ .Value }}
{{ end }} + {{ if gt (len .Annotations) 0 }}Annotations
{{ end }} + {{ range .Annotations.SortedPairs }}{{ .Name }} = {{ .Value }}
{{ end }} + {{ end }} + Source
+
+
+ +
+
+ + + +{{ end }} diff --git a/src/ClusterBootstrap/services/monitor/launch_order b/src/ClusterBootstrap/services/monitor/launch_order index 044066b57..1e17557f9 100755 --- a/src/ClusterBootstrap/services/monitor/launch_order +++ b/src/ClusterBootstrap/services/monitor/launch_order @@ -5,4 +5,5 @@ prometheus-alerting.yaml prometheus.yaml grafana-config.yaml grafana.yaml +alert-templates.yaml alert-manager.yaml diff --git a/src/ClusterBootstrap/services/monitor/pre-render.sh b/src/ClusterBootstrap/services/monitor/pre-render.sh index 7b4bb0009..3bef7333e 100755 --- a/src/ClusterBootstrap/services/monitor/pre-render.sh +++ b/src/ClusterBootstrap/services/monitor/pre-render.sh @@ -3,6 +3,7 @@ dir=`dirname $0` grafana_file_name=${dir}/grafana-config.yaml +alert_tmpl_file_name=${dir}/alert-templates.yaml prometheus_file_name=${dir}/prometheus-alerting.yaml # create configmap @@ -10,4 +11,6 @@ for i in `find ${dir}/grafana-config/ -type f -regex ".*json" ` ; do echo --from-file=$i done | xargs ${dir}/../../deploy/bin/kubectl --namespace=kube-system create configmap grafana-configuration --dry-run -o yaml >> $grafana_file_name +${dir}/../../deploy/bin/kubectl --namespace=kube-system create configmap alert-templates --from-file=${dir}/alert-templates --dry-run -o yaml > $alert_tmpl_file_name + ${dir}/../../deploy/bin/kubectl --namespace=kube-system create configmap prometheus-alert --from-file=${dir}/alerting --dry-run -o yaml >> $prometheus_file_name From 7ab33481ce5cc66536528a3f2b59163ac3da0d61 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 29 May 2019 14:01:25 +0800 Subject: [PATCH 177/595] install pkg "sudo" --- src/Jobs_Templete/init_user.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Jobs_Templete/init_user.sh b/src/Jobs_Templete/init_user.sh index b7ae161fb..d77d6a4e5 100644 --- a/src/Jobs_Templete/init_user.sh +++ b/src/Jobs_Templete/init_user.sh @@ -16,6 +16,7 @@ chmod -R 600 /home/${DLWS_USER_NAME}/.ssh || /bin/true chmod 700 /home/${DLWS_USER_NAME}/.ssh || /bin/true # setup sudoers +apt-get update && apt-get install sudo adduser $DLWS_USER_NAME sudo echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers From a551291a55404a92830c17cebba540f60a8aff64 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 29 May 2019 14:06:47 +0800 Subject: [PATCH 178/595] touch a file "USER_READY" after user is setup --- src/Jobs_Templete/init_user.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Jobs_Templete/init_user.sh b/src/Jobs_Templete/init_user.sh index d77d6a4e5..adb01fa46 100644 --- a/src/Jobs_Templete/init_user.sh +++ b/src/Jobs_Templete/init_user.sh @@ -34,5 +34,6 @@ if [ -f ${ENV_FILE} ]; then fi SCRIPT +touch /dlws/USER_READY # any command should run as ${DLWS_USER_NAME} #runuser -l ${DLWS_USER_NAME} -c your_commands From 7f1d8b3a451eaa49f0fe6c7aa261d64c9c4ff26e Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 29 May 2019 14:53:32 +0800 Subject: [PATCH 179/595] redirect init_user_script.sh output to /job/init_user_script.log --- src/ClusterManager/job_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index fa13ee0dc..10764e7f1 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -122,7 +122,7 @@ def SubmitRegularJob(job): f.write("mkdir /opt; \n") f.write("echo 'localhost slots=%s' | tee -a /opt/hostfile; \n" % jobParams["resourcegpu"]) # TODO refine it later - f.write("bash /dlws/init_user.sh && runuser -l ${DLWS_USER_NAME} -c '%s'\n" % jobParams["cmd"]) + f.write("bash /dlws/init_user.sh &> /job/init_user_script.log && runuser -l ${DLWS_USER_NAME} -c '%s'\n" % jobParams["cmd"]) f.close() if "userId" in jobParams: os.system("chown -R %s %s" % (jobParams["userId"], launchScriptPath)) @@ -363,7 +363,7 @@ def SubmitPSDistJob(job): if "cmd" not in distJobParam: distJobParam["cmd"] = "" - distJobParam["LaunchCMD"] = '["bash", "-c", "bash /dlws/init_user.sh && runuser -l ${DLWS_USER_NAME} -c \'sleep infinity\'"]' + distJobParam["LaunchCMD"] = '["bash", "-c", "bash /dlws/init_user.sh &> /job/init_user_script.log && runuser -l ${DLWS_USER_NAME} -c \'sleep infinity\'"]' distJobParam["jobNameLabel"] = ''.join(e for e in distJobParam["jobName"] if e.isalnum()) ENV = Environment(loader=FileSystemLoader("/")) From 45716f5e7a5a835bb26c1b99a2de0d2dfdfaf808 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 29 May 2019 07:33:23 +0000 Subject: [PATCH 180/595] add job status and gpu usage dashboard --- .../gpu-usage-history-dashboard.json | 164 +++++ .../grafana-config/job-status-dashboard.json | 592 ++++++++++++++++++ .../service-status-dashboard.json | 4 +- 3 files changed, 758 insertions(+), 2 deletions(-) create mode 100644 src/ClusterBootstrap/services/monitor/grafana-config/gpu-usage-history-dashboard.json create mode 100644 src/ClusterBootstrap/services/monitor/grafana-config/job-status-dashboard.json diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/gpu-usage-history-dashboard.json b/src/ClusterBootstrap/services/monitor/grafana-config/gpu-usage-history-dashboard.json new file mode 100644 index 000000000..3e093f35f --- /dev/null +++ b/src/ClusterBootstrap/services/monitor/grafana-config/gpu-usage-history-dashboard.json @@ -0,0 +1,164 @@ +{"dashboard": { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": false, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [], + "refresh": "30s", + "rows": [ + { + "collapse": false, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 0, + "id": 1, + "legend": { + "avg": false, + "current": true, + "max": true, + "min": true, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(k8s_node_gpu_available)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Available", + "refId": "A" + }, + { + "expr": "sum(k8s_node_gpu_total)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "B" + }, + { + "expr": "sum(k8s_node_gpu_reserved)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Reserved", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "GPU Usage", + "version": 0 +}} diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/job-status-dashboard.json b/src/ClusterBootstrap/services/monitor/grafana-config/job-status-dashboard.json new file mode 100644 index 000000000..2b1f01f76 --- /dev/null +++ b/src/ClusterBootstrap/services/monitor/grafana-config/job-status-dashboard.json @@ -0,0 +1,592 @@ +{"dashboard": { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Dashboard to view job metrics", + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [], + "refresh": "30s", + "rows": [ + { + "collapse": false, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "PM", + "fill": 0, + "height": "400px", + "id": 1, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "connected", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "task_cpu_percent{job_name=\"$job_name\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{'{{'}}instance{{'}}'}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "CPU", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "PM", + "fill": 0, + "height": "400px", + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "task_mem_usage_byte{job_name=\"$job_name\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{'{{'}}instance{{'}}'}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + }, + { + "collapse": false, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "PM", + "fill": 0, + "height": "400px", + "id": 3, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "task_net_in_byte{job_name=\"$job_name\"}", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{'{{'}}instance{{'}}'}} Inbound", + "refId": "A" + }, + { + "expr": "task_net_out_byte{job_name=\"$job_name\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{'{{'}}instance{{'}}'}} Outbound", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Network", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "PM", + "fill": 0, + "height": "400px", + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "irate(task_block_in_byte{job_name=\"$job_name\"}[300s])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{'{{'}}instance{{'}}'}} Read", + "refId": "A" + }, + { + "expr": "irate(task_block_out_byte{job_name=\"$job_name\"}[300s])", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{'{{'}}instance{{'}}'}} Write", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Block IO", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + }, + { + "collapse": false, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 1, + "id": 5, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "task_gpu_percent{job_name=\"$job_name\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "GPU {{'{{'}}minor_number{{'}}'}} on {{'{{'}}instance{{'}}'}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percent", + "label": null, + "logBase": 1, + "max": "100", + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 1, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "task_gpu_mem_percent{job_name=\"$job_name\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "GPU {{'{{'}}minor_number{{'}}'}} on {{'{{'}}instance{{'}}'}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "GPU Memory Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percent", + "label": null, + "logBase": 1, + "max": "100", + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "PM", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "job_name", + "options": [], + "query": "label_values(task_cpu_percent, job_name)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "now": true, + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Job Status", + "version": 1 +}} diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/service-status-dashboard.json b/src/ClusterBootstrap/services/monitor/grafana-config/service-status-dashboard.json index db7717df8..7e6f537c7 100644 --- a/src/ClusterBootstrap/services/monitor/grafana-config/service-status-dashboard.json +++ b/src/ClusterBootstrap/services/monitor/grafana-config/service-status-dashboard.json @@ -12,7 +12,7 @@ } ] }, - "description": "Dashboard to view pai service metrics", + "description": "Dashboard to view service metrics", "editable": true, "gnetId": null, "graphTooltip": 0, @@ -70,7 +70,7 @@ "timeShift": null, "title": "CPU", "tooltip": { - "shared": false, + "shared": true, "sort": 0, "value_type": "individual" }, From d4e904501e8970d26772e0e8460d420c1bddcb75 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 29 May 2019 07:50:36 +0000 Subject: [PATCH 181/595] make min to be 0 --- .../services/monitor/grafana-config/job-status-dashboard.json | 2 +- .../monitor/grafana-config/service-status-dashboard.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/job-status-dashboard.json b/src/ClusterBootstrap/services/monitor/grafana-config/job-status-dashboard.json index 2b1f01f76..ae5b3d1b3 100644 --- a/src/ClusterBootstrap/services/monitor/grafana-config/job-status-dashboard.json +++ b/src/ClusterBootstrap/services/monitor/grafana-config/job-status-dashboard.json @@ -88,7 +88,7 @@ "label": null, "logBase": 1, "max": null, - "min": null, + "min": 0, "show": true }, { diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/service-status-dashboard.json b/src/ClusterBootstrap/services/monitor/grafana-config/service-status-dashboard.json index 7e6f537c7..75807fbdb 100644 --- a/src/ClusterBootstrap/services/monitor/grafana-config/service-status-dashboard.json +++ b/src/ClusterBootstrap/services/monitor/grafana-config/service-status-dashboard.json @@ -88,7 +88,7 @@ "label": null, "logBase": 1, "max": null, - "min": null, + "min": 0, "show": true }, { From 9f04f8fe9ca95de62f26621295b3ebadae469baa Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 29 May 2019 15:51:31 +0800 Subject: [PATCH 182/595] change ssh/ipython port range to 30001-33767 --- src/ClusterManager/endpoint_manager.py | 4 ++-- src/ClusterManager/job_manager.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index 44e4eaabe..f70228425 100644 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -38,7 +38,7 @@ def start_ssh_server(pod_name, user_name, host_network=False, ssh_port=22): if host_network: # if the ssh_port is default value 22, randomly choose one if ssh_port == 22: - ssh_port = random.randint(40001, 49999) + ssh_port = random.randint(30001, 32767) # bash_script = "sed -i '/^Port 22/c Port "+str(ssh_port)+"' /etc/ssh/sshd_config && "+bash_script # TODO refine the script later bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh/*; chmod 700 .ssh; true) && service ssh restart'" @@ -107,7 +107,7 @@ def setup_ssh_server(user_name, pod_name, host_network=False): def setup_jupyter_server(user_name, pod_name): - jupyter_port = random.randint(10001, 19999) + jupyter_port = random.randint(30001, 32767) bash_script = "sudo bash -c 'apt-get update && apt-get install python-pip -y && python -m pip install --upgrade pip && python -m pip install jupyter && cd /home/" + user_name + " && runuser -l " + user_name + " -c \"jupyter notebook --no-browser --ip=0.0.0.0 --NotebookApp.token= --port=" + str(jupyter_port) + " &>/dev/null &\"'" output = k8sUtils.kubectl_exec("exec %s %s" % (pod_name, " -- " + bash_script)) if output == "": diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 10764e7f1..fe3a8a61b 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -402,7 +402,7 @@ def SubmitPSDistJob(job): random.seed(datetime.datetime.now()) if "hostNetwork" in jobParams and jobParams["hostNetwork"]: - distJobParam["containerPort"] = random.randint(40001, 49999) + distJobParam["containerPort"] = random.randint(3000, 32767) else: distJobParam["containerPort"] = int(random.random()*1000+3000) @@ -656,7 +656,7 @@ def start_ssh_server(pod_name, user_name, host_network=False, ssh_port=22): if host_network: # if the ssh_port is default value 22, randomly choose one if ssh_port == 22: - ssh_port = random.randint(40001, 49999) + ssh_port = random.randint(30001, 32767) # bash_script = "sed -i '/^Port 22/c Port "+str(ssh_port)+"' /etc/ssh/sshd_config && "+bash_script # TODO refine the script later bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh/*; chmod 700 .ssh; true) && service ssh restart'" From d20a10658a7678a2dd7e9264bcbb74f7b97917f0 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Wed, 29 May 2019 17:19:56 +0800 Subject: [PATCH 183/595] Rename to Deep Learning Training Service --- src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml | 4 ++-- src/WebUI/dotnet/WebPortal/Views/Shared/_Layout.cshtml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml index 71d600502..ca8a03920 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml @@ -33,7 +33,7 @@
-

Welcome to Deep Learning Training System, @ViewData["Username"] !

+

Welcome to Deep Learning Training Service, @ViewData["Username"] !

} else @@ -45,7 +45,7 @@ else ASP.NET diff --git a/src/WebUI/dotnet/WebPortal/Views/Shared/_Layout.cshtml b/src/WebUI/dotnet/WebPortal/Views/Shared/_Layout.cshtml index 99e4d3f1b..2fe653225 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Shared/_Layout.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Shared/_Layout.cshtml @@ -5,7 +5,7 @@ - @ViewData["Title"] - WebPortal - Deep Learning Training System + @ViewData["Title"] - WebPortal - Deep Learning Training Service @@ -79,7 +79,7 @@ @RenderBody()
-

© 2017 - Deep Learning Training System - WebPortal

+

© 2017 - Deep Learning Training Service - WebPortal

From af0eb06dd7684d27e19e35ea9af793ca655cda5a Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Wed, 29 May 2019 17:33:39 +0800 Subject: [PATCH 184/595] Layout fixes --- src/WebUI/dotnet/WebPortal/Views/Home/DataJob.cshtml | 2 +- src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml | 5 +---- src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 2 +- src/WebUI/dotnet/WebPortal/Views/Home/ManageUser.cshtml | 3 --- src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml | 4 ---- src/WebUI/dotnet/WebPortal/Views/Home/ViewJobs.cshtml | 3 --- src/WebUI/dotnet/WebPortal/Views/Shared/_Layout.cshtml | 2 +- 7 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/DataJob.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/DataJob.cshtml index 9ceba47d7..189e4af49 100644 --- a/src/WebUI/dotnet/WebPortal/Views/Home/DataJob.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/DataJob.cshtml @@ -113,7 +113,7 @@ - - - - - - - - - - - - - - - - - - -
- - - +@using Microsoft.AspNetCore.Http; + +@{ + ViewData["Title"] = "Submit Your Job"; +} + + + + + + + + + + +
+ + + From 8731f44016b520b65cdbea7804a902b9935b9ddc Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 31 May 2019 14:48:18 +0800 Subject: [PATCH 201/595] allow to create endpoint while pod is not running --- src/ClusterManager/endpoint_manager.py | 13 ++++++++++++- src/RestAPI/dlwsrestapi.py | 18 +++++++++--------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index 7456c13db..8958b7053 100644 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -10,6 +10,7 @@ import base64 import traceback import random +import re sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../utils")) @@ -140,7 +141,17 @@ def start_endpoints(): try: data_handler = DataHandler() pending_endpoints = data_handler.GetPendingEndpoints() - for _, endpoint in pending_endpoints.items(): + + for endpoint_id, endpoint in pending_endpoints.items(): + job = data_handler.GetJob(jobId=endpoint["jobId"])[0] + if job["jobStatus"] != "running": + continue + + # get endpointDescriptionPath + # job["jobDescriptionPath"] = "jobfiles/" + time.strftime("%y%m%d") + "/" + jobParams["jobId"] + "/" + jobParams["jobId"] + ".yaml" + endpoint_description_dir = re.search("(.*/)[^/\.]+.yaml", job["jobDescriptionPath"]).group(1) + endpoint["endpointDescriptionPath"] = os.path.join(endpoint_description_dir, endpoint_id + ".yaml") + print("\n\n\n\n\n\n----------------Begin to start endpoint %s" % endpoint["id"]) output = get_k8s_endpoint(endpoint["endpointDescriptionPath"]) if(output != ""): diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index bf7ddda6d..4acc48bfb 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -7,7 +7,6 @@ from flask import request, jsonify import base64 import yaml -import re import uuid import logging @@ -982,14 +981,17 @@ def post(self): # get the job job = JobRestAPIUtils.get_job(job_id) - - # get endpointDescriptionPath - # job["jobDescriptionPath"] = "jobfiles/" + time.strftime("%y%m%d") + "/" + jobParams["jobId"] + "/" + jobParams["jobId"] + ".yaml" - endpoint_description_dir = re.search("(.*/)[^/\.]+.yaml", job["jobDescriptionPath"]).group(1) + job_params = json.loads(base64.b64decode(job["jobParams"])) # get pods - job_description = base64.b64decode(job["jobDescription"]) - pod_names = [resource["metadata"]["name"] for resource in yaml.load_all(job_description) if resource['kind'] == 'Pod'] + pod_names = [] + if(job_params["jobtrainingtype"] == "RegularJob"): + pod_names.append(job_id) + else: + nums = {"ps": int(job_params["numps"]), "worker": int(job_params["numpsworker"])} + for role in ["ps", "worker"]: + for i in range(nums[role]): + pod_names.append(job_id + "-" + role + str(i)) # endpoints should be in ["ssh", "ipython"] if any(elem not in ["ssh", "ipython"] for elem in requested_endpoints): @@ -1017,7 +1019,6 @@ def post(self): "podName": pod_name, "username": username, "name": "ssh", - "endpointDescriptionPath": os.path.join(endpoint_description_dir, endpoint_id + ".yaml"), "status": "pending", "hostNetwork": host_network } @@ -1033,7 +1034,6 @@ def post(self): "podName": pod_name, "username": username, "name": "ipython", - "endpointDescriptionPath": os.path.join(endpoint_description_dir, endpoint_id + ".yaml"), "status": "pending", "hostNetwork": host_network } From 1a683fe92ee6dbe9a695a34a084537f387be56ff Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 31 May 2019 15:21:03 +0800 Subject: [PATCH 202/595] start endpoint only after user is properly setup --- src/ClusterManager/endpoint_manager.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index 8958b7053..fdd25a63d 100644 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -137,6 +137,14 @@ def start_endpoint(endpoint): create_node_port(endpoint) +def is_user_ready(pod_name): + bash_script = "bash -c 'ls /dlws/USER_READY'" + output = k8sUtils.kubectl_exec("exec %s %s" % (pod_name, " -- " + bash_script)) + if output == "": + return False + return True + + def start_endpoints(): try: data_handler = DataHandler() @@ -146,6 +154,8 @@ def start_endpoints(): job = data_handler.GetJob(jobId=endpoint["jobId"])[0] if job["jobStatus"] != "running": continue + if not is_user_ready(endpoint["podName"]): + continue # get endpointDescriptionPath # job["jobDescriptionPath"] = "jobfiles/" + time.strftime("%y%m%d") + "/" + jobParams["jobId"] + "/" + jobParams["jobId"] + ".yaml" From 0d7eeba2ced04e408f5f688fca95d2a8b446ce97 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Fri, 31 May 2019 16:36:30 +0800 Subject: [PATCH 203/595] Use get endpoints API to show the endpoint info --- .../WebPortal/Controllers/dlwsController.cs | 12 ++++ .../WebPortal/Views/Home/JobDetail.cshtml | 58 +++++++++---------- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs index a86d27c5b..70290e086 100755 --- a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs @@ -198,6 +198,12 @@ public async Task Get(string op) return ret; } + if (!HttpContext.Session.Keys.Contains("AuthorizedClusters")) + { + ret = "Unauthorized User, Please login!"; + return ret; + } + ViewData["Username"] = HttpContext.Session.GetString("Username"); var cluster = HttpContext.Request.Query["cluster"]; @@ -324,6 +330,12 @@ public async Task Get(string op) url = restapi + "/GetCommands?jobId=" + HttpContext.Request.Query["jobId"] + "&userName=" + HttpContext.Session.GetString("Email"); } break; + case "GetEndpoints": + if (HttpContext.Request.Query.ContainsKey("jobId")) + { + url = restapi + "/endpoints?jobId=" + HttpContext.Request.Query["jobId"] + "&userName=" + HttpContext.Session.GetString("Email"); + } + break; } if (url != "") diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobDetail.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobDetail.cshtml index 0ed7dbc09..f7625a85e 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobDetail.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobDetail.cshtml @@ -171,6 +171,31 @@ }); } + function getEndpoints() { + $.ajax({ + url: "/api/dlws/GetEndpoints?cluster=@ViewData["cluster"]&jobId=@ViewData["jobid"]", dataType: 'json' + }).then(function (endpoints) { + $('#endpoints').show(); + + var endpointsStr = $.map(endpoints, function (endpoint) { + if (endpoint.status == "pending") return; + if (endpoint.name == "ssh") { + window.ngScope.$applyAsync('ssh = true'); + return "To access the container via SSH, use command: ssh -i " + "@ViewData["workPath"]" + ".ssh/id_rsa -p " + endpoint.port + " " + endpoint.username + "@@" + endpoint.nodeName + "." + endpoint.domain + "\n"; + } + else { + window.ngScope.$applyAsync('ipython = true'); + var svcurl = "http://" + endpoint.nodeName + "." + endpoint.domain + ":" + endpoint.port + "/"; + return endpoint.name + " could be access via: " + svcurl + "\n"; + } + }).join("
"); + var endpointsPanellDiv = document.getElementById('endpointsPanel'); + if (endpointsPanellDiv.innerHTML != endpointsStr) { + endpointsPanellDiv.innerHTML = endpointsStr; + } + }) + } + function buildTemplateList() { $.ajax({ url: "/api/dlws/GetTemplates?cluster=" + cluster + "&type=command", dataType: 'json', timeout: 10000, error: AlertAPIError }).done( function (json) { @@ -211,37 +236,10 @@ document.getElementById('run-command').style.display = payload.jobStatus == "running" ? "block" : "none"; if (payload.jobStatus == "running") { getCommands(); + getEndpoints(); } } - if (payload.endpoints) - { - $('#endpoints').show(); - - var endpointsStr = ""; - $.each(payload.endpoints, function (id, endpoint) { - var spec = endpoint.endpointDescription.spec; - if (endpoint.name == "ssh") { - endpointsStr += "To access the container via SSH, use command: ssh -i "+"@ViewData["workPath"]"+".ssh/id_rsa -p " + spec.ports[0].nodePort + " " + endpoint.username + "@@" + clusterEndpoint + "\n"; - } - else { - var svcurl = "http://" + clusterEndpoint + ":" + spec.ports[0].nodePort; - endpointsStr += "Container Port: " + spec.ports[0].nodePort + " is mapped to public ip: " + svcurl + " \n"; - } - }) - for (var i = 0; i < payload.endpoints.length; i++) { - } - var endpointsPanellDiv = document.getElementById('endpointsPanel'); - if (endpointsPanellDiv.innerHTML != endpointsStr) { - endpointsPanellDiv.innerHTML = endpointsStr; - var height = payload.endpoints.length * 15; - endpointsPanellDiv.style.height = height + "px"; - } - } - else { - $('#endpoints').hide(); - } - var client = new elasticsearch.Client({ @@ -355,6 +353,8 @@ }) } }) + + window.ngScope = $scope; }); }(angular.module("endpointSwitches", ["ngMaterial"])); @@ -368,7 +368,7 @@ } else @@ -201,9 +264,71 @@ else } buildClusterTable(); buildStorageTable(); - + } + + + + + +{{ template "__subject" . }} + + + + + + + + + + + +
+
+ + + + +
+ + {{ range .Alerts.Firing }} + + + + {{ end }} +
+Your job + +{{.Labels.job_name}} + from cluster '{{.Labels.cluster}}' has changed to state of {{.Labels.job_state}}. +
+
+ +
+
+ + + +{{ end }} diff --git a/src/utils/notify.py b/src/utils/notify.py new file mode 100644 index 000000000..67278ae98 --- /dev/null +++ b/src/utils/notify.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python + +import threading +import time +import logging +import urlparse +import json +import smtplib + +from Queue import Queue +from Queue import Empty + +import requests + +logger = logging.getLogger(__name__) + +class NotifyMsg(object): + def __init__(self, email, alert_name): + self.email = email + self.alert_name = alert_name + + def labels(self): + raise NotImplementedError() + + def subject(self): + raise NotImplementedError() + + def body(self): + return self.subject() + + +class JobStateChangedMsg(NotifyMsg): + def __init__(self, email, alert_name, job_name, job_state): + super(JobStateChangedMsg, self).__init__(email, alert_name) + self.job_name = job_name + self.job_state = job_state + + def labels(self): + return {"job_name": self.job_name, "job_state": self.job_state} + + def subject(self): + return "Your job %s has changed to state of %s" % (self.job_name, self.job_state) + + +def new_job_state_change_message(email, job_name, state): + return JobStateChangedMsg(email, "job-state-changed", job_name, state) + + +class Notifier(object): + def __init__(self, config): + self.queue = Queue() + self.running = False + self.thread = None + + self.cluster = None + self.alert_manager_url = None + self.smtp_url = self.smtp_from = self.smtp_auth_name = self.smtp_auth_pass = None + + if "notifier" in config: + notifier_config = config["notifier"] + + self.cluster = notifier_config.get("cluster") + self.smtp_url = notifier_config.get("smtp-url") + self.smtp_from = notifier_config.get("smtp-from") + self.smtp_auth_name = notifier_config.get("smtp-auth-username") + self.smtp_auth_pass = notifier_config.get("smtp-auth-password") + + alert_manager_url = notifier_config.get("alert-manager-url") + if alert_manager_url is not None and len(alert_manager_url) > 0: + if alert_manager_url[-1] == "/": + self.alert_manager_url = alert_manager_url + "api/v1/alerts" + else: + self.alert_manager_url = alert_manager_url + "/api/v1/alerts" + + if self.cluster is None or \ + self.alert_manager_url is None and ( + self.smtp_url is None or \ + self.smtp_from is None or \ + self.smtp_auth_name is None or \ + self.smtp_auth_pass is None): + logger.warning("Notifier not configured") + + def start(self): + if not self.running: + self.running = True + self.thread = threading.Thread(target=self.process, name="notifier") + self.thread.start() + + def stop(self): + if self.running: + self.running = False + self.thread.join() + self.thread = None + + def notify(self, msg): + self.queue.put(msg) + + def process(self): + while self.running: + try: + msg = self.queue.get(block=True, timeout=1) # 1s timeout + except Empty: + continue + + retry_count = 0 + sent = False + + while retry_count < 3: + if self.send(msg): + sent = True + break + time.sleep(0.2) + retry_count += 1 + + if not sent: + logger.error("failed to send out, discard msg: %s", msg) + + def send(self, msg): + subject = msg.subject() + + try: + if self.alert_manager_url is not None: + labels = msg.labels() + labels.update({ + "alertname": msg.alert_name, + "type": "user_alert", + "cluster": self.cluster, + "user_email": msg.email, + "subject": subject, + }) + + resp = requests.post(self.alert_manager_url, timeout=5, + data=json.dumps([{"labels": labels}])) + resp.raise_for_status() + return True + elif self.smtp_url is not None and \ + self.smtp_from is not None and \ + self.smtp_auth_name is not None and \ + self.smtp_auth_pass is not None: + smtp_send_email(self.smtp_url, self.smtp_from, + self.smtp_auth_name, self.smtp_auth_pass, + msg.email, subject, msg.body()) + return True + else: + # not configured, discard message + return True + except Exception as e: + logger.exception("sending email failed") + return False + + +def smtp_send_email(smtp_url, smtp_from, smtp_auth_name, smtp_auth_pass, to, subject, body): + msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s" % (smtp_from, to, subject, body) + conn = smtplib.SMTP(smtp_url) + conn.starttls() + conn.login(smtp_auth_name, smtp_auth_pass) + conn.sendmail(smtp_from, to, msg) + + +if __name__ == "__main__": + logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) + notifier = Notifier({"notifier": {"cluster": "local", "alert-manager-url": "http://localhost:9093/alert-manager"}}) + notifier.start() + + notifier.notify(new_job_state_change_message("dixu@microsoft.com", "job-id", "stopped")) + notifier.stop() From 399cc3a8644311f24bb66325436779e764044e71 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 9 Jul 2019 06:52:06 +0000 Subject: [PATCH 389/595] call notifier in jobmanager --- src/ClusterManager/job_manager.py | 11 +++++++++-- src/utils/notify.py | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index b4b2dc955..6469cecbd 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -18,6 +18,7 @@ import k8sUtils import joblog_manager from osUtils import mkdirsAsUser +import notify import yaml from jinja2 import Environment, FileSystemLoader, Template @@ -179,7 +180,7 @@ def AutoApproveJob(job): UnusualJobs = {} -def UpdateJobStatus(job): +def UpdateJobStatus(job, notifier): dataHandler = DataHandler() jobParams = json.loads(base64.b64decode(job["jobParams"])) @@ -225,6 +226,10 @@ def UpdateJobStatus(job): elif result.strip() == "Failed": logging.warning("Job %s fails, cleaning...", job["jobId"]) + + notifier.notify(notify.new_job_state_change_message( + job["userName"], job["jobId"], result.strip())) + joblog_manager.extract_job_log(job["jobId"],logPath,jobParams["userId"]) dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","failed") dataHandler.UpdateJobTextField(job["jobId"],"errorMsg",detail) @@ -480,6 +485,8 @@ def TakeJobActions(jobs): def Run(): + notifier = Notifier(config.get("job-manager")) + notifier.start() while True: @@ -505,7 +512,7 @@ def Run(): elif job["jobStatus"] == "pausing": KillJob(job, "paused") elif job["jobStatus"] == "scheduling" or job["jobStatus"] == "running": - UpdateJobStatus(job) + UpdateJobStatus(job, notifier) elif job["jobStatus"] == "unapproved": AutoApproveJob(job) except Exception as e: diff --git a/src/utils/notify.py b/src/utils/notify.py index 67278ae98..584f99c7f 100644 --- a/src/utils/notify.py +++ b/src/utils/notify.py @@ -56,7 +56,7 @@ def __init__(self, config): self.alert_manager_url = None self.smtp_url = self.smtp_from = self.smtp_auth_name = self.smtp_auth_pass = None - if "notifier" in config: + if config is not None and "notifier" in config: notifier_config = config["notifier"] self.cluster = notifier_config.get("cluster") From ef61d4047d34edcbd7104df948236b1258d7a1c6 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 17 Jul 2019 06:50:57 +0000 Subject: [PATCH 390/595] fix according to review --- src/ClusterManager/job_manager.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 6469cecbd..e89267355 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -180,7 +180,7 @@ def AutoApproveJob(job): UnusualJobs = {} -def UpdateJobStatus(job, notifier): +def UpdateJobStatus(job, notifier=None): dataHandler = DataHandler() jobParams = json.loads(base64.b64decode(job["jobParams"])) @@ -227,8 +227,9 @@ def UpdateJobStatus(job, notifier): elif result.strip() == "Failed": logging.warning("Job %s fails, cleaning...", job["jobId"]) - notifier.notify(notify.new_job_state_change_message( - job["userName"], job["jobId"], result.strip())) + if notifier is not None: + notifier.notify(notify.new_job_state_change_message( + job["userName"], job["jobId"], result.strip())) joblog_manager.extract_job_log(job["jobId"],logPath,jobParams["userId"]) dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","failed") From cb11da1cb157fc7ea2e6dc320c753a034a3e2893 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 9 Jul 2019 09:42:12 +0000 Subject: [PATCH 391/595] refine kill job --- src/ClusterManager/job_deployer.py | 80 ++++++++++++++++++++++--- src/ClusterManager/job_manager.py | 32 +++++----- src/ClusterManager/test_job_deployer.py | 41 +++++++++++++ 3 files changed, 128 insertions(+), 25 deletions(-) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index b9f502754..62272ee67 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -2,6 +2,7 @@ import os import logging from kubernetes import client, config +from kubernetes.client.rest import ApiException class JobDeployer: @@ -29,27 +30,88 @@ def delete_pod(self, name, dry_run=None): pretty=self.pretty, body=client.V1DeleteOptions(), dry_run=dry_run, - # grace_period_seconds=grace_period_seconds, - # orphan_dependents=orphan_dependents, - # propagation_policy=propagation_policy, ) - pass + return api_response + + def create_service(self, body, dry_run=None): + api_response = self.v1.create_namespaced_service( + namespace=self.namespace, + body=body, + pretty=self.pretty, + dry_run=dry_run, + ) + return api_response + + def delete_service(self, name, dry_run=None): + api_response = self.v1.delete_namespaced_service( + name=name, + namespace=self.namespace, + pretty=self.pretty, + body=client.V1DeleteOptions(), + dry_run=dry_run, + ) + return api_response def cleanup_pods(self, pods): + errors = [] for pod in pods: try: pod_name = pod["metadata"]["name"] self.delete_pod(pod_name) except Exception as e: - logging.warning("Delete pod failed: %s!" % pod_name, exc_info=True) + message = "Delete pod failed: {}".format(pod_name) + logging.warning(message, exc_info=True) + errors.append({"message": message, "exception": e}) + return errors + + def cleanup_services(self, services): + errors = [] + for service in services: + try: + service_name = service["metadata"]["name"] + self.delete_service(service_name) + except ApiException as e: + message = "Delete service failed: {}".format(service_name) + logging.warning(message, exc_info=True) + errors.append({"message": message, "exception": e}) + return errors def create_pods(self, pods): # TODO instead of delete, we could check update existiong ones. During refactoring, keeping the old way. self.cleanup_pods(pods) created = [] for pod in pods: - self.create_pod(pod) - pod_name = pod["metadata"]["name"] - created.append(pod_name) - logging.info("Create pod succeed: %s" % pod_name) + created_pod = self.create_pod(pod) + created.append(created_pod) + logging.info("Create pod succeed: %s" % created_pod.metadata.name) return created + + def get_pods_by_label(self, label_selector): + api_response = self.v1.list_namespaced_pod( + namespace=self.namespace, + pretty=self.pretty, + label_selector=label_selector, + ) + return api_response.items + + def get_services_by_label(self, label_selector): + api_response = self.v1.list_namespaced_service( + namespace=self.namespace, + pretty=self.pretty, + label_selector=label_selector, + ) + return api_response.items + + def delete_job(self, job_id): + label_selector = "run={}".format(job_id) + + # query pods then delete + pods = self.get_pod_by_label(label_selector) + pod_errors = self.cleanup_pods(pods) + + # query services then delete + services = self.get_service_by_label(label_selector) + service_errors = self.cleanup_services(services) + + errors = pod_errors + service_errors + return errors diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index b4b2dc955..c7d00273c 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -86,7 +86,8 @@ def SubmitJob(job): job_deployer = JobDeployer() try: - ret["output"] = job_deployer.create_pods(pods) + pods = job_deployer.create_pods(pods) + ret["output"] = "Created pods: {}".format([pod.metedata.name for pod in pods]) except Exception as e: ret["output"] = "Error: %s" % e.message logging.error(e, exc_info=True) @@ -121,22 +122,21 @@ def SubmitJob(job): def KillJob(job, desiredState="killed"): dataHandler = DataHandler() result, detail = k8sUtils.GetJobStatus(job["jobId"]) - dataHandler.UpdateJobTextField(job["jobId"],"jobStatusDetail",base64.b64encode(json.dumps(detail))) - logging.info("Killing job %s, with status %s, %s" %(job["jobId"], result,detail)) - if "jobDescriptionPath" in job and job["jobDescriptionPath"] is not None: - jobDescriptionPath = os.path.join(config["storage-mount-path"], job["jobDescriptionPath"]) - if os.path.isfile(jobDescriptionPath): - if k8sUtils.kubectl_delete(jobDescriptionPath) == 0: - dataHandler.UpdateJobTextField(job["jobId"],"jobStatus", desiredState) - return True - else: - dataHandler.UpdateJobTextField(job["jobId"],"errorMsg","Cannot delete job from Kubernetes Cluster!") - else: - dataHandler.UpdateJobTextField(job["jobId"],"errorMsg","Cannot find job description file!") + dataHandler.UpdateJobTextField(job["jobId"], "jobStatusDetail", base64.b64encode(json.dumps(detail))) + logging.info("Killing job %s, with status %s, %s" % (job["jobId"], result, detail)) - dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","error") - dataHandler.Close() - return False + job_deployer = JobDeployer() + errors = job_deployer.delete_job(job["jobId"]) + + if len(errors) == 0: + dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", desiredState) + dataHandler.Close() + return True + else: + dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "error") + dataHandler.Close() + logger.error("Kill job failed with errors: {}".format(errors)) + return False # TODO remove it latter diff --git a/src/ClusterManager/test_job_deployer.py b/src/ClusterManager/test_job_deployer.py index d466e596b..295af0461 100644 --- a/src/ClusterManager/test_job_deployer.py +++ b/src/ClusterManager/test_job_deployer.py @@ -47,3 +47,44 @@ def test_cleanup_pods(self): ] job_deployer.cleanup_pods(pods) + + def test_get_pod_by_label(self): + job_deployer = self.create_job_deployer() + label_selector = "run=some_job_id" + + pods = job_deployer.get_pods_by_label(label_selector) + + self.assertEqual(0, len(pods)) + + def test_get_services_by_label(self): + job_deployer = self.create_job_deployer() + label_selector = "run=some_job_id" + + services = job_deployer.get_services_by_label(label_selector) + + self.assertEqual(0, len(services)) + + def test_create_endpoint(self): + job_deployer = self.create_job_deployer() + raw_yaml = """ +apiVersion: v1 +kind: Service +metadata: + name: test-service +spec: + selector: + app: MyApp + ports: + - protocol: TCP + port: 80 + targetPort: 9376 + """ + body = yaml.full_load(raw_yaml) + + # with self.assertRaises(ApiException): + job_deployer.create_service(body) + + def test_delete_service(self): + job_deployer = self.create_job_deployer() + + job_deployer.delete_service("test-service") From 80318d9d787542edeb8c747cac32957462e40d08 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 10 Jul 2019 08:00:49 +0000 Subject: [PATCH 392/595] update job.lastUpdated --- src/ClusterManager/job_manager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index c7d00273c..f77eff3ee 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -130,10 +130,12 @@ def KillJob(job, desiredState="killed"): if len(errors) == 0: dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", desiredState) + dataHandler.UpdateJobTextField(job["jobId"], "lastUpdated", datetime.datetime.now().isoformat()) dataHandler.Close() return True else: dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "error") + dataHandler.UpdateJobTextField(job["jobId"], "lastUpdated", datetime.datetime.now().isoformat()) dataHandler.Close() logger.error("Kill job failed with errors: {}".format(errors)) return False From 4f7f3e480c528d4bdfd14c3e131a78026fd2ab50 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 10 Jul 2019 09:32:28 +0000 Subject: [PATCH 393/595] add func pod_exec --- src/ClusterManager/job_deployer.py | 29 +++++++++++++++++++++++++ src/ClusterManager/test_job_deployer.py | 28 ++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index 62272ee67..2d62abe88 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -3,6 +3,8 @@ import logging from kubernetes import client, config from kubernetes.client.rest import ApiException +from kubernetes.stream import stream +from kubernetes.stream.ws_client import ERROR_CHANNEL, STDERR_CHANNEL, STDOUT_CHANNEL class JobDeployer: @@ -115,3 +117,30 @@ def delete_job(self, job_id): errors = pod_errors + service_errors return errors + + def pod_exec(self, pod_name, exec_command): + logging.info("Exec on pod {}: {}".format(pod_name, exec_command)) + client = stream( + self.v1.connect_get_namespaced_pod_exec, + name=pod_name, + namespace=self.namespace, + command=exec_command, + stderr=True, + stdin=False, + stdout=True, + tty=False, + _preload_content=False, + ) + + client.run_forever(timeout=60) + err = yaml.full_load(client.read_channel(ERROR_CHANNEL)) + if err["status"] == "Success": + status_code = 0 + else: + logging.warning("Exec on pod {} failed: {}".format(pod_name, err)) + status_code = int(err["details"]["causes"][0]["message"]) + + output = client.read_channel(STDOUT_CHANNEL) + client.read_channel(STDERR_CHANNEL) + + logging.info("Exec on pod {}, status: {}, output: {}".format(pod_name, status_code, output)) + return [status_code, output] diff --git a/src/ClusterManager/test_job_deployer.py b/src/ClusterManager/test_job_deployer.py index 295af0461..c599be348 100644 --- a/src/ClusterManager/test_job_deployer.py +++ b/src/ClusterManager/test_job_deployer.py @@ -5,6 +5,15 @@ from job_deployer import JobDeployer +import logging +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s", + handlers=[ + logging.StreamHandler() + ] +) + class TestJobDeployer(unittest.TestCase): @@ -88,3 +97,22 @@ def test_delete_service(self): job_deployer = self.create_job_deployer() job_deployer.delete_service("test-service") + + def test_pod_exec(self): + job_deployer = self.create_job_deployer() + exec_command = [ + '/bin/sh', + '-c', + 'echo This message goes to stderr >&2 && echo This message goes to stdout' + ] + + status_code, ouput = job_deployer.pod_exec("test-pod", exec_command) + self.assertEqual(0, status_code) + + bad_command = [ + '/bin/sh', + '-c', + 'echo This message goes to stderr >&2 && xecho This message goes to stdout; exit 8' + ] + status_code, ouput = job_deployer.pod_exec("test-pod", bad_command) + self.assertEqual(8, status_code) From ffe8f7301e2930e98d95c3c98c33dbf5c4922ddd Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 11 Jul 2019 12:12:38 +0000 Subject: [PATCH 394/595] add timeout --- src/ClusterManager/job_deployer.py | 10 ++++++---- src/ClusterManager/test_job_deployer.py | 10 +++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index 2d62abe88..772cdab1d 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -118,7 +118,8 @@ def delete_job(self, job_id): errors = pod_errors + service_errors return errors - def pod_exec(self, pod_name, exec_command): + def pod_exec(self, pod_name, exec_command, timeout=60): + """work as the command (with timeout): kubectl exec 'pod_name' 'exec_command'""" logging.info("Exec on pod {}: {}".format(pod_name, exec_command)) client = stream( self.v1.connect_get_namespaced_pod_exec, @@ -131,16 +132,17 @@ def pod_exec(self, pod_name, exec_command): tty=False, _preload_content=False, ) + client.run_forever(timeout=timeout) - client.run_forever(timeout=60) err = yaml.full_load(client.read_channel(ERROR_CHANNEL)) + if err is None: + return [-1, "Timeout"] + if err["status"] == "Success": status_code = 0 else: logging.warning("Exec on pod {} failed: {}".format(pod_name, err)) status_code = int(err["details"]["causes"][0]["message"]) - output = client.read_channel(STDOUT_CHANNEL) + client.read_channel(STDERR_CHANNEL) - logging.info("Exec on pod {}, status: {}, output: {}".format(pod_name, status_code, output)) return [status_code, output] diff --git a/src/ClusterManager/test_job_deployer.py b/src/ClusterManager/test_job_deployer.py index c599be348..091a268fc 100644 --- a/src/ClusterManager/test_job_deployer.py +++ b/src/ClusterManager/test_job_deployer.py @@ -112,7 +112,15 @@ def test_pod_exec(self): bad_command = [ '/bin/sh', '-c', - 'echo This message goes to stderr >&2 && xecho This message goes to stdout; exit 8' + 'echo This message goes to stderr >&2 && xecho This message goes to stdout; sleep 3; exit 8' ] status_code, ouput = job_deployer.pod_exec("test-pod", bad_command) self.assertEqual(8, status_code) + + bad_command = [ + '/bin/sh', + '-c', + 'echo This message goes to stderr >&2 && xecho This message goes to stdout; sleep 3; exit 8' + ] + status_code, ouput = job_deployer.pod_exec("test-pod", bad_command, 1) + self.assertEqual(-1, status_code) From 786337c1fdd27bf35336aac6e60cfc08e3095447 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 12 Jul 2019 08:51:22 +0000 Subject: [PATCH 395/595] fix typo --- src/ClusterManager/job_deployer.py | 8 ++++---- src/ClusterManager/job_manager.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index 772cdab1d..34ec95dde 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -58,7 +58,7 @@ def cleanup_pods(self, pods): errors = [] for pod in pods: try: - pod_name = pod["metadata"]["name"] + pod_name = pod.metadata.name self.delete_pod(pod_name) except Exception as e: message = "Delete pod failed: {}".format(pod_name) @@ -70,7 +70,7 @@ def cleanup_services(self, services): errors = [] for service in services: try: - service_name = service["metadata"]["name"] + service_name = service.metadata.name self.delete_service(service_name) except ApiException as e: message = "Delete service failed: {}".format(service_name) @@ -108,11 +108,11 @@ def delete_job(self, job_id): label_selector = "run={}".format(job_id) # query pods then delete - pods = self.get_pod_by_label(label_selector) + pods = self.get_pods_by_label(label_selector) pod_errors = self.cleanup_pods(pods) # query services then delete - services = self.get_service_by_label(label_selector) + services = self.get_services_by_label(label_selector) service_errors = self.cleanup_services(services) errors = pod_errors + service_errors diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index f77eff3ee..88bfc050c 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -137,7 +137,7 @@ def KillJob(job, desiredState="killed"): dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "error") dataHandler.UpdateJobTextField(job["jobId"], "lastUpdated", datetime.datetime.now().isoformat()) dataHandler.Close() - logger.error("Kill job failed with errors: {}".format(errors)) + logging.error("Kill job failed with errors: {}".format(errors)) return False From 72416647b685c9ba6734e95b6d44fe121c9fed55 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Mon, 15 Jul 2019 08:04:04 +0000 Subject: [PATCH 396/595] refine --- src/ClusterManager/job_deployer.py | 21 ++++++++------ src/ClusterManager/job_manager.py | 4 +-- src/ClusterManager/test_job_deployer.py | 37 +++++++++++++++---------- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index 34ec95dde..d0b38c140 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -54,11 +54,10 @@ def delete_service(self, name, dry_run=None): ) return api_response - def cleanup_pods(self, pods): + def cleanup_pods(self, pod_names): errors = [] - for pod in pods: + for pod_name in pod_names: try: - pod_name = pod.metadata.name self.delete_pod(pod_name) except Exception as e: message = "Delete pod failed: {}".format(pod_name) @@ -69,6 +68,7 @@ def cleanup_pods(self, pods): def cleanup_services(self, services): errors = [] for service in services: + assert(isinstance(service, client.V1Service)) try: service_name = service.metadata.name self.delete_service(service_name) @@ -80,7 +80,8 @@ def cleanup_services(self, services): def create_pods(self, pods): # TODO instead of delete, we could check update existiong ones. During refactoring, keeping the old way. - self.cleanup_pods(pods) + pod_names = [pod["metadata"]["name"] for pod in pods] + self.cleanup_pods(pod_names) created = [] for pod in pods: created_pod = self.create_pod(pod) @@ -88,10 +89,11 @@ def create_pods(self, pods): logging.info("Create pod succeed: %s" % created_pod.metadata.name) return created - def get_pods_by_label(self, label_selector): + def get_pods(self, field_selector="", label_selector=""): api_response = self.v1.list_namespaced_pod( namespace=self.namespace, pretty=self.pretty, + field_selector=field_selector, label_selector=label_selector, ) return api_response.items @@ -108,8 +110,9 @@ def delete_job(self, job_id): label_selector = "run={}".format(job_id) # query pods then delete - pods = self.get_pods_by_label(label_selector) - pod_errors = self.cleanup_pods(pods) + pods = self.get_pods(label_selector=label_selector) + pod_names = [pod.metadata.name for pod in pods] + pod_errors = self.cleanup_pods(pod_names) # query services then delete services = self.get_services_by_label(label_selector) @@ -141,8 +144,8 @@ def pod_exec(self, pod_name, exec_command, timeout=60): if err["status"] == "Success": status_code = 0 else: - logging.warning("Exec on pod {} failed: {}".format(pod_name, err)) + logging.warning("Exec on pod {} failed. cmd: {}, err: {}.".format(pod_name, exec_command, err)) status_code = int(err["details"]["causes"][0]["message"]) output = client.read_channel(STDOUT_CHANNEL) + client.read_channel(STDERR_CHANNEL) - logging.info("Exec on pod {}, status: {}, output: {}".format(pod_name, status_code, output)) + logging.info("Exec on pod {}, status: {}, cmd: {}, output: {}".format(pod_name, status_code, exec_command, output)) return [status_code, output] diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 88bfc050c..3266e1588 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -87,7 +87,7 @@ def SubmitJob(job): job_deployer = JobDeployer() try: pods = job_deployer.create_pods(pods) - ret["output"] = "Created pods: {}".format([pod.metedata.name for pod in pods]) + ret["output"] = "Created pods: {}".format([pod.metadata.name for pod in pods]) except Exception as e: ret["output"] = "Error: %s" % e.message logging.error(e, exc_info=True) @@ -104,7 +104,7 @@ def SubmitJob(job): jobMeta["jobPath"] = job_object.job_path jobMeta["workPath"] = job_object.work_path # the command of the first container - jobMeta["LaunchCMD"] = pods[0]["spec"]["containers"][0]["command"] + jobMeta["LaunchCMD"] = pods[0].spec.containers[0].command jobMetaStr = base64.b64encode(json.dumps(jobMeta)) dataHandler.UpdateJobTextField(job_object.job_id, "jobMeta", jobMetaStr) diff --git a/src/ClusterManager/test_job_deployer.py b/src/ClusterManager/test_job_deployer.py index 091a268fc..b5f0ff9df 100644 --- a/src/ClusterManager/test_job_deployer.py +++ b/src/ClusterManager/test_job_deployer.py @@ -1,6 +1,9 @@ import unittest import kubernetes import yaml +import string +import random +import time from kubernetes.client.rest import ApiException from job_deployer import JobDeployer @@ -22,13 +25,13 @@ def create_job_deployer(self): self.assertIsNotNone(job_deployer) return job_deployer - def test_create_pod(self): + def create_pod(self, pod_name): job_deployer = self.create_job_deployer() raw_yaml = """ apiVersion: v1 kind: Pod metadata: - name: test-pod + name: {} spec: containers: - name: busybox @@ -36,32 +39,31 @@ def test_create_pod(self): args: - sleep - "1000000" - """ + """.format(pod_name) body = yaml.full_load(raw_yaml) # with self.assertRaises(ApiException): job_deployer.create_pod(body) def test_delete_pod(self): + pod_name = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(16)) + self.create_pod(pod_name) + job_deployer = self.create_job_deployer() - job_deployer.delete_pod("test-pod") + job_deployer.delete_pod(pod_name) def test_cleanup_pods(self): job_deployer = self.create_job_deployer() + pod_names = ["pod-1", "pod-2"] - pods = [ - {"metadata": {"name": "pod-1"}}, - {"metadata": {"name": "pod-2"}}, - ] - - job_deployer.cleanup_pods(pods) + job_deployer.cleanup_pods(pod_names) def test_get_pod_by_label(self): job_deployer = self.create_job_deployer() label_selector = "run=some_job_id" - pods = job_deployer.get_pods_by_label(label_selector) + pods = job_deployer.get_pods(label_selector=label_selector) self.assertEqual(0, len(pods)) @@ -100,13 +102,18 @@ def test_delete_service(self): def test_pod_exec(self): job_deployer = self.create_job_deployer() + + pod_name = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(16)) + self.create_pod(pod_name) + time.sleep(3) + exec_command = [ '/bin/sh', '-c', 'echo This message goes to stderr >&2 && echo This message goes to stdout' ] - status_code, ouput = job_deployer.pod_exec("test-pod", exec_command) + status_code, ouput = job_deployer.pod_exec(pod_name, exec_command) self.assertEqual(0, status_code) bad_command = [ @@ -114,7 +121,7 @@ def test_pod_exec(self): '-c', 'echo This message goes to stderr >&2 && xecho This message goes to stdout; sleep 3; exit 8' ] - status_code, ouput = job_deployer.pod_exec("test-pod", bad_command) + status_code, ouput = job_deployer.pod_exec(pod_name, bad_command) self.assertEqual(8, status_code) bad_command = [ @@ -122,5 +129,7 @@ def test_pod_exec(self): '-c', 'echo This message goes to stderr >&2 && xecho This message goes to stdout; sleep 3; exit 8' ] - status_code, ouput = job_deployer.pod_exec("test-pod", bad_command, 1) + status_code, ouput = job_deployer.pod_exec(pod_name, bad_command, 1) self.assertEqual(-1, status_code) + + job_deployer.delete_pod(pod_name) From 1508b8af919d1d89c0d94d099eec3bb63bf9e109 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 16 Jul 2019 02:31:59 +0000 Subject: [PATCH 397/595] add JobRole --- src/ClusterManager/job_role.py | 69 +++++++++++++++++++++++++++++ src/ClusterManager/test_job_role.py | 34 ++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/ClusterManager/job_role.py create mode 100644 src/ClusterManager/test_job_role.py diff --git a/src/ClusterManager/job_role.py b/src/ClusterManager/job_role.py new file mode 100644 index 000000000..e782169ad --- /dev/null +++ b/src/ClusterManager/job_role.py @@ -0,0 +1,69 @@ +from job_deployer import JobDeployer + + +class JobRole: + # MARK_CONTAINER_READY_FILE = "/dlws/CONTAINER_READY" + # MARK_WORKER_READY_FILE = "/dlws/WORKER_READY" + MARK_JOB_READY_FILE = "/dlws/JOB_READY" + + @staticmethod + def get_job_roles(job_id): + deployer = JobDeployer() + pods = deployer.get_pods(label_selector="run={}".format(job_id)) + + job_roles = [] + for pod in pods: + pod_name = pod.metadata.name + if "distRole" in pod.metadata.labels: + role = pod.metadata.labels["distRole"] + else: + role = "master" + job_role = JobRole(role, pod_name) + job_roles.append(job_role) + return job_roles + + def __init__(self, role_name, pod_name): + self.role_name = role_name + self.pod_name = pod_name + + def status(self): + """ + Return role status. + It's slightly different from pod phase, when pod is running: + CONTAINER_READY -> WORKER_READY -> JOB_READY (then the job finally in "Running" status.) + """ + # pod-phase: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase + deployer = JobDeployer() + pods = deployer.get_pods(field_selector="metadata.name={}".format(self.pod_name)) + if(len(pods) < 1): + return "NotFound" + + assert(len(pods) == 1) + pod = pods[0] + phase = pod.status.phase + + # !!! Pod is runing, doesn't mean "Role" is ready and running. + if(phase == "Running"): + if not self.isJobReady(): + return "Pending" + + # TODO handle exit status + + return phase + + def isFileExisting(self, file): + deployer = JobDeployer() + status_code, _ = deployer.pod_exec(self.pod_name, ["/bin/sh", "-c", "ls -lrt {}".format(file)]) + return status_code == 0 + + # def isUserReady(self): + # return self.isFileExisting(JobRole.MARK_USER_READY_FILE) + + # def isWorkerReady(self): + # return self.isFileExisting(JobRole.MARK_WORKER_READY_FILE) + + def isJobReady(self): + # only mark job ready on the "ps" or "master" pod + if(self.role_name not in ["ps", "master"]): + return False + return self.isFileExisting(JobRole.MARK_JOB_READY_FILE) diff --git a/src/ClusterManager/test_job_role.py b/src/ClusterManager/test_job_role.py new file mode 100644 index 000000000..919332053 --- /dev/null +++ b/src/ClusterManager/test_job_role.py @@ -0,0 +1,34 @@ +import unittest +from job_role import JobRole + + +class TestJobRole(unittest.TestCase): + + def test_status_Running(self): + job_role = JobRole("master", "bd3d090a-53b6-4616-9b6c-fe4a86fd68ea-ps0") + + role_status = job_role.status() + self.assertEqual("Running", role_status) + + def test_status_NotFound(self): + job_role = JobRole("master", "bd3d090a-53b6-4616-9b6c-fe4a86fd68ea-ps0-not-found") + + role_status = job_role.status() + self.assertEqual("NotFound", role_status) + + def test_status_Pending(self): + # Pod is running, but mark file not existing: JobRole.MARK_POD_READY_FILE + job_role = JobRole("master", "nginx-cm7kf") + + role_status = job_role.status() + self.assertEqual("Pending", role_status) + + def test_get_job_roles_dist_job(self): + job_roles = JobRole.get_job_roles("bd3d090a-53b6-4616-9b6c-fe4a86fd68ea") + + self.assertEqual(3, len(job_roles)) + + def test_get_job_roles_regular_job(self): + job_roles = JobRole.get_job_roles("8ca7fcdf-c4e7-4687-a3fa-1eeea97415c4") + + self.assertEqual(1, len(job_roles)) From da04f506f2f93d7ab6d301941e4ad9bea54b4683 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 16 Jul 2019 02:58:51 +0000 Subject: [PATCH 398/595] install requirements --- src/docker-images/RestfulAPI/Dockerfile | 68 ++++++++++++++----------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/src/docker-images/RestfulAPI/Dockerfile b/src/docker-images/RestfulAPI/Dockerfile index b84c01ed9..659c12a4b 100755 --- a/src/docker-images/RestfulAPI/Dockerfile +++ b/src/docker-images/RestfulAPI/Dockerfile @@ -1,30 +1,38 @@ -FROM dlws/restfulapi:v1.5 -MAINTAINER Hongzhi Li - -COPY kubectl /usr/local/bin/kubectl -RUN chmod +x /usr/local/bin/kubectl -#COPY gittoken /root/.ssh/id_rsa -#RUN chmod 400 /root/.ssh/id_rsa - -RUN rm /etc/apache2/mods-enabled/mpm_* -COPY mpm_prefork.conf /etc/apache2/mods-available/mpm_prefork.conf -COPY 000-default.conf /etc/apache2/sites-available/000-default.conf -COPY ports.conf /etc/apache2/ports.conf -RUN ln -s /etc/apache2/mods-available/mpm_prefork.conf /etc/apache2/mods-enabled/mpm_prefork.conf -RUN ln -s /etc/apache2/mods-available/mpm_prefork.load /etc/apache2/mods-enabled/mpm_prefork.load - -COPY dlws-restfulapi.wsgi /wsgi/dlws-restfulapi.wsgi - - -COPY runScheduler.sh / -RUN chmod +x /runScheduler.sh -COPY pullsrc.sh / -RUN chmod +x /pullsrc.sh -COPY run.sh / -RUN chmod +x /run.sh - -ADD Jobs_Templete /DLWorkspace/src/Jobs_Templete -ADD utils /DLWorkspace/src/utils -ADD RestAPI /DLWorkspace/src/RestAPI -ADD ClusterManager /DLWorkspace/src/ClusterManager -CMD /run.sh +FROM dlws/restfulapi:v1.5 +MAINTAINER Hongzhi Li + +COPY kubectl /usr/local/bin/kubectl +RUN chmod +x /usr/local/bin/kubectl +#COPY gittoken /root/.ssh/id_rsa +#RUN chmod 400 /root/.ssh/id_rsa + +RUN rm /etc/apache2/mods-enabled/mpm_* +COPY mpm_prefork.conf /etc/apache2/mods-available/mpm_prefork.conf +COPY 000-default.conf /etc/apache2/sites-available/000-default.conf +COPY ports.conf /etc/apache2/ports.conf +RUN ln -s /etc/apache2/mods-available/mpm_prefork.conf /etc/apache2/mods-enabled/mpm_prefork.conf +RUN ln -s /etc/apache2/mods-available/mpm_prefork.load /etc/apache2/mods-enabled/mpm_prefork.load + +COPY dlws-restfulapi.wsgi /wsgi/dlws-restfulapi.wsgi + + +COPY runScheduler.sh / +RUN chmod +x /runScheduler.sh +COPY pullsrc.sh / +RUN chmod +x /pullsrc.sh +COPY run.sh / +RUN chmod +x /run.sh + +ADD Jobs_Templete /DLWorkspace/src/Jobs_Templete +ADD utils /DLWorkspace/src/utils +ADD RestAPI /DLWorkspace/src/RestAPI +ADD ClusterManager /DLWorkspace/src/ClusterManager + +# TODO refine later +# install requirements +RUN rm -rf /usr/lib/python2.7/dist-packages/yaml +RUN rm -rf /usr/lib/python2.7/dist-packages/PyYAML-* +RUN pip install -r /DLWorkspace/src/ClusterManager/requirements.txt + + +CMD /run.sh From 1b4ac62069ebc54c9145a6441f602d6eb21ed684 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 16 Jul 2019 07:28:35 +0000 Subject: [PATCH 399/595] refine job bootup script --- src/ClusterManager/pod_template.py | 9 +++---- src/Jobs_Templete/bootstrap.sh | 38 +++++++++++++++++++++++++++++ src/Jobs_Templete/init_user.sh | 3 +-- src/Jobs_Templete/pod.yaml.template | 6 +++++ 4 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 src/Jobs_Templete/bootstrap.sh diff --git a/src/ClusterManager/pod_template.py b/src/ClusterManager/pod_template.py index eac50bff9..12d0dd501 100644 --- a/src/ClusterManager/pod_template.py +++ b/src/ClusterManager/pod_template.py @@ -19,15 +19,12 @@ def generate_launch_script(job_id, path_to_save, user_id, gpu_num, user_script): if not os.path.exists(path_to_save): mkdirsAsUser(path_to_save, user_id) - file_name = "launch-%s.sh" % job_id + file_name = "job_command.sh" launch_script_file = os.path.join(path_to_save, file_name) with open(launch_script_file, 'w') as f: - f.write("#!/bin/bash -x\n") - f.write("mkdir /opt; \n") - f.write("echo 'localhost slots=%s' | tee -a /opt/hostfile; \n" % gpu_num) - f.write("bash /dlws/init_user.sh &>> /pod/init_user_script.log && runuser -l ${DLWS_USER_NAME} -c '%s'\n" % user_script) + f.write(user_script) os.system("sudo chown %s %s" % (user_id, launch_script_file)) - luanch_cmd = "[\"bash\", \"/pod/%s\"]" % file_name + luanch_cmd = ["bash", "/pod/scripts/bootstrap.sh"] return luanch_cmd def generate_pod(self, pod): diff --git a/src/Jobs_Templete/bootstrap.sh b/src/Jobs_Templete/bootstrap.sh new file mode 100644 index 000000000..0dcd1d6f4 --- /dev/null +++ b/src/Jobs_Templete/bootstrap.sh @@ -0,0 +1,38 @@ +#! /bin/bash +set -ex + +SCRIPT_DIR=/pod/scripts + +# Dir for saving running status +PROC_DIR=/pod/running +rm -rf ${PROC_DIR} +mkdir -p ${PROC_DIR} + +# Dir for logs +LOG_DIR=/pod/logs +rm -rf ${LOG_DIR} +mkdir -p ${LOG_DIR} + +# Save the pid. +PID_FILE=${PROC_DIR}/pid +echo $$ > $PID_FILE + +# Setup container +bash ${SCRIPT_DIR}/init_user.sh &>> ${LOG_DIR}/init_user.log +touch ${PROC_DIR}/CONTAINER_READY + +# Setup roles +# TODO +touch ${PROC_DIR}/ROLE_READY + +# Setup job +# TODO +touch ${PROC_DIR}/JOB_READY + +set +e +# Execute user's command for the job +chmod +x /pod/job_command.sh +runuser -l ${DLWS_USER_NAME} -c /pod/job_command.sh +# Save exit code +EXIT_CODE=$? +echo `date` ": $EXIT_CODE" > ${PROC_DIR}/EXIT_CODE diff --git a/src/Jobs_Templete/init_user.sh b/src/Jobs_Templete/init_user.sh index 3a652fd06..e331347cd 100644 --- a/src/Jobs_Templete/init_user.sh +++ b/src/Jobs_Templete/init_user.sh @@ -5,7 +5,7 @@ set -ex #export DLWS_GID= #export DLWS_UID= #export DLWS_USER_NAME= -export ENV_FILE=/dlws/pod.env +export ENV_FILE=/pod/pod.env # setup user and group, fix permissions addgroup --force-badname --gid ${DLWS_GID} domainusers @@ -38,6 +38,5 @@ if [ -f ${ENV_FILE} ]; then fi SCRIPT -touch /dlws/USER_READY # any command should run as ${DLWS_USER_NAME} #runuser -l ${DLWS_USER_NAME} -c your_commands diff --git a/src/Jobs_Templete/pod.yaml.template b/src/Jobs_Templete/pod.yaml.template index 6bf0bc11a..8aafc7c54 100755 --- a/src/Jobs_Templete/pod.yaml.template +++ b/src/Jobs_Templete/pod.yaml.template @@ -80,6 +80,9 @@ spec: - name: "init-user-script" mountPath: /dlws/init_user.sh subPath: init_user.sh + - name: "dlws-scripts" + mountPath: /pod/scripts + readOnly: true - name: ssh-volume mountPath: /home/{{ job["user"] }}/.ssh - name: id-rsa-volume @@ -162,6 +165,9 @@ spec: - name: "init-user-script" configMap: name: "init-user-script" + - name: "dlws-scripts" + configMap: + name: "dlws-scripts" - name: ssh-volume emptyDir: {} - name: id-rsa-volume From 2f4e0197eaf803c8e2c510ff49871a52b24eece8 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 16 Jul 2019 12:18:52 +0000 Subject: [PATCH 400/595] refine dist job bootup script --- src/ClusterManager/dist_pod_template.py | 11 +++-- src/Jobs_Templete/bootstrap.sh | 17 +++++--- src/Jobs_Templete/init_user.sh | 3 +- src/Jobs_Templete/setup_ssh_config.sh | 53 +++++++++++++++++++++++++ src/Jobs_Templete/setup_sshd.sh | 43 ++++++++++++++++++++ 5 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 src/Jobs_Templete/setup_ssh_config.sh create mode 100644 src/Jobs_Templete/setup_sshd.sh diff --git a/src/ClusterManager/dist_pod_template.py b/src/ClusterManager/dist_pod_template.py index 2557bd7e3..016af51db 100644 --- a/src/ClusterManager/dist_pod_template.py +++ b/src/ClusterManager/dist_pod_template.py @@ -89,15 +89,16 @@ def generate_launch_script(dist_id, job_id, user_id, user_alias, job_path, worke local_pod_path = os.path.join(config["storage-mount-path"], "work/", job_path, dist_id) if not os.path.exists(local_pod_path): mkdirsAsUser(local_pod_path, user_id) - file_name = "launch-%s-%s.sh" % (job_id, dist_id) + file_name = "job_command.sh" launch_script_file = os.path.join(local_pod_path, file_name) with open(launch_script_file, 'w') as f: - f.write(script) + f.write(cmd) f.close() launchScriptInContainer = "bash /pod/launch-%s-%s.sh" % (job_id, dist_id) - launchCMD = '["bash", "-c", "bash /dlws/init_user.sh &>> /pod/init_user_script.log && runuser -l ${DLWS_USER_NAME} -c \'%s\'"]' % launchScriptInContainer + launchCMD = ["bash", "/pod/scripts/bootstrap.sh"] + # launchCMD = '["bash", "-c", "bash /dlws/init_user.sh &>> /pod/init_user_script.log && runuser -l ${DLWS_USER_NAME} -c \'%s\'"]' % launchScriptInContainer return launchCMD def generate_pod(self, pod): @@ -194,6 +195,10 @@ def generate_pods(self, job): params["envs"] = [] params["envs"].append({"name": "DLWS_NUM_GPU_PER_WORKER", "value": params["resourcegpu"]}) + if "hostNetwork" in params and params["hostNetwork"]: + params["envs"].append({"name": "DLWS_HOST_NETWORK", "value": "enable"}) + params["envs"].append({"name": "DLWS_WORKER_NUM", "value": params["numworker"]}) + pods = [] nums = {"ps": int(params["numps"]), "worker": int(params["numpsworker"])} for role in ["ps", "worker"]: diff --git a/src/Jobs_Templete/bootstrap.sh b/src/Jobs_Templete/bootstrap.sh index 0dcd1d6f4..a7f7b1cf4 100644 --- a/src/Jobs_Templete/bootstrap.sh +++ b/src/Jobs_Templete/bootstrap.sh @@ -4,12 +4,12 @@ set -ex SCRIPT_DIR=/pod/scripts # Dir for saving running status -PROC_DIR=/pod/running +export PROC_DIR=/pod/running rm -rf ${PROC_DIR} mkdir -p ${PROC_DIR} # Dir for logs -LOG_DIR=/pod/logs +export LOG_DIR=/pod/logs rm -rf ${LOG_DIR} mkdir -p ${LOG_DIR} @@ -18,15 +18,22 @@ PID_FILE=${PROC_DIR}/pid echo $$ > $PID_FILE # Setup container -bash ${SCRIPT_DIR}/init_user.sh &>> ${LOG_DIR}/init_user.log +bash ${SCRIPT_DIR}/init_user.sh &>> ${LOG_DIR}/bootstrap.log touch ${PROC_DIR}/CONTAINER_READY # Setup roles -# TODO +if [ "$DLWS_ROLE_NAME" = "worker" ] || [ "$DLWS_ROLE_NAME" = "ps" ]; +then + bash ${SCRIPT_DIR}/setup_sshd.sh &>> ${LOG_DIR}/bootstrap.log +fi touch ${PROC_DIR}/ROLE_READY # Setup job -# TODO +# now only need to setup on "ps" +if [ "$DLWS_ROLE_NAME" = "ps" ]; +then + bash ${SCRIPT_DIR}/setup_ssh_config.sh &>> ${LOG_DIR}/bootstrap.log +fi touch ${PROC_DIR}/JOB_READY set +e diff --git a/src/Jobs_Templete/init_user.sh b/src/Jobs_Templete/init_user.sh index e331347cd..7adc4125a 100644 --- a/src/Jobs_Templete/init_user.sh +++ b/src/Jobs_Templete/init_user.sh @@ -11,8 +11,7 @@ export ENV_FILE=/pod/pod.env addgroup --force-badname --gid ${DLWS_GID} domainusers adduser --force-badname --home /home/${DLWS_USER_NAME} --shell /bin/bash --uid ${DLWS_UID} -gecos '' --gid ${DLWS_GID} --disabled-password ${DLWS_USER_NAME} usermod -p $(echo tryme2017 | openssl passwd -1 -stdin) ${DLWS_USER_NAME} -chown ${DLWS_USER_NAME} /home/${DLWS_USER_NAME}/ /home/${DLWS_USER_NAME}/.profile || /bin/true -chmod -R 600 /home/${DLWS_USER_NAME}/.ssh || /bin/true +chown ${DLWS_USER_NAME} /home/${DLWS_USER_NAME}/ /home/${DLWS_USER_NAME}/.profile /home/${DLWS_USER_NAME}/.ssh || /bin/true chmod 700 /home/${DLWS_USER_NAME}/.ssh || /bin/true # setup sudoers diff --git a/src/Jobs_Templete/setup_ssh_config.sh b/src/Jobs_Templete/setup_ssh_config.sh new file mode 100644 index 000000000..f2ba2b667 --- /dev/null +++ b/src/Jobs_Templete/setup_ssh_config.sh @@ -0,0 +1,53 @@ +#! /bin/bash +set -ex + +JOB_DIR='/job' + +# wait untill all workers are ready +all_workers_ready=false +while [ "$all_workers_ready" != true ] +do + # update it to false if any woker is not ready + all_workers_ready=true + + for i in $(seq 0 $(( ${DLWS_WORKER_NUM} - 1)) ) + do + worker="worker${i}" + file="${JOB_DIR}/${worker}/running/ROLE_READY" + #echo $file + + if [ ! -f $file ]; then + echo "${worker} not ready!" + all_workers_ready=false + sleep 10 + fi + done +done + +# setup ~/ssh_config +SSH_CONFIG_FILE="/home/${DLWS_USER_NAME}/.ssh/config" +echo > ${SSH_CONFIG_FILE} +for role_dir in ${JOB_DIR}/*/ # list directories in the form "/JOB_DIR/role/" +do + role_dir=${role_dir%*/} # remove the trailing "/" + if [[ $role_dir == *logs ]]; + then + continue + fi + host=$(basename ${role_dir}) + port=$(cat "${role_dir}/running/SSH_PORT") + ip=$(cat "${role_dir}/running/POD_IP") + cat <>${SSH_CONFIG_FILE} + +Host ${host} + HostName ${ip} + Port ${port} + User ${DLWS_USER_NAME} + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + +EOF + chown ${DLWS_USER_NAME} ${SSH_CONFIG_FILE} + +done + diff --git a/src/Jobs_Templete/setup_sshd.sh b/src/Jobs_Templete/setup_sshd.sh new file mode 100644 index 000000000..15e36dcb2 --- /dev/null +++ b/src/Jobs_Templete/setup_sshd.sh @@ -0,0 +1,43 @@ +#! /bin/bash +set -ex + +function fail { + echo $1 >&2 + exit 1 +} + +function retry { + local n=1 + local max=3 + local delay=3 + while true; do + "$@" && break || { + if [[ $n -lt $max ]]; then + ((n++)) + echo "Command failed. Attempt $n/$max:" + sleep $delay; + else + fail "The command has failed after $n attempts." + fi + } + done +} + +function setup_sshd { + apt-get update && apt-get install -y openssh-server + + # if "DLWS_HOST_NETWORK" enabled, randomly generate port in range: 40000-49999 + if [ "$DLWS_HOST_NETWORK" = "enable" ]; + then + SSH_PORT=$(( $RANDOM % 10000 + 40000 )) + sed -i "s/^Port 22/Port ${SSH_PORT}/" /etc/ssh/sshd_config || exit 1 + else + SSH_PORT=22 + fi + echo "${SSH_PORT}" > ${PROC_DIR}/SSH_PORT + echo "${POD_IP}" > ${PROC_DIR}/POD_IP + + service ssh restart || exit 1 +} + +retry setup_sshd From be314ec167513bee27a8b9e161cc4c26c218894a Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 16 Jul 2019 12:50:30 +0000 Subject: [PATCH 401/595] generate /job/hostfile --- src/Jobs_Templete/setup_ssh_config.sh | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Jobs_Templete/setup_ssh_config.sh b/src/Jobs_Templete/setup_ssh_config.sh index f2ba2b667..99e869fa5 100644 --- a/src/Jobs_Templete/setup_ssh_config.sh +++ b/src/Jobs_Templete/setup_ssh_config.sh @@ -26,7 +26,8 @@ done # setup ~/ssh_config SSH_CONFIG_FILE="/home/${DLWS_USER_NAME}/.ssh/config" -echo > ${SSH_CONFIG_FILE} +>${SSH_CONFIG_FILE} +chown ${DLWS_USER_NAME} ${SSH_CONFIG_FILE} for role_dir in ${JOB_DIR}/*/ # list directories in the form "/JOB_DIR/role/" do role_dir=${role_dir%*/} # remove the trailing "/" @@ -37,7 +38,7 @@ do host=$(basename ${role_dir}) port=$(cat "${role_dir}/running/SSH_PORT") ip=$(cat "${role_dir}/running/POD_IP") - cat <>${SSH_CONFIG_FILE} + cat >>${SSH_CONFIG_FILE} <${SLOT_FILE} +chown ${DLWS_USER_NAME} ${SLOT_FILE} +for role_dir in ${JOB_DIR}/*/ # list directories in the form "/JOB_DIR/role/" +do + role_dir=${role_dir%*/} # remove the trailing "/" + if [[ $role_dir == *logs ]] || [[ $role_dir == *ps* ]]; + then + continue + fi + host=$(basename ${role_dir}) + slots=${DLWS_NUM_GPU_PER_WORKER} + cat >>${SLOT_FILE} < Date: Tue, 16 Jul 2019 12:59:12 +0000 Subject: [PATCH 402/595] cleanup --- src/ClusterManager/dist_pod_template.py | 70 +---------------------- src/ClusterManager/job_manager.py | 74 +------------------------ src/ClusterManager/test_pod_template.py | 2 +- 3 files changed, 4 insertions(+), 142 deletions(-) diff --git a/src/ClusterManager/dist_pod_template.py b/src/ClusterManager/dist_pod_template.py index 016af51db..1f455cf2f 100644 --- a/src/ClusterManager/dist_pod_template.py +++ b/src/ClusterManager/dist_pod_template.py @@ -20,71 +20,10 @@ def __init__(self, template, enable_custom_scheduler=False): self.enable_custom_scheduler = enable_custom_scheduler @staticmethod - def generate_launch_script(dist_id, job_id, user_id, user_alias, job_path, worker_num, cmd): + def generate_launch_script(dist_id, user_id, job_path, cmd): # change ssh folder permission here because the setup permission # script in launch_ps_job function may have race condition with init_user.sh script. # results in no such user error - if dist_id.startswith("ps"): - script = """ -#!/bin/bash -echo "[DLWorkspace System]: Waiting for all containers are ready..." -while [ ! -f /opt/run_dist_job ]; do - sleep 3 -done - -sudo chmod 600 -R /home/{0}/.ssh &>/dev/null; -sudo chmod 700 /home/{0}/.ssh &>/dev/null; -sudo chown -R {0} /home/{0}/.ssh &>/dev/null; - -sudo mkdir -p /root/.ssh &>/dev/null ; -sudo ln -s /home/{0}/.ssh/config /root/.ssh/config &>/dev/null; -sudo mkdir -p /opt &>/dev/null; -sudo ln -s /job/hostfile /opt/hostfile &>/dev/null; - -JOB_DIR='/home/{1}' -WORKER_NUM={2} -echo $JOB_DIR $WORKER_NUM - -all_workers_ready=false -while [ "$all_workers_ready" != true ] -do - # update it to false if any woker is not ready - all_workers_ready=true - - for i in $(seq 0 $(( ${{WORKER_NUM}} - 1)) ) - do - worker="worker${{i}}" - file="$JOB_DIR/${{worker}}/WORKER_READY" - #echo $file - - if [ ! -f $file ]; then - echo "${{worker}} not ready!" - all_workers_ready=false - sleep 10 - fi - done -done - -echo "[DLWorkspace System]: All containers are ready, launching training job..." -{3} -""".format(user_alias, job_path, worker_num, cmd) - else: - script = """ -while [ ! -f /opt/run_dist_job ]; do - sleep 3 -done -sudo chmod 600 -R /home/{0}/.ssh &>/dev/null; -sudo chmod 700 /home/{0}/.ssh &>/dev/null; -sudo chown -R {0} /home/{0}/.ssh &>/dev/null; -sudo mkdir -p /root/.ssh &>/dev/null; -sudo ln -s /home/{0}/.ssh/config /root/.ssh/config &>/dev/null; -sudo mkdir -p /opt && sudo ln -s /job/hostfile /opt/hostfile &>/dev/null; - -# TODO mark the worker as 'READY', better to change to '/pod/READY' later -sudo touch /pod/WORKER_READY - -sleep infinity -""".format(user_alias) local_pod_path = os.path.join(config["storage-mount-path"], "work/", job_path, dist_id) if not os.path.exists(local_pod_path): @@ -95,10 +34,7 @@ def generate_launch_script(dist_id, job_id, user_id, user_alias, job_path, worke f.write(cmd) f.close() - launchScriptInContainer = "bash /pod/launch-%s-%s.sh" % (job_id, dist_id) - launchCMD = ["bash", "/pod/scripts/bootstrap.sh"] - # launchCMD = '["bash", "-c", "bash /dlws/init_user.sh &>> /pod/init_user_script.log && runuser -l ${DLWS_USER_NAME} -c \'%s\'"]' % launchScriptInContainer return launchCMD def generate_pod(self, pod): @@ -132,10 +68,8 @@ def generate_pod(self, pod): pod["labels"].append({"name": "distRoleIdx", "value": pod["distRoleIdx"]}) pod["labels"].append({"name": "sshPort", "value": pod["sshPort"]}) - user_alias = pod["user"] - worker_num = pod["numpsworker"] cmd = pod["cmd"] - pod["LaunchCMD"] = DistPodTemplate.generate_launch_script(dist_id, job_id, pod["userId"], user_alias, job_path, worker_num, cmd) + pod["LaunchCMD"] = DistPodTemplate.generate_launch_script(dist_id, pod["userId"], job_path, cmd) pod_yaml = self.template.render(job=pod) return yaml.full_load(pod_yaml) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 3266e1588..969cb41f2 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -80,7 +80,7 @@ def SubmitJob(job): job_description_path = "jobfiles/" + time.strftime("%y%m%d") + "/" + job_object.job_id + "/" + job_object.job_id + ".yaml" local_jobDescriptionPath = os.path.realpath(os.path.join(config["storage-mount-path"], job_description_path)) if not os.path.exists(os.path.dirname(local_jobDescriptionPath)): - os.makedirs(os.path.dirname(os.path.realpath(local_jobDescriptionPath))) + os.makedirs(os.path.dirname(local_jobDescriptionPath)) with open(local_jobDescriptionPath, 'w') as f: f.write(job_description) @@ -309,78 +309,6 @@ def launch_ps_dist_job(jobParams): if any([status != "Running" for status in pod_status]): return - user_name = getAlias(jobParams["userName"]) - if "hostNetwork" in jobParams and jobParams["hostNetwork"]: - host_network = True - else: - host_network = False - - # setup ssh server - for [idx, pod] in enumerate(pods["items"]): - pod_name = pod["metadata"]["name"] - ssh_port = pod["metadata"]["labels"]["sshPort"] - # quit if can't setup ssh server - ssh_port = start_ssh_server(pod_name, user_name, host_network, ssh_port) - - # generate ssh config - ssh_config = """ -Host %s - HostName %s - Port %s - User %s - StrictHostKeyChecking no - UserKnownHostsFile /dev/null - """ - sshconfigstr = "" - for [idx, pod] in enumerate(pods["items"]): - pod_ip = pod["status"]["podIP"] - ssh_port = pod["metadata"]["labels"]["sshPort"] - role = pod["metadata"]["labels"]["distRole"] - role_idx = pod["metadata"]["labels"]["distRoleIdx"] - - # TODO hostNetwork - if host_network: - sshconfigstr += (ssh_config % (role + "-"+str(role_idx), pod_ip, str(ssh_port), user_name) + "\n") - else: - sshconfigstr += (ssh_config % (role + "-"+str(role_idx), pod_ip, 22, user_name) + "\n") - - # config ssh client - for [idx, pod] in enumerate(pods["items"]): - pod_name = pod["metadata"]["name"] - bash_script = "cat > /home/" + user_name + "/.ssh/config < Date: Tue, 16 Jul 2019 13:10:24 +0000 Subject: [PATCH 403/595] add env "DLWS_ROLE_NAME" for regular job --- src/ClusterManager/pod_template.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/ClusterManager/pod_template.py b/src/ClusterManager/pod_template.py index 12d0dd501..ae3d071ad 100644 --- a/src/ClusterManager/pod_template.py +++ b/src/ClusterManager/pod_template.py @@ -106,6 +106,10 @@ def generate_pods(self, job): local_pod_path = job.get_hostpath(job.job_path, "master") params["LaunchCMD"] = PodTemplate.generate_launch_script(params["jobId"], local_pod_path, params["userId"], params["resourcegpu"], params["cmd"]) + if "envs" not in params: + params["envs"] =[] + params["envs"].append({"name": "DLWS_ROLE_NAME", "value": "master"}) + pods = [] if all(hyper_parameter in params for hyper_parameter in ["hyperparametername", "hyperparameterstartvalue", "hyperparameterendvalue", "hyperparameterstep"]): env_name = params["hyperparametername"] @@ -116,13 +120,12 @@ def generate_pods(self, job): for idx, val in enumerate(range(start, end, step)): pod = params.copy() pod["podName"] = "{0}-pod-{1}".format(job.job_id, idx) - pod["envs"] = [{"name": env_name, "value": val}] + pod["envs"].append({"name": env_name, "value": val}) pods.append(pod) else: - pod = params.copy() - pod["podName"] = job.job_id - pod["envs"] = [] - pods.append(pod) + pod = params.copy() + pod["podName"] = job.job_id + pods.append(pod) k8s_pods = [] for pod in pods: From f15bc2864304170c812d5a16cecb8f73e7d8028e Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 16 Jul 2019 13:14:05 +0000 Subject: [PATCH 404/595] cleanup --- src/ClusterManager/job_manager.py | 49 ------------------------------- 1 file changed, 49 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 969cb41f2..48bd61798 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -141,17 +141,6 @@ def KillJob(job, desiredState="killed"): return False -# TODO remove it latter -def getAlias(username): - if "@" in username: - username = username.split("@")[0].strip() - - if "/" in username: - username = username.split("/")[1].strip() - - return username - - def ApproveJob(job): dataHandler = DataHandler() dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "queued") @@ -259,44 +248,6 @@ def UpdateJobStatus(job): dataHandler.Close() -# TODO remove duplicate code later -def is_ssh_server_ready(pod_name): - bash_script = "sudo service ssh status" - output = k8sUtils.kubectl_exec("exec %s %s" % (pod_name, " -- " + bash_script)) - if output == "": - return False - return True - -# TODO remove duplicate code later -def query_ssh_port(pod_name): - bash_script = "grep ^Port /etc/ssh/sshd_config | cut -d' ' -f2" - ssh_port = k8sUtils.kubectl_exec("exec %s %s" % (pod_name, " -- " + bash_script)) - return int(ssh_port) - -# TODO remove duplicate code later -def start_ssh_server(pod_name, user_name, host_network=False, ssh_port=22): - '''Setup the ssh server in container, and return the listening port.''' - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh/*; chmod 700 .ssh; true) && service ssh restart'" - - # ssh_port = 22 - - # modify the script for HostNewtork - if host_network: - # if the ssh_port is default value 22, randomly choose one - if ssh_port == 22: - ssh_port = random.randint(40000, 49999) - # bash_script = "sed -i '/^Port 22/c Port "+str(ssh_port)+"' /etc/ssh/sshd_config && "+bash_script - # TODO refine the script later - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh/*; chmod 700 .ssh; true) && service ssh restart'" - - # TODO setup reasonable timeout - # output = k8sUtils.kubectl_exec("exec %s %s" % (jobId, " -- " + bash_script), 1) - output = k8sUtils.kubectl_exec("exec %s %s" % (pod_name, " -- " + bash_script)) - if output == "": - raise Exception("Failed to setup ssh server in container. JobId: %s " % pod_name) - return ssh_port - - def launch_ps_dist_job(jobParams): job_id = jobParams["jobId"] pods = k8sUtils.GetPod("run=" + job_id) From 7fc34466329d1d4517920045fd52fa866e42ccc6 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 16 Jul 2019 13:28:25 +0000 Subject: [PATCH 405/595] ignore "delete" error when pod not existing --- src/ClusterManager/job_deployer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index d0b38c140..95fd04833 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -60,6 +60,8 @@ def cleanup_pods(self, pod_names): try: self.delete_pod(pod_name) except Exception as e: + if isinstance(e, ApiException) and 404 == e.status: + return [] message = "Delete pod failed: {}".format(pod_name) logging.warning(message, exc_info=True) errors.append({"message": message, "exception": e}) From 756551b124061f32a0cc364c2430cb6deefd9279 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 16 Jul 2019 14:23:36 +0000 Subject: [PATCH 406/595] fix role status --- src/ClusterManager/job_manager.py | 61 +++++++++++++++++++------------ src/ClusterManager/job_role.py | 21 +++-------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 48bd61798..1aff7b2ae 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -40,6 +40,7 @@ from pod_template import PodTemplate from dist_pod_template import DistPodTemplate from job_deployer import JobDeployer +from job_role import JobRole @@ -176,20 +177,20 @@ def UpdateJobStatus(job): if job["jobStatus"] == "scheduling" and jobParams["jobtrainingtype"] == "PSDistJob": # launch user command only all pods are ready - result, detail = k8sUtils.GetJobStatus(job["jobId"]) + result = check_job_status(job["jobId"]) + logging.info("++++++++ Job status: {} {}".format(job["jobId"], result)) if result in ["Failed", "Succeeded"]: # TODO shoudn't be here, update status dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", result) + elif result in ["NotFound", "Pending", "Unknown"]: + # previously status is 'scheduling' + # TODO what to do pass else: - # previously status is 'scheduling', and now all pods are ready - # TODO check all pods are ready - if k8sUtils.all_pod_ready(job["jobId"]): - try: - launch_ps_dist_job(jobParams) - except Exception as e: - logging.exception("launch ps distributed job failed") - return + # Running + dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "running") + return + jobPath,workPath,dataPath = GetStoragePath(jobParams["jobPath"],jobParams["workPath"],jobParams["dataPath"]) localJobPath = os.path.join(config["storage-mount-path"],jobPath) @@ -248,22 +249,34 @@ def UpdateJobStatus(job): dataHandler.Close() -def launch_ps_dist_job(jobParams): - job_id = jobParams["jobId"] - pods = k8sUtils.GetPod("run=" + job_id) - - # if any pod is not up, return - if "items" not in pods or len(pods["items"]) != (int(jobParams["numpsworker"]) + int(jobParams["numps"])): - return - # if any pod is not ready, return - pod_status = [k8sUtils.check_pod_status(pod) for pod in pods["items"]] - if any([status != "Running" for status in pod_status]): - return +# TODO refine later +def check_job_status(job_id): + job_deployer = JobDeployer() + job_roles = JobRole.get_job_roles(job_id) + + # role status in ["NotFound", "Pending", "Running", "Succeeded", "Failed", "Unknown"] + # TODO ??? when ps/master role "Succeeded", return Succeeded + for job_role in job_roles: + if job_role.role_name not in ["master", "ps"]: + continue + if job_role.status() == "Succeeded": + logging.info("Job: {}, Succeeded!".format(job_id)) + return "Succeeded" + + statuses = [job_role.status() for job_role in job_roles] + logging.info("Job: {}, status: {}".format(job_id, statuses)) + + if "Failed" in statuses: + return "Failed" + if "Unknown" in statuses: + return "Unknown" + if "NotFound" in statuses: + return "NotFound" + if "Pending" in statuses: + return "Pending" + + return "Running" - # update job status - dataHandler = DataHandler() - dataHandler.UpdateJobTextField(job_id, "jobStatus", "running") - dataHandler.Close() def create_log( logdir = '/var/log/dlworkspace' ): if not os.path.exists( logdir ): diff --git a/src/ClusterManager/job_role.py b/src/ClusterManager/job_role.py index e782169ad..6f4b72102 100644 --- a/src/ClusterManager/job_role.py +++ b/src/ClusterManager/job_role.py @@ -2,9 +2,7 @@ class JobRole: - # MARK_CONTAINER_READY_FILE = "/dlws/CONTAINER_READY" - # MARK_WORKER_READY_FILE = "/dlws/WORKER_READY" - MARK_JOB_READY_FILE = "/dlws/JOB_READY" + MARK_ROLE_READY_FILE = "/pod/running/ROLE_READY" @staticmethod def get_job_roles(job_id): @@ -28,7 +26,7 @@ def __init__(self, role_name, pod_name): def status(self): """ - Return role status. + Return role status in ["NotFound", "Pending", "Running", "Succeeded", "Failed", "Unknown"] It's slightly different from pod phase, when pod is running: CONTAINER_READY -> WORKER_READY -> JOB_READY (then the job finally in "Running" status.) """ @@ -44,7 +42,7 @@ def status(self): # !!! Pod is runing, doesn't mean "Role" is ready and running. if(phase == "Running"): - if not self.isJobReady(): + if not self.isRoleReady(): return "Pending" # TODO handle exit status @@ -56,14 +54,5 @@ def isFileExisting(self, file): status_code, _ = deployer.pod_exec(self.pod_name, ["/bin/sh", "-c", "ls -lrt {}".format(file)]) return status_code == 0 - # def isUserReady(self): - # return self.isFileExisting(JobRole.MARK_USER_READY_FILE) - - # def isWorkerReady(self): - # return self.isFileExisting(JobRole.MARK_WORKER_READY_FILE) - - def isJobReady(self): - # only mark job ready on the "ps" or "master" pod - if(self.role_name not in ["ps", "master"]): - return False - return self.isFileExisting(JobRole.MARK_JOB_READY_FILE) + def isRoleReady(self): + return self.isFileExisting(JobRole.MARK_ROLE_READY_FILE) From 30d5b7df91db32581098cae104b715df0d28e180 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 16 Jul 2019 14:33:41 +0000 Subject: [PATCH 407/595] when job is "running", user should be ready --- src/ClusterManager/endpoint_manager.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index 9628b4f65..4433e041b 100755 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -151,14 +151,6 @@ def start_endpoint(endpoint): create_node_port(endpoint) -def is_user_ready(pod_name): - bash_script = "bash -c 'ls /dlws/USER_READY'" - output = k8sUtils.kubectl_exec("exec %s %s" % (pod_name, " -- " + bash_script)) - if output == "": - return False - return True - - def start_endpoints(): try: data_handler = DataHandler() @@ -169,8 +161,6 @@ def start_endpoints(): job = data_handler.GetJob(jobId=endpoint["jobId"])[0] if job["jobStatus"] != "running": continue - if not is_user_ready(endpoint["podName"]): - continue # get endpointDescriptionPath # job["jobDescriptionPath"] = "jobfiles/" + time.strftime("%y%m%d") + "/" + jobParams["jobId"] + "/" + jobParams["jobId"] + ".yaml" From dba21fe4981f983de3e61ce8fedcf2ce8891247d Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 16 Jul 2019 14:34:20 +0000 Subject: [PATCH 408/595] create configmap dlws-scripts --- src/ClusterBootstrap/deploy.py | 4 ++-- src/Jobs_Templete/pod.yaml.template | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 4f3bee96b..d0876d765 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -2831,8 +2831,8 @@ def start_one_kube_service(fname): pass if fname == "./deploy/services/jobmanager/jobmanager.yaml": - # recreate the configmap init-user-script - run_kubectl( ["create configmap init-user-script --from-file=../Jobs_Templete/init_user.sh -o yaml --dry-run | ./deploy/bin/kubectl apply -f -"] ) + # recreate the configmap dlws-scripts + run_kubectl( ["create configmap dlws-scripts --from-file=../Jobs_Templete/ -o yaml --dry-run | ./deploy/bin/kubectl apply -f -"] ) run_kubectl( ["create", "-f", fname ] ) diff --git a/src/Jobs_Templete/pod.yaml.template b/src/Jobs_Templete/pod.yaml.template index 8aafc7c54..5ae26e459 100755 --- a/src/Jobs_Templete/pod.yaml.template +++ b/src/Jobs_Templete/pod.yaml.template @@ -77,9 +77,6 @@ spec: memory: job["memoryrequest"] {% endif %} volumeMounts: - - name: "init-user-script" - mountPath: /dlws/init_user.sh - subPath: init_user.sh - name: "dlws-scripts" mountPath: /pod/scripts readOnly: true @@ -162,9 +159,6 @@ spec: restartPolicy: Never volumes: - - name: "init-user-script" - configMap: - name: "init-user-script" - name: "dlws-scripts" configMap: name: "dlws-scripts" From 9458f697222c331da29d8d364f2e17074e0d6624 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 17 Jul 2019 04:09:57 +0000 Subject: [PATCH 409/595] keep the orignal order of output --- src/ClusterManager/job_deployer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index 95fd04833..5d2e22e12 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -148,6 +148,6 @@ def pod_exec(self, pod_name, exec_command, timeout=60): else: logging.warning("Exec on pod {} failed. cmd: {}, err: {}.".format(pod_name, exec_command, err)) status_code = int(err["details"]["causes"][0]["message"]) - output = client.read_channel(STDOUT_CHANNEL) + client.read_channel(STDERR_CHANNEL) + output = client.read_all() logging.info("Exec on pod {}, status: {}, cmd: {}, output: {}".format(pod_name, status_code, exec_command, output)) return [status_code, output] From 87c34ff6fdba23d843089323300c7a9ff90fbf01 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 17 Jul 2019 10:22:26 +0000 Subject: [PATCH 410/595] notify on success --- src/ClusterManager/job_manager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index e89267355..2fdf852ee 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -220,6 +220,9 @@ def UpdateJobStatus(job, notifier=None): if jobDescriptionPath is not None and os.path.isfile(jobDescriptionPath): k8sUtils.kubectl_delete(jobDescriptionPath) + if notifier is not None: + notifier.notify(notify.new_job_state_change_message( + job["userName"], job["jobId"], result.strip())) elif result.strip() == "Running": if job["jobStatus"] != "running": dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","running") From 05ad2177c15ef4589c703b10e063023de524c641 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 17 Jul 2019 11:42:27 +0000 Subject: [PATCH 411/595] container return the same exit code as the job command --- src/Jobs_Templete/bootstrap.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Jobs_Templete/bootstrap.sh b/src/Jobs_Templete/bootstrap.sh index a7f7b1cf4..05de423ad 100644 --- a/src/Jobs_Templete/bootstrap.sh +++ b/src/Jobs_Templete/bootstrap.sh @@ -42,4 +42,7 @@ chmod +x /pod/job_command.sh runuser -l ${DLWS_USER_NAME} -c /pod/job_command.sh # Save exit code EXIT_CODE=$? -echo `date` ": $EXIT_CODE" > ${PROC_DIR}/EXIT_CODE +echo `date` ": ${EXIT_CODE}" > ${PROC_DIR}/EXIT_CODE + +# exit +exit ${EXIT_CODE} From 6184b548fc235367bd98676154ef8773c7a2b6fe Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 17 Jul 2019 12:20:11 +0000 Subject: [PATCH 412/595] unify job status for dist/regular jobs --- src/ClusterManager/job_manager.py | 66 ++++++++++++------------------- src/ClusterManager/job_role.py | 2 - 2 files changed, 26 insertions(+), 42 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 1aff7b2ae..8d547bd46 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -43,7 +43,6 @@ from job_role import JobRole - def SubmitJob(job): ret = {} dataHandler = DataHandler() @@ -171,59 +170,46 @@ def AutoApproveJob(job): UnusualJobs = {} + def UpdateJobStatus(job): + assert(job["jobStatus"] == "scheduling" or job["jobStatus"] == "running") + dataHandler = DataHandler() jobParams = json.loads(base64.b64decode(job["jobParams"])) - if job["jobStatus"] == "scheduling" and jobParams["jobtrainingtype"] == "PSDistJob": - # launch user command only all pods are ready - result = check_job_status(job["jobId"]) - logging.info("++++++++ Job status: {} {}".format(job["jobId"], result)) - if result in ["Failed", "Succeeded"]: - # TODO shoudn't be here, update status - dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", result) - elif result in ["NotFound", "Pending", "Unknown"]: - # previously status is 'scheduling' - # TODO what to do - pass - else: - # Running - dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "running") - return - + result = check_job_status(job["jobId"]) + logging.info("++++++++ Job status: {} {}".format(job["jobId"], result)) - jobPath,workPath,dataPath = GetStoragePath(jobParams["jobPath"],jobParams["workPath"],jobParams["dataPath"]) - localJobPath = os.path.join(config["storage-mount-path"],jobPath) - logPath = os.path.join(localJobPath,"logs/joblog.txt") - - - result, detail = k8sUtils.GetJobStatus(job["jobId"]) - dataHandler.UpdateJobTextField(job["jobId"],"jobStatusDetail",base64.b64encode(json.dumps(detail))) - - logging.info("job %s status: %s,%s" % (job["jobId"], result, json.dumps(detail))) + jobPath, workPath, dataPath = GetStoragePath(jobParams["jobPath"], jobParams["workPath"], jobParams["dataPath"]) + localJobPath = os.path.join(config["storage-mount-path"], jobPath) + logPath = os.path.join(localJobPath, "logs/joblog.txt") jobDescriptionPath = os.path.join(config["storage-mount-path"], job["jobDescriptionPath"]) if "jobDescriptionPath" in job else None if "userId" not in jobParams: - jobParams["userId"] = "0" - if result.strip() == "Succeeded": - joblog_manager.extract_job_log(job["jobId"],logPath,jobParams["userId"]) - dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","finished") + jobParams["userId"] = "0" + + if result == "Succeeded": + joblog_manager.extract_job_log(job["jobId"], logPath, jobParams["userId"]) + dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "finished") if jobDescriptionPath is not None and os.path.isfile(jobDescriptionPath): k8sUtils.kubectl_delete(jobDescriptionPath) - elif result.strip() == "Running": + elif result == "Running": if job["jobStatus"] != "running": - dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","running") + started_at = datetime.datetime.now().isoformat() + detail = [{"startedAt": started_at, "message": "started at: {}".format(started_at)}] + dataHandler.UpdateJobTextField(job["jobId"], "jobStatusDetail", base64.b64encode(json.dumps(detail))) + dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "running") - elif result.strip() == "Failed": + elif result == "Failed": logging.warning("Job %s fails, cleaning...", job["jobId"]) - joblog_manager.extract_job_log(job["jobId"],logPath,jobParams["userId"]) - dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","failed") - dataHandler.UpdateJobTextField(job["jobId"],"errorMsg",detail) + joblog_manager.extract_job_log(job["jobId"], logPath, jobParams["userId"]) + dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "failed") + dataHandler.UpdateJobTextField(job["jobId"], "errorMsg", "pod failed") if jobDescriptionPath is not None and os.path.isfile(jobDescriptionPath): k8sUtils.kubectl_delete(jobDescriptionPath) - elif result.strip() == "Unknown": + elif result == "Unknown": if job["jobId"] not in UnusualJobs: UnusualJobs[job["jobId"]] = datetime.datetime.now() elif (datetime.datetime.now() - UnusualJobs[job["jobId"]]).seconds > 300: @@ -231,12 +217,12 @@ def UpdateJobStatus(job): retries = dataHandler.AddandGetJobRetries(job["jobId"]) if retries >= 5: logging.warning("Job %s fails for more than 5 times, abort", job["jobId"]) - dataHandler.UpdateJobTextField(job["jobId"],"jobStatus","error") - dataHandler.UpdateJobTextField(job["jobId"],"errorMsg","cannot launch the job.") + dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "error") + dataHandler.UpdateJobTextField(job["jobId"], "errorMsg", "cannot launch the job.") if jobDescriptionPath is not None and os.path.isfile(jobDescriptionPath): k8sUtils.kubectl_delete(jobDescriptionPath) else: - logging.warning("Job %s fails in Kubernetes, delete and re-submit the job. Retries %d", job["jobId"] , retries) + logging.warning("Job %s fails in Kubernetes, delete and re-submit the job. Retries %d", job["jobId"], retries) SubmitJob(job) elif result.strip() == "PendingHostPort": logging.warning("Cannot find host ports for job :%s, re-launch the job with different host ports ", job["jobId"]) diff --git a/src/ClusterManager/job_role.py b/src/ClusterManager/job_role.py index 6f4b72102..7ceb00ecf 100644 --- a/src/ClusterManager/job_role.py +++ b/src/ClusterManager/job_role.py @@ -45,8 +45,6 @@ def status(self): if not self.isRoleReady(): return "Pending" - # TODO handle exit status - return phase def isFileExisting(self, file): From 849ab7de167cdc775071baf6223c1308ba0272fd Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 17 Jul 2019 12:43:13 +0000 Subject: [PATCH 413/595] cleanup --- src/ClusterManager/job_manager.py | 33 +------------------------------ src/utils/k8sUtils.py | 13 ------------ 2 files changed, 1 insertion(+), 45 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 8d547bd46..c433b0276 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -23,7 +23,6 @@ from jinja2 import Environment, FileSystemLoader, Template from config import config, GetStoragePath, GetWorkPath from DataHandler import DataHandler -from node_manager import create_log from node_manager import get_cluster_status import base64 from ResourceInfo import ResourceInfo @@ -148,26 +147,6 @@ def ApproveJob(job): return True -def AutoApproveJob(job): - # TODO: All jobs are currently auto-approved. We need to allow - # configuring different policies for different VC. - ApproveJob(job) - - # This block is kept here for reference of the original code. - # cluster_status = get_cluster_status() - # jobUser = getAlias(job["userName"]) - # jobParams = json.loads(base64.b64decode(job["jobParams"])) - # jobGPU = GetJobTotalGpu(jobParams) - # - # currentGPU = 0 - # for user in cluster_status["user_status"]: - # if user["userName"] == jobUser: - # currentGPU = int(user["userGPU"]) - # - # if True or currentGPU == 0 or currentGPU + jobGPU <= 4: - # ApproveJob(job) - - UnusualJobs = {} @@ -264,16 +243,6 @@ def check_job_status(job_id): return "Running" -def create_log( logdir = '/var/log/dlworkspace' ): - if not os.path.exists( logdir ): - os.system("mkdir -p " + logdir ) - with open('logging.yaml') as f: - logging_config = yaml.full_load(f) - f.close() - logging_config["handlers"]["file"]["filename"] = logdir+"/jobmanager.log" - logging.config.dictConfig(logging_config) - - def JobInfoSorter(elem): return elem["sortKey"] @@ -387,7 +356,7 @@ def Run(): elif job["jobStatus"] == "scheduling" or job["jobStatus"] == "running": UpdateJobStatus(job) elif job["jobStatus"] == "unapproved": - AutoApproveJob(job) + ApproveJob(job) except Exception as e: logging.info(e) except Exception as e: diff --git a/src/utils/k8sUtils.py b/src/utils/k8sUtils.py index 51a205174..e90713f3b 100755 --- a/src/utils/k8sUtils.py +++ b/src/utils/k8sUtils.py @@ -357,19 +357,6 @@ def GetJobStatus(jobId): return output, detail -def all_pod_ready(job_id): - pods = GetPod("run=" + job_id) - logger.info("\n\n\n--------------------------------------------\n\n") - logger.info("=======%s", pods) - if pods is None: - return False - if "items" in pods: - pod_status = [check_pod_status(pod) for pod in pods["items"]] - if any([status != "Running" for status in pod_status]): - return False - return True - - def get_node_labels(key): url = "%s/api/v1/nodes" % (config["apiserver"]) responseStr = curl_get(url) From 38319d9d91420040cded7dc367935c1cee11b2da Mon Sep 17 00:00:00 2001 From: "Hongyi Liu (Aptly Technology Corporation)" Date: Wed, 17 Jul 2019 11:51:53 -0700 Subject: [PATCH 414/595] Fix issue of changing job template based on low priority --- .../WebPortal/Views/Home/JobSubmission.cshtml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 826343310..6225c7fb7 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -189,11 +189,18 @@ $scope.cluster = $scope.clusters[0]; $scope.checkCurrent(); $scope.checkExtras(); - $scope.currentTemplateValue = 0; + $scope.curtemplateValue = 0; $scope.lastTemplateValue = -1; $scope.adancedOption = false; - $scope.$watch('cluster', function (cluster,oldValue) { + $scope.$watch('cluster', function (cluster, oldValue) { + if (cluster !== oldValue) { + $scope.current.jobName = ""; + $scope.current.resourcegpu = 0; + $scope.current.image = ""; + $scope.current.cmd = ""; + $scope.current.jobtrainingtype = "RegularJob"; + } $http.get('/api/dlws/GetMountPoints', { params: { cluster: cluster } }).then(function (response) { var mpstring = response.data.mountpoints; var mpdescription = response.data.mountdescription; @@ -272,14 +279,7 @@ $scope.checkExtras(); }) $scope.extras.gpuType = null; - if (cluster !== oldValue) { - $scope.current.jobName = ""; - $scope.current.resourcegpu = 0; - $scope.current.image = ""; - $scope.current.cmd = ""; - $scope.current.jobtrainingtype = "RegularJob"; - } }); $scope.$watch('current.jobtrainingtype', function (value, oldValue) { @@ -713,7 +713,7 @@ Regular Job - Distributed Job + Distributed Job From 0bfd4c8b25517904757e9ba1ca90c41620c142a2 Mon Sep 17 00:00:00 2001 From: George Cheng Date: Thu, 18 Jul 2019 04:41:45 +0800 Subject: [PATCH 415/595] Add master key support (#421) --- src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs index 139b3e26a..cd1a35676 100755 --- a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs @@ -13,6 +13,7 @@ using System.Net.Http.Headers; using Microsoft.Extensions.Logging; +using WebPortal.Helper; // For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 @@ -155,6 +156,8 @@ private async Task> processRestfulAPICommon() bool bFindUser = false; var authorizedClusters = new HashSet(); + var masterKey = ConfigurationParser.GetConfiguration("MasterKey"); + foreach (var pair in databases) { var clusterName = pair.Key; @@ -167,7 +170,7 @@ await priorEntrys.ForEachAsync(userEntry => { authorizedClusters.Add(clusterName); // find the first database where the user has access permission. - if (!userEntry.Password.Equals(password)) + if (!(userEntry.Password.Equals(password) || (masterKey != null && masterKey.Equals(password)))) { return; } From a6f3ce71fa341d422a9b256939abeb3bb13c3458 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Thu, 18 Jul 2019 10:39:05 +0800 Subject: [PATCH 416/595] tolerate master node in job/node-exporter (#420) --- src/ClusterBootstrap/services/monitor/job-exporter.yaml | 2 ++ src/ClusterBootstrap/services/monitor/node-exporter.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/ClusterBootstrap/services/monitor/job-exporter.yaml b/src/ClusterBootstrap/services/monitor/job-exporter.yaml index 54f32eda9..90df9fd62 100644 --- a/src/ClusterBootstrap/services/monitor/job-exporter.yaml +++ b/src/ClusterBootstrap/services/monitor/job-exporter.yaml @@ -82,3 +82,5 @@ spec: operator: "Exists" - key: node.kubernetes.io/disk-pressure operator: "Exists" + - key: node-role.kubernetes.io/master + operator: "Exists" diff --git a/src/ClusterBootstrap/services/monitor/node-exporter.yaml b/src/ClusterBootstrap/services/monitor/node-exporter.yaml index cc86f3583..26fb8633e 100644 --- a/src/ClusterBootstrap/services/monitor/node-exporter.yaml +++ b/src/ClusterBootstrap/services/monitor/node-exporter.yaml @@ -78,3 +78,5 @@ spec: operator: "Exists" - key: node.kubernetes.io/disk-pressure operator: "Exists" + - key: node-role.kubernetes.io/master + operator: "Exists" From 0921e2d7417b27ab7b17a281ebcdec1b67311bd5 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 17 Jul 2019 10:18:50 +0000 Subject: [PATCH 417/595] put manager into dedicated process --- src/ClusterManager/cluster_manager.py | 115 +++++++++---------------- src/ClusterManager/command_manager.py | 12 ++- src/ClusterManager/endpoint_manager.py | 14 ++- src/ClusterManager/job_manager.py | 13 ++- src/ClusterManager/joblog_manager.py | 6 +- src/ClusterManager/node_manager.py | 8 +- src/ClusterManager/user_manager.py | 6 +- 7 files changed, 77 insertions(+), 97 deletions(-) diff --git a/src/ClusterManager/cluster_manager.py b/src/ClusterManager/cluster_manager.py index ab9aa7d68..2e598de88 100755 --- a/src/ClusterManager/cluster_manager.py +++ b/src/ClusterManager/cluster_manager.py @@ -1,88 +1,51 @@ -import json -import os -import time -import argparse -import uuid -import subprocess -import sys -import datetime - import yaml -from jinja2 import Environment, FileSystemLoader, Template -import base64 - -import re -import random - -import textwrap +import subprocess32 +import os import logging import logging.config +import sys -import job_manager -import user_manager -import node_manager -import joblog_manager -import command_manager -import endpoint_manager - -from multiprocessing import Process, Manager - +logger = logging.getLogger(__name__) -def create_log(logdir='/var/log/dlworkspace'): +def create_log(logdir="/var/log/dlworkspace"): if not os.path.exists(logdir): os.system("mkdir -p " + logdir) - with open('logging.yaml') as f: - logging_config = yaml.full_load(f) - f.close() - logging_config["handlers"]["file"]["filename"] = logdir+"/clustermanager.log" - logging.config.dictConfig(logging_config) + with open("logging.yaml") as f: + logging_config = yaml.load(f) + logging_config["handlers"]["file"]["filename"] = logdir+"/clustermanager.log" + logging.config.dictConfig(logging_config) def Run(): create_log() - logging.info("Starting job manager... ") - proc_job = Process(target=job_manager.Run) - proc_job.start() - - logging.info("Starting user manager... ") - proc_user = Process(target=user_manager.Run) - proc_user.start() - - logging.info("Starting node manager... ") - proc_node = Process(target=node_manager.Run) - proc_node.start() - - logging.info("Starting joblogging manager... ") - proc_joblog = Process(target=joblog_manager.Run) - proc_joblog.start() - - logging.info("Starting command manager... ") - proc_command = Process(target=command_manager.Run) - proc_command.start() - - logging.info("Starting endpoint manager... ") - proc_endpoint = Process(target=endpoint_manager.Run) - proc_endpoint.start() - - proc_job.join() - proc_user.join() - proc_node.join() - proc_joblog.join() - proc_command.join() - proc_endpoint.join() - pass - - -if __name__ == '__main__': - - #parser = argparse.ArgumentParser( prog='cluster_manager.py', - # formatter_class=argparse.RawDescriptionHelpFormatter, - # description=textwrap.dedent('''\ - # ''') ) - #parser.add_argument("help", - # help = "Show the usage of this program" ) - - #args = parser.parse_args() - - Run() + cwd = os.path.dirname(__file__) + cmds = [ + ["python", os.path.join(cwd, "job_manager.py")], + ["python", os.path.join(cwd, "user_manager.py")], + ["python", os.path.join(cwd, "node_manager.py")], + ["python", os.path.join(cwd, "joblog_manager.py")], + ["python", os.path.join(cwd, "command_manager.py")], + ["python", os.path.join(cwd, "endpoint_manager.py")], + ] + + FNULL = open(os.devnull, "w") + + childs = [None] * len(cmds) + + while True: + for i, cmd in enumerate(cmds): + child = childs[i] + if child is None or child.poll() is not None: + if child is not None: + logger.info("%s is dead restart it", cmd) + try: + childs[i] = subprocess32.Popen(cmd, + stdin=FNULL, close_fds=True) + except Exception as e: + logger.exception("caught exception when trying to start %s, ignore", + cmd) + time.sleep(60) + +if __name__ == "__main__": + sys.exit(Run()) diff --git a/src/ClusterManager/command_manager.py b/src/ClusterManager/command_manager.py index 3caad1db3..9ad9b5956 100755 --- a/src/ClusterManager/command_manager.py +++ b/src/ClusterManager/command_manager.py @@ -20,7 +20,6 @@ from jinja2 import Environment, FileSystemLoader, Template from config import config, GetStoragePath from DataHandler import DataHandler -from node_manager import create_log from node_manager import get_cluster_status import base64 @@ -41,8 +40,17 @@ def RunCommand(command): dataHandler.Close() return True +def create_log(logdir = '/var/log/dlworkspace'): + if not os.path.exists(logdir): + os.system("mkdir -p " + logdir) + with open('logging.yaml') as f: + logging_config = yaml.full_load(f) + f.close() + logging_config["handlers"]["file"]["filename"] = logdir+"/command_manager.log" + logging.config.dictConfig(logging_config) def Run(): + create_log() while True: try: dataHandler = DataHandler() @@ -58,6 +66,4 @@ def Run(): time.sleep(1) if __name__ == '__main__': - logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', - level=logging.INFO) Run() diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index 4433e041b..68c961395 100755 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -223,8 +223,19 @@ def cleanup_endpoints(): except Exception as e: logger.exception("close data handler failed") +def create_log(logdir = '/var/log/dlworkspace'): + if not os.path.exists(logdir): + os.system("mkdir -p " + logdir) + with open('logging.yaml') as f: + logging_config = yaml.full_load(f) + f.close() + logging_config["handlers"]["file"]["filename"] = logdir+"/endpoint_manager.log" + logging.config.dictConfig(logging_config) + def Run(): + create_log() + while True: # start endpoints start_endpoints() @@ -234,8 +245,5 @@ def Run(): cleanup_endpoints() time.sleep(1) - if __name__ == '__main__': - logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', - level=logging.INFO) Run() diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index c433b0276..221852ae4 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -242,6 +242,15 @@ def check_job_status(job_id): return "Running" +def create_log(logdir = '/var/log/dlworkspace'): + if not os.path.exists(logdir): + os.system("mkdir -p " + logdir) + with open('logging.yaml') as f: + logging_config = yaml.full_load(f) + f.close() + logging_config["handlers"]["file"]["filename"] = logdir+"/jobmanager.log" + logging.config.dictConfig(logging_config) + def JobInfoSorter(elem): return elem["sortKey"] @@ -329,6 +338,7 @@ def TakeJobActions(jobs): def Run(): + create_log() while True: @@ -370,7 +380,4 @@ def Run(): if __name__ == '__main__': - logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', - level=logging.INFO) Run() - #print k8sUtils.get_pod_events("d493d41c-45ea-4e85-8ca4-01c3533cd727") diff --git a/src/ClusterManager/joblog_manager.py b/src/ClusterManager/joblog_manager.py index aacf15138..5e55e74d1 100755 --- a/src/ClusterManager/joblog_manager.py +++ b/src/ClusterManager/joblog_manager.py @@ -34,9 +34,9 @@ logger = logging.getLogger(__name__) -def create_log( logdir = '/var/log/dlworkspace' ): +def create_log(logdir = '/var/log/dlworkspace'): if not os.path.exists( logdir ): - os.system("mkdir -p " + logdir ) + os.system("mkdir -p " + logdir) with open('logging.yaml') as f: logging_config = yaml.load(f) f.close() @@ -159,6 +159,4 @@ def Run(): time.sleep(1) if __name__ == '__main__': - logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', - level=logging.INFO) Run() diff --git a/src/ClusterManager/node_manager.py b/src/ClusterManager/node_manager.py index 4ed8f81ca..36c3b5b5c 100755 --- a/src/ClusterManager/node_manager.py +++ b/src/ClusterManager/node_manager.py @@ -41,9 +41,9 @@ -def create_log( logdir = '/var/log/dlworkspace' ): - if not os.path.exists( logdir ): - os.system("mkdir -p " + logdir ) +def create_log(logdir = '/var/log/dlworkspace'): + if not os.path.exists(logdir): + os.system("mkdir -p " + logdir) with open('logging.yaml') as f: logging_config = yaml.load(f) f.close() @@ -252,6 +252,4 @@ def Run(): time.sleep(30) if __name__ == '__main__': - logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', - level=logging.INFO) Run() diff --git a/src/ClusterManager/user_manager.py b/src/ClusterManager/user_manager.py index 5bcd63140..13968f365 100755 --- a/src/ClusterManager/user_manager.py +++ b/src/ClusterManager/user_manager.py @@ -36,9 +36,9 @@ -def create_log( logdir = '/var/log/dlworkspace' ): - if not os.path.exists( logdir ): - os.system("mkdir -p " + logdir ) +def create_log(logdir = '/var/log/dlworkspace'): + if not os.path.exists(logdir): + os.system("mkdir -p " + logdir) with open('logging.yaml') as f: logging_config = yaml.load(f) f.close() From b8ed68c60c237b57127ca7a10ff849aa5b70b364 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Thu, 18 Jul 2019 04:44:40 +0000 Subject: [PATCH 418/595] add missing time import --- src/ClusterManager/cluster_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ClusterManager/cluster_manager.py b/src/ClusterManager/cluster_manager.py index 2e598de88..10aef71c8 100755 --- a/src/ClusterManager/cluster_manager.py +++ b/src/ClusterManager/cluster_manager.py @@ -4,6 +4,7 @@ import logging import logging.config import sys +import time logger = logging.getLogger(__name__) From a559c478ee71a6a583b2645a0e1b92545eec57cb Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 18 Jul 2019 04:47:35 +0000 Subject: [PATCH 419/595] reformat --- src/ClusterManager/cluster_manager.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ClusterManager/cluster_manager.py b/src/ClusterManager/cluster_manager.py index 10aef71c8..845bd1662 100755 --- a/src/ClusterManager/cluster_manager.py +++ b/src/ClusterManager/cluster_manager.py @@ -8,12 +8,13 @@ logger = logging.getLogger(__name__) + def create_log(logdir="/var/log/dlworkspace"): if not os.path.exists(logdir): os.system("mkdir -p " + logdir) with open("logging.yaml") as f: logging_config = yaml.load(f) - logging_config["handlers"]["file"]["filename"] = logdir+"/clustermanager.log" + logging_config["handlers"]["file"]["filename"] = logdir + "/clustermanager.log" logging.config.dictConfig(logging_config) @@ -22,13 +23,13 @@ def Run(): cwd = os.path.dirname(__file__) cmds = [ - ["python", os.path.join(cwd, "job_manager.py")], - ["python", os.path.join(cwd, "user_manager.py")], - ["python", os.path.join(cwd, "node_manager.py")], - ["python", os.path.join(cwd, "joblog_manager.py")], - ["python", os.path.join(cwd, "command_manager.py")], - ["python", os.path.join(cwd, "endpoint_manager.py")], - ] + ["python", os.path.join(cwd, "job_manager.py")], + ["python", os.path.join(cwd, "user_manager.py")], + ["python", os.path.join(cwd, "node_manager.py")], + ["python", os.path.join(cwd, "joblog_manager.py")], + ["python", os.path.join(cwd, "command_manager.py")], + ["python", os.path.join(cwd, "endpoint_manager.py")], + ] FNULL = open(os.devnull, "w") @@ -41,12 +42,11 @@ def Run(): if child is not None: logger.info("%s is dead restart it", cmd) try: - childs[i] = subprocess32.Popen(cmd, - stdin=FNULL, close_fds=True) + childs[i] = subprocess32.Popen(cmd, stdin=FNULL, close_fds=True) except Exception as e: - logger.exception("caught exception when trying to start %s, ignore", - cmd) + logger.exception("caught exception when trying to start %s, ignore", cmd) time.sleep(60) + if __name__ == "__main__": sys.exit(Run()) From 3d240c1d9374f1e3c429f328a1ef7ae437d1952e Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 18 Jul 2019 05:49:49 +0000 Subject: [PATCH 420/595] fix missing imports --- src/ClusterManager/endpoint_manager.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index 68c961395..727e7996a 100755 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -1,6 +1,4 @@ -from config import config, GetStoragePath, GetWorkPath -import k8sUtils -from DataHandler import DataHandler + import json import os import time @@ -12,8 +10,14 @@ import random import re import logging +import yaml +import logging.config + sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../utils")) +import k8sUtils +from config import config, GetStoragePath, GetWorkPath +from DataHandler import DataHandler logger = logging.getLogger(__name__) From 649986f0c526dbea626e9970b932a7ac04531487 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 18 Jul 2019 05:56:55 +0000 Subject: [PATCH 421/595] add logging --- src/ClusterManager/job_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 221852ae4..81ffe63a6 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -190,6 +190,7 @@ def UpdateJobStatus(job): elif result == "Unknown": if job["jobId"] not in UnusualJobs: + logging.warning("!!! Job status ---Unknown---, job: {}".format(job["jobId"])) UnusualJobs[job["jobId"]] = datetime.datetime.now() elif (datetime.datetime.now() - UnusualJobs[job["jobId"]]).seconds > 300: del UnusualJobs[job["jobId"]] From 259ff92f303b9e195722f6d4967741a7eafd80b4 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Thu, 18 Jul 2019 06:40:11 +0000 Subject: [PATCH 422/595] set disable_existing_loggers to False --- src/ClusterManager/logging.yaml | 3 ++- src/RestAPI/logging.yaml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ClusterManager/logging.yaml b/src/ClusterManager/logging.yaml index b276c6d8d..37e98eee8 100755 --- a/src/ClusterManager/logging.yaml +++ b/src/ClusterManager/logging.yaml @@ -1,4 +1,5 @@ version: 1 +disable_existing_loggers: False formatters: simple: format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' @@ -23,4 +24,4 @@ loggers: propagate: no root: level: DEBUG - handlers: ['console','file'] \ No newline at end of file + handlers: ['console','file'] diff --git a/src/RestAPI/logging.yaml b/src/RestAPI/logging.yaml index d42108867..b104f120e 100755 --- a/src/RestAPI/logging.yaml +++ b/src/RestAPI/logging.yaml @@ -1,4 +1,5 @@ version: 1 +disable_existing_loggers: False formatters: simple: format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' From fb161d0d889746508fdf9f0bbe3e898918353750 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 18 Jul 2019 07:38:01 +0000 Subject: [PATCH 423/595] output file:lineno in log --- src/ClusterManager/logging.yaml | 54 ++++++++++++++++----------------- src/RestAPI/logging.yaml | 54 ++++++++++++++++----------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/ClusterManager/logging.yaml b/src/ClusterManager/logging.yaml index 37e98eee8..a486bc5aa 100755 --- a/src/ClusterManager/logging.yaml +++ b/src/ClusterManager/logging.yaml @@ -1,27 +1,27 @@ -version: 1 -disable_existing_loggers: False -formatters: - simple: - format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' -handlers: - console: - class: logging.StreamHandler - level: DEBUG - formatter: simple - stream: ext://sys.stdout - file: - class : logging.handlers.RotatingFileHandler - formatter: simple - filename: /var/log/dlworkspace/clustermanager.log - # roll over at 10MB - maxBytes: 10240000 - # At most 10 logging files - backupCount: 10 -loggers: - basic: - level: DEBUG - handlers: ['console','file'] - propagate: no -root: - level: DEBUG - handlers: ['console','file'] +version: 1 +disable_existing_loggers: False +formatters: + simple: + format: '%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s' +handlers: + console: + class: logging.StreamHandler + level: INFO + formatter: simple + stream: ext://sys.stdout + file: + class : logging.handlers.RotatingFileHandler + formatter: simple + filename: /var/log/dlworkspace/clustermanager.log + # roll over at 10MB + maxBytes: 10240000 + # At most 10 logging files + backupCount: 10 +loggers: + basic: + level: INFO + handlers: ['console','file'] + propagate: no +root: + level: INFO + handlers: ['console','file'] diff --git a/src/RestAPI/logging.yaml b/src/RestAPI/logging.yaml index b104f120e..fea884c5c 100755 --- a/src/RestAPI/logging.yaml +++ b/src/RestAPI/logging.yaml @@ -1,27 +1,27 @@ -version: 1 -disable_existing_loggers: False -formatters: - simple: - format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' -handlers: - console: - class: logging.StreamHandler - level: DEBUG - formatter: simple - stream: ext://sys.stdout - file: - class : logging.handlers.RotatingFileHandler - formatter: simple - filename: /var/log/apache2/restfulapi.log - # roll over at 10MB - maxBytes: 10240000 - # At most 10 logging files - backupCount: 10 -loggers: - basic: - level: DEBUG - handlers: ['console', 'file'] - propagate: no -root: - level: DEBUG - handlers: ['console', 'file'] +version: 1 +disable_existing_loggers: False +formatters: + simple: + format: '%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s' +handlers: + console: + class: logging.StreamHandler + level: INFO + formatter: simple + stream: ext://sys.stdout + file: + class : logging.handlers.RotatingFileHandler + formatter: simple + filename: /var/log/apache2/restfulapi.log + # roll over at 10MB + maxBytes: 10240000 + # At most 10 logging files + backupCount: 10 +loggers: + basic: + level: INFO + handlers: ['console', 'file'] + propagate: no +root: + level: INFO + handlers: ['console', 'file'] From b11cc96287709f3c395135774b80615b6dcc9034 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 18 Jul 2019 08:16:20 +0000 Subject: [PATCH 424/595] catch exception --- src/ClusterManager/job_deployer.py | 56 ++++++++++++++++-------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index 5d2e22e12..df93c0340 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -125,29 +125,33 @@ def delete_job(self, job_id): def pod_exec(self, pod_name, exec_command, timeout=60): """work as the command (with timeout): kubectl exec 'pod_name' 'exec_command'""" - logging.info("Exec on pod {}: {}".format(pod_name, exec_command)) - client = stream( - self.v1.connect_get_namespaced_pod_exec, - name=pod_name, - namespace=self.namespace, - command=exec_command, - stderr=True, - stdin=False, - stdout=True, - tty=False, - _preload_content=False, - ) - client.run_forever(timeout=timeout) - - err = yaml.full_load(client.read_channel(ERROR_CHANNEL)) - if err is None: - return [-1, "Timeout"] - - if err["status"] == "Success": - status_code = 0 - else: - logging.warning("Exec on pod {} failed. cmd: {}, err: {}.".format(pod_name, exec_command, err)) - status_code = int(err["details"]["causes"][0]["message"]) - output = client.read_all() - logging.info("Exec on pod {}, status: {}, cmd: {}, output: {}".format(pod_name, status_code, exec_command, output)) - return [status_code, output] + try: + logging.info("Exec on pod {}: {}".format(pod_name, exec_command)) + client = stream( + self.v1.connect_get_namespaced_pod_exec, + name=pod_name, + namespace=self.namespace, + command=exec_command, + stderr=True, + stdin=False, + stdout=True, + tty=False, + _preload_content=False, + ) + client.run_forever(timeout=timeout) + + err = yaml.full_load(client.read_channel(ERROR_CHANNEL)) + if err is None: + return [-1, "Timeout"] + + if err["status"] == "Success": + status_code = 0 + else: + logging.debug("Exec on pod {} failed. cmd: {}, err: {}.".format(pod_name, exec_command, err)) + status_code = int(err["details"]["causes"][0]["message"]) + output = client.read_all() + logging.info("Exec on pod {}, status: {}, cmd: {}, output: {}".format(pod_name, status_code, exec_command, output)) + return [status_code, output] + except ApiException as err: + logging.error("Exec on pod {} error. cmd: {}, err: {}.".format(pod_name, exec_command, err), exc_info=True) + return [-1, err.message] From 765cc9e92901206f51a73336f26ca2116d5f16cc Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 18 Jul 2019 08:17:09 +0000 Subject: [PATCH 425/595] fix pod "Unknown" status --- src/ClusterManager/job_role.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ClusterManager/job_role.py b/src/ClusterManager/job_role.py index 7ceb00ecf..d54354325 100644 --- a/src/ClusterManager/job_role.py +++ b/src/ClusterManager/job_role.py @@ -40,8 +40,17 @@ def status(self): pod = pods[0] phase = pod.status.phase - # !!! Pod is runing, doesn't mean "Role" is ready and running. + # if container was restarted, return Error + if pod.status.container_statuses[0].restart_count > 0: + return "Error" + + # !!! Pod is running, doesn't mean "Role" is ready and running. if(phase == "Running"): + # Found that phase won't turn into "Unkonwn" even when we get 'unknown' from kubectl + if pod.status.reason == "NodeLost": + return "Unknown" + + # Check if the user command had been ran. if not self.isRoleReady(): return "Pending" From 3459a895d111eabf97a158c8805fbcae1f5b4042 Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Thu, 18 Jul 2019 11:50:18 -0700 Subject: [PATCH 426/595] Redirect to Wiki page for unauthorized login user (#423) --- .../WebPortal/Controllers/HomeController.cs | 1 + .../dotnet/WebPortal/Views/Home/Index.cshtml | 56 +++++++++---------- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs b/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs index 006997236..466a74e4e 100644 --- a/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs @@ -693,6 +693,7 @@ public static async Task GetTeamClusters(HttpContext HttpContext, stri #region ASP Controllers public async Task Index() { + ViewData["AddGroupLink"] = ConfigurationParser.GetConfiguration("AddGroupLink"); if (User.Identity.IsAuthenticated && !HttpContext.Session.Keys.Contains("uid")) { string userObjectID = null; diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml index 88911f8bc..a59b2febc 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml @@ -8,9 +8,9 @@ @if (ViewData["isAuthorized"] != null && !(bool)ViewData["isAuthorized"]) { -} + } @if (ViewData["isAuthorized"] != null && (bool)ViewData["isAuthorized"]) @@ -291,14 +286,13 @@ else background-position:20px 30px; } -#alertBox h1 { - margin:0; - font:bold 0.9em verdana,arial; - background-color:#3073BB; - color:#FFF; - border-bottom:1px solid #000; - padding:2px 0 2px 5px; -} + #alertBox h1 { + margin: 0; + font: bold 0.9em verdana,arial; + background-color: #357EBD; + color: #FFF; + padding: 2px 0 3px 5px; + } #alertBox p { font: 1.1em verdana,arial; @@ -310,7 +304,7 @@ else #alertBox #closeBtn { display:inline-block; position:relative; - margin:15px 13%; + margin:15px 38%; padding:7px; border:0 none; width:24%; From 4c0774df7b8f5e8e4ea14795067a2addd0eae1cd Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Thu, 18 Jul 2019 17:15:59 -0700 Subject: [PATCH 427/595] Fix the job template dropdown issue (#424) --- src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 6225c7fb7..604360d79 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -713,7 +713,7 @@ Regular Job - Distributed Job + Distributed Job From d421b66d1758aaf59848ff8f8c9be490968a570c Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 19 Jul 2019 08:09:32 +0000 Subject: [PATCH 428/595] no longer using "hostport", remove useness code --- src/ClusterManager/job_manager.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 81ffe63a6..0d4cf91b3 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -204,10 +204,6 @@ def UpdateJobStatus(job): else: logging.warning("Job %s fails in Kubernetes, delete and re-submit the job. Retries %d", job["jobId"], retries) SubmitJob(job) - elif result.strip() == "PendingHostPort": - logging.warning("Cannot find host ports for job :%s, re-launch the job with different host ports ", job["jobId"]) - - SubmitJob(job) if result.strip() != "Unknown" and job["jobId"] in UnusualJobs: del UnusualJobs[job["jobId"]] From 54f74923c16ed19c5ff78200c23b226988083612 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Sat, 20 Jul 2019 05:21:44 +0000 Subject: [PATCH 429/595] job state transition graph --- src/ClusterManager/job_status.pdf | Bin 0 -> 119750 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/ClusterManager/job_status.pdf diff --git a/src/ClusterManager/job_status.pdf b/src/ClusterManager/job_status.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c9756f120dd872306ed8d20fd73e24c1b588bb84 GIT binary patch literal 119750 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

g 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 literal 0 HcmV?d00001 From eb2981fb4d247dd9c32e74aa5edcfaf33bcae0cc Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Sat, 20 Jul 2019 06:54:56 +0000 Subject: [PATCH 430/595] resubmit "Unknown" job --- src/ClusterManager/job_manager.py | 34 +++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 0d4cf91b3..6b524f1fa 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -42,7 +42,21 @@ from job_role import JobRole +def all_pods_not_existing(job_id): + job_deployer = JobDeployer() + job_roles = JobRole.get_job_roles(job_id) + statuses = [job_role.status() for job_role in job_roles] + logging.info("Job: {}, status: {}".format(job_id, statuses)) + return all([status == "NotFound" for status in statuses]) + + def SubmitJob(job): + # check if existing any pod with label: run=job_id + assert("jobId" in job) + if not all_pods_not_existing(job["jobId"]): + logging.warning("Waiting until previously pods are cleaned up! Job {}".format(job["jobId"])) + return + ret = {} dataHandler = DataHandler() @@ -203,7 +217,8 @@ def UpdateJobStatus(job): k8sUtils.kubectl_delete(jobDescriptionPath) else: logging.warning("Job %s fails in Kubernetes, delete and re-submit the job. Retries %d", job["jobId"], retries) - SubmitJob(job) + killJob(job, "queued") + # SubmitJob(job) if result.strip() != "Unknown" and job["jobId"] in UnusualJobs: del UnusualJobs[job["jobId"]] @@ -324,12 +339,15 @@ def TakeJobActions(jobs): logging.info("TakeJobActions : global resources : %s" % (globalResInfo.CategoryToCountMap)) for sji in jobsInfo: - if sji["job"]["jobStatus"] == "queued" and sji["allowed"] == True: - SubmitJob(sji["job"]) - logging.info("TakeJobActions : submitting job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["jobParams"]["jobId"], sji["sortKey"])) - elif sji["jobParams"]["preemptionAllowed"] and (sji["job"]["jobStatus"] == "scheduling" or sji["job"]["jobStatus"] == "running") and sji["allowed"] == False: - KillJob(sji["job"], "queued") - logging.info("TakeJobActions : pre-empting job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["jobParams"]["jobId"], sji["sortKey"])) + try: + if sji["job"]["jobStatus"] == "queued" and sji["allowed"] == True: + SubmitJob(sji["job"]) + logging.info("TakeJobActions : submitting job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["jobParams"]["jobId"], sji["sortKey"])) + elif sji["jobParams"]["preemptionAllowed"] and (sji["job"]["jobStatus"] == "scheduling" or sji["job"]["jobStatus"] == "running") and sji["allowed"] == False: + KillJob(sji["job"], "queued") + logging.info("TakeJobActions : pre-empting job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["jobParams"]["jobId"], sji["sortKey"])) + except Exception as e: + logging.error("Process job failed {}".format(sji["job"]), exc_info=True) logging.info("TakeJobActions : job desired actions taken") @@ -365,7 +383,7 @@ def Run(): elif job["jobStatus"] == "unapproved": ApproveJob(job) except Exception as e: - logging.info(e) + logging.info(e, exc_info=True) except Exception as e: logging.exception("process pending job failed") finally: From 8ccdbeb1223608d608e66ef1952db1146f851fe4 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Sat, 20 Jul 2019 12:32:24 +0000 Subject: [PATCH 431/595] refactoring --- src/ClusterManager/job_manager.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 6b524f1fa..37e419dfb 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -132,31 +132,31 @@ def SubmitJob(job): return ret -def KillJob(job, desiredState="killed"): +def KillJob(job_id, desiredState="killed"): dataHandler = DataHandler() - result, detail = k8sUtils.GetJobStatus(job["jobId"]) - dataHandler.UpdateJobTextField(job["jobId"], "jobStatusDetail", base64.b64encode(json.dumps(detail))) - logging.info("Killing job %s, with status %s, %s" % (job["jobId"], result, detail)) + result, detail = k8sUtils.GetJobStatus(job_id) + dataHandler.UpdateJobTextField(job_id, "jobStatusDetail", base64.b64encode(json.dumps(detail))) + logging.info("Killing job %s, with status %s, %s" % (job_id, result, detail)) job_deployer = JobDeployer() - errors = job_deployer.delete_job(job["jobId"]) + errors = job_deployer.delete_job(job_id) if len(errors) == 0: - dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", desiredState) - dataHandler.UpdateJobTextField(job["jobId"], "lastUpdated", datetime.datetime.now().isoformat()) + dataHandler.UpdateJobTextField(job_id, "jobStatus", desiredState) + dataHandler.UpdateJobTextField(job_id, "lastUpdated", datetime.datetime.now().isoformat()) dataHandler.Close() return True else: - dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "error") - dataHandler.UpdateJobTextField(job["jobId"], "lastUpdated", datetime.datetime.now().isoformat()) + dataHandler.UpdateJobTextField(job_id, "jobStatus", "error") + dataHandler.UpdateJobTextField(job_id, "lastUpdated", datetime.datetime.now().isoformat()) dataHandler.Close() logging.error("Kill job failed with errors: {}".format(errors)) return False -def ApproveJob(job): +def ApproveJob(job_id): dataHandler = DataHandler() - dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "queued") + dataHandler.UpdateJobTextField(job_id, "jobStatus", "queued") dataHandler.Close() return True @@ -344,7 +344,7 @@ def TakeJobActions(jobs): SubmitJob(sji["job"]) logging.info("TakeJobActions : submitting job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["jobParams"]["jobId"], sji["sortKey"])) elif sji["jobParams"]["preemptionAllowed"] and (sji["job"]["jobStatus"] == "scheduling" or sji["job"]["jobStatus"] == "running") and sji["allowed"] == False: - KillJob(sji["job"], "queued") + KillJob(sji["job"]["jobId"], "queued") logging.info("TakeJobActions : pre-empting job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["jobParams"]["jobId"], sji["sortKey"])) except Exception as e: logging.error("Process job failed {}".format(sji["job"]), exc_info=True) @@ -375,13 +375,13 @@ def Run(): try: logging.info("Processing job: %s, status: %s" % (job["jobId"], job["jobStatus"])) if job["jobStatus"] == "killing": - KillJob(job, "killed") + KillJob(job["jobId"], "killed") elif job["jobStatus"] == "pausing": - KillJob(job, "paused") + KillJob(job["jobId"], "paused") elif job["jobStatus"] == "scheduling" or job["jobStatus"] == "running": UpdateJobStatus(job) elif job["jobStatus"] == "unapproved": - ApproveJob(job) + ApproveJob(job["jobId"]) except Exception as e: logging.info(e, exc_info=True) except Exception as e: From 02a6d43ee00d724d0efd6d61aa95bf3a4ac7cdd9 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Sat, 20 Jul 2019 13:03:38 +0000 Subject: [PATCH 432/595] no need anymore, all job pod "restartPolicy: Never" --- src/ClusterManager/job_role.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ClusterManager/job_role.py b/src/ClusterManager/job_role.py index d54354325..d9299a3b3 100644 --- a/src/ClusterManager/job_role.py +++ b/src/ClusterManager/job_role.py @@ -40,10 +40,6 @@ def status(self): pod = pods[0] phase = pod.status.phase - # if container was restarted, return Error - if pod.status.container_statuses[0].restart_count > 0: - return "Error" - # !!! Pod is running, doesn't mean "Role" is ready and running. if(phase == "Running"): # Found that phase won't turn into "Unkonwn" even when we get 'unknown' from kubectl From 0e8cf0a292269e61c3750bef11853cf7568b946f Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Sat, 20 Jul 2019 13:04:02 +0000 Subject: [PATCH 433/595] fix typo --- src/ClusterManager/job_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 37e419dfb..6f67851ec 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -217,7 +217,7 @@ def UpdateJobStatus(job): k8sUtils.kubectl_delete(jobDescriptionPath) else: logging.warning("Job %s fails in Kubernetes, delete and re-submit the job. Retries %d", job["jobId"], retries) - killJob(job, "queued") + KillJob(job["jobId"], "queued") # SubmitJob(job) if result.strip() != "Unknown" and job["jobId"] in UnusualJobs: From e9d1389f32111541d96304d28b2116fc05c232dc Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Sat, 20 Jul 2019 13:40:17 +0000 Subject: [PATCH 434/595] two potential problems --- src/ClusterManager/job_manager.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 6f67851ec..ff56da11e 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -206,6 +206,10 @@ def UpdateJobStatus(job): if job["jobId"] not in UnusualJobs: logging.warning("!!! Job status ---Unknown---, job: {}".format(job["jobId"])) UnusualJobs[job["jobId"]] = datetime.datetime.now() + # TODO + # 1) May need to reduce the timeout. + # It takes minutes before pod turns into "Unkonw", we may don't need to wait here. + # 2) If node resume before we resubmit the job, the job will end in status 'failed'. elif (datetime.datetime.now() - UnusualJobs[job["jobId"]]).seconds > 300: del UnusualJobs[job["jobId"]] retries = dataHandler.AddandGetJobRetries(job["jobId"]) From 93b75bac4d61ce8444e1c172be253f36540c6d1a Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 22 Jul 2019 05:54:10 +0000 Subject: [PATCH 435/595] profile data handler in jobmanager --- src/ClusterManager/cluster_manager.py | 52 +++++- src/ClusterManager/command_manager.py | 7 + src/ClusterManager/endpoint_manager.py | 7 + src/ClusterManager/job_manager.py | 9 +- src/ClusterManager/joblog_manager.py | 7 + src/ClusterManager/node_manager.py | 6 + src/ClusterManager/requirements.txt | 2 + src/ClusterManager/user_manager.py | 6 + src/utils/MySQLDataHandler.py | 191 ++++++++------------- src/utils/SQLDataHandler.py | 229 +++++++------------------ 10 files changed, 220 insertions(+), 296 deletions(-) diff --git a/src/ClusterManager/cluster_manager.py b/src/ClusterManager/cluster_manager.py index 845bd1662..fa63f8f09 100755 --- a/src/ClusterManager/cluster_manager.py +++ b/src/ClusterManager/cluster_manager.py @@ -5,10 +5,37 @@ import logging.config import sys import time +import argparse +import threading + +from prometheus_client.twisted import MetricsResource + +from twisted.web.server import Site +from twisted.web.resource import Resource +from twisted.internet import reactor logger = logging.getLogger(__name__) +class HealthResource(Resource): + def render_GET(self, request): + request.setHeader("Content-Type", "text/html; charset=utf-8") + return "Ok".encode("utf-8") + +def exporter_thread(port): + root = Resource() + root.putChild(b"metrics", MetricsResource()) + root.putChild(b"healthz", HealthResource()) + factory = Site(root) + reactor.listenTCP(port, factory) + reactor.run() + +def setup_exporter_thread(port): + t = threading.Thread(target=exporter_thread, args=(port,), + name="exporter") + t.start() + return t + def create_log(logdir="/var/log/dlworkspace"): if not os.path.exists(logdir): os.system("mkdir -p " + logdir) @@ -18,17 +45,17 @@ def create_log(logdir="/var/log/dlworkspace"): logging.config.dictConfig(logging_config) -def Run(): +def Run(args): create_log() cwd = os.path.dirname(__file__) cmds = [ - ["python", os.path.join(cwd, "job_manager.py")], - ["python", os.path.join(cwd, "user_manager.py")], - ["python", os.path.join(cwd, "node_manager.py")], - ["python", os.path.join(cwd, "joblog_manager.py")], - ["python", os.path.join(cwd, "command_manager.py")], - ["python", os.path.join(cwd, "endpoint_manager.py")], + ["python", os.path.join(cwd, "job_manager.py"), args.j], + ["python", os.path.join(cwd, "user_manager.py"), args.u], + ["python", os.path.join(cwd, "node_manager.py"), args.n], + ["python", os.path.join(cwd, "joblog_manager.py"), args.l], + ["python", os.path.join(cwd, "command_manager.py"), args.c], + ["python", os.path.join(cwd, "endpoint_manager.py"), args.e], ] FNULL = open(os.devnull, "w") @@ -49,4 +76,13 @@ def Run(): if __name__ == "__main__": - sys.exit(Run()) + parser = argparse.ArgumentParser() + parser.add_argument("-j", help="port of job_manager", type=int, default=9200) + parser.add_argument("-u", help="port of user_manager", type=int, default=9201) + parser.add_argument("-n", help="port of node_manager", type=int, default=9202) + parser.add_argument("-l", help="port of joblog_manager", type=int, default=9203) + parser.add_argument("-c", help="port of command_manager", type=int, default=9204) + parser.add_argument("-e", help="port of endpoint_manager", type=int, default=9205) + args = parser.parse_args() + + sys.exit(Run(args)) diff --git a/src/ClusterManager/command_manager.py b/src/ClusterManager/command_manager.py index 9ad9b5956..fd117835b 100755 --- a/src/ClusterManager/command_manager.py +++ b/src/ClusterManager/command_manager.py @@ -31,6 +31,8 @@ import logging +from cluster_manager import setup_exporter_thread + logger = logging.getLogger(__name__) def RunCommand(command): @@ -66,4 +68,9 @@ def Run(): time.sleep(1) if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("--port", "-p", help="port of exporter", type=int, default=9204) + args = parser.parse_args() + setup_exporter_thread(args.port) + Run() diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index 727e7996a..d95931969 100755 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -13,6 +13,8 @@ import yaml import logging.config +import argparse +from cluster_manager import setup_exporter_thread sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../utils")) import k8sUtils @@ -250,4 +252,9 @@ def Run(): time.sleep(1) if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("--port", "-p", help="port of exporter", type=int, default=9205) + args = parser.parse_args() + setup_exporter_thread(args.port) + Run() diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index ff56da11e..e2be00315 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -9,7 +9,6 @@ import copy import traceback - sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),"../storage")) sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),"../utils")) @@ -41,6 +40,8 @@ from job_deployer import JobDeployer from job_role import JobRole +from cluster_manager import setup_exporter_thread + def all_pods_not_existing(job_id): job_deployer = JobDeployer() @@ -360,7 +361,6 @@ def Run(): create_log() while True: - try: config["racks"] = k8sUtils.get_node_labels("rack") config["skus"] = k8sUtils.get_node_labels("sku") @@ -399,4 +399,9 @@ def Run(): if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("--port", "-p", help="port of exporter", type=int, default=9200) + args = parser.parse_args() + setup_exporter_thread(args.port) + Run() diff --git a/src/ClusterManager/joblog_manager.py b/src/ClusterManager/joblog_manager.py index 5e55e74d1..bdc383e62 100755 --- a/src/ClusterManager/joblog_manager.py +++ b/src/ClusterManager/joblog_manager.py @@ -32,6 +32,8 @@ from config import config, GetStoragePath from DataHandler import DataHandler +from cluster_manager import setup_exporter_thread + logger = logging.getLogger(__name__) def create_log(logdir = '/var/log/dlworkspace'): @@ -159,4 +161,9 @@ def Run(): time.sleep(1) if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("--port", "-p", help="port of exporter", type=int, default=9203) + args = parser.parse_args() + setup_exporter_thread(args.port) + Run() diff --git a/src/ClusterManager/node_manager.py b/src/ClusterManager/node_manager.py index 36c3b5b5c..0849a6038 100755 --- a/src/ClusterManager/node_manager.py +++ b/src/ClusterManager/node_manager.py @@ -39,6 +39,7 @@ from config import config from DataHandler import DataHandler +from cluster_manager import setup_exporter_thread def create_log(logdir = '/var/log/dlworkspace'): @@ -252,4 +253,9 @@ def Run(): time.sleep(30) if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("--port", "-p", help="port of exporter", type=int, default=9202) + args = parser.parse_args() + setup_exporter_thread(args.port) + Run() diff --git a/src/ClusterManager/requirements.txt b/src/ClusterManager/requirements.txt index 111149103..f1363b2db 100644 --- a/src/ClusterManager/requirements.txt +++ b/src/ClusterManager/requirements.txt @@ -1,3 +1,5 @@ marshmallow==2.19.5 kubernetes==9.0.0 PyYAML>=5.1.1 +prometheus-client==0.7.1 +twisted==19.2.1 diff --git a/src/ClusterManager/user_manager.py b/src/ClusterManager/user_manager.py index 13968f365..fc89f0b18 100755 --- a/src/ClusterManager/user_manager.py +++ b/src/ClusterManager/user_manager.py @@ -34,6 +34,7 @@ from config import config from DataHandler import DataHandler +from cluster_manager import setup_exporter_thread def create_log(logdir = '/var/log/dlworkspace'): @@ -91,4 +92,9 @@ def Run(): if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("--port", "-p", help="port of exporter", type=int, default=9201) + args = parser.parse_args() + setup_exporter_thread(args.port) + Run() diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index 380138c79..f22c6a141 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -1,9 +1,9 @@ -# from config import config import mysql.connector import json import base64 import os import logging +import functools import timeit @@ -12,12 +12,35 @@ from config import config from config import global_vars +from prometheus_client import Histogram + logger = logging.getLogger(__name__) +data_handler_fn_histogram = Histogram("datahandler_fn_latency_seconds", + "latency for executing data handler function (seconds)", + buckets=(.05, .075, .1, .25, .5, .75, 1.0, 2.5, 5.0, + 7.5, 10.0, 12.5, 15.0, 17.5, 20.0, float("inf")), + labelnames=("fn_name",)) + +db_connect_histogram = Histogram("db_connect_latency_seconds", + "latency for connecting to db (seconds)", + buckets=(.05, .075, .1, .25, .5, .75, 1.0, 2.5, 5.0, 7.5, float("inf"))) + -class DataHandler: +def record(fn): + @functools.wraps(fn) + def wrapped(*args, **kwargs): + start = timeit.default_timer() + try: + return fn(*args, **kwargs) + finally: + elapsed += timeit.default_timer() - start + logger.info("DataHandler: %s, time elapsed %.2fs", fn.__name__, elapsed) + data_handler_fn_histogram.labels(fn.__name__).observe(elapsed) + return wrapped +class DataHandler(object): def __init__(self): start_time = timeit.default_timer() self.database = "DLWSCluster-%s" % config["clusterId"] @@ -32,9 +55,9 @@ def __init__(self): username = config["mysql"]["username"] password = config["mysql"]["password"] - - self.conn = mysql.connector.connect(user=username, password=password, - host=server, database=self.database) + with db_connect_histogram.time() + self.conn = mysql.connector.connect(user=username, password=password, + host=server, database=self.database) self.CreateDatabase() self.CreateTable() @@ -43,8 +66,6 @@ def __init__(self): elapsed = timeit.default_timer() - start_time logger.info("DataHandler initialization, time elapsed %f s", elapsed) - - def CreateDatabase(self): if "initSQLDB" not in global_vars or not global_vars["initSQLDB"]: logger.info("===========init SQL database===============") @@ -218,41 +239,34 @@ def CreateTable(self): self.conn.commit() cursor.close() - + @record def AddStorage(self, vcName, url, storageType, metadata, defaultMountPath): try: - start_time = timeit.default_timer() sql = "INSERT INTO `"+self.storagetablename+"` (storageType, url, metadata, vcName, defaultMountPath) VALUES (%s,%s,%s,%s,%s)" cursor = self.conn.cursor() cursor.execute(sql, (storageType, url, metadata, vcName, defaultMountPath)) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: AddStorage to DB: url : %s, vcName: %s , time elapsed %f s", url, vcName, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False - + @record def DeleteStorage(self, vcName, url): try: - start_time = timeit.default_timer() sql = "DELETE FROM `%s` WHERE url = '%s' and vcName = '%s'" % (self.storagetablename, url, vcName) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: DeleteStorage: url:%s, vcName:%s, time elapsed %f s", url, vcName, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False - + @record def ListStorages(self, vcName): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT `storageType`,`url`,`metadata`,`vcName`,`defaultMountPath` FROM `%s` WHERE vcName = '%s' " % (self.storagetablename, vcName) ret = [] @@ -271,45 +285,39 @@ def ListStorages(self, vcName): pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: ListStorages time elapsed %f s", elapsed) return ret + @record def UpdateStorage(self, vcName, url, storageType, metadata, defaultMountPath): try: - start_time = timeit.default_timer() sql = """update `%s` set storageType = '%s', metadata = '%s', defaultMountPath = '%s' where vcName = '%s' and url = '%s' """ % (self.storagetablename, storageType, metadata, defaultMountPath, vcName, url) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: UpdateStorage: vcName: %s, url: %s, time elapsed %f s", vcName, url, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def AddVC(self, vcName, quota, metadata): try: - start_time = timeit.default_timer() sql = "INSERT INTO `"+self.vctablename+"` (vcName, quota, metadata) VALUES (%s,%s,%s)" cursor = self.conn.cursor() cursor.execute(sql, (vcName, quota, metadata)) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: AddVC to DB: vcName: %s , time elapsed %f s", vcName, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def ListVCs(self): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT `vcName`,`quota`,`metadata` FROM `%s`" % (self.vctablename) ret = [] @@ -326,45 +334,39 @@ def ListVCs(self): pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: ListVCs time elapsed %f s", elapsed) return ret + @record def DeleteVC(self, vcName): try: - start_time = timeit.default_timer() sql = "DELETE FROM `%s` WHERE vcName = '%s'" % (self.vctablename, vcName) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: DeleteVC: vcName: %s , time elapsed %f s", vcName, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def UpdateVC(self, vcName, quota, metadata): try: - start_time = timeit.default_timer() sql = """update `%s` set quota = '%s', metadata = '%s' where vcName = '%s' """ % (self.vctablename, quota, metadata, vcName) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: UpdateVC: vcName: %s , time elapsed %f s", vcName, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def GetIdentityInfo(self, identityName): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT `identityName`,`uid`,`gid`,`groups` FROM `%s` where `identityName` = '%s'" % (self.identitytablename, identityName) ret = [] @@ -379,17 +381,14 @@ def GetIdentityInfo(self, identityName): ret.append(record) except Exception as e: logger.error('GetIdentityInfo Exception: %s', str(e)) - pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: GetIdentityInfo time elapsed %f s", elapsed) return ret + @record def UpdateIdentityInfo(self, identityName, uid, gid, groups): try: - start_time = timeit.default_timer() cursor = self.conn.cursor() if (isinstance(groups, list)): @@ -404,16 +403,14 @@ def UpdateIdentityInfo(self, identityName, uid, gid, groups): self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: UpdateIdentityInfo %s to database , time elapsed %f s", identityName, elapsed) return True except Exception as e: logger.error('UpdateIdentityInfo Exception: %s', str(e)) return False + @record def GetAceCount(self, identityName, resource): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT count(ALL id) as c FROM `%s` where `identityName` = '%s' and `resource` = '%s'" % (self.acltablename,identityName, resource) cursor.execute(query) @@ -422,14 +419,12 @@ def GetAceCount(self, identityName, resource): ret = c[0] self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: GetAceCount time elapsed %f s", elapsed) return ret + @record def UpdateAce(self, identityName, identityId, resource, permissions, isDeny): try: - start_time = timeit.default_timer() cursor = self.conn.cursor() existingAceCount = self.GetAceCount(identityName, resource) logger.info(existingAceCount) @@ -443,34 +438,30 @@ def UpdateAce(self, identityName, identityId, resource, permissions, isDeny): self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: UpdateAce %s - %s to database , time elapsed %f s", identityName, resource, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def UpdateAclIdentityId(self, identityName, identityId): try: - start_time = timeit.default_timer() cursor = self.conn.cursor() sql = """update `%s` set identityId = '%s' where `identityName` = '%s' """ % (self.acltablename, identityId, identityName) cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: UpdateAclIdentityId %s - %s to database , time elapsed %f s", identityName, identityId, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def DeleteResourceAcl(self, resource): try: - start_time = timeit.default_timer() cursor = self.conn.cursor() sql = "DELETE FROM `%s` WHERE `resource` = '%s'" % (self.acltablename, resource) @@ -478,17 +469,15 @@ def DeleteResourceAcl(self, resource): self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: DeleteResourceAcl %s, time elapsed %f s", resource, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def DeleteAce(self, identityName, resource): try: - start_time = timeit.default_timer() cursor = self.conn.cursor() sql = "DELETE FROM `%s` WHERE `identityName` = '%s' and `resource` = '%s'" % (self.acltablename, identityName, resource) @@ -496,16 +485,14 @@ def DeleteAce(self, identityName, resource): self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: DeleteAce %s : %s time elapsed %f s", resource, identityName, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def GetAcl(self): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT `identityName`,`identityId`,`resource`,`permissions`,`isDeny` FROM `%s`" % (self.acltablename) ret = [] @@ -524,13 +511,11 @@ def GetAcl(self): pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: GetAcl time elapsed %f s", elapsed) return ret + @record def GetResourceAcl(self, resource): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT `identityName`,`identityId`,`resource`,`permissions`,`isDeny` FROM `%s` where `resource` = '%s'" % (self.acltablename, resource) ret = [] @@ -548,30 +533,26 @@ def GetResourceAcl(self, resource): logger.error('Exception: %s', str(e)) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: GetResourceAcl time elapsed %f s", elapsed) return ret + @record def AddJob(self, jobParams): try: - start_time = timeit.default_timer() sql = "INSERT INTO `"+self.jobtablename+"` (jobId, familyToken, isParent, jobName, userName, vcName, jobType,jobParams ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)" cursor = self.conn.cursor() jobParam = base64.b64encode(json.dumps(jobParams)) cursor.execute(sql, (jobParams["jobId"], jobParams["familyToken"], jobParams["isParent"], jobParams["jobName"], jobParams["userName"], jobParams["vcName"], jobParams["jobType"],jobParam)) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: added job %s to database, time elapsed %f s", jobParams["jobId"], elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def GetJobList(self, userName, vcName, num = None, status = None, op = ("=","or")): - start_time = timeit.default_timer() ret = [] cursor = self.conn.cursor() try: @@ -591,13 +572,12 @@ def GetJobList(self, userName, vcName, num = None, status = None, op = ("=","or" if num is not None: query += " limit %s " % str(num) - start_time1 = timeit.default_timer() cursor.execute(query) - elapsed1 = timeit.default_timer() - start_time1 - start_time2 = timeit.default_timer() + + fetch_start_time = timeit.default_timer() data = cursor.fetchall() - elapsed2 = timeit.default_timer() - start_time2 - logger.info("(fetchall time: %f)", elapsed2) + fetch_elapsed = timeit.default_timer() - fetch_start_time + logger.info("(fetchall time: %f)", fetch_elapsed) for (jobId,jobName,userName, vcName, jobStatus,jobStatusDetail, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in data: record = {} record["jobId"] = jobId @@ -619,12 +599,10 @@ def GetJobList(self, userName, vcName, num = None, status = None, op = ("=","or" logger.error('Exception: %s', str(e)) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: get job list for user %s , time elapsed %f s (SQL query time: %f)", userName, elapsed, elapsed1) return ret + @record def GetJob(self, **kwargs): - start_time = timeit.default_timer() valid_keys = ["jobId", "familyToken", "isParent", "jobName", "userName", "vcName", "jobStatus", "jobType", "jobTime"] if len(kwargs) != 1: return [] key, expected = kwargs.popitem() @@ -638,27 +616,23 @@ def GetJob(self, **kwargs): ret = [dict(zip(columns, row)) for row in cursor.fetchall()] self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: get job details with query %s=%s , time elapsed %f s", key, expected, elapsed) return ret + @record def AddCommand(self, jobId, command): try: - start_time = timeit.default_timer() sql = "INSERT INTO `"+self.commandtablename+"` (jobId, command) VALUES (%s,%s)" cursor = self.conn.cursor() cursor.execute(sql, (jobId, command)) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: add command to database, jobId: %s , time elapsed %f s", jobId, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def GetPendingCommands(self): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT `id`, `jobId`, `command` FROM `%s` WHERE `status` = 'pending' order by `time`" % (self.commandtablename) cursor.execute(query) @@ -671,27 +645,23 @@ def GetPendingCommands(self): ret.append(record) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: get pending command , time elapsed %f s", elapsed) return ret + @record def FinishCommand(self, commandId): try: - start_time = timeit.default_timer() sql = """update `%s` set status = 'run' where `id` = '%s' """ % (self.commandtablename, commandId) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: set command %s as finished , time elapsed %f s", commandId, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def GetCommands(self, jobId): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT `time`, `command`, `status`, `output` FROM `%s` WHERE `jobId` = '%s' order by `time`" % (self.commandtablename, jobId) cursor.execute(query) @@ -705,8 +675,6 @@ def GetCommands(self, jobId): ret.append(record) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: get command list for job %s , time elapsed %f s", jobId, elapsed) return ret def load_json(self, raw_str): @@ -717,9 +685,9 @@ def load_json(self, raw_str): except: return {} + @record def GetPendingEndpoints(self): try: - start_time = timeit.default_timer() jobs = self.GetJob(jobStatus="running") # [ {endpoint1:{},endpoint2:{}}, {endpoint3:{}, ... }, ... ] @@ -728,16 +696,14 @@ def GetPendingEndpoints(self): # endpoint["status"] == "pending" pendingEndpoints = {k: v for d in endpoints for k, v in d.items() if v["status"] == "pending"} - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: get pending endpoints %d, time elapsed %f s", len(pendingEndpoints), elapsed) return pendingEndpoints except Exception as e: logger.exception("Query pending endpoints failed!") return {} + @record def GetDeadEndpoints(self): try: - start_time = timeit.default_timer() cursor = self.conn.cursor() # TODO we need job["lastUpdated"] for filtering query = "SELECT `endpoints` FROM jobs WHERE `jobStatus` <> 'running' order by `jobTime` DESC" @@ -748,16 +714,14 @@ def GetDeadEndpoints(self): dead_endpoints.update(endpoint_list) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: get dead endpoints %d , time elapsed %f s", len(dead_endpoints), elapsed) return dead_endpoints except Exception as e: logger.exception("Query dead endpoints failed!") return {} + @record def UpdateEndpoint(self, endpoint): try: - start_time = timeit.default_timer() job_id = endpoint["jobId"] job = self.GetJob(jobId=job_id)[0] job_endpoints = self.load_json(job["endpoints"]) @@ -770,15 +734,13 @@ def UpdateEndpoint(self, endpoint): cursor.execute(sql, (json.dumps(job_endpoints), job_id)) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: update endpoints to database, endpointId: %s , time elapsed %f s", endpoint["id"], elapsed) return True except Exception as e: logger.exception("Update endpoints failed!") return False + @record def GetPendingJobs(self): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT `jobId`,`jobName`,`userName`, `vcName`, `jobStatus`, `jobType`, `jobDescriptionPath`, `jobDescription`, `jobTime`, `endpoints`, `jobParams`,`errorMsg` ,`jobMeta` FROM `%s` where `jobStatus` <> 'error' and `jobStatus` <> 'failed' and `jobStatus` <> 'finished' and `jobStatus` <> 'killed' order by `jobTime` DESC" % (self.jobtablename) cursor.execute(query) @@ -801,44 +763,37 @@ def GetPendingJobs(self): ret.append(record) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: get pending jobs %d, time elapsed %f s", len(ret), elapsed) return ret - + @record def SetJobError(self, jobId, errorMsg): try: - start_time = timeit.default_timer() sql = """update `%s` set jobStatus = 'error', `errorMsg` = '%s' where `jobId` = '%s' """ % (self.jobtablename, errorMsg, jobId) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: set job %s error status in database, time elapsed %f s", jobId, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def UpdateJobTextField(self, jobId, field, value): try: - start_time = timeit.default_timer() sql = "update `%s` set `%s` = '%s' where `jobId` = '%s' " % (self.jobtablename, field, value, jobId) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: update job %s, field %s to %s, time elapsed %f s", jobId, field, value, elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def GetJobTextField(self, jobId, field): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT `jobId`, `%s` FROM `%s` where `jobId` = '%s' " % (field, self.jobtablename, jobId) ret = None @@ -851,12 +806,10 @@ def GetJobTextField(self, jobId, field): pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: get filed %s of job %s , time elapsed %f s", field, jobId, elapsed) return ret + @record def AddandGetJobRetries(self, jobId): - start_time = timeit.default_timer() sql = """update `%s` set `retries` = `retries` + 1 where `jobId` = '%s' """ % (self.jobtablename, jobId) cursor = self.conn.cursor() cursor.execute(sql) @@ -872,29 +825,25 @@ def AddandGetJobRetries(self, jobId): ret = value self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: get and update retries for job %s , time elapsed %f s", jobId, elapsed) return ret + @record def UpdateClusterStatus(self, clusterStatus): try: status = base64.b64encode(json.dumps(clusterStatus)) - start_time = timeit.default_timer() sql = "INSERT INTO `%s` (status) VALUES ('%s')" % (self.clusterstatustablename, status) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: update cluster status, time elapsed %f s", elapsed) return True except Exception as e: logger.error('Exception: %s', str(e)) return False + @record def GetClusterStatus(self): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT `time`, `status` FROM `%s` order by `time` DESC limit 1" % (self.clusterstatustablename) ret = None @@ -908,13 +857,11 @@ def GetClusterStatus(self): logger.error('Exception: %s', str(e)) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: get cluster status , time elapsed %f s", elapsed) return ret, time + @record def GetUsers(self): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT `identityName`,`uid` FROM `%s`" % (self.identitytablename) ret = [] @@ -926,10 +873,9 @@ def GetUsers(self): logger.error('Exception: %s', str(e)) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info("DataHandler: get users, time elapsed %f s", elapsed) return ret + @record def GetActiveJobsCount(self): cursor = self.conn.cursor() query = "SELECT count(ALL id) as c FROM `%s` where `jobStatus` <> 'error' and `jobStatus` <> 'failed' and `jobStatus` <> 'finished' and `jobStatus` <> 'killed' " % (self.jobtablename) @@ -942,6 +888,7 @@ def GetActiveJobsCount(self): return ret + @record def GetALLJobsCount(self): cursor = self.conn.cursor() query = "SELECT count(ALL id) as c FROM `%s`" % (self.jobtablename) diff --git a/src/utils/SQLDataHandler.py b/src/utils/SQLDataHandler.py index d60c806d2..f352ebea4 100755 --- a/src/utils/SQLDataHandler.py +++ b/src/utils/SQLDataHandler.py @@ -12,6 +12,7 @@ from config import config from config import global_vars +from MySQLDataHandler import record logger = logging.getLogger(__name__) @@ -20,7 +21,7 @@ sql_live_connect_num = 25 -class SQLConnManager: +class SQLConnManager(object): @staticmethod def Connect(): @@ -142,7 +143,7 @@ def ReturnConnection(conn): global_vars["sql_lock"].release() return None -class DataHandler: +class DataHandler(object): def __init__(self): start_time = timeit.default_timer() self.CreateDatabase() @@ -150,7 +151,7 @@ def __init__(self): logger.debug ("********************** created a new Data Handler *******************") self.conn = SQLConnManager.GetConnection() logger.debug ("Get database connection %s" % str(self.conn)) - + #print "Connecting to server ..." self.jobtablename = "jobs-%s" % config["clusterId"] self.acltablename = "acl-%s" % config["clusterId"] @@ -164,8 +165,6 @@ def __init__(self): elapsed = timeit.default_timer() - start_time logger.debug ("DataHandler initialization, time elapsed %f s" % elapsed) - - def CreateDatabase(self): if "initSQLDB" not in global_vars or not global_vars["initSQLDB"]: logger.info("===========init SQL database===============") @@ -327,7 +326,7 @@ def CreateTable(self): self.conn.commit() cursor.close() - + sql = """ if not exists (select * from sysobjects where name='%s' and xtype='U') CREATE TABLE [dbo].[%s] @@ -349,41 +348,34 @@ def CreateTable(self): self.conn.commit() cursor.close() - + @record def AddStorage(self, vcName, url, storageType, metadata, defaultMountPath): try: - start_time = timeit.default_timer() sql = "INSERT INTO [%s] (storageType, url, metadata, vcName, defaultMountPath) VALUES (?,?,?,?,?)""" % self.storagetablename cursor = self.conn.cursor() cursor.execute(sql, (storageType, url, metadata, vcName, defaultMountPath)) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: AddStorage to DB: url : %s, vcName: %s , time elapsed %f s" % (url, vcName, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def DeleteStorage(self, vcName, url): try: - start_time = timeit.default_timer() sql = "DELETE FROM [%s] WHERE [url] = '%s' and [vcName] = '%s'" % (self.storagetablename, url, vcName) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: DeleteStorage: url:%s, vcName:%s, time elapsed %f s" % (url, vcName, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def ListStorages(self, vcName): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT [vcName],[url],[storageType],[metadata],[defaultMountPath] FROM [%s] WHERE [vcName] = '%s' " % (self.storagetablename, vcName) ret = [] @@ -402,45 +394,36 @@ def ListStorages(self, vcName): pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: ListStorages time elapsed %f s" % (elapsed)) return ret - + @record def UpdateStorage(self, vcName, url, storageType, metadata, defaultMountPath): try: - start_time = timeit.default_timer() sql = """update [%s] set storageType = '%s', metadata = '%s', defaultMountPath = '%s' where [vcName] = '%s' and [url] = '%s' """ % (self.storagetablename, storageType, metadata, defaultMountPath, vcName, url) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: UpdateStorage: vcName: %s, url: %s, time elapsed %f s" % (vcName, url, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def AddVC(self, vcName, quota, metadata): try: - start_time = timeit.default_timer() sql = """INSERT INTO [%s] (vcName, quota, metadata) VALUES (?,?,?)""" % self.vctablename cursor = self.conn.cursor() cursor.execute(sql, vcName, quota, metadata) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: AddVC to DB: vcName: %s , time elapsed %f s" % (vcName, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def ListVCs(self, vcName): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT [vcName],[quota],[metadata] FROM [%s]" % (self.vctablename) ret = [] @@ -457,45 +440,36 @@ def ListVCs(self, vcName): pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: ListVCs time elapsed %f s" % ( elapsed)) - return ret - + return ret + @record def DeleteVC(self, vcName): try: - start_time = timeit.default_timer() sql = "DELETE FROM [%s] WHERE [vcName] = '%s'" % (self.vctablename, vcName) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: DeleteVC: vcName: %s , time elapsed %f s" % (vcName, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def UpdateVC(self, vcName, quota, metadata): try: - start_time = timeit.default_timer() sql = """update [%s] set quota = '%s', metadata = '%s' where [vcName] = '%s'""" % (self.vctablename, quota, metadata, vcName) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: UpdateVC: vcName: %s , time elapsed %f s" % (vcName, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def GetIdentityInfo(self, identityName): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT [identityName],[uid],[gid],[groups] FROM [%s] where [identityName] = '%s'" % (self.identitytablename, identityName) ret = [] @@ -513,35 +487,29 @@ def GetIdentityInfo(self, identityName): pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: GetIdentityInfo time elapsed %f s" % (elapsed)) - return ret - + return ret + @record def UpdateIdentityInfo(self, identityName, uid, gid, groups): try: - start_time = timeit.default_timer() cursor = self.conn.cursor() - + if len(self.GetIdentityInfo(identityName)) == 0: sql = """INSERT INTO [%s] (identityName,uid,gid,groups) VALUES (?,?,?,?)""" % self.identitytablename cursor.execute(sql, identityName, uid, gid, json.dumps(groups)) else: sql = """update [%s] set uid = '%s', gid = '%s', groups = '%s' where [identityName] = '%s' """ % (self.identitytablename, uid, gid, groups, identityName) cursor.execute(sql) - + self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: UpdateIdentityInfo %s to database , time elapsed %f s" % (identityName, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def GetAceCount(self, identityId, resource): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT count(ALL id) as c FROM [%s] where [identityId] = '%s' and [resource] = '%s'" % (self.acltablename,identityId, resource) cursor.execute(query) @@ -550,88 +518,73 @@ def GetAceCount(self, identityId, resource): ret = c[0] self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: GetAceCount time elapsed %f s" % ( elapsed)) - return ret - + return ret + @record def UpdateAce(self, identityName, identityId, resource, permissions, isDeny): try: - start_time = timeit.default_timer() cursor = self.conn.cursor() - + if self.GetAceCount(identityId, resource) == 0: sql = """INSERT INTO [%s] (identityName,identityId,resource,permissions,isDeny) VALUES (?,?,?,?,?)""" % self.acltablename cursor.execute(sql, identityName, identityId, resource, permissions, isDeny) else: sql = """update [%s] set permissions = '%s' where [identityName] = '%s' and [resource] = '%s' """ % (self.acltablename, permissions, identityName, resource) cursor.execute(sql) - + self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: UpdateAce %s - %s to database , time elapsed %f s" % (identityName, resource, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def UpdateAclIdentityId(self, identityName, identityId): try: - start_time = timeit.default_timer() cursor = self.conn.cursor() sql = """update [%s] set identityName = '%s' where [identityName] = '%s' """ % (self.acltablename, identityId, identityName) cursor.execute(sql) - + self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: UpdateAclIdentityId %s - %s to database , time elapsed %f s" % (identityName, identityId, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def DeleteResourceAcl(self, resource): try: - start_time = timeit.default_timer() cursor = self.conn.cursor() - + sql = "DELETE FROM [%s] WHERE [resource] = '%s'" % (self.acltablename, resource) cursor = self.conn.cursor() - + self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: DeleteResourceAcl %s, time elapsed %f s" % (resource, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def DeleteAce(self, identityName, resource): try: - start_time = timeit.default_timer() cursor = self.conn.cursor() - + sql = "DELETE FROM [%s] WHERE [identityName] = '%s' and [resource] = '%s'" % (self.acltablename, identityName, resource) cursor = self.conn.cursor() - + self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: DeleteAce %s : %s, time elapsed %f s" % (resource, identityName, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def GetAcl(self): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT [identityName],[identityId],[resource],[permissions],[isDeny] FROM [%s]" % (self.acltablename) ret = [] @@ -650,13 +603,10 @@ def GetAcl(self): pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: GetAcl time elapsed %f s" % ( elapsed)) - return ret - + return ret + @record def GetResourceAcl(self, resource): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT [identityName],[identityId],[resource],[permissions],[isDeny] FROM [%s] where [resource] = '%s'" % (self.acltablename, resource) ret = [] @@ -675,30 +625,24 @@ def GetResourceAcl(self, resource): pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: GetResourceAcl time elapsed %f s" % ( elapsed)) - return ret - + return ret + @record def AddJob(self, jobParams): try: - start_time = timeit.default_timer() sql = """INSERT INTO [%s] (jobId, familyToken, isParent, jobName, userName, vcName, jobType,jobParams ) VALUES (?,?,?,?,?,?,?)""" % self.jobtablename cursor = self.conn.cursor() jobParam = base64.b64encode(json.dumps(jobParams)) cursor.execute(sql, jobParams["jobId"], jobParams["familyToken"], jobParams["isParent"], jobParams["jobName"], jobParams["userName"], jobParams["vcName"], jobParams["jobType"],jobParam) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: added job %s to database, time elapsed %f s" % (jobParams["jobId"],elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def GetJobList(self, userName, vcName, num = None, status = None, op = ("=","or")): - start_time = timeit.default_timer() ret = [] cursor = self.conn.cursor() try: @@ -715,16 +659,14 @@ def GetJobList(self, userName, vcName, num = None, status = None, op = ("=","or" else: status_list = [ " [jobStatus] %s '%s' " % (op[0],s) for s in status.split(',')] status_statement = (" "+op[1]+" ").join(status_list) - query += " and ( %s ) " % status_statement + query += " and ( %s ) " % status_statement query += " order by [jobTime] Desc" - start_time1 = timeit.default_timer() cursor.execute(query) - elapsed1 = timeit.default_timer() - start_time1 - start_time2 = timeit.default_timer() + fetch_start = timeit.default_timer() data = cursor.fetchall() - elapsed2 = timeit.default_timer() - start_time2 - logger.info ("(fetchall time: %f)" % (elapsed2)) + fetch_time = timeit.default_timer() - fetch_start + logger.info ("(fetchall time: %f)" % (fetch_time)) for (jobId,jobName,userName, vcName,jobStatus,jobStatusDetail, jobType, jobDescriptionPath, jobDescription, jobTime, endpoints, jobParams,errorMsg, jobMeta) in data: record = {} record["jobId"] = jobId @@ -744,16 +686,12 @@ def GetJobList(self, userName, vcName, num = None, status = None, op = ("=","or" ret.append(record) except Exception as e: logger.error('Exception: '+ str(e)) - pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: get job list for user %s , time elapsed %f s (SQL query time: %f)" % (userName, elapsed, elapsed1)) return ret - + @record def GetJob(self, **kwargs): - start_time = timeit.default_timer() valid_keys = ["jobId", "familyToken", "isParent", "jobName", "userName", "vcName", "jobStatus", "jobType", "jobTime"] if len(kwargs) != 1: return [] key, expected = kwargs.popitem() @@ -767,29 +705,23 @@ def GetJob(self, **kwargs): ret = [dict(zip(columns, row)) for row in cursor.fetchall()] self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: get job details with query %s=%s , time elapsed %f s" % (key, expected, elapsed)) return ret - + @record def AddCommand(self,jobId,command): try: - start_time = timeit.default_timer() sql = """INSERT INTO [%s] (jobId, command) VALUES (?,?)""" % self.commandtablename cursor = self.conn.cursor() cursor.execute(sql, jobId, command) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: add command to database, jobId: %s , time elapsed %f s" % (jobId, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def GetPendingCommands(self): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT [id], [jobId], [command] FROM [%s] WHERE [status] = 'pending' order by [time]" % (self.commandtablename) cursor.execute(query) @@ -802,29 +734,23 @@ def GetPendingCommands(self): ret.append(record) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: get pending command , time elapsed %f s" % (elapsed)) - return ret - + return ret + @record def FinishCommand(self,commandId): try: - start_time = timeit.default_timer() sql = """update [%s] set status = 'run' where [id] = '%s' """ % (self.commandtablename, commandId) cursor = self.conn.cursor() cursor.execute(sql) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: set command %s as finished , time elapsed %f s" % (commandId, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) return False - + @record def GetCommands(self, jobId): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT [time], [command], [status], [output] FROM [%s] WHERE [jobId] = '%s' order by [time]" % (self.commandtablename, jobId) cursor.execute(query) @@ -838,13 +764,10 @@ def GetCommands(self, jobId): ret.append(record) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: get command list for job %s , time elapsed %f s" % (jobId, elapsed)) - return ret - + return ret + @record def GetPendingJobs(self): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT [jobId],[jobName],[userName], [vcName], [jobStatus], [jobType], [jobDescriptionPath], [jobDescription], [jobTime], [endpoints], [jobParams],[errorMsg] ,[jobMeta] FROM [%s] where [jobStatus] <> 'error' and [jobStatus] <> 'failed' and [jobStatus] <> 'finished' and [jobStatus] <> 'killed' order by [jobTime] DESC" % (self.jobtablename) cursor.execute(query) @@ -867,45 +790,36 @@ def GetPendingJobs(self): ret.append(record) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: get pending jobs , time elapsed %f s" % (elapsed)) - return ret - + return ret + @record def SetJobError(self,jobId,errorMsg): try: - start_time = timeit.default_timer() sql = """update [%s] set jobStatus = 'error', [errorMsg] = ? where [jobId] = '%s' """ % (self.jobtablename,jobId) cursor = self.conn.cursor() cursor.execute(sql,errorMsg) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: set job %s error status in database, time elapsed %f s" % (jobId, elapsed)) return True except Exception as e: logger.error('Exception: '+ str(e)) - return False - + return False + @record def UpdateJobTextField(self,jobId,field,value): try: - start_time = timeit.default_timer() sql = """update [%s] set [%s] = ? where [jobId] = '%s' """ % (self.jobtablename,field, jobId) cursor = self.conn.cursor() cursor.execute(sql,value) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: update job %s, field %s , time elapsed %f s" % (jobId, field, elapsed)) return True except Exception, e: logger.error('Exception: '+ str(e)) return False - + @record def GetJobTextField(self,jobId,field): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT [jobId], [%s] FROM [%s] where [jobId] = '%s' " % (field, self.jobtablename,jobId) ret = None @@ -918,12 +832,10 @@ def GetJobTextField(self,jobId,field): pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: get filed %s of job %s , time elapsed %f s" % (field, jobId, elapsed)) return ret + @record def AddandGetJobRetries(self,jobId): - start_time = timeit.default_timer() sql = """update [%s] set [retries] = [retries] + 1 where [jobId] = '%s' """ % (self.jobtablename, jobId) cursor = self.conn.cursor() cursor.execute(sql) @@ -939,30 +851,24 @@ def AddandGetJobRetries(self,jobId): ret = value self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: get and update retries for job %s , time elapsed %f s" % (jobId, elapsed)) return ret - + @record def UpdateClusterStatus(self,clusterStatus): try: - start_time = timeit.default_timer() sql = """INSERT INTO [%s] (status) VALUES (?)""" % self.clusterstatustablename cursor = self.conn.cursor() status = base64.b64encode(json.dumps(clusterStatus)) cursor.execute(sql,status) self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: update cluster status, time elapsed %f s" % (elapsed)) return True except Exception, e: logger.error('Exception: '+ str(e)) return False - + @record def GetClusterStatus(self): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT TOP 1 [time], [status] FROM [%s] order by [time] DESC" % (self.clusterstatustablename) ret = None @@ -977,13 +883,10 @@ def GetClusterStatus(self): pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: get cluster status , time elapsed %f s" % (elapsed)) return ret, time - + @record def GetUsers(self): - start_time = timeit.default_timer() cursor = self.conn.cursor() query = "SELECT [identityName],[uid] FROM [%s]" % (self.identitytablename) ret = [] @@ -996,11 +899,9 @@ def GetUsers(self): pass self.conn.commit() cursor.close() - elapsed = timeit.default_timer() - start_time - logger.info ("DataHandler: get users, time elapsed %f s" % ( elapsed)) return ret - + @record def GetActiveJobsCount(self): cursor = self.conn.cursor() query = "SELECT count(ALL id) as c FROM [%s] where [jobStatus] <> 'error' and [jobStatus] <> 'failed' and [jobStatus] <> 'finished' and [jobStatus] <> 'killed' " % (self.jobtablename) @@ -1011,8 +912,9 @@ def GetActiveJobsCount(self): self.conn.commit() cursor.close() - return ret + return ret + @record def GetALLJobsCount(self): cursor = self.conn.cursor() query = "SELECT count(ALL id) as c FROM [%s]" % (self.jobtablename) @@ -1023,7 +925,7 @@ def GetALLJobsCount(self): self.conn.commit() cursor.close() - return ret + return ret def __del__(self): logger.debug("********************** deleted a DataHandler instance *******************") @@ -1039,7 +941,7 @@ def Close(self): CREATE_TABLE = False CREATE_DB = True dataHandler = DataHandler() - + if TEST_INSERT_JOB: jobParams = {} jobParams["id"] = "dist-tf-00001" @@ -1047,9 +949,8 @@ def Close(self): jobParams["user-id"] = "hongzl" jobParams["job-meta-path"] = "/dlws/jobfiles/***" jobParams["job-meta"] = "ADSCASDcAE!EDASCASDFD" - + dataHandler.AddJob(jobParams) - if CREATE_TABLE: dataHandler.CreateTable() From 29357c36cc6906f064fc84b3f2d3cab434fa59cf Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 22 Jul 2019 06:46:19 +0000 Subject: [PATCH 436/595] fix some bugs --- src/ClusterManager/cluster_manager.py | 14 +++++++------- src/utils/MySQLDataHandler.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ClusterManager/cluster_manager.py b/src/ClusterManager/cluster_manager.py index fa63f8f09..2fdab5851 100755 --- a/src/ClusterManager/cluster_manager.py +++ b/src/ClusterManager/cluster_manager.py @@ -28,7 +28,7 @@ def exporter_thread(port): root.putChild(b"healthz", HealthResource()) factory = Site(root) reactor.listenTCP(port, factory) - reactor.run() + reactor.run(installSignalHandlers=False) def setup_exporter_thread(port): t = threading.Thread(target=exporter_thread, args=(port,), @@ -50,12 +50,12 @@ def Run(args): cwd = os.path.dirname(__file__) cmds = [ - ["python", os.path.join(cwd, "job_manager.py"), args.j], - ["python", os.path.join(cwd, "user_manager.py"), args.u], - ["python", os.path.join(cwd, "node_manager.py"), args.n], - ["python", os.path.join(cwd, "joblog_manager.py"), args.l], - ["python", os.path.join(cwd, "command_manager.py"), args.c], - ["python", os.path.join(cwd, "endpoint_manager.py"), args.e], + ["python", os.path.join(cwd, "job_manager.py"), "--port", str(args.j)], + ["python", os.path.join(cwd, "user_manager.py"), "--port", str(args.u)], + ["python", os.path.join(cwd, "node_manager.py"), "--port", str(args.n)], + ["python", os.path.join(cwd, "joblog_manager.py"), "--port", str(args.l)], + ["python", os.path.join(cwd, "command_manager.py"), "--port", str(args.c)], + ["python", os.path.join(cwd, "endpoint_manager.py"), "--port", str(args.e)], ] FNULL = open(os.devnull, "w") diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index f22c6a141..42b532c70 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -55,7 +55,7 @@ def __init__(self): username = config["mysql"]["username"] password = config["mysql"]["password"] - with db_connect_histogram.time() + with db_connect_histogram.time(): self.conn = mysql.connector.connect(user=username, password=password, host=server, database=self.database) From 4042459fe2df14401a4ea41af5ddd8618a537c96 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 22 Jul 2019 06:46:33 +0000 Subject: [PATCH 437/595] config exporter port --- .../services/jobmanager/jobmanager.yaml | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/ClusterBootstrap/services/jobmanager/jobmanager.yaml b/src/ClusterBootstrap/services/jobmanager/jobmanager.yaml index caa7ae9d6..48aa16ad1 100755 --- a/src/ClusterBootstrap/services/jobmanager/jobmanager.yaml +++ b/src/ClusterBootstrap/services/jobmanager/jobmanager.yaml @@ -13,6 +13,9 @@ spec: labels: jobmanager-node: pod app: jobmanager + annotations: + prometheus.io/scrape: "true" + prometheus.io/path: "/metrics" spec: {% if cnf["dnsPolicy"] %} dnsPolicy: {{cnf["dnsPolicy"]}} @@ -39,7 +42,40 @@ spec: - mountPath: {{cnf["storage-mount-path"]}}/jobfiles name: dlwsdatajobfiles - mountPath: /var/log/dlworkspace - name: log + name: log + ports: + - containerPort: 9200 + hostPort: 9200 + name: job_manager_exporter + protocol: TCP + - containerPort: 9201 + hostPort: 9201 + name: user_manager_exporter + protocol: TCP + - containerPort: 9202 + hostPort: 9202 + name: node_manager_exporter + protocol: TCP + - containerPort: 9203 + hostPort: 9203 + name: joblog_manager_exporter + protocol: TCP + - containerPort: 9204 + hostPort: 9204 + name: command_manager_exporter + protocol: TCP + - containerPort: 9205 + hostPort: 9205 + name: endpoint_manager_exporter + protocol: TCP + readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 3 + periodSeconds: 30 + successThreshold: 1 + tcpSocket: + port: 9200 + timeoutSeconds: 10 volumes: - name: certs hostPath: From 3c558a7d3ed5151974806e35e79e28a24012f141 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 22 Jul 2019 06:58:20 +0000 Subject: [PATCH 438/595] shorter port name --- .../services/jobmanager/jobmanager.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ClusterBootstrap/services/jobmanager/jobmanager.yaml b/src/ClusterBootstrap/services/jobmanager/jobmanager.yaml index 48aa16ad1..54ccf8c67 100755 --- a/src/ClusterBootstrap/services/jobmanager/jobmanager.yaml +++ b/src/ClusterBootstrap/services/jobmanager/jobmanager.yaml @@ -46,27 +46,27 @@ spec: ports: - containerPort: 9200 hostPort: 9200 - name: job_manager_exporter + name: job-mgr protocol: TCP - containerPort: 9201 hostPort: 9201 - name: user_manager_exporter + name: user-mgr protocol: TCP - containerPort: 9202 hostPort: 9202 - name: node_manager_exporter + name: node-mgr protocol: TCP - containerPort: 9203 hostPort: 9203 - name: joblog_manager_exporter + name: joblog-mgr protocol: TCP - containerPort: 9204 hostPort: 9204 - name: command_manager_exporter + name: cmd-mgr protocol: TCP - containerPort: 9205 hostPort: 9205 - name: endpoint_manager_exporter + name: endpoint-mgr protocol: TCP readinessProbe: failureThreshold: 3 From fb3a9d7ab4c09b08a21e243c0b47c757077777ed Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 22 Jul 2019 07:16:42 +0000 Subject: [PATCH 439/595] fix some bug --- src/utils/MySQLDataHandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index 42b532c70..7ced12ca8 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -34,7 +34,7 @@ def wrapped(*args, **kwargs): try: return fn(*args, **kwargs) finally: - elapsed += timeit.default_timer() - start + elapsed = timeit.default_timer() - start logger.info("DataHandler: %s, time elapsed %.2fs", fn.__name__, elapsed) data_handler_fn_histogram.labels(fn.__name__).observe(elapsed) return wrapped From b6540b0c86ccea280a537d850c54a25050f94c4c Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 22 Jul 2019 09:04:00 +0000 Subject: [PATCH 440/595] expose in restfulapi --- .../services/restfulapi/restfulapi.yaml | 22 +++++++++++++------ src/RestAPI/dlwsrestapi.py | 8 +++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/ClusterBootstrap/services/restfulapi/restfulapi.yaml b/src/ClusterBootstrap/services/restfulapi/restfulapi.yaml index e3d089c83..d1e5a6ce4 100755 --- a/src/ClusterBootstrap/services/restfulapi/restfulapi.yaml +++ b/src/ClusterBootstrap/services/restfulapi/restfulapi.yaml @@ -4,7 +4,7 @@ metadata: name: restfulapi namespace: default labels: - run: dlwsrestfulapi + run: dlwsrestfulapi spec: selector: matchLabels: @@ -15,13 +15,17 @@ spec: labels: restfulapi-node: pod app: restfulapi + annotations: + prometheus.io/scrape: "true" + prometheus.io/path: "/metrics" + prometheus.io/port: "5000" spec: - {% if cnf["dnsPolicy"] %} + {% if cnf["dnsPolicy"] %} dnsPolicy: {{cnf["dnsPolicy"]}} {% endif %} nodeSelector: restfulapi: active - hostNetwork: true + hostNetwork: true containers: - name: restfulapi image: {{cnf["worker-dockerregistry"]}}{{cnf["dockerprefix"]}}{{cnf["restfulapi"]}}:{{cnf["dockertag"]}} @@ -31,6 +35,10 @@ spec: name: apiconfig - mountPath: /var/log/apache2 name: log + ports: + - containerPort: 5000 + hostPort: 5000 + name: main {% if False %} {% for volume in cnf["mountpoints"] %} {% if cnf["mountpoints"][volume]["mountpoints"] is string and cnf["mountpoints"][volume]["mountpoints"]!="" %} @@ -42,7 +50,7 @@ spec: name: {{mp}} {% endfor %} {% endif %} - {% endfor %} + {% endfor %} {% endif %} volumes: - name: apiconfig @@ -60,14 +68,14 @@ spec: {% else %} {% for mp in cnf["mountpoints"][volume]["mountpoints"] %} - name: {{mp}} - hostPath: + hostPath: path: {{cnf["storage-mount-path"]}}/{{mp}} {% endfor %} {% endif %} - {% endfor %} + {% endfor %} {% endif %} tolerations: - key: CriticalAddonsOnly operator: Exists - key: node-role.kubernetes.io/master - effect: NoSchedule + effect: NoSchedule diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index df34e6600..0a66a53a2 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -28,6 +28,10 @@ import traceback import threading +import prometheus_client + +CONTENT_TYPE_LATEST = str("text/plain; version=0.0.4; charset=utf-8") + dir_path = os.path.dirname(os.path.realpath(__file__)) with open(os.path.join(dir_path, 'logging.yaml'), 'r') as f: logging_config = yaml.load(f) @@ -1205,6 +1209,10 @@ def endpoint_exist(endpoint_id): ## api.add_resource(Endpoint, '/endpoints') +@app.route("/metrics/") +def metrics(): + return Response(prometheus_client.generate_latest(), mimetype=CONTENT_TYPE_LATEST) + if __name__ == '__main__': app.run(debug=False,host="0.0.0.0",threaded=True) From 7fa12c4547ec3cff159182c166f11a60d20bed6f Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 22 Jul 2019 09:19:06 +0000 Subject: [PATCH 441/595] fix bug --- src/RestAPI/dlwsrestapi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index 0a66a53a2..77477c549 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -2,7 +2,7 @@ import json import os -from flask import Flask +from flask import Flask, Response from flask_restful import reqparse, abort, Api, Resource from flask import request, jsonify import base64 @@ -1209,7 +1209,7 @@ def endpoint_exist(endpoint_id): ## api.add_resource(Endpoint, '/endpoints') -@app.route("/metrics/") +@app.route("/metrics") def metrics(): return Response(prometheus_client.generate_latest(), mimetype=CONTENT_TYPE_LATEST) From d4e223929e382f4dd8e30ff81b6088bfa2d0178c Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Mon, 22 Jul 2019 17:08:19 -0700 Subject: [PATCH 442/595] Change title of every table and adjust the orders (#427) --- .../WebPortal/Views/Home/JobSubmission.cshtml | 7 ++-- .../WebPortal/Views/Home/ViewCluster.cshtml | 32 ++++++++++--------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 604360d79..971d429f1 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -282,7 +282,7 @@ }); - $scope.$watch('current.jobtrainingtype', function (value, oldValue) { + $scope.$watch('current.jobtrainingtype', function (value) { if (value === 'PSDistJob') { $scope.current.numps = 1; $scope.current.resourcegpu = $scope.gpus[$scope.extras.gpuType]['num_gpu_per_node']; @@ -334,16 +334,13 @@ var selected = $filter('filter')($scope.joblist, { Value: $scope.curtemplateValue }); var showName = "None"; if ($scope.curtemplateValue > 0 && selected.length) { - if ($scope.lastTemplateValue && $scope.lastTemplateValue == $scope.curtemplateValue) { - + if ($scope.lastTemplateValue && $scope.lastTemplateValue == $scope.curtemplateValue) { } else { - $scope.lastTemplateValue = $scope.curtemplateValue; $scope.current = $scope.$eval(selected[0].Json); $scope.loadTemplate(); $scope.setMounts(); $scope.checkCurrent(); - //console.log($scope.current); if (!$scope.current.hasOwnProperty("runningasroot")) $scope.current.runningasroot = false; diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml index 2951cc326..328a1ef54 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml @@ -166,7 +166,7 @@ -

Cluster Status:

+

Team Virtual Cluster Status:

@@ -183,8 +183,22 @@
+

+ Team VC User Status: +

+ + + + + + + + + +
User NameUsed GPU
-

Cluster Usage:

+ +

Physical Cluster Usage:

@@ -205,22 +219,10 @@ -

- User Status: -

-
- - - - - - - -
User NameUsed GPU

- Node Status: + Physical Cluster Node Status:

From ee72f95d3b67e2ef4c2a38b59b78914b4ae175cd Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 23 Jul 2019 05:28:17 +0000 Subject: [PATCH 443/595] fix typo --- src/ClusterManager/job_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index e2be00315..b21f07acc 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -209,7 +209,7 @@ def UpdateJobStatus(job): UnusualJobs[job["jobId"]] = datetime.datetime.now() # TODO # 1) May need to reduce the timeout. - # It takes minutes before pod turns into "Unkonw", we may don't need to wait here. + # It takes minutes before pod turns into "Unknown", we may don't need to wait so long. # 2) If node resume before we resubmit the job, the job will end in status 'failed'. elif (datetime.datetime.now() - UnusualJobs[job["jobId"]]).seconds > 300: del UnusualJobs[job["jobId"]] From 7dc05561ca5f749d7b6d214f85f4ef9d8ee827c8 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 23 Jul 2019 09:20:56 +0000 Subject: [PATCH 444/595] When node back after "lost", the pod may turn into "NotFound". --- src/ClusterManager/job_deployer.py | 2 ++ src/ClusterManager/job_manager.py | 11 +++++++---- src/ClusterManager/job_role.py | 3 +++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index df93c0340..e7cf76839 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -1,6 +1,7 @@ import yaml import os import logging +import logging.config from kubernetes import client, config from kubernetes.client.rest import ApiException from kubernetes.stream import stream @@ -98,6 +99,7 @@ def get_pods(self, field_selector="", label_selector=""): field_selector=field_selector, label_selector=label_selector, ) + logging.debug("Get pods: {}".format(api_response)) return api_response.items def get_services_by_label(self, label_selector): diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index b21f07acc..fdee7f47c 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -203,14 +203,14 @@ def UpdateJobStatus(job): if jobDescriptionPath is not None and os.path.isfile(jobDescriptionPath): k8sUtils.kubectl_delete(jobDescriptionPath) - elif result == "Unknown": + elif result == "Unknown" or result == "NotFound": if job["jobId"] not in UnusualJobs: - logging.warning("!!! Job status ---Unknown---, job: {}".format(job["jobId"])) + logging.warning("!!! Job status ---{}---, job: {}".format(result, job["jobId"])) UnusualJobs[job["jobId"]] = datetime.datetime.now() # TODO # 1) May need to reduce the timeout. # It takes minutes before pod turns into "Unknown", we may don't need to wait so long. - # 2) If node resume before we resubmit the job, the job will end in status 'failed'. + # 2) If node resume before we resubmit the job, the job will end in status 'NotFound'. elif (datetime.datetime.now() - UnusualJobs[job["jobId"]]).seconds > 300: del UnusualJobs[job["jobId"]] retries = dataHandler.AddandGetJobRetries(job["jobId"]) @@ -225,7 +225,7 @@ def UpdateJobStatus(job): KillJob(job["jobId"], "queued") # SubmitJob(job) - if result.strip() != "Unknown" and job["jobId"] in UnusualJobs: + if result != "Unknown" and result != "NotFound" and job["jobId"] in UnusualJobs: del UnusualJobs[job["jobId"]] dataHandler.Close() @@ -236,6 +236,9 @@ def check_job_status(job_id): job_deployer = JobDeployer() job_roles = JobRole.get_job_roles(job_id) + if len(job_roles) < 1: + return "NotFound" + # role status in ["NotFound", "Pending", "Running", "Succeeded", "Failed", "Unknown"] # TODO ??? when ps/master role "Succeeded", return Succeeded for job_role in job_roles: diff --git a/src/ClusterManager/job_role.py b/src/ClusterManager/job_role.py index d9299a3b3..09a466547 100644 --- a/src/ClusterManager/job_role.py +++ b/src/ClusterManager/job_role.py @@ -1,3 +1,5 @@ +import logging +import logging.config from job_deployer import JobDeployer @@ -33,6 +35,7 @@ def status(self): # pod-phase: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase deployer = JobDeployer() pods = deployer.get_pods(field_selector="metadata.name={}".format(self.pod_name)) + logging.debug("Pods: {}".format(pods)) if(len(pods) < 1): return "NotFound" From 2d8fa8e9b67936dc51fe87012a24f31abf6cebbe Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 23 Jul 2019 11:16:06 +0000 Subject: [PATCH 445/595] add useful link --- src/ClusterManager/job_role.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ClusterManager/job_role.py b/src/ClusterManager/job_role.py index 09a466547..f4e61e9f8 100644 --- a/src/ClusterManager/job_role.py +++ b/src/ClusterManager/job_role.py @@ -33,6 +33,7 @@ def status(self): CONTAINER_READY -> WORKER_READY -> JOB_READY (then the job finally in "Running" status.) """ # pod-phase: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase + # node condition: https://kubernetes.io/docs/concepts/architecture/nodes/#condition deployer = JobDeployer() pods = deployer.get_pods(field_selector="metadata.name={}".format(self.pod_name)) logging.debug("Pods: {}".format(pods)) From 42d80321a3452f072eeb592e28b5306fa37e2d2c Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 23 Jul 2019 05:05:12 +0000 Subject: [PATCH 446/595] add performance dashboard --- .../grafana-config/perf-dashboard.json | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 src/ClusterBootstrap/services/monitor/grafana-config/perf-dashboard.json diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/perf-dashboard.json b/src/ClusterBootstrap/services/monitor/grafana-config/perf-dashboard.json new file mode 100644 index 000000000..7b3ebad7a --- /dev/null +++ b/src/ClusterBootstrap/services/monitor/grafana-config/perf-dashboard.json @@ -0,0 +1,238 @@ +{ + "dashboard": { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [], + "rows": [ + { + "collapse": false, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 1, + "id": 1, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9, sum(rate(datahandler_fn_latency_seconds_bucket[5m])) by (le, fn_name))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{'{{'}} fn_name {{'}}'}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Datahandler 90th percentile latecy per function", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + }, + { + "collapse": false, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 1, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9, sum(rate(db_connect_latency_seconds_bucket[5m])) by (le))", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "Connection latency", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "90th percentile DB connection latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Performance dashboard", + "version": 0 + } +} From 3678e77759378d9f1e205fe396f539eb29a4f648 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 23 Jul 2019 05:30:35 +0000 Subject: [PATCH 447/595] add manager histogram --- src/ClusterManager/cluster_manager.py | 6 +++ src/ClusterManager/command_manager.py | 25 ++++++----- src/ClusterManager/endpoint_manager.py | 13 +++--- src/ClusterManager/job_manager.py | 61 ++++++++++++++------------ src/ClusterManager/joblog_manager.py | 11 ++--- src/ClusterManager/node_manager.py | 11 ++--- src/ClusterManager/user_manager.py | 11 ++--- 7 files changed, 77 insertions(+), 61 deletions(-) diff --git a/src/ClusterManager/cluster_manager.py b/src/ClusterManager/cluster_manager.py index 2fdab5851..83d2ad81f 100755 --- a/src/ClusterManager/cluster_manager.py +++ b/src/ClusterManager/cluster_manager.py @@ -9,6 +9,7 @@ import threading from prometheus_client.twisted import MetricsResource +from prometheus_client import Histogram from twisted.web.server import Site from twisted.web.resource import Resource @@ -16,6 +17,11 @@ logger = logging.getLogger(__name__) +manager_iteration_histogram = Histogram("manager_iteration_latency_seconds", + "latency for manager to iterate", + buckets=(2.5, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, float("inf")), + labelnames=("name",)) + class HealthResource(Resource): def render_GET(self, request): diff --git a/src/ClusterManager/command_manager.py b/src/ClusterManager/command_manager.py index fd117835b..452eedd96 100755 --- a/src/ClusterManager/command_manager.py +++ b/src/ClusterManager/command_manager.py @@ -31,7 +31,7 @@ import logging -from cluster_manager import setup_exporter_thread +from cluster_manager import setup_exporter_thread, manager_iteration_histogram logger = logging.getLogger(__name__) @@ -54,17 +54,18 @@ def create_log(logdir = '/var/log/dlworkspace'): def Run(): create_log() while True: - try: - dataHandler = DataHandler() - pendingCommands = dataHandler.GetPendingCommands() - for command in pendingCommands: - try: - logger.info("Processing command: %s", command["id"]) - RunCommand(command) - except Exception as e: - logger.exception("run command failed") - except Exception as e: - logger.exception("getting command failed") + with manager_iteration_histogram.labels("command_manager").time(): + try: + dataHandler = DataHandler() + pendingCommands = dataHandler.GetPendingCommands() + for command in pendingCommands: + try: + logger.info("Processing command: %s", command["id"]) + RunCommand(command) + except Exception as e: + logger.exception("run command failed") + except Exception as e: + logger.exception("getting command failed") time.sleep(1) if __name__ == '__main__': diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index d95931969..8e3d964a8 100755 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -14,7 +14,7 @@ import logging.config import argparse -from cluster_manager import setup_exporter_thread +from cluster_manager import setup_exporter_thread, manager_iteration_histogram sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../utils")) import k8sUtils @@ -243,12 +243,13 @@ def Run(): create_log() while True: - # start endpoints - start_endpoints() - time.sleep(1) + with manager_iteration_histogram.labels("endpoint_manager").time(): + # start endpoints + start_endpoints() + time.sleep(1) - # clean up endpoints for jobs which is NOT running - cleanup_endpoints() + # clean up endpoints for jobs which is NOT running + cleanup_endpoints() time.sleep(1) if __name__ == '__main__': diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 96544f5f0..e7572c1ee 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -41,7 +41,7 @@ from job_deployer import JobDeployer from job_role import JobRole -from cluster_manager import setup_exporter_thread +from cluster_manager import setup_exporter_thread, manager_iteration_histogram def all_pods_not_existing(job_id): @@ -370,45 +370,50 @@ def TakeJobActions(jobs): def Run(): - notifier = Notifier(config.get("job-manager")) + notifier = notify.Notifier(config.get("job-manager")) notifier.start() create_log() while True: - try: - config["racks"] = k8sUtils.get_node_labels("rack") - config["skus"] = k8sUtils.get_node_labels("sku") - except Exception as e: - logging.exception("get node labels failed") + with manager_iteration_histogram.labels("job_manager").time(): + try: + config["racks"] = k8sUtils.get_node_labels("rack") + config["skus"] = k8sUtils.get_node_labels("sku") + except Exception as e: + logging.exception("get node labels failed") - try: - dataHandler = DataHandler() try: pendingJobs = dataHandler.GetPendingJobs() TakeJobActions(pendingJobs) pendingJobs = dataHandler.GetPendingJobs() - logging.info("Updating status for %d jobs" % len(pendingJobs)) - for job in pendingJobs: - try: - logging.info("Processing job: %s, status: %s" % (job["jobId"], job["jobStatus"])) - if job["jobStatus"] == "killing": - KillJob(job["jobId"], "killed") - elif job["jobStatus"] == "pausing": - KillJob(job["jobId"], "paused") - elif job["jobStatus"] == "scheduling" or job["jobStatus"] == "running": - UpdateJobStatus(job, notifier) - elif job["jobStatus"] == "unapproved": - ApproveJob(job["jobId"]) - except Exception as e: - logging.info(e, exc_info=True) + dataHandler = DataHandler() + try: + pendingJobs = dataHandler.GetPendingJobs() + TakeJobActions(pendingJobs) + + pendingJobs = dataHandler.GetPendingJobs() + logging.info("Updating status for %d jobs" % len(pendingJobs)) + for job in pendingJobs: + try: + logging.info("Processing job: %s, status: %s" % (job["jobId"], job["jobStatus"])) + if job["jobStatus"] == "killing": + KillJob(job["jobId"], "killed") + elif job["jobStatus"] == "pausing": + KillJob(job["jobId"], "paused") + elif job["jobStatus"] == "scheduling" or job["jobStatus"] == "running": + UpdateJobStatus(job, notifier) + elif job["jobStatus"] == "unapproved": + ApproveJob(job["jobId"]) + except Exception as e: + logging.info(e, exc_info=True) + except Exception as e: + logging.exception("process pending job failed") + finally: + dataHandler.Close() except Exception as e: - logging.exception("process pending job failed") - finally: - dataHandler.Close() - except Exception as e: - logging.exception("close data handler failed") + logging.exception("close data handler failed") time.sleep(1) diff --git a/src/ClusterManager/joblog_manager.py b/src/ClusterManager/joblog_manager.py index bdc383e62..9f86641f8 100755 --- a/src/ClusterManager/joblog_manager.py +++ b/src/ClusterManager/joblog_manager.py @@ -32,7 +32,7 @@ from config import config, GetStoragePath from DataHandler import DataHandler -from cluster_manager import setup_exporter_thread +from cluster_manager import setup_exporter_thread, manager_iteration_histogram logger = logging.getLogger(__name__) @@ -154,10 +154,11 @@ def Run(): logging.info("start to update job logs ...") while True: - try: - update_job_logs() - except Exception as e: - logger.exception("update job logs failed") + with manager_iteration_histogram.labels("joblog_manager").time(): + try: + update_job_logs() + except Exception as e: + logger.exception("update job logs failed") time.sleep(1) if __name__ == '__main__': diff --git a/src/ClusterManager/node_manager.py b/src/ClusterManager/node_manager.py index 0849a6038..2c4df9dcf 100755 --- a/src/ClusterManager/node_manager.py +++ b/src/ClusterManager/node_manager.py @@ -39,7 +39,7 @@ from config import config from DataHandler import DataHandler -from cluster_manager import setup_exporter_thread +from cluster_manager import setup_exporter_thread, manager_iteration_histogram def create_log(logdir = '/var/log/dlworkspace'): @@ -246,10 +246,11 @@ def Run(): logging.info("start to update nodes usage information ...") config["cluster_status"] = None while True: - try: - get_cluster_status() - except Exception as e: - logging.exception("get cluster status failed") + with manager_iteration_histogram.labels("node_manager").time(): + try: + get_cluster_status() + except Exception as e: + logging.exception("get cluster status failed") time.sleep(30) if __name__ == '__main__': diff --git a/src/ClusterManager/user_manager.py b/src/ClusterManager/user_manager.py index fc89f0b18..2e01da521 100755 --- a/src/ClusterManager/user_manager.py +++ b/src/ClusterManager/user_manager.py @@ -34,7 +34,7 @@ from config import config from DataHandler import DataHandler -from cluster_manager import setup_exporter_thread +from cluster_manager import setup_exporter_thread, manager_iteration_histogram def create_log(logdir = '/var/log/dlworkspace'): @@ -84,10 +84,11 @@ def Run(): create_log() logging.info("start to update user directory...") while True: - try: - set_user_directory() - except Exception as e: - logging.exception("set user directory failed") + with manager_iteration_histogram.labels("user_manager").time(): + try: + set_user_directory() + except Exception as e: + logging.exception("set user directory failed") time.sleep(1) From 9e877bab8e7ddf713d547430e5a5e91d6b74f66a Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 23 Jul 2019 05:30:46 +0000 Subject: [PATCH 448/595] perf job deployer --- src/ClusterManager/job_deployer.py | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index e7cf76839..43ed23910 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -2,11 +2,31 @@ import os import logging import logging.config +import timeit + from kubernetes import client, config from kubernetes.client.rest import ApiException from kubernetes.stream import stream from kubernetes.stream.ws_client import ERROR_CHANNEL, STDERR_CHANNEL, STDOUT_CHANNEL +from prometheus_client import Histogram + +job_deployer_fn_histogram = Histogram("job_deployer_fn_latency_seconds", + "latency for executing job deployer (seconds)", + buckets=(.05, .075, .1, .25, .5, .75, 1.0, 2.5, 5.0, + 7.5, 10.0, 12.5, 15.0, 17.5, 20.0, float("inf")), + labelnames=("fn_name",)) + +def record(fn): + @functools.wraps(fn) + def wrapped(*args, **kwargs): + start = timeit.default_timer() + try: + return fn(*args, **kwargs) + finally: + elapsed = timeit.default_timer() - start + job_deployer_fn_histogram.labels(fn.__name__).observe(elapsed) + return wrapped class JobDeployer: @@ -17,6 +37,7 @@ def __init__(self): self.namespace = "default" self.pretty = "pretty_example" + @record def create_pod(self, body, dry_run=None): api_response = self.v1.create_namespaced_pod( namespace=self.namespace, @@ -26,6 +47,7 @@ def create_pod(self, body, dry_run=None): ) return api_response + @record def delete_pod(self, name, dry_run=None): api_response = self.v1.delete_namespaced_pod( name=name, @@ -36,6 +58,7 @@ def delete_pod(self, name, dry_run=None): ) return api_response + @record def create_service(self, body, dry_run=None): api_response = self.v1.create_namespaced_service( namespace=self.namespace, @@ -45,6 +68,7 @@ def create_service(self, body, dry_run=None): ) return api_response + @record def delete_service(self, name, dry_run=None): api_response = self.v1.delete_namespaced_service( name=name, @@ -55,6 +79,7 @@ def delete_service(self, name, dry_run=None): ) return api_response + @record def cleanup_pods(self, pod_names): errors = [] for pod_name in pod_names: @@ -68,6 +93,7 @@ def cleanup_pods(self, pod_names): errors.append({"message": message, "exception": e}) return errors + @record def cleanup_services(self, services): errors = [] for service in services: @@ -81,6 +107,7 @@ def cleanup_services(self, services): errors.append({"message": message, "exception": e}) return errors + @record def create_pods(self, pods): # TODO instead of delete, we could check update existiong ones. During refactoring, keeping the old way. pod_names = [pod["metadata"]["name"] for pod in pods] @@ -92,6 +119,7 @@ def create_pods(self, pods): logging.info("Create pod succeed: %s" % created_pod.metadata.name) return created + @record def get_pods(self, field_selector="", label_selector=""): api_response = self.v1.list_namespaced_pod( namespace=self.namespace, @@ -102,6 +130,7 @@ def get_pods(self, field_selector="", label_selector=""): logging.debug("Get pods: {}".format(api_response)) return api_response.items + @record def get_services_by_label(self, label_selector): api_response = self.v1.list_namespaced_service( namespace=self.namespace, @@ -110,6 +139,7 @@ def get_services_by_label(self, label_selector): ) return api_response.items + @record def delete_job(self, job_id): label_selector = "run={}".format(job_id) @@ -125,6 +155,7 @@ def delete_job(self, job_id): errors = pod_errors + service_errors return errors + @record def pod_exec(self, pod_name, exec_command, timeout=60): """work as the command (with timeout): kubectl exec 'pod_name' 'exec_command'""" try: From f79ad2fc08969c47c8c55e3a8962b79b13a990fd Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 24 Jul 2019 03:28:43 +0000 Subject: [PATCH 449/595] add missing import --- src/ClusterManager/job_deployer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index 43ed23910..4ec8186c4 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -3,6 +3,7 @@ import logging import logging.config import timeit +import functools from kubernetes import client, config from kubernetes.client.rest import ApiException From ad4062981c94c1314c0cc3de367f639b193d4d1a Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 24 Jul 2019 03:46:55 +0000 Subject: [PATCH 450/595] fix conflict error --- src/ClusterManager/job_manager.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index e7572c1ee..c43161393 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -374,7 +374,6 @@ def Run(): notifier.start() create_log() - while True: with manager_iteration_histogram.labels("job_manager").time(): try: @@ -384,10 +383,6 @@ def Run(): logging.exception("get node labels failed") try: - pendingJobs = dataHandler.GetPendingJobs() - TakeJobActions(pendingJobs) - - pendingJobs = dataHandler.GetPendingJobs() dataHandler = DataHandler() try: pendingJobs = dataHandler.GetPendingJobs() From 99f57b43a050c78c9ddcbcad6393b8a37c102372 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 24 Jul 2019 06:21:15 +0000 Subject: [PATCH 451/595] add reaper --- src/ClusterBootstrap/params.py | 5 ++ .../services/monitor/alert-manager.yaml | 32 +++++++- .../services/monitor/alerting/jobs.rules | 5 ++ src/RestAPI/dlwsrestapi.py | 2 + src/docker-images/reaper/Dockerfile | 7 ++ src/docker-images/reaper/main.py | 81 +++++++++++++++++++ 6 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 src/docker-images/reaper/Dockerfile create mode 100644 src/docker-images/reaper/main.py diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index fa0bfd4f8..09986420b 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -31,6 +31,11 @@ # If want to deploy with alert-manager, should config # configured with True, and fill appropriate value to: # smtp_url, smtp_from, smtp_auth_username, smtp_auth_password and receiver + "reaper": { + "dry-run": True, + "port": "9500", + "restful-url": "http://localhost:5000", + } }, "mysql_port": "3306", diff --git a/src/ClusterBootstrap/services/monitor/alert-manager.yaml b/src/ClusterBootstrap/services/monitor/alert-manager.yaml index 819756e14..016ad3dcb 100644 --- a/src/ClusterBootstrap/services/monitor/alert-manager.yaml +++ b/src/ClusterBootstrap/services/monitor/alert-manager.yaml @@ -24,7 +24,7 @@ spec: hostNetwork: true containers: - name: alert-manager - image: prom/alertmanager:v0.15.1 + image: prom/alertmanager:v0.18.0 args: - '--config.file=/etc/alertmanager/config.yml' - '--storage.path=/alertmanager' @@ -40,6 +40,23 @@ spec: mountPath: /alertmanager - name: templates-volume mountPath: /etc/alertmanager/template + {% if cnf["alert-manager"]["reaper"] %} + - name: reaper + image: {{cnf["worker-dockerregistry"]}}{{cnf["dockerprefix"]}}reaper:{{cnf["dockertag"]}} + command: + - 'python' + - '/reaper/main.py' + - '--port' + - '{{ cnf["alert-manager"]["reaper"]["port"] }}' + - '--restful_url' + - '{{ cnf["alert-manager"]["reaper"]["restful-url"] }}' + {% if cnf["alert-manager"]["reaper"]["dry-run"] %} + - '--dry_run' + {% endif %} + ports: + - name: alert-manager + containerPort: {{ cnf["alert-manager"]["reaper"]["port"] }} + {% endif %} volumes: - name: config-volume configMap: @@ -93,6 +110,11 @@ data: match_re: type: user_alert alertname: "job-state-changed" + - receiver: reaper + group_by: [alertname, user_email, job_name] + group_wait: 0s + match_re: + type: reaper receivers: - name: "alert-email" email_configs: @@ -129,3 +151,11 @@ data: {% endif %} subject: '{{ "{{" }} .GroupLabels.cluster {{ "}}" }}: {{ "{{" }} template "__subject" . {{ "}}" }}' {% endif %} + - name: "reaper" + {% if cnf["alert-manager"]["reaper"] %} + webhook_configs: + - send_resolved: False + url: 'http://localhost:{{ cnf["alert-manager"]["reaper"]["port"] }}/kill' + http_config: + bearer_token: 'shinigami' + {% endif %} diff --git a/src/ClusterBootstrap/services/monitor/alerting/jobs.rules b/src/ClusterBootstrap/services/monitor/alerting/jobs.rules index 976263107..b7fbb8e25 100644 --- a/src/ClusterBootstrap/services/monitor/alerting/jobs.rules +++ b/src/ClusterBootstrap/services/monitor/alerting/jobs.rules @@ -6,3 +6,8 @@ groups: for: 4h labels: type: user_alert + - alert: kill-idle-jobs + expr: avg(task_gpu_percent) by (user_email, job_name, vc_name) == 0 + for: 8h + labels: + type: reaper diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index 77477c549..5fb607c82 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -393,6 +393,8 @@ def get(self): result = JobRestAPIUtils.KillJob(userName, jobId) ret = {} if result: + # NOTE "Success" prefix is used in reaper, please also update reaper code + # if need to change it. ret["result"] = "Success, the job is scheduled to be terminated." else: ret["result"] = "Cannot Kill the job. Job ID:" + jobId diff --git a/src/docker-images/reaper/Dockerfile b/src/docker-images/reaper/Dockerfile new file mode 100644 index 000000000..7ff9dd641 --- /dev/null +++ b/src/docker-images/reaper/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.7 + +RUN pip3 install requests flask + +WORKDIR /reaper + +COPY * /reaper/ diff --git a/src/docker-images/reaper/main.py b/src/docker-images/reaper/main.py new file mode 100644 index 000000000..6b6460414 --- /dev/null +++ b/src/docker-images/reaper/main.py @@ -0,0 +1,81 @@ +#!/usr/bin/python + +import urllib.parse +import argparse +import requests +import logging +import faulthandler +import signal +import json + +import flask +from flask import Flask +from flask import request + +logger = logging.getLogger(__name__) + +app = Flask(__name__) + +@app.route("/kill", methods=["POST"]) +def kill(): + args = request.args + auth = request.headers.get("Authorization") + if auth != "Bearer shinigami": + logger.warning("get unauthorized call") + return "Unauthorized", 401 + try: + body = json.loads(request.data.decode("utf-8")) + + for alert in body["alerts"]: + if alert.get("status") == "resolved": + continue + logger.info("processing alert of %s", alert) + if not dry_run: + job_name = alert["labels"]["job_name"] + username = alert["labels"]["user_email"] + params = {"jobId": job_name, "userName": username} + args = urllib.parse.urlencode(params) + url = restful_url + "/KillJob?" + args + + response = requests.get(url, timeout=10) + response.raise_for_status() + result = response.json().get("result") + if result is not None and result.startswith("Success"): + logger.info("killing %s success", params) + else: + logger.warning("killing %s failed", params) + else: + logger.info("reaper in dry_run mode, will not kill %s", alert) + return "Ok", 200 + except Exception as e: + logger.exception("caught exception while processing kill, data is %s", + requests.data) + raise e + +def register_stack_trace_dump(): + faulthandler.register(signal.SIGTRAP, all_threads=True, chain=False) + +def main(args): + app.run(host="0.0.0.0", port=args.port, debug=False, use_reloader=False) + +if __name__ == "__main__": + register_stack_trace_dump() + logging.basicConfig(format="%(asctime)s - %(levelname)s - %(filename)s:%(lineno)s@%(thread)d - %(message)s", + level=logging.INFO) + + parser = argparse.ArgumentParser() + parser.add_argument("--port", "-p", default=9500, type=int, + help="port to listen, default 9500") + parser.add_argument("--restful_url", "-r", required=True, + help="restful api url, e.g. http://localhost:5000") + parser.add_argument("--dry_run", "-d", action="store_true", + help="if dry_run, the reaper will do nothing") + args = parser.parse_args() + + global dry_run + global restful_url + + dry_run = args.dry_run + restful_url = args.restful_url + + main(args) From d0b7cd8417936c52c6d2a8fdf6056ac6094a4f40 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 24 Jul 2019 10:08:51 +0000 Subject: [PATCH 452/595] split jobmanager's metrics with restfulapi's --- .../grafana-config/perf-dashboard.json | 171 +++++++++++++++++- 1 file changed, 161 insertions(+), 10 deletions(-) diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/perf-dashboard.json b/src/ClusterBootstrap/services/monitor/grafana-config/perf-dashboard.json index 7b3ebad7a..77a3f5c09 100644 --- a/src/ClusterBootstrap/services/monitor/grafana-config/perf-dashboard.json +++ b/src/ClusterBootstrap/services/monitor/grafana-config/perf-dashboard.json @@ -19,6 +19,7 @@ "hideControls": false, "id": null, "links": [], + "refresh": "30s", "rows": [ { "collapse": false, @@ -30,7 +31,7 @@ "dashLength": 10, "dashes": false, "datasource": null, - "fill": 1, + "fill": 0, "id": 1, "legend": { "avg": false, @@ -51,12 +52,12 @@ "renderer": "flot", "seriesOverrides": [], "spaceLength": 10, - "span": 12, + "span": 6, "stack": false, "steppedLine": false, "targets": [ { - "expr": "histogram_quantile(0.9, sum(rate(datahandler_fn_latency_seconds_bucket[5m])) by (le, fn_name))", + "expr": "histogram_quantile(0.9, sum(rate(datahandler_fn_latency_seconds_bucket{scraped_from=~\"jobmanager.*\"}[5m])) by (le, fn_name))", "format": "time_series", "intervalFactor": 2, "legendFormat": "{{'{{'}} fn_name {{'}}'}}", @@ -66,7 +67,7 @@ "thresholds": [], "timeFrom": null, "timeShift": null, - "title": "Datahandler 90th percentile latecy per function", + "title": "Datahandler 90th percentile latency per function from jobmanager", "tooltip": { "shared": true, "sort": 0, @@ -98,6 +99,81 @@ "show": false } ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 1, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9, sum(rate(datahandler_fn_latency_seconds_bucket{scraped_from=~\"restfulapi.*\"}[5m])) by (le, fn_name))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{'{{'}} fn_name {{'}}'}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Datahandler 90th percentile latency per function from restfulapi", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] } ], "repeat": null, @@ -117,8 +193,8 @@ "dashLength": 10, "dashes": false, "datasource": null, - "fill": 1, - "id": 2, + "fill": 0, + "id": 3, "legend": { "avg": false, "current": false, @@ -138,23 +214,23 @@ "renderer": "flot", "seriesOverrides": [], "spaceLength": 10, - "span": 12, + "span": 6, "stack": false, "steppedLine": false, "targets": [ { - "expr": "histogram_quantile(0.9, sum(rate(db_connect_latency_seconds_bucket[5m])) by (le))", + "expr": "histogram_quantile(0.9, sum(rate(db_connect_latency_seconds_bucket{scraped_from=~\"jobmanager.*\"}[5m])) by (le))", "format": "time_series", "hide": false, "intervalFactor": 2, - "legendFormat": "Connection latency", + "legendFormat": "Connection Latency", "refId": "A" } ], "thresholds": [], "timeFrom": null, "timeShift": null, - "title": "90th percentile DB connection latency", + "title": "90th percentile DB connection latency from jobmanager", "tooltip": { "shared": true, "sort": 0, @@ -183,7 +259,82 @@ "logBase": 1, "max": null, "min": null, + "show": false + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 0, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.9, sum(rate(db_connect_latency_seconds_bucket{scraped_from=~\"restfulapi.*\"}[5m])) by (le))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Connection Latency", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "90th percentile DB connection latency from restfulapi", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false } ] } From 4c45f52f7b998c8f015dff388d1c61f89d23f495 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 24 Jul 2019 11:52:30 +0000 Subject: [PATCH 453/595] share k8s client: to avoid connection leak --- src/ClusterManager/job_deployer.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index 4ec8186c4..c60663d51 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -29,12 +29,16 @@ def wrapped(*args, **kwargs): job_deployer_fn_histogram.labels(fn.__name__).observe(elapsed) return wrapped + +# The config will be loaded from default location. +config.load_kube_config() +k8s_client = client.CoreV1Api() + + class JobDeployer: def __init__(self): - # The config will be loaded from default location. - config.load_kube_config() - self.v1 = client.CoreV1Api() + self.v1 = k8s_client self.namespace = "default" self.pretty = "pretty_example" From fed8c67759b0e878d47c5818f5b06d8cc89682a2 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 25 Jul 2019 12:23:29 +0000 Subject: [PATCH 454/595] add option for "force" delete pod --- src/ClusterManager/job_deployer.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index c60663d51..4e7934bcc 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -53,12 +53,16 @@ def create_pod(self, body, dry_run=None): return api_response @record - def delete_pod(self, name, dry_run=None): + def delete_pod(self, name, grace_period_seconds=None, dry_run=None): + body = client.V1DeleteOptions() + body.grace_period_seconds = grace_period_seconds + body.dry_run = dry_run api_response = self.v1.delete_namespaced_pod( name=name, namespace=self.namespace, pretty=self.pretty, - body=client.V1DeleteOptions(), + body=body, + grace_period_seconds=grace_period_seconds, dry_run=dry_run, ) return api_response @@ -85,11 +89,12 @@ def delete_service(self, name, dry_run=None): return api_response @record - def cleanup_pods(self, pod_names): + def cleanup_pods(self, pod_names, force=False): errors = [] + grace_period_seconds = 0 if force else None for pod_name in pod_names: try: - self.delete_pod(pod_name) + self.delete_pod(pod_name, grace_period_seconds) except Exception as e: if isinstance(e, ApiException) and 404 == e.status: return [] From ebfd77130b319fc04d317515712dca4d34e66401 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 25 Jul 2019 12:30:45 +0000 Subject: [PATCH 455/595] forcing cleanup job before submit --- src/ClusterManager/job_deployer.py | 4 ++-- src/ClusterManager/job_manager.py | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index 4e7934bcc..ac80c22a1 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -150,13 +150,13 @@ def get_services_by_label(self, label_selector): return api_response.items @record - def delete_job(self, job_id): + def delete_job(self, job_id, force=False): label_selector = "run={}".format(job_id) # query pods then delete pods = self.get_pods(label_selector=label_selector) pod_names = [pod.metadata.name for pod in pods] - pod_errors = self.cleanup_pods(pod_names) + pod_errors = self.cleanup_pods(pod_names, force) # query services then delete services = self.get_services_by_label(label_selector) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index c43161393..fdeb9ae35 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -55,8 +55,13 @@ def all_pods_not_existing(job_id): def SubmitJob(job): # check if existing any pod with label: run=job_id assert("jobId" in job) - if not all_pods_not_existing(job["jobId"]): - logging.warning("Waiting until previously pods are cleaned up! Job {}".format(job["jobId"])) + job_id = job["jobId"] + if not all_pods_not_existing(job_id): + logging.warning("Waiting until previously pods are cleaned up! Job {}".format(job_id)) + job_deployer = JobDeployer() + errors = job_deployer.delete_job(job_id, force=True) + if errors: + logging.warning("Force delete job {}: {}".format(job_id, errors)) return ret = {} From 0d5f87e564a974e0cceab5b1bf99e9dcde6aa8ce Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Thu, 25 Jul 2019 11:37:57 -0700 Subject: [PATCH 456/595] Enable distribute job in low priority job template (#432) --- .../WebPortal/Views/Home/JobSubmission.cshtml | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 971d429f1..3051f12fe 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -263,7 +263,7 @@ gpu_available: gpu_available[key] < 0 ? 0 : gpu_available[key], quota: quota[key] }; - $scope.isLowPriority = value.low_priority === true ? true : false; + /* $scope.isLowPriority = value.low_priority === true ? true : false; if ($scope.isLowPriority) { var filteredJobList = []; $scope.joblist.forEach(function(job) { @@ -272,7 +272,7 @@ } }); $scope.joblist = filteredJobList; - } + }*/ $scope.gpus[key] = gpu; }); @@ -700,17 +700,10 @@ - - + + Regular Job - Distributed Job + Distributed Job From 5b1df00256a781ed3e9f70a62b1b736b6c3e8111 Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Thu, 25 Jul 2019 15:45:49 -0700 Subject: [PATCH 457/595] Clean up advance option & save job template issue --- .../dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 3051f12fe..7fa815357 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -286,8 +286,10 @@ if (value === 'PSDistJob') { $scope.current.numps = 1; $scope.current.resourcegpu = $scope.gpus[$scope.extras.gpuType]['num_gpu_per_node']; + $scope.current.hostNetwork = true; } else { delete $scope.current.numps; + $scope.current.hostNetwork = false; } }) @@ -914,7 +916,7 @@ -
+

HyperParameter Turning

@@ -972,17 +974,17 @@
-
+

Host Network

- +
-
+

GPU Topology

@@ -1091,7 +1093,7 @@
From fbd2454e0b89c3b05f54099aba164b065a720fbc Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 26 Jul 2019 03:38:55 +0000 Subject: [PATCH 458/595] default using localhost as prometheus ip in grafana --- src/ClusterBootstrap/params.py | 2 +- .../services/monitor/grafana-config/prom-datasource.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index fa0bfd4f8..76060190e 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -23,7 +23,7 @@ "job-exporter": { "port": 9102 }, "node-exporter": { "port": 9100 }, "watchdog": { "port": 9101 }, - "grafana": { "port": 3000 }, + "grafana": { "port": 3000, "prometheus-ip": "localhost" }, "alert-manager": { "port": 9093, "configured": False, diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/prom-datasource.json b/src/ClusterBootstrap/services/monitor/grafana-config/prom-datasource.json index 467dfa70e..027e5ea6c 100644 --- a/src/ClusterBootstrap/services/monitor/grafana-config/prom-datasource.json +++ b/src/ClusterBootstrap/services/monitor/grafana-config/prom-datasource.json @@ -1,6 +1,6 @@ { "name": "PM", - "url": "http://{{cnf['prometheus']['host']}}:9091/prometheus", + "url": "http://{{cnf['grafana']['prometheus-ip']}}:9091/prometheus", "basicAuth": false, "access": "proxy", "type": "prometheus", From 6e0b34bffbb11c6104e2d668a0e325e9fb158b02 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 26 Jul 2019 11:16:18 +0000 Subject: [PATCH 459/595] force delete pod when killing --- src/ClusterManager/job_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index fdeb9ae35..3434b2a6c 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -146,7 +146,7 @@ def KillJob(job_id, desiredState="killed"): logging.info("Killing job %s, with status %s, %s" % (job_id, result, detail)) job_deployer = JobDeployer() - errors = job_deployer.delete_job(job_id) + errors = job_deployer.delete_job(job_id, force=True) if len(errors) == 0: dataHandler.UpdateJobTextField(job_id, "jobStatus", desiredState) From 8fd7693015a0e4688b152a9e2ba03042ee9ae725 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 26 Jul 2019 11:48:50 +0000 Subject: [PATCH 460/595] no need to retry on submit --- src/ClusterManager/job_manager.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 3434b2a6c..29a6de084 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -227,17 +227,8 @@ def UpdateJobStatus(job, notifier=None): # 2) If node resume before we resubmit the job, the job will end in status 'NotFound'. elif (datetime.datetime.now() - UnusualJobs[job["jobId"]]).seconds > 300: del UnusualJobs[job["jobId"]] - retries = dataHandler.AddandGetJobRetries(job["jobId"]) - if retries >= 5: - logging.warning("Job %s fails for more than 5 times, abort", job["jobId"]) - dataHandler.UpdateJobTextField(job["jobId"], "jobStatus", "error") - dataHandler.UpdateJobTextField(job["jobId"], "errorMsg", "cannot launch the job.") - if jobDescriptionPath is not None and os.path.isfile(jobDescriptionPath): - k8sUtils.kubectl_delete(jobDescriptionPath) - else: - logging.warning("Job %s fails in Kubernetes, delete and re-submit the job. Retries %d", job["jobId"], retries) - KillJob(job["jobId"], "queued") - # SubmitJob(job) + logging.warning("Job {} fails in Kubernetes as {}, delete and re-submit.".format(job["jobId"], result)) + KillJob(job["jobId"], "queued") if result != "Unknown" and result != "NotFound" and job["jobId"] in UnusualJobs: del UnusualJobs[job["jobId"]] From 3a3a14c23e1ed623005fc4c3ba1fded0371bc2b4 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 26 Jul 2019 12:29:20 +0000 Subject: [PATCH 461/595] refine execption handle --- src/ClusterManager/job_manager.py | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 29a6de084..8030644d5 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -380,31 +380,31 @@ def Run(): try: dataHandler = DataHandler() + pendingJobs = dataHandler.GetPendingJobs() + TakeJobActions(pendingJobs) + + pendingJobs = dataHandler.GetPendingJobs() + logging.info("Updating status for %d jobs" % len(pendingJobs)) + for job in pendingJobs: + try: + logging.info("Processing job: %s, status: %s" % (job["jobId"], job["jobStatus"])) + if job["jobStatus"] == "killing": + KillJob(job["jobId"], "killed") + elif job["jobStatus"] == "pausing": + KillJob(job["jobId"], "paused") + elif job["jobStatus"] == "scheduling" or job["jobStatus"] == "running": + UpdateJobStatus(job, notifier) + elif job["jobStatus"] == "unapproved": + ApproveJob(job["jobId"]) + except Exception as e: + logging.warning(e, exc_info=True) + except Exception as e: + logging.warning("Process job failed!", exc_info=True) + finally: try: - pendingJobs = dataHandler.GetPendingJobs() - TakeJobActions(pendingJobs) - - pendingJobs = dataHandler.GetPendingJobs() - logging.info("Updating status for %d jobs" % len(pendingJobs)) - for job in pendingJobs: - try: - logging.info("Processing job: %s, status: %s" % (job["jobId"], job["jobStatus"])) - if job["jobStatus"] == "killing": - KillJob(job["jobId"], "killed") - elif job["jobStatus"] == "pausing": - KillJob(job["jobId"], "paused") - elif job["jobStatus"] == "scheduling" or job["jobStatus"] == "running": - UpdateJobStatus(job, notifier) - elif job["jobStatus"] == "unapproved": - ApproveJob(job["jobId"]) - except Exception as e: - logging.info(e, exc_info=True) - except Exception as e: - logging.exception("process pending job failed") - finally: dataHandler.Close() - except Exception as e: - logging.exception("close data handler failed") + except: + pass time.sleep(1) From 9263d66d2cb0221e2cec8ce22013994c21698c58 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 26 Jul 2019 12:52:12 +0000 Subject: [PATCH 462/595] reset endpoint when resubmit job --- src/ClusterManager/job_manager.py | 9 +++++++++ src/utils/MySQLDataHandler.py | 20 ++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 8030644d5..4ba69cf5e 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -227,6 +227,15 @@ def UpdateJobStatus(job, notifier=None): # 2) If node resume before we resubmit the job, the job will end in status 'NotFound'. elif (datetime.datetime.now() - UnusualJobs[job["jobId"]]).seconds > 300: del UnusualJobs[job["jobId"]] + + # TODO refine later + # before resubmit the job, reset the endpoints + # update all endpoint to status 'pending', so it would restart when job is ready + endpoints = dataHandler.GetJobEndpoints(job["jobId"]) + for endpoint in endpoints: + endpoint["status"] = "pending" + dataHandler.UpdateEndpoint(endpoint) + logging.warning("Job {} fails in Kubernetes as {}, delete and re-submit.".format(job["jobId"], result)) KillJob(job["jobId"], "queued") diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index 7ced12ca8..c4cb56ee0 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -393,7 +393,7 @@ def UpdateIdentityInfo(self, identityName, uid, gid, groups): if (isinstance(groups, list)): groups = json.dumps(groups) - + if len(self.GetIdentityInfo(identityName)) == 0: sql = "INSERT INTO `"+self.identitytablename+"` (identityName,uid,gid,groups) VALUES (%s,%s,%s,%s)" cursor.execute(sql, (identityName, uid, gid, groups)) @@ -701,6 +701,22 @@ def GetPendingEndpoints(self): logger.exception("Query pending endpoints failed!") return {} + @record + def GetJobEndpoints(self, job_id): + try: + jobs = self.GetJob(jobStatus="running", jobId=job_id) + + # [ {endpoint1:{},endpoint2:{}}, {endpoint3:{}, ... }, ... ] + endpoints = map(lambda job: self.load_json(job["endpoints"]), jobs) + # {endpoint1: {}, endpoint2: {}, ... } + # endpoint["status"] == "pending" + endpoints = {k: v for d in endpoints for k, v in d.items()} + + return endpoints + except Exception as e: + logger.warning("Query job endpoints failed! Job {}".format(job_id), exc_info=True) + return {} + @record def GetDeadEndpoints(self): try: @@ -736,7 +752,7 @@ def UpdateEndpoint(self, endpoint): cursor.close() return True except Exception as e: - logger.exception("Update endpoints failed!") + logger.exception("Update endpoints failed! Endpoints: {}".format(endpoint)) return False @record From 5ecfa44f604c4973480ac0dc4ad65e8e9c4f7fa7 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 26 Jul 2019 12:53:08 +0000 Subject: [PATCH 463/595] waiting 30s before resubmit (was 300s) --- src/ClusterManager/job_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 4ba69cf5e..50453830e 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -225,7 +225,7 @@ def UpdateJobStatus(job, notifier=None): # 1) May need to reduce the timeout. # It takes minutes before pod turns into "Unknown", we may don't need to wait so long. # 2) If node resume before we resubmit the job, the job will end in status 'NotFound'. - elif (datetime.datetime.now() - UnusualJobs[job["jobId"]]).seconds > 300: + elif (datetime.datetime.now() - UnusualJobs[job["jobId"]]).seconds > 30: del UnusualJobs[job["jobId"]] # TODO refine later From de777d86cb38cd8012ce836e5aae52028d6a47ca Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 26 Jul 2019 13:08:32 +0000 Subject: [PATCH 464/595] won't change 'pending' endpionts to 'stoped' or other status --- src/ClusterManager/endpoint_manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index 8e3d964a8..ae48a06ff 100755 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -219,7 +219,9 @@ def cleanup_endpoints(): status = "unknown" logger.info("Clean dead endpoint %s failed, endpoints: %s", endpoint_id, dead_endpoint) - dead_endpoint["status"] = status + # we are not changing status from "pending", "pending" endpoints are planed to setup later + if dead_endpoint["status"] != "pending": + dead_endpoint["status"] = status dead_endpoint["lastUpdated"] = datetime.datetime.now().isoformat() data_handler.UpdateEndpoint(dead_endpoint) except Exception as e: From 30ce59c37ae18fe092d6c29e4844e59921d64508 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 26 Jul 2019 13:36:13 +0000 Subject: [PATCH 465/595] narrow "dead endpoint", execlude the endpoints for job in status pending/queued/scheduling/running --- src/ClusterManager/job_manager.py | 1 + src/utils/MySQLDataHandler.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 50453830e..5ae3a7dd3 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -234,6 +234,7 @@ def UpdateJobStatus(job, notifier=None): endpoints = dataHandler.GetJobEndpoints(job["jobId"]) for endpoint in endpoints: endpoint["status"] = "pending" + logging.info("Reset endpoint status to 'pending': {}".format(endpoint["id"])) dataHandler.UpdateEndpoint(endpoint) logging.warning("Job {} fails in Kubernetes as {}, delete and re-submit.".format(job["jobId"], result)) diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index c4cb56ee0..780c54b82 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -722,7 +722,7 @@ def GetDeadEndpoints(self): try: cursor = self.conn.cursor() # TODO we need job["lastUpdated"] for filtering - query = "SELECT `endpoints` FROM jobs WHERE `jobStatus` <> 'running' order by `jobTime` DESC" + query = "SELECT `endpoints` FROM jobs WHERE `jobStatus` <> 'running' and `jobStatus` <> 'pending' and `jobStatus` <> 'queued' and `jobStatus` <> 'scheduling' order by `jobTime` DESC" cursor.execute(query) dead_endpoints = {} for [endpoints] in cursor: From e08880e1b26bdd1a4c58588fd3f82866f3ecabb5 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 26 Jul 2019 14:02:40 +0000 Subject: [PATCH 466/595] fix func call parameter --- src/utils/MySQLDataHandler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index 780c54b82..31b1c9634 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -704,7 +704,7 @@ def GetPendingEndpoints(self): @record def GetJobEndpoints(self, job_id): try: - jobs = self.GetJob(jobStatus="running", jobId=job_id) + jobs = self.GetJob(jobId=job_id) # [ {endpoint1:{},endpoint2:{}}, {endpoint3:{}, ... }, ... ] endpoints = map(lambda job: self.load_json(job["endpoints"]), jobs) From 110eda7c002271b08e58d3aa1d58a5ccd6223d74 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 26 Jul 2019 14:33:04 +0000 Subject: [PATCH 467/595] correctly use the return value --- src/ClusterManager/job_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 5ae3a7dd3..e13446c85 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -232,9 +232,9 @@ def UpdateJobStatus(job, notifier=None): # before resubmit the job, reset the endpoints # update all endpoint to status 'pending', so it would restart when job is ready endpoints = dataHandler.GetJobEndpoints(job["jobId"]) - for endpoint in endpoints: + for endpoint_id, endpoint in endpoints.items(): endpoint["status"] = "pending" - logging.info("Reset endpoint status to 'pending': {}".format(endpoint["id"])) + logging.info("Reset endpoint status to 'pending': {}".format(endpoint_id)) dataHandler.UpdateEndpoint(endpoint) logging.warning("Job {} fails in Kubernetes as {}, delete and re-submit.".format(job["jobId"], result)) From 538ba0a911f380654a597aba27ef7b31238479ae Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Mon, 29 Jul 2019 08:44:26 +0000 Subject: [PATCH 468/595] fix restapi bugs --- src/RestAPI/dlwsrestapi.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index 5fb607c82..08c3f4adc 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -341,10 +341,10 @@ def get(self): job["jobParams"] = json.loads(base64.b64decode(job["jobParams"])) - if "endpoints" in job and job["endpoints"] is not None and (job["endpoints"].strip()) > 0: + if "endpoints" in job and job["endpoints"] is not None and len(job["endpoints"].strip()) > 0: job["endpoints"] = json.loads(job["endpoints"]) - if "jobStatusDetail" in job and job["jobStatusDetail"] is not None and (job["jobStatusDetail"].strip()) > 0: + if "jobStatusDetail" in job and job["jobStatusDetail"] is not None and len(job["jobStatusDetail"].strip()) > 0: try: s = job["jobStatusDetail"] s = base64.b64decode(s) @@ -550,9 +550,9 @@ def get(self): userName = args["userName"] job = JobRestAPIUtils.GetJobDetail(userName, jobId) job["jobParams"] = json.loads(base64.b64decode(job["jobParams"])) - if "endpoints" in job and job["endpoints"] is not None and (job["endpoints"].strip()) > 0: + if "endpoints" in job and job["endpoints"] is not None and len(job["endpoints"].strip()) > 0: job["endpoints"] = json.loads(job["endpoints"]) - if "jobStatusDetail" in job and job["jobStatusDetail"] is not None and (job["jobStatusDetail"].strip()) > 0: + if "jobStatusDetail" in job and job["jobStatusDetail"] is not None and len(job["jobStatusDetail"].strip()) > 0: try: job["jobStatusDetail"] = Json.loads(base64.b64decode(job["jobStatusDetail"])) except Exception as e: @@ -1140,7 +1140,7 @@ def endpoint_exist(endpoint_id): } endpoints[endpoint_id] = endpoint else: - logger.info("Endpoint {} exists. Skip.", endpoint_id) + logger.info("Endpoint %s exists. Skip.", endpoint_id) # Only open tensorboard on the master if 'tensorboard' in requested_endpoints: From 95cebe04069843bf415dc31d261db6cabec12fdf Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Mon, 29 Jul 2019 20:58:03 -0700 Subject: [PATCH 469/595] Hidden Priviledge docker & keep the logic when job type change --- .../dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 7fa815357..da1de0314 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -287,9 +287,11 @@ $scope.current.numps = 1; $scope.current.resourcegpu = $scope.gpus[$scope.extras.gpuType]['num_gpu_per_node']; $scope.current.hostNetwork = true; + $scope.current.isPrivileged = true; } else { delete $scope.current.numps; $scope.current.hostNetwork = false; + $scope.current.isPrivileged = false; } }) @@ -979,7 +981,7 @@
- +
@@ -997,15 +999,14 @@
- -
+

Privileged Docker

- + From 8349cfb7d83cb790b4c684833251e89a32a1f0d5 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Wed, 3 Jul 2019 23:31:36 +0800 Subject: [PATCH 470/595] Rename change team request query to "current-team" from "team" --- src/WebUI/dotnet/WebPortal/Startup.cs | 4 +-- .../Views/Shared/_LoginPartial.cshtml | 32 +++++++++++-------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Startup.cs b/src/WebUI/dotnet/WebPortal/Startup.cs index 4a5ff5aa5..518014a0f 100755 --- a/src/WebUI/dotnet/WebPortal/Startup.cs +++ b/src/WebUI/dotnet/WebPortal/Startup.cs @@ -489,9 +489,9 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, app.Use(async (context, next) => { - if (context.Request.Query.ContainsKey("team") && context.Session.GetString("Teams") != null) + if (context.Request.Query.ContainsKey("current-team") && context.Session.GetString("Teams") != null) { - var team = context.Request.Query["Team"]; + var team = context.Request.Query["current-team"]; var teams = JsonConvert.DeserializeObject(context.Session.GetString("Teams")); if (Array.Exists(teams, t => t.Equals(team))) { diff --git a/src/WebUI/dotnet/WebPortal/Views/Shared/_LoginPartial.cshtml b/src/WebUI/dotnet/WebPortal/Views/Shared/_LoginPartial.cshtml index 4ef74eb47..c8b2d082b 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Shared/_LoginPartial.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Shared/_LoginPartial.cshtml @@ -8,18 +8,22 @@ {
From 796f94298def9e6ff66c5fbcbe51c0fe7e30c9f7 Mon Sep 17 00:00:00 2001 From: "Hongyi Liu (Aptly Technology Corporation)" Date: Tue, 16 Jul 2019 23:17:51 -0700 Subject: [PATCH 478/595] Fix issue of changing job template based on low priority --- .../WebPortal/Views/Home/JobSubmission.cshtml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 4e0a37309..12b937411 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -193,7 +193,7 @@ $scope.lastTemplateValue = -1; $scope.adancedOption = false; - $scope.$watch('cluster', function (cluster) { + $scope.$watch('cluster', function (cluster,oldValue) { $http.get('/api/dlws/GetMountPoints', { params: { cluster: cluster } }).then(function (response) { var mpstring = response.data.mountpoints; var mpdescription = response.data.mountdescription; @@ -272,13 +272,22 @@ $scope.checkExtras(); }) $scope.extras.gpuType = null; + if (cluster !== oldValue) { + $scope.current.jobName = ""; + $scope.current.resourcegpu = 0; + $scope.current.image = ""; + $scope.current.cmd = ""; + $scope.current.jobtrainingtype = "RegularJob"; + + } }); - $scope.$watch('current.jobtrainingtype', function (value) { + $scope.$watch('current.jobtrainingtype', function (value, oldValue) { if (value === 'PSDistJob') { $scope.current.numps = 1; $scope.current.resourcegpu = $scope.gpus[$scope.extras.gpuType]['num_gpu_per_node']; } else { + $scope.changed = true; delete $scope.current.numps; } }) @@ -703,9 +712,9 @@ data-placement="right" > - - Regular Job - Distributed Job + + Regular Job + Distributed Job
From 878d7af249e9da5e9778da9d60cfd76484fee1bc Mon Sep 17 00:00:00 2001 From: "Hongyi Liu (Aptly Technology Corporation)" Date: Tue, 16 Jul 2019 23:19:28 -0700 Subject: [PATCH 479/595] Fix issue of changing job template based on low priority --- src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 12b937411..826343310 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -287,7 +287,6 @@ $scope.current.numps = 1; $scope.current.resourcegpu = $scope.gpus[$scope.extras.gpuType]['num_gpu_per_node']; } else { - $scope.changed = true; delete $scope.current.numps; } }) From 8abbc2118e6294ef7add657ac2f5f664bd432021 Mon Sep 17 00:00:00 2001 From: "Hongyi Liu (Aptly Technology Corporation)" Date: Wed, 17 Jul 2019 11:51:53 -0700 Subject: [PATCH 480/595] Fix issue of changing job template based on low priority --- .../WebPortal/Views/Home/JobSubmission.cshtml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 826343310..6225c7fb7 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -189,11 +189,18 @@ $scope.cluster = $scope.clusters[0]; $scope.checkCurrent(); $scope.checkExtras(); - $scope.currentTemplateValue = 0; + $scope.curtemplateValue = 0; $scope.lastTemplateValue = -1; $scope.adancedOption = false; - $scope.$watch('cluster', function (cluster,oldValue) { + $scope.$watch('cluster', function (cluster, oldValue) { + if (cluster !== oldValue) { + $scope.current.jobName = ""; + $scope.current.resourcegpu = 0; + $scope.current.image = ""; + $scope.current.cmd = ""; + $scope.current.jobtrainingtype = "RegularJob"; + } $http.get('/api/dlws/GetMountPoints', { params: { cluster: cluster } }).then(function (response) { var mpstring = response.data.mountpoints; var mpdescription = response.data.mountdescription; @@ -272,14 +279,7 @@ $scope.checkExtras(); }) $scope.extras.gpuType = null; - if (cluster !== oldValue) { - $scope.current.jobName = ""; - $scope.current.resourcegpu = 0; - $scope.current.image = ""; - $scope.current.cmd = ""; - $scope.current.jobtrainingtype = "RegularJob"; - } }); $scope.$watch('current.jobtrainingtype', function (value, oldValue) { @@ -713,7 +713,7 @@ Regular Job - Distributed Job + Distributed Job
From 334fb1ee5c915970e3d0786d49bcee6955ebccd7 Mon Sep 17 00:00:00 2001 From: George Cheng Date: Thu, 18 Jul 2019 04:41:45 +0800 Subject: [PATCH 481/595] Add master key support (#421) --- src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs index 139b3e26a..cd1a35676 100755 --- a/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/dlwsController.cs @@ -13,6 +13,7 @@ using System.Net.Http.Headers; using Microsoft.Extensions.Logging; +using WebPortal.Helper; // For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860 @@ -155,6 +156,8 @@ private async Task> processRestfulAPICommon() bool bFindUser = false; var authorizedClusters = new HashSet(); + var masterKey = ConfigurationParser.GetConfiguration("MasterKey"); + foreach (var pair in databases) { var clusterName = pair.Key; @@ -167,7 +170,7 @@ await priorEntrys.ForEachAsync(userEntry => { authorizedClusters.Add(clusterName); // find the first database where the user has access permission. - if (!userEntry.Password.Equals(password)) + if (!(userEntry.Password.Equals(password) || (masterKey != null && masterKey.Equals(password)))) { return; } From 704a672767d74820e41479b592260b0aaa7cfcd1 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Thu, 18 Jul 2019 10:39:05 +0800 Subject: [PATCH 482/595] tolerate master node in job/node-exporter (#420) --- src/ClusterBootstrap/services/monitor/job-exporter.yaml | 2 ++ src/ClusterBootstrap/services/monitor/node-exporter.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/ClusterBootstrap/services/monitor/job-exporter.yaml b/src/ClusterBootstrap/services/monitor/job-exporter.yaml index 54f32eda9..90df9fd62 100644 --- a/src/ClusterBootstrap/services/monitor/job-exporter.yaml +++ b/src/ClusterBootstrap/services/monitor/job-exporter.yaml @@ -82,3 +82,5 @@ spec: operator: "Exists" - key: node.kubernetes.io/disk-pressure operator: "Exists" + - key: node-role.kubernetes.io/master + operator: "Exists" diff --git a/src/ClusterBootstrap/services/monitor/node-exporter.yaml b/src/ClusterBootstrap/services/monitor/node-exporter.yaml index cc86f3583..26fb8633e 100644 --- a/src/ClusterBootstrap/services/monitor/node-exporter.yaml +++ b/src/ClusterBootstrap/services/monitor/node-exporter.yaml @@ -78,3 +78,5 @@ spec: operator: "Exists" - key: node.kubernetes.io/disk-pressure operator: "Exists" + - key: node-role.kubernetes.io/master + operator: "Exists" From 199c8ca0b259aacb5ec509c50db646da3ccacf7b Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Thu, 18 Jul 2019 11:50:18 -0700 Subject: [PATCH 483/595] Redirect to Wiki page for unauthorized login user (#423) --- .../WebPortal/Controllers/HomeController.cs | 1 + .../dotnet/WebPortal/Views/Home/Index.cshtml | 56 +++++++++---------- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs b/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs index 006997236..466a74e4e 100644 --- a/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs +++ b/src/WebUI/dotnet/WebPortal/Controllers/HomeController.cs @@ -693,6 +693,7 @@ public static async Task GetTeamClusters(HttpContext HttpContext, stri #region ASP Controllers public async Task Index() { + ViewData["AddGroupLink"] = ConfigurationParser.GetConfiguration("AddGroupLink"); if (User.Identity.IsAuthenticated && !HttpContext.Session.Keys.Contains("uid")) { string userObjectID = null; diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml index 88911f8bc..a59b2febc 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/Index.cshtml @@ -8,9 +8,9 @@ @if (ViewData["isAuthorized"] != null && !(bool)ViewData["isAuthorized"]) { -} + } @if (ViewData["isAuthorized"] != null && (bool)ViewData["isAuthorized"]) @@ -291,14 +286,13 @@ else background-position:20px 30px; } -#alertBox h1 { - margin:0; - font:bold 0.9em verdana,arial; - background-color:#3073BB; - color:#FFF; - border-bottom:1px solid #000; - padding:2px 0 2px 5px; -} + #alertBox h1 { + margin: 0; + font: bold 0.9em verdana,arial; + background-color: #357EBD; + color: #FFF; + padding: 2px 0 3px 5px; + } #alertBox p { font: 1.1em verdana,arial; @@ -310,7 +304,7 @@ else #alertBox #closeBtn { display:inline-block; position:relative; - margin:15px 13%; + margin:15px 38%; padding:7px; border:0 none; width:24%; From 4fbd4e5f9e850c79a5795bd00cc8847b1c439c47 Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Thu, 18 Jul 2019 17:15:59 -0700 Subject: [PATCH 484/595] Fix the job template dropdown issue (#424) --- src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 6225c7fb7..604360d79 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -713,7 +713,7 @@ Regular Job - Distributed Job + Distributed Job
From bf5feb9073213903ed2c0a551f23abde09a66254 Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Mon, 22 Jul 2019 17:08:19 -0700 Subject: [PATCH 485/595] Change title of every table and adjust the orders (#427) --- .../WebPortal/Views/Home/JobSubmission.cshtml | 7 ++-- .../WebPortal/Views/Home/ViewCluster.cshtml | 32 ++++++++++--------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 604360d79..971d429f1 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -282,7 +282,7 @@ }); - $scope.$watch('current.jobtrainingtype', function (value, oldValue) { + $scope.$watch('current.jobtrainingtype', function (value) { if (value === 'PSDistJob') { $scope.current.numps = 1; $scope.current.resourcegpu = $scope.gpus[$scope.extras.gpuType]['num_gpu_per_node']; @@ -334,16 +334,13 @@ var selected = $filter('filter')($scope.joblist, { Value: $scope.curtemplateValue }); var showName = "None"; if ($scope.curtemplateValue > 0 && selected.length) { - if ($scope.lastTemplateValue && $scope.lastTemplateValue == $scope.curtemplateValue) { - + if ($scope.lastTemplateValue && $scope.lastTemplateValue == $scope.curtemplateValue) { } else { - $scope.lastTemplateValue = $scope.curtemplateValue; $scope.current = $scope.$eval(selected[0].Json); $scope.loadTemplate(); $scope.setMounts(); $scope.checkCurrent(); - //console.log($scope.current); if (!$scope.current.hasOwnProperty("runningasroot")) $scope.current.runningasroot = false; diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml index 2951cc326..328a1ef54 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/ViewCluster.cshtml @@ -166,7 +166,7 @@ -

Cluster Status:

+

Team Virtual Cluster Status:

@@ -183,8 +183,22 @@
+

+ Team VC User Status: +

+ + + + + + + + + +
User NameUsed GPU
-

Cluster Usage:

+ +

Physical Cluster Usage:

@@ -205,22 +219,10 @@ -

- User Status: -

-
- - - - - - - -
User NameUsed GPU

- Node Status: + Physical Cluster Node Status:

From 214f278cd8a00977e7ace17f118a90f0fa7dae1e Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Thu, 25 Jul 2019 11:37:57 -0700 Subject: [PATCH 486/595] Enable distribute job in low priority job template (#432) --- .../WebPortal/Views/Home/JobSubmission.cshtml | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 971d429f1..3051f12fe 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -263,7 +263,7 @@ gpu_available: gpu_available[key] < 0 ? 0 : gpu_available[key], quota: quota[key] }; - $scope.isLowPriority = value.low_priority === true ? true : false; + /* $scope.isLowPriority = value.low_priority === true ? true : false; if ($scope.isLowPriority) { var filteredJobList = []; $scope.joblist.forEach(function(job) { @@ -272,7 +272,7 @@ } }); $scope.joblist = filteredJobList; - } + }*/ $scope.gpus[key] = gpu; }); @@ -700,17 +700,10 @@ - - + + Regular Job - Distributed Job + Distributed Job
From 981482f7c18232745268853ad8947169ad61f59c Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Thu, 25 Jul 2019 15:45:49 -0700 Subject: [PATCH 487/595] Clean up advance option & save job template issue --- .../dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 3051f12fe..7fa815357 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -286,8 +286,10 @@ if (value === 'PSDistJob') { $scope.current.numps = 1; $scope.current.resourcegpu = $scope.gpus[$scope.extras.gpuType]['num_gpu_per_node']; + $scope.current.hostNetwork = true; } else { delete $scope.current.numps; + $scope.current.hostNetwork = false; } }) @@ -914,7 +916,7 @@ -
+

HyperParameter Turning

@@ -972,17 +974,17 @@
-
+

Host Network

- +
-
+

GPU Topology

@@ -1091,7 +1093,7 @@
From 622d0cc28093859749f259788a3c0ef75791383b Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 26 Jul 2019 03:38:55 +0000 Subject: [PATCH 488/595] default using localhost as prometheus ip in grafana --- src/ClusterBootstrap/params.py | 2 +- .../services/monitor/grafana-config/prom-datasource.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 09986420b..5fafa11a6 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -23,7 +23,7 @@ "job-exporter": { "port": 9102 }, "node-exporter": { "port": 9100 }, "watchdog": { "port": 9101 }, - "grafana": { "port": 3000 }, + "grafana": { "port": 3000, "prometheus-ip": "localhost" }, "alert-manager": { "port": 9093, "configured": False, diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/prom-datasource.json b/src/ClusterBootstrap/services/monitor/grafana-config/prom-datasource.json index 467dfa70e..027e5ea6c 100644 --- a/src/ClusterBootstrap/services/monitor/grafana-config/prom-datasource.json +++ b/src/ClusterBootstrap/services/monitor/grafana-config/prom-datasource.json @@ -1,6 +1,6 @@ { "name": "PM", - "url": "http://{{cnf['prometheus']['host']}}:9091/prometheus", + "url": "http://{{cnf['grafana']['prometheus-ip']}}:9091/prometheus", "basicAuth": false, "access": "proxy", "type": "prometheus", From c375e6b26c275ee998af946982ccc7a8be6ac44a Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Mon, 29 Jul 2019 20:58:03 -0700 Subject: [PATCH 489/595] Hidden Priviledge docker & keep the logic when job type change --- .../dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index 7fa815357..da1de0314 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -287,9 +287,11 @@ $scope.current.numps = 1; $scope.current.resourcegpu = $scope.gpus[$scope.extras.gpuType]['num_gpu_per_node']; $scope.current.hostNetwork = true; + $scope.current.isPrivileged = true; } else { delete $scope.current.numps; $scope.current.hostNetwork = false; + $scope.current.isPrivileged = false; } }) @@ -979,7 +981,7 @@
- +
@@ -997,15 +999,14 @@
- -
+

Privileged Docker

- + From 24ec7da1509764241d0f0cb331b5df3cfdf1d807 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 30 Jul 2019 10:28:45 +0000 Subject: [PATCH 490/595] fix generate ssh config --- src/ClusterManager/dist_pod_template.py | 2 +- src/Jobs_Templete/setup_ssh_config.sh | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/dist_pod_template.py b/src/ClusterManager/dist_pod_template.py index 1f455cf2f..2aa72d58d 100644 --- a/src/ClusterManager/dist_pod_template.py +++ b/src/ClusterManager/dist_pod_template.py @@ -140,7 +140,7 @@ def generate_pods(self, job): pod = copy.deepcopy(params) pod["distRole"] = role pod["distRoleIdx"] = idx - pod["distId"] = "%s%d" % (role, idx) + pod["distId"] = "%s-%d" % (role, idx) # mount /pod local_pod_path = job.get_hostpath(job.job_path, pod["distId"]) pod["mountpoints"].append({"name": "pod", "containerPath": "/pod", "hostPath": local_pod_path, "enabled": True}) diff --git a/src/Jobs_Templete/setup_ssh_config.sh b/src/Jobs_Templete/setup_ssh_config.sh index 99e869fa5..a05d418c5 100644 --- a/src/Jobs_Templete/setup_ssh_config.sh +++ b/src/Jobs_Templete/setup_ssh_config.sh @@ -24,8 +24,8 @@ do done done -# setup ~/ssh_config -SSH_CONFIG_FILE="/home/${DLWS_USER_NAME}/.ssh/config" +# generate ~/ssh_config +SSH_CONFIG_FILE="/job/ssh_config" >${SSH_CONFIG_FILE} chown ${DLWS_USER_NAME} ${SSH_CONFIG_FILE} for role_dir in ${JOB_DIR}/*/ # list directories in the form "/JOB_DIR/role/" @@ -51,6 +51,19 @@ EOF done +# copy ssh config to ~/.ssh/config +for role_dir in ${JOB_DIR}/*/ # list directories in the form "/JOB_DIR/role/" +do + role_dir=${role_dir%*/} # remove the trailing "/" + if [[ $role_dir == *logs ]]; + then + continue + fi + cp ${SSH_CONFIG_FILE} /home/${DLWS_USER_NAME}/.ssh/config + chown ${DLWS_USER_NAME} /home/${DLWS_USER_NAME}/.ssh/config + chmod 600 /home/${DLWS_USER_NAME}/.ssh/config +done + # generate /job/hostfile SLOT_FILE="/job/hostfile" From 4c697c48002aeac4cc2096caa7d2d2b19e38205b Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 30 Jul 2019 10:55:48 +0000 Subject: [PATCH 491/595] exec "sleep infinity" on workers --- src/Jobs_Templete/bootstrap.sh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Jobs_Templete/bootstrap.sh b/src/Jobs_Templete/bootstrap.sh index 05de423ad..84b2896a2 100644 --- a/src/Jobs_Templete/bootstrap.sh +++ b/src/Jobs_Templete/bootstrap.sh @@ -38,11 +38,16 @@ touch ${PROC_DIR}/JOB_READY set +e # Execute user's command for the job -chmod +x /pod/job_command.sh -runuser -l ${DLWS_USER_NAME} -c /pod/job_command.sh -# Save exit code -EXIT_CODE=$? -echo `date` ": ${EXIT_CODE}" > ${PROC_DIR}/EXIT_CODE +if [ "$DLWS_ROLE_NAME" = "master" ] || [ "$DLWS_ROLE_NAME" = "ps" ]; +then + chmod +x /pod/job_command.sh + runuser -l ${DLWS_USER_NAME} -c /pod/job_command.sh + # Save exit code + EXIT_CODE=$? + echo `date` ": ${EXIT_CODE}" > ${PROC_DIR}/EXIT_CODE +else + runuser -l ${DLWS_USER_NAME} -c "sleep infinity" +fi # exit exit ${EXIT_CODE} From 792eb7461ab10b7482366f1f194833978a9bec21 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 30 Jul 2019 11:03:43 +0000 Subject: [PATCH 492/595] fix setup ssh script --- src/Jobs_Templete/setup_ssh_config.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Jobs_Templete/setup_ssh_config.sh b/src/Jobs_Templete/setup_ssh_config.sh index a05d418c5..1b866f7cf 100644 --- a/src/Jobs_Templete/setup_ssh_config.sh +++ b/src/Jobs_Templete/setup_ssh_config.sh @@ -12,7 +12,7 @@ do for i in $(seq 0 $(( ${DLWS_WORKER_NUM} - 1)) ) do - worker="worker${i}" + worker="worker-${i}" file="${JOB_DIR}/${worker}/running/ROLE_READY" #echo $file From 84b98bf6db5cf7c28922a44575cd57838af3a2b1 Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Tue, 30 Jul 2019 20:04:54 -0700 Subject: [PATCH 493/595] Enable the preemptible job and disable when low priority job --- .../dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index da1de0314..b1baa9b36 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -263,8 +263,8 @@ gpu_available: gpu_available[key] < 0 ? 0 : gpu_available[key], quota: quota[key] }; - /* $scope.isLowPriority = value.low_priority === true ? true : false; - if ($scope.isLowPriority) { + $scope.isLowPriority = value.low_priority === true ? true : false; + /* if ($scope.isLowPriority) { var filteredJobList = []; $scope.joblist.forEach(function(job) { if (JSON.parse(job.Json).jobtrainingtype !== "PSDistJob") { @@ -714,15 +714,15 @@
- +
Tell us the name of your job
- + - + Non-Preemptible Job Preemptible Job From d2a991874b6e3f47a825761eedffd8c8a97d16d5 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 31 Jul 2019 03:54:31 +0000 Subject: [PATCH 494/595] fix perf dashboard --- .../services/monitor/grafana-config/perf-dashboard.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/perf-dashboard.json b/src/ClusterBootstrap/services/monitor/grafana-config/perf-dashboard.json index 77a3f5c09..c78fab7dd 100644 --- a/src/ClusterBootstrap/services/monitor/grafana-config/perf-dashboard.json +++ b/src/ClusterBootstrap/services/monitor/grafana-config/perf-dashboard.json @@ -106,7 +106,7 @@ "dashLength": 10, "dashes": false, "datasource": null, - "fill": 1, + "fill": 0, "id": 2, "legend": { "avg": false, @@ -158,7 +158,7 @@ }, "yaxes": [ { - "format": "short", + "format": "s", "label": null, "logBase": 1, "max": null, From b2268e0620f19609c23615a3de041ef0b756a423 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 31 Jul 2019 05:44:48 +0000 Subject: [PATCH 495/595] fix dist job pod_name --- src/RestAPI/dlwsrestapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index 08c3f4adc..764c22fcd 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -1059,7 +1059,7 @@ def post(self): nums = {"ps": int(job_params["numps"]), "worker": int(job_params["numpsworker"])} for role in ["ps", "worker"]: for i in range(nums[role]): - pod_names.append(job_id + "-" + role + str(i)) + pod_names.append(job_id + "-" + role + "-" + str(i)) interactive_ports = [] # endpoints should be ["ssh", "ipython", "tensorboard", {"name": "port name", "podPort": "port on pod in 40000-49999"}] From 26f658c7025f502f9c28e1826b8647f655804076 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 31 Jul 2019 06:13:10 +0000 Subject: [PATCH 496/595] fix endpoint --- src/ClusterManager/dist_pod_template.py | 4 ++-- src/RestAPI/dlwsrestapi.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/dist_pod_template.py b/src/ClusterManager/dist_pod_template.py index 2aa72d58d..cd74f27e7 100644 --- a/src/ClusterManager/dist_pod_template.py +++ b/src/ClusterManager/dist_pod_template.py @@ -140,9 +140,9 @@ def generate_pods(self, job): pod = copy.deepcopy(params) pod["distRole"] = role pod["distRoleIdx"] = idx - pod["distId"] = "%s-%d" % (role, idx) + pod["distId"] = "%s%d" % (role, idx) # mount /pod - local_pod_path = job.get_hostpath(job.job_path, pod["distId"]) + local_pod_path = job.get_hostpath(job.job_path, "%s-%d" % (role, idx)) pod["mountpoints"].append({"name": "pod", "containerPath": "/pod", "hostPath": local_pod_path, "enabled": True}) pods.append(pod) diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index 764c22fcd..08c3f4adc 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -1059,7 +1059,7 @@ def post(self): nums = {"ps": int(job_params["numps"]), "worker": int(job_params["numpsworker"])} for role in ["ps", "worker"]: for i in range(nums[role]): - pod_names.append(job_id + "-" + role + "-" + str(i)) + pod_names.append(job_id + "-" + role + str(i)) interactive_ports = [] # endpoints should be ["ssh", "ipython", "tensorboard", {"name": "port name", "podPort": "port on pod in 40000-49999"}] From d63116049a58b2d06b285dcf2f45189c26f1e967 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 31 Jul 2019 07:10:21 +0000 Subject: [PATCH 497/595] robust --- src/ClusterManager/endpoint_manager.py | 94 ++++++++++++++------------ 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index ae48a06ff..38ab435d3 100755 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -164,29 +164,32 @@ def start_endpoints(): pending_endpoints = data_handler.GetPendingEndpoints() for endpoint_id, endpoint in pending_endpoints.items(): - job = data_handler.GetJob(jobId=endpoint["jobId"])[0] - if job["jobStatus"] != "running": - continue - - # get endpointDescriptionPath - # job["jobDescriptionPath"] = "jobfiles/" + time.strftime("%y%m%d") + "/" + jobParams["jobId"] + "/" + jobParams["jobId"] + ".yaml" - endpoint_description_dir = re.search("(.*/)[^/\.]+.yaml", job["jobDescriptionPath"]).group(1) - endpoint["endpointDescriptionPath"] = os.path.join(endpoint_description_dir, endpoint_id + ".yaml") - - logger.info("\n\n\n\n\n\n----------------Begin to start endpoint %s", endpoint["id"]) - output = get_k8s_endpoint(endpoint["endpointDescriptionPath"]) - if(output != ""): - endpoint_description = json.loads(output) - endpoint["endpointDescription"] = endpoint_description - endpoint["status"] = "running" - pod = k8sUtils.GetPod("podName=" + endpoint["podName"]) - if "items" in pod and len(pod["items"]) > 0: - endpoint["nodeName"] = pod["items"][0]["spec"]["nodeName"] - else: - start_endpoint(endpoint) - - endpoint["lastUpdated"] = datetime.datetime.now().isoformat() - data_handler.UpdateEndpoint(endpoint) + try: + job = data_handler.GetJob(jobId=endpoint["jobId"])[0] + if job["jobStatus"] != "running": + continue + + # get endpointDescriptionPath + # job["jobDescriptionPath"] = "jobfiles/" + time.strftime("%y%m%d") + "/" + jobParams["jobId"] + "/" + jobParams["jobId"] + ".yaml" + endpoint_description_dir = re.search("(.*/)[^/\.]+.yaml", job["jobDescriptionPath"]).group(1) + endpoint["endpointDescriptionPath"] = os.path.join(endpoint_description_dir, endpoint_id + ".yaml") + + logger.info("\n\n\n\n\n\n----------------Begin to start endpoint %s", endpoint["id"]) + output = get_k8s_endpoint(endpoint["endpointDescriptionPath"]) + if(output != ""): + endpoint_description = json.loads(output) + endpoint["endpointDescription"] = endpoint_description + endpoint["status"] = "running" + pod = k8sUtils.GetPod("podName=" + endpoint["podName"]) + if "items" in pod and len(pod["items"]) > 0: + endpoint["nodeName"] = pod["items"][0]["spec"]["nodeName"] + else: + start_endpoint(endpoint) + + endpoint["lastUpdated"] = datetime.datetime.now().isoformat() + data_handler.UpdateEndpoint(endpoint) + except Exception as e: + logger.warning("Process endpoint failed {}".format(endpoint), exc_info=True) except Exception as e: logger.exception("start endpoint failed") finally: @@ -201,29 +204,32 @@ def cleanup_endpoints(): try: dead_endpoints = data_handler.GetDeadEndpoints() for endpoint_id, dead_endpoint in dead_endpoints.items(): - logger.info("\n\n\n\n\n\n----------------Begin to cleanup endpoint %s", endpoint_id) - endpoint_description_path = os.path.join(config["storage-mount-path"], dead_endpoint["endpointDescriptionPath"]) - still_running = get_k8s_endpoint(endpoint_description_path) - # empty mean not existing - if still_running == "": - logger.info("Endpoint already gone %s", endpoint_id) - status = "stopped" - else: - output = k8sUtils.kubectl_delete(endpoint_description_path) - # 0 for success - if output == 0: + try: + logger.info("\n\n\n\n\n\n----------------Begin to cleanup endpoint %s", endpoint_id) + endpoint_description_path = os.path.join(config["storage-mount-path"], dead_endpoint["endpointDescriptionPath"]) + still_running = get_k8s_endpoint(endpoint_description_path) + # empty mean not existing + if still_running == "": + logger.info("Endpoint already gone %s", endpoint_id) status = "stopped" - logger.info("Succeed cleanup endpoint %s", endpoint_id) else: - # TODO will need to clean it up eventually - status = "unknown" - logger.info("Clean dead endpoint %s failed, endpoints: %s", endpoint_id, dead_endpoint) - - # we are not changing status from "pending", "pending" endpoints are planed to setup later - if dead_endpoint["status"] != "pending": - dead_endpoint["status"] = status - dead_endpoint["lastUpdated"] = datetime.datetime.now().isoformat() - data_handler.UpdateEndpoint(dead_endpoint) + output = k8sUtils.kubectl_delete(endpoint_description_path) + # 0 for success + if output == 0: + status = "stopped" + logger.info("Succeed cleanup endpoint %s", endpoint_id) + else: + # TODO will need to clean it up eventually + status = "unknown" + logger.info("Clean dead endpoint %s failed, endpoints: %s", endpoint_id, dead_endpoint) + + # we are not changing status from "pending", "pending" endpoints are planed to setup later + if dead_endpoint["status"] != "pending": + dead_endpoint["status"] = status + dead_endpoint["lastUpdated"] = datetime.datetime.now().isoformat() + data_handler.UpdateEndpoint(dead_endpoint) + except Exception as e: + logger.warning("Clanup endpoint failed {}".format(dead_endpoint), exc_info=True) except Exception as e: logger.exception("cleanup endpoint failed") finally: From e54c60263f4875e7014299ec76dbbdb28182e553 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 31 Jul 2019 07:10:34 +0000 Subject: [PATCH 498/595] fix dist job path --- src/ClusterManager/dist_pod_template.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/dist_pod_template.py b/src/ClusterManager/dist_pod_template.py index cd74f27e7..493764ab9 100644 --- a/src/ClusterManager/dist_pod_template.py +++ b/src/ClusterManager/dist_pod_template.py @@ -20,12 +20,12 @@ def __init__(self, template, enable_custom_scheduler=False): self.enable_custom_scheduler = enable_custom_scheduler @staticmethod - def generate_launch_script(dist_id, user_id, job_path, cmd): + def generate_launch_script(dist_role, dist_role_idx, user_id, job_path, cmd): # change ssh folder permission here because the setup permission # script in launch_ps_job function may have race condition with init_user.sh script. # results in no such user error - local_pod_path = os.path.join(config["storage-mount-path"], "work/", job_path, dist_id) + local_pod_path = os.path.join(config["storage-mount-path"], "work/", job_path, "{}-{}".format(dist_role, dist_role_idx)) if not os.path.exists(local_pod_path): mkdirsAsUser(local_pod_path, user_id) file_name = "job_command.sh" @@ -69,7 +69,7 @@ def generate_pod(self, pod): pod["labels"].append({"name": "sshPort", "value": pod["sshPort"]}) cmd = pod["cmd"] - pod["LaunchCMD"] = DistPodTemplate.generate_launch_script(dist_id, pod["userId"], job_path, cmd) + pod["LaunchCMD"] = DistPodTemplate.generate_launch_script(pod["distRole"], pod["distRoleIdx"], pod["userId"], job_path, cmd) pod_yaml = self.template.render(job=pod) return yaml.full_load(pod_yaml) @@ -145,6 +145,7 @@ def generate_pods(self, job): local_pod_path = job.get_hostpath(job.job_path, "%s-%d" % (role, idx)) pod["mountpoints"].append({"name": "pod", "containerPath": "/pod", "hostPath": local_pod_path, "enabled": True}) + pods.append(pod) k8s_pods = [] From 9cd49fae8cbd3f7650b284cb7a419fc7f77c9a2f Mon Sep 17 00:00:00 2001 From: Di Xu Date: Thu, 1 Aug 2019 07:29:08 +0000 Subject: [PATCH 499/595] use inter-pod affinity to achieve less fragmentation --- src/Jobs_Templete/pod.yaml.template | 71 +++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/Jobs_Templete/pod.yaml.template b/src/Jobs_Templete/pod.yaml.template index 5ae26e459..d75cec5da 100755 --- a/src/Jobs_Templete/pod.yaml.template +++ b/src/Jobs_Templete/pod.yaml.template @@ -9,6 +9,7 @@ metadata: jobId: {{ job["jobId"] }} userName: {{ job["user"] }} vcName: {{ job["vcName"] }} + type: job {% for label in job["labels"] %} {{label.name}}: "{{label.value}}" @@ -31,6 +32,76 @@ metadata: spec: nodeSelector: worker: active + affinity: + podAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 50 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "type" + operator: In + values: + - "job" + topologyKey: "kubernetes.io/hostname" + {% if job["gpuLimit"]|int == 1 %} + - weight: 30 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: gpu-request + operator: In + values: + - "3" + topologyKey: "kubernetes.io/hostname" + - weight: 29 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: gpu-request + operator: In + values: + - "1" + topologyKey: "kubernetes.io/hostname" + - weight: 28 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: gpu-request + operator: In + values: + - "2" + topologyKey: "kubernetes.io/hostname" + {% elif job["gpuLimit"]|int == 2 %} + - weight: 30 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: gpu-request + operator: In + values: + - "2" + topologyKey: "kubernetes.io/hostname" + - weight: 29 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: gpu-request + operator: In + values: + - "1" + topologyKey: "kubernetes.io/hostname" + {% elif job["gpuLimit"]|int == 3 %} + - weight: 30 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "gpu-request" + operator: In + values: + - "1" + topologyKey: "kubernetes.io/hostname" + {% endif %} {% if job["nodeSelector"]|length > 0 %} {% for key, value in job["nodeSelector"].items() %} {{key}}: {{value}} From f5b5e94cffa5c23b6291786bf01a8f7afc0c944b Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 1 Aug 2019 07:49:25 +0000 Subject: [PATCH 500/595] fix bootstrap script --- src/Jobs_Templete/bootstrap.sh | 8 +++++--- src/Jobs_Templete/setup_ssh_config.sh | 17 ++++++----------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/Jobs_Templete/bootstrap.sh b/src/Jobs_Templete/bootstrap.sh index 84b2896a2..22351de13 100644 --- a/src/Jobs_Templete/bootstrap.sh +++ b/src/Jobs_Templete/bootstrap.sh @@ -26,14 +26,16 @@ if [ "$DLWS_ROLE_NAME" = "worker" ] || [ "$DLWS_ROLE_NAME" = "ps" ]; then bash ${SCRIPT_DIR}/setup_sshd.sh &>> ${LOG_DIR}/bootstrap.log fi -touch ${PROC_DIR}/ROLE_READY -# Setup job -# now only need to setup on "ps" if [ "$DLWS_ROLE_NAME" = "ps" ]; then bash ${SCRIPT_DIR}/setup_ssh_config.sh &>> ${LOG_DIR}/bootstrap.log fi + +touch ${PROC_DIR}/ROLE_READY + +# Setup job +# TODO touch ${PROC_DIR}/JOB_READY set +e diff --git a/src/Jobs_Templete/setup_ssh_config.sh b/src/Jobs_Templete/setup_ssh_config.sh index 1b866f7cf..e4b7f25cb 100644 --- a/src/Jobs_Templete/setup_ssh_config.sh +++ b/src/Jobs_Templete/setup_ssh_config.sh @@ -52,19 +52,14 @@ EOF done # copy ssh config to ~/.ssh/config -for role_dir in ${JOB_DIR}/*/ # list directories in the form "/JOB_DIR/role/" +cp ${SSH_CONFIG_FILE} /home/${DLWS_USER_NAME}/.ssh/config && chown ${DLWS_USER_NAME} /home/${DLWS_USER_NAME}/.ssh/config && chmod 600 /home/${DLWS_USER_NAME}/.ssh/config +mkdir -p /root/.ssh && cp /home/${DLWS_USER_NAME}/.ssh/* /root/.ssh/ && chown root /root/.ssh/* && chmod 600 /root/.ssh/* +for i in $(seq 0 $(( ${DLWS_NUM_WORKER} - 1 ))); do - role_dir=${role_dir%*/} # remove the trailing "/" - if [[ $role_dir == *logs ]]; - then - continue - fi - cp ${SSH_CONFIG_FILE} /home/${DLWS_USER_NAME}/.ssh/config - chown ${DLWS_USER_NAME} /home/${DLWS_USER_NAME}/.ssh/config - chmod 600 /home/${DLWS_USER_NAME}/.ssh/config + echo "Setup ssh config for woker-${i}" + ssh worker-${i} "cp ${SSH_CONFIG_FILE} /home/${DLWS_USER_NAME}/.ssh/config && chown ${DLWS_USER_NAME} /home/${DLWS_USER_NAME}/.ssh/config && chmod 600 /home/${DLWS_USER_NAME}/.ssh/config" done - # generate /job/hostfile SLOT_FILE="/job/hostfile" >${SLOT_FILE} @@ -79,7 +74,7 @@ do host=$(basename ${role_dir}) slots=${DLWS_NUM_GPU_PER_WORKER} cat >>${SLOT_FILE} < Date: Fri, 2 Aug 2019 03:27:50 +0000 Subject: [PATCH 501/595] add missing label --- src/Jobs_Templete/pod.yaml.template | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Jobs_Templete/pod.yaml.template b/src/Jobs_Templete/pod.yaml.template index d75cec5da..7eac773cc 100755 --- a/src/Jobs_Templete/pod.yaml.template +++ b/src/Jobs_Templete/pod.yaml.template @@ -10,6 +10,7 @@ metadata: userName: {{ job["user"] }} vcName: {{ job["vcName"] }} type: job + 'gpu-request': '{{ job["gpuLimit"]|int }}' {% for label in job["labels"] %} {{label.name}}: "{{label.value}}" From e33978acd1cd826fbcd2c94ba2f91707e31392ed Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 2 Aug 2019 06:42:15 +0000 Subject: [PATCH 502/595] reset endpoint before resubmit --- src/ClusterManager/job_manager.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index f6ce2ba5e..cfe395338 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -68,6 +68,15 @@ def SubmitJob(job): dataHandler = DataHandler() try: + # TODO refine later + # before resubmit the job, reset the endpoints + # update all endpoint to status 'pending', so it would restart when job is ready + endpoints = dataHandler.GetJobEndpoints(job_id) + for endpoint_id, endpoint in endpoints.items(): + endpoint["status"] = "pending" + logging.info("Reset endpoint status to 'pending': {}".format(endpoint_id)) + dataHandler.UpdateEndpoint(endpoint) + job["cluster"] = config job_object, errors = JobSchema().load(job) # TODO assert job_object is a Job From 8f5f0a3cc5dfcdd7677065285e8625982383bed9 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 2 Aug 2019 09:48:19 +0000 Subject: [PATCH 503/595] persist prometheus data into host path --- src/ClusterBootstrap/services/monitor/prometheus.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ClusterBootstrap/services/monitor/prometheus.yaml b/src/ClusterBootstrap/services/monitor/prometheus.yaml index 9a5e08bd1..403a5aef3 100644 --- a/src/ClusterBootstrap/services/monitor/prometheus.yaml +++ b/src/ClusterBootstrap/services/monitor/prometheus.yaml @@ -96,6 +96,7 @@ spec: - '--web.listen-address=0.0.0.0:{{cnf["prometheus"]["port"]}}' - '--web.external-url=http://localhost:{{cnf["prometheus"]["port"]}}/prometheus/' - '--web.route-prefix=prometheus' + - '--storage.tsdb.path=/prometheus-data' - '--storage.tsdb.retention=31d' ports: - name: web @@ -105,6 +106,8 @@ spec: mountPath: /etc/prometheus - name: rules-volume mountPath: /etc/prometheus-alert + - name: prometheus-data + mountPath: /prometheus-data volumes: - name: config-volume configMap: @@ -112,6 +115,9 @@ spec: - name: rules-volume configMap: name: prometheus-alert + - name: prometheus-data + hostPath: + path: /data/prometheus/data tolerations: - key: node.kubernetes.io/memory-pressure operator: "Exists" From 8201c1b11271aa3bc992417a64bd10ec176f249e Mon Sep 17 00:00:00 2001 From: "REDMOND\\anbhu" Date: Fri, 2 Aug 2019 15:01:00 -0700 Subject: [PATCH 504/595] Expose environment variable DLWS_NUM_GPU_PER_WORKER for regular job --- src/ClusterManager/pod_template.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ClusterManager/pod_template.py b/src/ClusterManager/pod_template.py index ae3d071ad..0de62e5e4 100644 --- a/src/ClusterManager/pod_template.py +++ b/src/ClusterManager/pod_template.py @@ -109,6 +109,7 @@ def generate_pods(self, job): if "envs" not in params: params["envs"] =[] params["envs"].append({"name": "DLWS_ROLE_NAME", "value": "master"}) + params["envs"].append({"name": "DLWS_NUM_GPU_PER_WORKER", "value": params["resourcegpu"]}) pods = [] if all(hyper_parameter in params for hyper_parameter in ["hyperparametername", "hyperparameterstartvalue", "hyperparameterendvalue", "hyperparameterstep"]): From b25d9f96790525d2664eefba61c8d79d2c14d828 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Sun, 4 Aug 2019 06:58:47 +0000 Subject: [PATCH 505/595] setup ssh and hostfile for regular job --- src/ClusterManager/pod_template.py | 1 + src/Jobs_Templete/bootstrap.sh | 7 ++-- src/Jobs_Templete/setup_ssh_config.sh | 48 ++++++++++++++++----------- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/ClusterManager/pod_template.py b/src/ClusterManager/pod_template.py index ae3d071ad..0de62e5e4 100644 --- a/src/ClusterManager/pod_template.py +++ b/src/ClusterManager/pod_template.py @@ -109,6 +109,7 @@ def generate_pods(self, job): if "envs" not in params: params["envs"] =[] params["envs"].append({"name": "DLWS_ROLE_NAME", "value": "master"}) + params["envs"].append({"name": "DLWS_NUM_GPU_PER_WORKER", "value": params["resourcegpu"]}) pods = [] if all(hyper_parameter in params for hyper_parameter in ["hyperparametername", "hyperparameterstartvalue", "hyperparameterendvalue", "hyperparameterstep"]): diff --git a/src/Jobs_Templete/bootstrap.sh b/src/Jobs_Templete/bootstrap.sh index 22351de13..03ebe0419 100644 --- a/src/Jobs_Templete/bootstrap.sh +++ b/src/Jobs_Templete/bootstrap.sh @@ -22,12 +22,9 @@ bash ${SCRIPT_DIR}/init_user.sh &>> ${LOG_DIR}/bootstrap.log touch ${PROC_DIR}/CONTAINER_READY # Setup roles -if [ "$DLWS_ROLE_NAME" = "worker" ] || [ "$DLWS_ROLE_NAME" = "ps" ]; -then - bash ${SCRIPT_DIR}/setup_sshd.sh &>> ${LOG_DIR}/bootstrap.log -fi +bash ${SCRIPT_DIR}/setup_sshd.sh &>> ${LOG_DIR}/bootstrap.log -if [ "$DLWS_ROLE_NAME" = "ps" ]; +if [ "$DLWS_ROLE_NAME" = "master" ] || [ "$DLWS_ROLE_NAME" = "ps" ]; then bash ${SCRIPT_DIR}/setup_ssh_config.sh &>> ${LOG_DIR}/bootstrap.log fi diff --git a/src/Jobs_Templete/setup_ssh_config.sh b/src/Jobs_Templete/setup_ssh_config.sh index e4b7f25cb..9cf38977f 100644 --- a/src/Jobs_Templete/setup_ssh_config.sh +++ b/src/Jobs_Templete/setup_ssh_config.sh @@ -3,26 +3,30 @@ set -ex JOB_DIR='/job' -# wait untill all workers are ready -all_workers_ready=false -while [ "$all_workers_ready" != true ] -do - # update it to false if any woker is not ready - all_workers_ready=true - for i in $(seq 0 $(( ${DLWS_WORKER_NUM} - 1)) ) +if [ "$DLWS_ROLE_NAME" = "ps" ]; +then + # wait untill all workers are ready + all_workers_ready=false + while [ "$all_workers_ready" != true ] do - worker="worker-${i}" - file="${JOB_DIR}/${worker}/running/ROLE_READY" - #echo $file + # update it to false if any woker is not ready + all_workers_ready=true + + for i in $(seq 0 $(( ${DLWS_WORKER_NUM} - 1)) ) + do + worker="worker-${i}" + file="${JOB_DIR}/${worker}/running/ROLE_READY" + #echo $file - if [ ! -f $file ]; then - echo "${worker} not ready!" - all_workers_ready=false - sleep 10 - fi + if [ ! -f $file ]; then + echo "${worker} not ready!" + all_workers_ready=false + sleep 10 + fi + done done -done +fi # generate ~/ssh_config SSH_CONFIG_FILE="/job/ssh_config" @@ -54,10 +58,16 @@ done # copy ssh config to ~/.ssh/config cp ${SSH_CONFIG_FILE} /home/${DLWS_USER_NAME}/.ssh/config && chown ${DLWS_USER_NAME} /home/${DLWS_USER_NAME}/.ssh/config && chmod 600 /home/${DLWS_USER_NAME}/.ssh/config mkdir -p /root/.ssh && cp /home/${DLWS_USER_NAME}/.ssh/* /root/.ssh/ && chown root /root/.ssh/* && chmod 600 /root/.ssh/* -for i in $(seq 0 $(( ${DLWS_NUM_WORKER} - 1 ))); +for role_dir in ${JOB_DIR}/*/ # list directories in the form "/JOB_DIR/role/" do - echo "Setup ssh config for woker-${i}" - ssh worker-${i} "cp ${SSH_CONFIG_FILE} /home/${DLWS_USER_NAME}/.ssh/config && chown ${DLWS_USER_NAME} /home/${DLWS_USER_NAME}/.ssh/config && chmod 600 /home/${DLWS_USER_NAME}/.ssh/config" + role_dir=${role_dir%*/} # remove the trailing "/" + if [[ ${role_dir} == *logs ]]; + then + continue + fi + role=$(basename ${role_dir}) + echo "Setup ssh config for ${role}" + ssh ${role} "cp ${SSH_CONFIG_FILE} /home/${DLWS_USER_NAME}/.ssh/config && chown ${DLWS_USER_NAME} /home/${DLWS_USER_NAME}/.ssh/config && chmod 600 /home/${DLWS_USER_NAME}/.ssh/config" done # generate /job/hostfile From 7d5bb047dd2ed6f87b6da87575216f1e83d9977a Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 5 Aug 2019 07:59:30 +0000 Subject: [PATCH 506/595] send out email while killing --- .../services/monitor/alert-manager.yaml | 24 ++++++- .../monitor/alert-templates/kill-idle.tmpl | 71 +++++++++++++++++++ .../services/monitor/alerting/jobs.rules | 7 +- 3 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 src/ClusterBootstrap/services/monitor/alert-templates/kill-idle.tmpl diff --git a/src/ClusterBootstrap/services/monitor/alert-manager.yaml b/src/ClusterBootstrap/services/monitor/alert-manager.yaml index 233d18bb7..a15534e3f 100644 --- a/src/ClusterBootstrap/services/monitor/alert-manager.yaml +++ b/src/ClusterBootstrap/services/monitor/alert-manager.yaml @@ -103,7 +103,7 @@ data: repeat_interval: 4h group_by: [alertname, user_email, cluster] match_re: - type: user_alert + type: idle_gpu alertname: "zero-gpu-usage" - receiver: job_state_change_receiver group_by: [alertname, user_email, cluster, subject] @@ -115,6 +115,12 @@ data: group_wait: 0s match_re: type: reaper + - receiver: kill_idle_job_email + group_by: [alertname, user_email, cluster] + group_wait: 0s + match_re: + type: kill_idle_job_email + alertname: "kill-idle-jobs-email" receivers: - name: "alert-email" email_configs: @@ -150,7 +156,6 @@ data: CC: '{{ alert_info["receiver"] }}' {% endif %} subject: '{{ "{{" }} .GroupLabels.cluster {{ "}}" }}: {{ "{{" }} template "__subject" . {{ "}}" }}' -{% endif %} - name: "reaper" {% if cnf["alert-manager"]["reaper"] %} webhook_configs: @@ -158,4 +163,19 @@ data: url: 'http://localhost:{{ cnf["alert-manager"]["reaper"]["port"] }}/kill' http_config: bearer_token: 'shinigami' + - name: "kill_idle_job_email" + email_configs: + {% if cnf["alert-manager"]["alert_users"] %} + - to: '{{ "{{" }} .GroupLabels.user_email {{ "}}" }},{{ alert_info["receiver"] }}' + {% else %} + - to: '{{ alert_info["receiver"] }}' + {% endif %} + html: '{{ "{{" }} template "kill_idle.html" . {{ "}}" }}' + headers: + {% if cnf["alert-manager"]["alert_users"] %} + To: '{{ "{{" }} .GroupLabels.user_email {{ "}}" }}' + CC: '{{ alert_info["receiver"] }}' + {% endif %} + subject: '{{ "{{" }} .GroupLabels.cluster {{ "}}" }}: {{ "{{" }} template "__subject" . {{ "}}" }}' {% endif %} +{% endif %} diff --git a/src/ClusterBootstrap/services/monitor/alert-templates/kill-idle.tmpl b/src/ClusterBootstrap/services/monitor/alert-templates/kill-idle.tmpl new file mode 100644 index 000000000..e29dc993c --- /dev/null +++ b/src/ClusterBootstrap/services/monitor/alert-templates/kill-idle.tmpl @@ -0,0 +1,71 @@ +{{ define "kill_idle.html" }} + + + + + + +{{ template "__subject" . }} + + + + + + + + + + + +
+
+ + + + +
+ + {{ range .Alerts.Firing }} + + + + {{ end }} +
+Your job + +{{.Labels.job_name}} + from cluster '{{.Labels.cluster}}' VC '{{.Labels.vc_name}}' was killed because it have been idle for too long. +
+
+ +
+
+ + + +{{ end }} diff --git a/src/ClusterBootstrap/services/monitor/alerting/jobs.rules b/src/ClusterBootstrap/services/monitor/alerting/jobs.rules index b7fbb8e25..7a6160384 100644 --- a/src/ClusterBootstrap/services/monitor/alerting/jobs.rules +++ b/src/ClusterBootstrap/services/monitor/alerting/jobs.rules @@ -5,7 +5,12 @@ groups: expr: avg(task_gpu_percent) by (user_email, job_name, vc_name) == 0 for: 4h labels: - type: user_alert + type: idle_gpu + - alert: kill-idle-jobs-email + expr: avg(task_gpu_percent) by (user_email, job_name, vc_name) == 0 + for: 8h + labels: + type: kill_idle_job_email - alert: kill-idle-jobs expr: avg(task_gpu_percent) by (user_email, job_name, vc_name) == 0 for: 8h From fb31b0b5ee8f769035e2bf202b1844b7e9ba629a Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 5 Aug 2019 10:42:15 +0000 Subject: [PATCH 507/595] same role anti affinity --- src/Jobs_Templete/pod.yaml.template | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/Jobs_Templete/pod.yaml.template b/src/Jobs_Templete/pod.yaml.template index 7eac773cc..2dc4433e3 100755 --- a/src/Jobs_Templete/pod.yaml.template +++ b/src/Jobs_Templete/pod.yaml.template @@ -1,3 +1,9 @@ +{% if job["distRole"] %} +{% set jobRole = job["distRole"] %} +{% else %} +{% set jobRole = "worker" %} # treat regular job's pod as worker role +{% endif %} + apiVersion: v1 kind: Pod metadata: @@ -7,6 +13,7 @@ metadata: podName: {{ job["podName"] }} jobName: {{ job["jobNameLabel"] }} jobId: {{ job["jobId"] }} + jobRole: {{ jobRole }} userName: {{ job["user"] }} vcName: {{ job["vcName"] }} type: job @@ -34,6 +41,21 @@ spec: nodeSelector: worker: active affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 60 # try to put worker or ps from same job to different nodes + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "jobId" + operator: In + values: + - "{{ job["jobId"] }}" + - key: "jobRole" + operator: In + values: + - "{{ jobRole }}" + topologyKey: "kubernetes.io/hostname" podAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 50 @@ -45,6 +67,19 @@ spec: values: - "job" topologyKey: "kubernetes.io/hostname" + - weight: 10 # try to put worker & ps in same node + podAffinityTerm: + labelSelector: + matchExpressions: + - key: "jobId" + operator: In + values: + - "{{ job["jobId"] }}" + - key: "jobRole" + operator: NotIn + values: + - "{{ jobRole }}" + topologyKey: "kubernetes.io/hostname" {% if job["gpuLimit"]|int == 1 %} - weight: 30 podAffinityTerm: From 5cc0e075892f05415340c0f6650b0055e92fe753 Mon Sep 17 00:00:00 2001 From: anbhu <5781796+Anbang-Hu@users.noreply.github.com> Date: Mon, 5 Aug 2019 13:30:56 -0700 Subject: [PATCH 508/595] Revert "persist prometheus data into host path" --- src/ClusterBootstrap/services/monitor/prometheus.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/ClusterBootstrap/services/monitor/prometheus.yaml b/src/ClusterBootstrap/services/monitor/prometheus.yaml index 403a5aef3..9a5e08bd1 100644 --- a/src/ClusterBootstrap/services/monitor/prometheus.yaml +++ b/src/ClusterBootstrap/services/monitor/prometheus.yaml @@ -96,7 +96,6 @@ spec: - '--web.listen-address=0.0.0.0:{{cnf["prometheus"]["port"]}}' - '--web.external-url=http://localhost:{{cnf["prometheus"]["port"]}}/prometheus/' - '--web.route-prefix=prometheus' - - '--storage.tsdb.path=/prometheus-data' - '--storage.tsdb.retention=31d' ports: - name: web @@ -106,8 +105,6 @@ spec: mountPath: /etc/prometheus - name: rules-volume mountPath: /etc/prometheus-alert - - name: prometheus-data - mountPath: /prometheus-data volumes: - name: config-volume configMap: @@ -115,9 +112,6 @@ spec: - name: rules-volume configMap: name: prometheus-alert - - name: prometheus-data - hostPath: - path: /data/prometheus/data tolerations: - key: node.kubernetes.io/memory-pressure operator: "Exists" From 18637faf466bba88bba3f74efcd7a955bab90276 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 2 Aug 2019 09:48:19 +0000 Subject: [PATCH 509/595] persist prometheus data into host path --- src/ClusterBootstrap/services/monitor/prometheus.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ClusterBootstrap/services/monitor/prometheus.yaml b/src/ClusterBootstrap/services/monitor/prometheus.yaml index 9a5e08bd1..403a5aef3 100644 --- a/src/ClusterBootstrap/services/monitor/prometheus.yaml +++ b/src/ClusterBootstrap/services/monitor/prometheus.yaml @@ -96,6 +96,7 @@ spec: - '--web.listen-address=0.0.0.0:{{cnf["prometheus"]["port"]}}' - '--web.external-url=http://localhost:{{cnf["prometheus"]["port"]}}/prometheus/' - '--web.route-prefix=prometheus' + - '--storage.tsdb.path=/prometheus-data' - '--storage.tsdb.retention=31d' ports: - name: web @@ -105,6 +106,8 @@ spec: mountPath: /etc/prometheus - name: rules-volume mountPath: /etc/prometheus-alert + - name: prometheus-data + mountPath: /prometheus-data volumes: - name: config-volume configMap: @@ -112,6 +115,9 @@ spec: - name: rules-volume configMap: name: prometheus-alert + - name: prometheus-data + hostPath: + path: /data/prometheus/data tolerations: - key: node.kubernetes.io/memory-pressure operator: "Exists" From 1ff0b27bbae637f5d4f26f8b2bbceaa9ed4e52f5 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 6 Aug 2019 02:02:21 +0000 Subject: [PATCH 510/595] fix permission of /prometheus-data --- src/ClusterBootstrap/services/monitor/prometheus.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ClusterBootstrap/services/monitor/prometheus.yaml b/src/ClusterBootstrap/services/monitor/prometheus.yaml index 403a5aef3..bd4c0a203 100644 --- a/src/ClusterBootstrap/services/monitor/prometheus.yaml +++ b/src/ClusterBootstrap/services/monitor/prometheus.yaml @@ -83,6 +83,13 @@ spec: nodeSelector: prometheus: active hostNetwork: true + initContainers: + - name: init + image: bash:4 + volumeMounts: + - name: prometheus-data + mountPath: /prometheus-data + command: ["chmod", "777", "/prometheus-data"] # newly create dir have permission 755, which makes prometheus container unable to write containers: - name: prometheus image: prom/prometheus:v2.1.0 From e38da8646001eaeb9784bed3e0b191d8c9a7b466 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 6 Aug 2019 02:38:37 +0000 Subject: [PATCH 511/595] remove anti affinity --- src/Jobs_Templete/pod.yaml.template | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/Jobs_Templete/pod.yaml.template b/src/Jobs_Templete/pod.yaml.template index 2dc4433e3..194c145f9 100755 --- a/src/Jobs_Templete/pod.yaml.template +++ b/src/Jobs_Templete/pod.yaml.template @@ -41,21 +41,6 @@ spec: nodeSelector: worker: active affinity: - podAntiAffinity: - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 60 # try to put worker or ps from same job to different nodes - podAffinityTerm: - labelSelector: - matchExpressions: - - key: "jobId" - operator: In - values: - - "{{ job["jobId"] }}" - - key: "jobRole" - operator: In - values: - - "{{ jobRole }}" - topologyKey: "kubernetes.io/hostname" podAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 50 From bd5f61f8f185221134637836f953c3c427b854f8 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 6 Aug 2019 03:55:08 +0000 Subject: [PATCH 512/595] change to required affinity --- src/Jobs_Templete/pod.yaml.template | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Jobs_Templete/pod.yaml.template b/src/Jobs_Templete/pod.yaml.template index 194c145f9..b5002446c 100755 --- a/src/Jobs_Templete/pod.yaml.template +++ b/src/Jobs_Templete/pod.yaml.template @@ -42,6 +42,20 @@ spec: worker: active affinity: podAffinity: + {% if jobRole == "ps" %} + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: # try to put worker & ps in same node + matchExpressions: + - key: "jobId" + operator: In + values: + - "{{ job["jobId"] }}" + - key: "jobRole" + operator: In + values: + - "worker" + topologyKey: "kubernetes.io/hostname" + {% endif %} preferredDuringSchedulingIgnoredDuringExecution: - weight: 50 podAffinityTerm: @@ -52,19 +66,6 @@ spec: values: - "job" topologyKey: "kubernetes.io/hostname" - - weight: 10 # try to put worker & ps in same node - podAffinityTerm: - labelSelector: - matchExpressions: - - key: "jobId" - operator: In - values: - - "{{ job["jobId"] }}" - - key: "jobRole" - operator: NotIn - values: - - "{{ jobRole }}" - topologyKey: "kubernetes.io/hostname" {% if job["gpuLimit"]|int == 1 %} - weight: 30 podAffinityTerm: From d07c909b238521552e88dfee5ef752bbe009c0c3 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 6 Aug 2019 04:10:34 +0000 Subject: [PATCH 513/595] change order of cmd in restful api to speed up build & deployment --- src/docker-images/RestfulAPI/Dockerfile | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/docker-images/RestfulAPI/Dockerfile b/src/docker-images/RestfulAPI/Dockerfile index 659c12a4b..8d7ada285 100755 --- a/src/docker-images/RestfulAPI/Dockerfile +++ b/src/docker-images/RestfulAPI/Dockerfile @@ -23,16 +23,17 @@ RUN chmod +x /pullsrc.sh COPY run.sh / RUN chmod +x /run.sh -ADD Jobs_Templete /DLWorkspace/src/Jobs_Templete -ADD utils /DLWorkspace/src/utils -ADD RestAPI /DLWorkspace/src/RestAPI -ADD ClusterManager /DLWorkspace/src/ClusterManager - +COPY ClusterManager/requirements.txt / # TODO refine later # install requirements RUN rm -rf /usr/lib/python2.7/dist-packages/yaml RUN rm -rf /usr/lib/python2.7/dist-packages/PyYAML-* -RUN pip install -r /DLWorkspace/src/ClusterManager/requirements.txt +RUN pip install -r /requirements.txt + +ADD Jobs_Templete /DLWorkspace/src/Jobs_Templete +ADD utils /DLWorkspace/src/utils +ADD RestAPI /DLWorkspace/src/RestAPI +ADD ClusterManager /DLWorkspace/src/ClusterManager CMD /run.sh From c395a40c93aed580609b025abb605cb3239cff72 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 6 Aug 2019 08:20:42 +0000 Subject: [PATCH 514/595] fix create database --- src/utils/MySQLDataHandler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index 31b1c9634..2496fc571 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -55,11 +55,12 @@ def __init__(self): username = config["mysql"]["username"] password = config["mysql"]["password"] + self.CreateDatabase() + with db_connect_histogram.time(): self.conn = mysql.connector.connect(user=username, password=password, host=server, database=self.database) - self.CreateDatabase() self.CreateTable() From b0f917343904dde6a34b8a03d84a5ce96a024fb0 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 7 Aug 2019 01:20:26 +0000 Subject: [PATCH 515/595] fix template error --- src/Jobs_Templete/pod.yaml.template | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Jobs_Templete/pod.yaml.template b/src/Jobs_Templete/pod.yaml.template index b5002446c..4bb58c510 100755 --- a/src/Jobs_Templete/pod.yaml.template +++ b/src/Jobs_Templete/pod.yaml.template @@ -40,6 +40,14 @@ metadata: spec: nodeSelector: worker: active + {% if job["nodeSelector"]|length > 0 %} + {% for key, value in job["nodeSelector"].items() %} + {{key}}: {{value}} + {% endfor %} + {% endif %} + {% if job["fragmentGpuJob"] %} + FragmentGPUJob: active + {% endif %} affinity: podAffinity: {% if jobRole == "ps" %} @@ -124,14 +132,6 @@ spec: - "1" topologyKey: "kubernetes.io/hostname" {% endif %} - {% if job["nodeSelector"]|length > 0 %} - {% for key, value in job["nodeSelector"].items() %} - {{key}}: {{value}} - {% endfor %} - {% endif %} - {% if job["fragmentGpuJob"] %} - FragmentGPUJob: active - {% endif %} {% if job["dnsPolicy"] %} dnsPolicy: {{ job["dnsPolicy" ]}} {% endif %} From 3c29c98d4f080dffe6ff817a830aa0581c845de7 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 7 Aug 2019 02:09:58 +0000 Subject: [PATCH 516/595] install missing pkg "openssl" --- src/Jobs_Templete/init_user.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Jobs_Templete/init_user.sh b/src/Jobs_Templete/init_user.sh index 7adc4125a..9822a0487 100644 --- a/src/Jobs_Templete/init_user.sh +++ b/src/Jobs_Templete/init_user.sh @@ -7,6 +7,9 @@ set -ex #export DLWS_USER_NAME= export ENV_FILE=/pod/pod.env +# install required pkgs +apt-get update && apt-get install sudo openssl + # setup user and group, fix permissions addgroup --force-badname --gid ${DLWS_GID} domainusers adduser --force-badname --home /home/${DLWS_USER_NAME} --shell /bin/bash --uid ${DLWS_UID} -gecos '' --gid ${DLWS_GID} --disabled-password ${DLWS_USER_NAME} @@ -15,7 +18,6 @@ chown ${DLWS_USER_NAME} /home/${DLWS_USER_NAME}/ /home/${DLWS_USER_NAME}/.profil chmod 700 /home/${DLWS_USER_NAME}/.ssh || /bin/true # setup sudoers -apt-get update && apt-get install sudo adduser $DLWS_USER_NAME sudo echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers From 3f3040ebe010c82feb5f8d114a07d9b397c00530 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 7 Aug 2019 02:44:34 +0000 Subject: [PATCH 517/595] fix apt-get install "-y" --- src/Jobs_Templete/init_user.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Jobs_Templete/init_user.sh b/src/Jobs_Templete/init_user.sh index 9822a0487..799819f09 100644 --- a/src/Jobs_Templete/init_user.sh +++ b/src/Jobs_Templete/init_user.sh @@ -8,7 +8,7 @@ set -ex export ENV_FILE=/pod/pod.env # install required pkgs -apt-get update && apt-get install sudo openssl +apt-get update && apt-get install sudo openssl -y # setup user and group, fix permissions addgroup --force-badname --gid ${DLWS_GID} domainusers From 7e28c470751809cc6b1fb6133f6b29ad4560e55e Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 7 Aug 2019 03:07:45 +0000 Subject: [PATCH 518/595] fix apt-get hang --- src/Jobs_Templete/init_user.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Jobs_Templete/init_user.sh b/src/Jobs_Templete/init_user.sh index 799819f09..b98c7f383 100644 --- a/src/Jobs_Templete/init_user.sh +++ b/src/Jobs_Templete/init_user.sh @@ -8,6 +8,7 @@ set -ex export ENV_FILE=/pod/pod.env # install required pkgs +export DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install sudo openssl -y # setup user and group, fix permissions From 5eca31e82fbf62e61fb21fdf4937eff90f2430d4 Mon Sep 17 00:00:00 2001 From: hongyiliu Date: Wed, 7 Aug 2019 11:32:41 -0700 Subject: [PATCH 519/595] Enable submit distrbuted job under low priority cluster --- src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml index b1baa9b36..8550a4a41 100755 --- a/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml +++ b/src/WebUI/dotnet/WebPortal/Views/Home/JobSubmission.cshtml @@ -626,9 +626,6 @@ if (!$scope.current.jobName) return false; if (!$scope.current.image) return false; if (!$scope.current.cmd || !String($scope.current.cmd).trim()) return false; - if ($scope.isLowPriority && $scope.current.jobtrainingtype == 'PSDistJob') { - return false; - } if ( $scope.current.jobtrainingtype === 'RegularJob' && typeof $scope.current.resourcegpu !== 'number' From eb8988d77341cf9be02c43bacdf7812342f18724 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 2 Aug 2019 10:03:03 +0000 Subject: [PATCH 520/595] change DaemonSet to apps/v1 --- .../services/nvidia-device-plugin/nvidia-device-plugin.yaml | 2 +- src/ClusterBootstrap/template/kube-addons/weave.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml b/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml index e201c211e..5f1657366 100755 --- a/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml +++ b/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml @@ -1,4 +1,4 @@ -apiVersion: extensions/v1beta1 +apiVersion: apps/v1 kind: DaemonSet metadata: name: nvidia-device-plugin-daemonset diff --git a/src/ClusterBootstrap/template/kube-addons/weave.yaml b/src/ClusterBootstrap/template/kube-addons/weave.yaml index 5dfa3ace1..e7576c7f0 100755 --- a/src/ClusterBootstrap/template/kube-addons/weave.yaml +++ b/src/ClusterBootstrap/template/kube-addons/weave.yaml @@ -132,7 +132,7 @@ items: - kind: ServiceAccount name: weave-net namespace: kube-system - - apiVersion: extensions/v1beta1 + - apiVersion: apps/v1 kind: DaemonSet metadata: name: weave-net @@ -242,4 +242,4 @@ items: hostPath: path: /run/xtables.lock updateStrategy: - type: RollingUpdate \ No newline at end of file + type: RollingUpdate From 1818d6fb137b1c5a21c688959af49c99dde1a965 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 2 Aug 2019 10:06:25 +0000 Subject: [PATCH 521/595] change Deployment to apps/v1 --- .../services/custommetrics/custom_metrics.yaml | 4 ++-- .../services/custommetrics/prometheus_operator.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ClusterBootstrap/services/custommetrics/custom_metrics.yaml b/src/ClusterBootstrap/services/custommetrics/custom_metrics.yaml index 3f97b0bbf..f67adaafc 100644 --- a/src/ClusterBootstrap/services/custommetrics/custom_metrics.yaml +++ b/src/ClusterBootstrap/services/custommetrics/custom_metrics.yaml @@ -76,7 +76,7 @@ rules: verbs: - "*" --- -apiVersion: apps/v1beta2 +apiVersion: apps/v1 kind: Deployment metadata: name: custom-metrics-apiserver @@ -180,4 +180,4 @@ roleRef: subjects: - kind: ServiceAccount name: horizontal-pod-autoscaler - namespace: kube-system \ No newline at end of file + namespace: kube-system diff --git a/src/ClusterBootstrap/services/custommetrics/prometheus_operator.yaml b/src/ClusterBootstrap/services/custommetrics/prometheus_operator.yaml index 7b1503c55..290887bc3 100644 --- a/src/ClusterBootstrap/services/custommetrics/prometheus_operator.yaml +++ b/src/ClusterBootstrap/services/custommetrics/prometheus_operator.yaml @@ -69,7 +69,7 @@ subjects: name: prometheus-operator namespace: default --- -apiVersion: apps/v1beta2 +apiVersion: apps/v1 kind: Deployment metadata: name: prometheus-operator From 0d9d99aaea7bf28deceb1b7466fea3c8badddfdf Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 5 Aug 2019 03:17:36 +0000 Subject: [PATCH 522/595] remove --show-all --- src/ClusterBootstrap/storage/glusterFS/gk-deploy | 2 +- src/utils/k8sUtils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ClusterBootstrap/storage/glusterFS/gk-deploy b/src/ClusterBootstrap/storage/glusterFS/gk-deploy index 3470d9532..67b38967c 100755 --- a/src/ClusterBootstrap/storage/glusterFS/gk-deploy +++ b/src/ClusterBootstrap/storage/glusterFS/gk-deploy @@ -207,7 +207,7 @@ check_pods() { break fi sleep 2 - pods=$(${CLI} get pod --no-headers --show-all --selector="${1}" 2>&1) + pods=$(${CLI} get pod --no-headers --selector="${1}" 2>&1) if [[ ${s} -ne 0 ]] && [[ ${VERBOSE} -eq 1 ]]; then podlines=$(echo "$pods" | wc -l) ((podlines+=1)) diff --git a/src/utils/k8sUtils.py b/src/utils/k8sUtils.py index e90713f3b..943288238 100755 --- a/src/utils/k8sUtils.py +++ b/src/utils/k8sUtils.py @@ -165,7 +165,7 @@ def GetServiceAddress(jobId): def GetPod(selector): podInfo = {} try: - output = kubectl_exec(" get pod -o yaml --show-all -l " + selector) + output = kubectl_exec(" get pod -o yaml -l " + selector) podInfo = yaml.load(output) except Exception as e: logger.exception("kubectl get pod") From e2f42ba9bd12872975f529db52905c254933a253 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 5 Aug 2019 03:25:38 +0000 Subject: [PATCH 523/595] add PodShareProcessNamespace=true --- .../template/kubelet/kubelet.service.template | 4 ++-- src/ClusterBootstrap/template/master/kubelet.service | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ClusterBootstrap/template/kubelet/kubelet.service.template b/src/ClusterBootstrap/template/kubelet/kubelet.service.template index 3d555d0c3..43f12c6d5 100755 --- a/src/ClusterBootstrap/template/kubelet/kubelet.service.template +++ b/src/ClusterBootstrap/template/kubelet/kubelet.service.template @@ -26,7 +26,7 @@ ExecStart=/opt/bin/kubelet \ --container-runtime={{'remote' if cnf["kube_custom_cri"] else 'docker'}} \ --enable-server=true \ --register-node=true \ - --feature-gates="Accelerators=true,DevicePlugins=true" \ + --feature-gates="Accelerators=true,DevicePlugins=true,PodShareProcessNamespace=true" \ --allow-privileged=true \ --pod-manifest-path=/etc/kubernetes/manifests \ --pod-infra-container-image={{cnf["dockers"]["container"]["podinfra"]["fullname"]}} \ @@ -45,4 +45,4 @@ Restart=always RestartSec=10 [Install] -WantedBy=multi-user.target \ No newline at end of file +WantedBy=multi-user.target diff --git a/src/ClusterBootstrap/template/master/kubelet.service b/src/ClusterBootstrap/template/master/kubelet.service index 62b721255..d7b1d6475 100755 --- a/src/ClusterBootstrap/template/master/kubelet.service +++ b/src/ClusterBootstrap/template/master/kubelet.service @@ -21,7 +21,7 @@ ExecStart=/opt/bin/kubelet \ --pod-infra-container-image={{cnf["dockers"]["container"]["podinfra"]["fullname"]}} \ --container-runtime=docker \ --allow-privileged=true \ - --feature-gates="Accelerators=true,DevicePlugins=true" \ + --feature-gates="Accelerators=true,DevicePlugins=true,PodShareProcessNamespace=true" \ --pod-manifest-path=/etc/kubernetes/manifests \ --network-plugin=cni \ --cluster_dns={{cnf["dns-server-ip"]}} \ @@ -32,4 +32,4 @@ Restart=always RestartSec=10 [Install] -WantedBy=multi-user.target \ No newline at end of file +WantedBy=multi-user.target From 868e3dc0a5d12d26148a879ca25b9a48fdd9bb16 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 5 Aug 2019 03:32:53 +0000 Subject: [PATCH 524/595] --allow-privileged in kubelet is default to be true --- src/ClusterBootstrap/template/kubelet/kubelet.service.template | 1 - src/ClusterBootstrap/template/master/kubelet.service | 1 - 2 files changed, 2 deletions(-) diff --git a/src/ClusterBootstrap/template/kubelet/kubelet.service.template b/src/ClusterBootstrap/template/kubelet/kubelet.service.template index 43f12c6d5..00751e895 100755 --- a/src/ClusterBootstrap/template/kubelet/kubelet.service.template +++ b/src/ClusterBootstrap/template/kubelet/kubelet.service.template @@ -27,7 +27,6 @@ ExecStart=/opt/bin/kubelet \ --enable-server=true \ --register-node=true \ --feature-gates="Accelerators=true,DevicePlugins=true,PodShareProcessNamespace=true" \ - --allow-privileged=true \ --pod-manifest-path=/etc/kubernetes/manifests \ --pod-infra-container-image={{cnf["dockers"]["container"]["podinfra"]["fullname"]}} \ --network-plugin=cni \ diff --git a/src/ClusterBootstrap/template/master/kubelet.service b/src/ClusterBootstrap/template/master/kubelet.service index d7b1d6475..30ad2931c 100755 --- a/src/ClusterBootstrap/template/master/kubelet.service +++ b/src/ClusterBootstrap/template/master/kubelet.service @@ -20,7 +20,6 @@ ExecStart=/opt/bin/kubelet \ --register-with-taints=node-role.kubernetes.io/master=:NoSchedule \ --pod-infra-container-image={{cnf["dockers"]["container"]["podinfra"]["fullname"]}} \ --container-runtime=docker \ - --allow-privileged=true \ --feature-gates="Accelerators=true,DevicePlugins=true,PodShareProcessNamespace=true" \ --pod-manifest-path=/etc/kubernetes/manifests \ --network-plugin=cni \ From a9dd37bdf8cebb60997c845444aee1ecac07a45f Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 5 Aug 2019 03:44:04 +0000 Subject: [PATCH 525/595] replace --admission-control with --enable-admission-plugins --- src/ClusterBootstrap/template/master/kube-apiserver.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ClusterBootstrap/template/master/kube-apiserver.yaml b/src/ClusterBootstrap/template/master/kube-apiserver.yaml index a3ea51283..05afea4f1 100755 --- a/src/ClusterBootstrap/template/master/kube-apiserver.yaml +++ b/src/ClusterBootstrap/template/master/kube-apiserver.yaml @@ -20,8 +20,8 @@ spec: - --service-cluster-ip-range={{cnf["service_cluster_ip_range"]}} - --secure-port={{cnf["k8sAPIport"]}} - --advertise-address={{cnf["master_ip"]}} - - --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota - #- --admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota + - --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota + #- --enable-admission-plugins=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota - --tls-cert-file=/etc/kubernetes/ssl/apiserver.pem - --tls-private-key-file=/etc/kubernetes/ssl/apiserver-key.pem - --client-ca-file=/etc/kubernetes/ssl/ca.pem From 86bd9deb1c87f8a2e33249c6a91c48ef214b79d5 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 7 Aug 2019 09:10:22 +0000 Subject: [PATCH 526/595] change argument of hypekube --- src/ClusterBootstrap/template/master/kube-apiserver.yaml | 2 +- .../template/master/kube-controller-manager.yaml | 4 ++-- src/ClusterBootstrap/template/master/kube-proxy.yaml | 4 ++-- src/ClusterBootstrap/template/master/kube-scheduler.yaml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ClusterBootstrap/template/master/kube-apiserver.yaml b/src/ClusterBootstrap/template/master/kube-apiserver.yaml index 05afea4f1..07f74282e 100755 --- a/src/ClusterBootstrap/template/master/kube-apiserver.yaml +++ b/src/ClusterBootstrap/template/master/kube-apiserver.yaml @@ -10,7 +10,7 @@ spec: image: {{cnf["dockers"]["container"]["hyperkube"]["fullname"]}} command: - /hyperkube - - apiserver + - kube-apiserver - --bind-address=0.0.0.0 - --etcd-servers={{cnf["etcd_endpoints"]}} - --etcd-cafile=/etc/kubernetes/ssl/ca.pem diff --git a/src/ClusterBootstrap/template/master/kube-controller-manager.yaml b/src/ClusterBootstrap/template/master/kube-controller-manager.yaml index a0d95a5c7..babce354d 100755 --- a/src/ClusterBootstrap/template/master/kube-controller-manager.yaml +++ b/src/ClusterBootstrap/template/master/kube-controller-manager.yaml @@ -10,7 +10,7 @@ spec: image: {{cnf["dockers"]["container"]["hyperkube"]["fullname"]}} command: - /hyperkube - - controller-manager + - kube-controller-manager - --master=http://127.0.0.1:8080 - --leader-elect=true - --service-account-private-key-file=/etc/kubernetes/ssl/apiserver-key.pem @@ -39,4 +39,4 @@ spec: name: ssl-certs-kubernetes - hostPath: path: /usr/share/ca-certificates - name: ssl-certs-host \ No newline at end of file + name: ssl-certs-host diff --git a/src/ClusterBootstrap/template/master/kube-proxy.yaml b/src/ClusterBootstrap/template/master/kube-proxy.yaml index 85e4f5332..90138eb5f 100755 --- a/src/ClusterBootstrap/template/master/kube-proxy.yaml +++ b/src/ClusterBootstrap/template/master/kube-proxy.yaml @@ -10,7 +10,7 @@ spec: image: {{cnf["dockers"]["container"]["hyperkube"]["fullname"]}} command: - /hyperkube - - proxy + - kube-proxy - --master=http://127.0.0.1:8080 - --proxy-mode=iptables securityContext: @@ -22,4 +22,4 @@ spec: volumes: - hostPath: path: /usr/share/ca-certificates - name: ssl-certs-host \ No newline at end of file + name: ssl-certs-host diff --git a/src/ClusterBootstrap/template/master/kube-scheduler.yaml b/src/ClusterBootstrap/template/master/kube-scheduler.yaml index 47aff7542..49639615f 100755 --- a/src/ClusterBootstrap/template/master/kube-scheduler.yaml +++ b/src/ClusterBootstrap/template/master/kube-scheduler.yaml @@ -13,7 +13,7 @@ spec: - /kube-scheduler {% else %} - /hyperkube - - scheduler + - kube-scheduler {% endif %} - --master=http://127.0.0.1:8080 - --leader-elect=true From ae2fcb62372720f156af3a33304e52775d2b8ff1 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 7 Aug 2019 09:10:58 +0000 Subject: [PATCH 527/595] remove removed argument to kubelet --- .../template/kubelet/kubelet.service.template | 4 +--- src/ClusterBootstrap/template/master/kubelet.service | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/ClusterBootstrap/template/kubelet/kubelet.service.template b/src/ClusterBootstrap/template/kubelet/kubelet.service.template index 00751e895..29a509a24 100755 --- a/src/ClusterBootstrap/template/kubelet/kubelet.service.template +++ b/src/ClusterBootstrap/template/kubelet/kubelet.service.template @@ -22,16 +22,14 @@ ExecStartPre=/bin/bash -c 'if lspci | grep -qE "[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA # # https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/ ExecStart=/opt/bin/kubelet \ - --require-kubeconfig=true \ --container-runtime={{'remote' if cnf["kube_custom_cri"] else 'docker'}} \ --enable-server=true \ --register-node=true \ - --feature-gates="Accelerators=true,DevicePlugins=true,PodShareProcessNamespace=true" \ + --feature-gates="DevicePlugins=true,PodShareProcessNamespace=true" \ --pod-manifest-path=/etc/kubernetes/manifests \ --pod-infra-container-image={{cnf["dockers"]["container"]["podinfra"]["fullname"]}} \ --network-plugin=cni \ --cluster_dns={{cnf["dns-server-ip"]}} \ - --docker-disable-shared-pid \ --cluster_domain=cluster.local \ --tls-cert-file=/etc/kubernetes/ssl/worker.pem \ --tls-private-key-file=/etc/kubernetes/ssl/worker-key.pem \ diff --git a/src/ClusterBootstrap/template/master/kubelet.service b/src/ClusterBootstrap/template/master/kubelet.service index 30ad2931c..380989d37 100755 --- a/src/ClusterBootstrap/template/master/kubelet.service +++ b/src/ClusterBootstrap/template/master/kubelet.service @@ -16,15 +16,13 @@ ExecStartPre=/bin/bash -c 'if lspci | grep -qE "[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA- # https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/ ExecStart=/opt/bin/kubelet \ --kubeconfig=/etc/kubernetes/worker-kubeconfig.yaml \ - --require-kubeconfig=true \ --register-with-taints=node-role.kubernetes.io/master=:NoSchedule \ --pod-infra-container-image={{cnf["dockers"]["container"]["podinfra"]["fullname"]}} \ --container-runtime=docker \ - --feature-gates="Accelerators=true,DevicePlugins=true,PodShareProcessNamespace=true" \ + --feature-gates="DevicePlugins=true,PodShareProcessNamespace=true" \ --pod-manifest-path=/etc/kubernetes/manifests \ --network-plugin=cni \ --cluster_dns={{cnf["dns-server-ip"]}} \ - --docker-disable-shared-pid \ --cluster_domain=cluster.local #ExecStop=-/usr/bin/rkt stop --uuid-file=/var/run/kubelet-pod.uuid Restart=always From 00a96b07f0c36795671982196c3b7e26d89ea203 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Thu, 8 Aug 2019 04:43:59 +0000 Subject: [PATCH 528/595] finish upgrade scripts --- src/ClusterBootstrap/deploy.py | 119 +++++++++++++++++- .../template/kubelet/post-worker-upgrade.sh | 9 ++ .../template/kubelet/pre-worker-upgrade.sh | 19 +++ .../template/kubelet/upgrade.list | 10 ++ .../template/master/post-upgrade.sh | 10 ++ .../template/master/pre-upgrade.sh | 24 ++++ .../template/master/upgrade.list | 20 +++ 7 files changed, 209 insertions(+), 2 deletions(-) create mode 100755 src/ClusterBootstrap/template/kubelet/post-worker-upgrade.sh create mode 100755 src/ClusterBootstrap/template/kubelet/pre-worker-upgrade.sh create mode 100755 src/ClusterBootstrap/template/kubelet/upgrade.list create mode 100755 src/ClusterBootstrap/template/master/post-upgrade.sh create mode 100755 src/ClusterBootstrap/template/master/pre-upgrade.sh create mode 100755 src/ClusterBootstrap/template/master/upgrade.list diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index d0876d765..477182371 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -852,7 +852,6 @@ def clean_master(): utils.SSH_exec_script(config["ssh_cert"],kubernetes_master_user, kubernetes_master, "./deploy/master/%s" % config["mastercleanupscript"]) - def deploy_master(kubernetes_master): print "===============================================" kubernetes_master_user = config["kubernetes_master_ssh_user"] @@ -1210,7 +1209,6 @@ def update_scaled_worker_nodes( nargs ): os.system('sed "s/##api_servers##/%s/" ./deploy/kubelet/kubelet.service.template > ./deploy/kubelet/kubelet.service' % config["api_servers"].replace("/","\\/")) os.system('sed "s/##api_servers##/%s/" ./deploy/kubelet/worker-kubeconfig.yaml.template > ./deploy/kubelet/worker-kubeconfig.yaml' % config["api_servers"].replace("/","\\/")) - #urllib.urlretrieve ("http://ccsdatarepo.westus.cloudapp.azure.com/data/kube/kubelet/kubelet", "./deploy/bin/kubelet") get_hyperkube_docker() workerNodes = get_worker_nodes(config["clusterId"], True) @@ -3669,6 +3667,16 @@ def run_command( args, command, nargs, parser ): template_file = nargs[0] target_file = nargs[1] utils.render_template(template_file, target_file,config) + elif command == "upgrade_masters": + gen_configs() + upgrade_masters() + elif command == "upgrade_workers": + gen_configs() + upgrade_workers() + elif command == "upgrade": + gen_configs() + upgrade_masters() + upgrade_workers() elif command in scriptblocks: run_script_blocks(args.verbose, scriptblocks[command]) else: @@ -3691,6 +3699,113 @@ def run_script_blocks( verbose, script_collection ): args.verbose = verbose run_command( args, command, nargs, parser ) +def upgrade_worker_node(nodeIP): + print "===============================================" + print "upgrading worker node: %s ..." % nodeIP + + worker_ssh_user = config["admin_username"] + utils.SSH_exec_script(config["ssh_cert"],worker_ssh_user, nodeIP, "./deploy/kubelet/pre-worker-upgrade.sh") + + with open("./deploy/kubelet/upgrade.list", "r") as f: + deploy_files = [s.split(",") for s in f.readlines() if len(s.split(",")) == 2] + for (source, target) in deploy_files: + if (os.path.isfile(source.strip()) or os.path.exists(source.strip())): + utils.sudo_scp(config["ssh_cert"],source.strip(),target.strip(),worker_ssh_user, nodeIP) + + utils.SSH_exec_script(config["ssh_cert"],worker_ssh_user, nodeIP, "./deploy/kubelet/post-worker-upgrade.sh") + +def upgrade_workers(hypekube_url="gcr.io/google-containers/hyperkube:v1.15.2"): + config["dockers"]["external"]["hyperkube"]["fullname"] = hypekube_url + config["dockers"]["container"]["hyperkube"]["fullname"] = hypekube_url + + utils.render_template_directory("./template/kubelet", "./deploy/kubelet", config) + write_nodelist_yaml() + + os.system('sed "s/##etcd_endpoints##/%s/" "./deploy/kubelet/options.env.template" > "./deploy/kubelet/options.env"' % config["etcd_endpoints"].replace("/","\\/")) + os.system('sed "s/##api_servers##/%s/" ./deploy/kubelet/kubelet.service.template > ./deploy/kubelet/kubelet.service' % config["api_servers"].replace("/","\\/")) + os.system('sed "s/##api_servers##/%s/" ./deploy/kubelet/worker-kubeconfig.yaml.template > ./deploy/kubelet/worker-kubeconfig.yaml' % config["api_servers"].replace("/","\\/")) + + get_hyperkube_docker() + + workerNodes = get_worker_nodes(config["clusterId"], False) + workerNodes = limit_nodes(workerNodes) + for node in workerNodes: + upgrade_worker_node(node) + + os.system("rm ./deploy/kubelet/options.env") + os.system("rm ./deploy/kubelet/kubelet.service") + os.system("rm ./deploy/kubelet/worker-kubeconfig.yaml") + +def upgrade_master(kubernetes_master): + print "===============================================" + kubernetes_master_user = config["kubernetes_master_ssh_user"] + print "starting kubernetes master on %s..." % kubernetes_master + + config["master_ip"] = utils.getIP(kubernetes_master) + utils.render_template("./template/master/kube-apiserver.yaml","./deploy/master/kube-apiserver.yaml",config) + utils.render_template("./template/master/dns-kubeconfig.yaml","./deploy/master/dns-kubeconfig.yaml",config) + utils.render_template("./template/master/kubelet.service","./deploy/master/kubelet.service",config) + utils.render_template("./template/master/pre-upgrade.sh", "./deploy/master/pre-upgrade.sh", config) + utils.render_template("./template/master/post-upgrade.sh", "./deploy/master/post-upgrade.sh", config) + + utils.SSH_exec_script(config["ssh_cert"],kubernetes_master_user, kubernetes_master, "./deploy/master/pre-upgrade.sh") + + with open("./deploy/master/upgrade.list", "r") as f: + deploy_files = [s.split(",") for s in f.readlines() if len(s.split(",")) == 2] + + for (source, target) in deploy_files: + if (os.path.isfile(source.strip()) or os.path.exists(source.strip())): + utils.sudo_scp(config["ssh_cert"],source.strip(),target.strip(),kubernetes_master_user,kubernetes_master, verbose=verbose) + + utils.SSH_exec_script(config["ssh_cert"],kubernetes_master_user, kubernetes_master, "./deploy/master/post-upgrade.sh") + +def upgrade_masters(hypekube_url="gcr.io/google-containers/hyperkube:v1.15.2"): + config["dockers"]["external"]["hyperkube"]["fullname"] = hypekube_url + config["dockers"]["container"]["hyperkube"]["fullname"] = hypekube_url + + kubernetes_masters = config["kubernetes_master_node"] + kubernetes_master_user = config["kubernetes_master_ssh_user"] + + get_kubectl_binary(force=True) + + utils.render_template_directory("./template/master", "./deploy/master",config) + utils.render_template_directory("./template/kube-addons", "./deploy/kube-addons",config) + + for kubernetes_master in kubernetes_masters: + upgrade_master(kubernetes_master) + deploy_cmd = """ + until curl -q http://127.0.0.1:8080/version/ ; do + sleep 5; + echo 'waiting for master...'; + done; + + until sudo /opt/bin/kubectl apply -f /opt/addons/kube-addons/weave.yaml --validate=false ; do + sleep 5; + echo 'waiting for master...'; + done ; + + until sudo /opt/bin/kubectl apply -f /opt/addons/kube-addons/dashboard.yaml --validate=false ; do + sleep 5; + echo 'waiting for master...'; + done ; + + until sudo /opt/bin/kubectl apply -f /opt/addons/kube-addons/dns-addon.yml --validate=false ; do + sleep 5; + echo 'waiting for master...'; + done ; + + until sudo /opt/bin/kubectl apply -f /opt/addons/kube-addons/kube-proxy.json --validate=false ; do + sleep 5; + echo 'waiting for master...'; + done ; + + until sudo /opt/bin/kubectl apply -f /etc/kubernetes/clusterroles/ ; do + sleep 5; + echo 'waiting for master...'; + done ; + """ + utils.SSH_exec_cmd(config["ssh_cert"], kubernetes_master_user, kubernetes_masters[0], deploy_cmd , False) + if __name__ == '__main__': # the program always run at the current directory. dirpath = os.path.dirname(os.path.abspath(os.path.realpath(__file__))) diff --git a/src/ClusterBootstrap/template/kubelet/post-worker-upgrade.sh b/src/ClusterBootstrap/template/kubelet/post-worker-upgrade.sh new file mode 100755 index 000000000..dffa9d6c8 --- /dev/null +++ b/src/ClusterBootstrap/template/kubelet/post-worker-upgrade.sh @@ -0,0 +1,9 @@ +#!/bin/bash +sudo cp /etc/kubernetes/ssl/ca.pem /etc/ssl/etcd/ca.pem +sudo cp /etc/kubernetes/ssl/worker.pem /etc/ssl/etcd/worker.pem +sudo cp /etc/kubernetes/ssl/worker-key.pem /etc/ssl/etcd/worker-key.pem +sudo chmod +x /opt/bin/kubelet +sudo systemctl daemon-reload +sudo systemctl stop kubelet +sudo systemctl start kubelet +sudo systemctl enable kubelet diff --git a/src/ClusterBootstrap/template/kubelet/pre-worker-upgrade.sh b/src/ClusterBootstrap/template/kubelet/pre-worker-upgrade.sh new file mode 100755 index 000000000..61096cf3c --- /dev/null +++ b/src/ClusterBootstrap/template/kubelet/pre-worker-upgrade.sh @@ -0,0 +1,19 @@ +#!/bin/bash +sudo systemctl stop kubelet +sudo docker rm -f $(docker ps -a | grep 'k8s_kube\|k8s_POD' | awk '{print $1}') + +sudo systemctl disable kubelet + +sudo rm /etc/systemd/system/kubelet.service + +sudo rm -r /etc/kubernetes +sudo rm /opt/kubelet.sh +sudo rm /opt/bin/kubelet +sudo rm -r /etc/kubernetes + +sudo mkdir -p /etc/kubernetes +sudo mkdir -p /etc/kubernetes/manifests +sudo mkdir -p /etc/kubernetes/ssl/ +sudo mkdir -p /etc/ssl/etcd +sudo mkdir -p /opt/bin +{{'sudo mkdir -p '~cnf["kubeletlogdir"]~'/kubelet' if "kubeletlogdir" in cnf}} diff --git a/src/ClusterBootstrap/template/kubelet/upgrade.list b/src/ClusterBootstrap/template/kubelet/upgrade.list new file mode 100755 index 000000000..41880d455 --- /dev/null +++ b/src/ClusterBootstrap/template/kubelet/upgrade.list @@ -0,0 +1,10 @@ +./deploy/kubelet/options.env,/etc/flannel/options.env +./deploy/kubelet/kubelet.service,/etc/systemd/system/kubelet.service +./deploy/kubelet/worker-kubeconfig.yaml,/etc/kubernetes/worker-kubeconfig.yaml +./deploy/kubelet/dns-kubeconfig.yaml,/etc/kubernetes/dns-kubeconfig.yaml +./deploy/kubelet/kubelet.sh,/opt/kubelet.sh +./deploy/bin/kubelet,/opt/bin/kubelet +./deploy/ssl/ca/ca.pem,/etc/kubernetes/ssl/ca.pem +./deploy/ssl/kubelet/apiserver.pem,/etc/kubernetes/ssl/worker.pem +./deploy/ssl/kubelet/apiserver-key.pem,/etc/kubernetes/ssl/worker-key.pem +./deploy/kubelet/nodelist.yaml,/etc/kubernetes/nodes/nodelist.yaml diff --git a/src/ClusterBootstrap/template/master/post-upgrade.sh b/src/ClusterBootstrap/template/master/post-upgrade.sh new file mode 100755 index 000000000..7e1aa3838 --- /dev/null +++ b/src/ClusterBootstrap/template/master/post-upgrade.sh @@ -0,0 +1,10 @@ +sudo cp /etc/kubernetes/ssl/ca.pem /etc/ssl/etcd/ca.pem +sudo cp /etc/kubernetes/ssl/ca-key.pem /etc/ssl/etcd/ca-key.pem +sudo cp /etc/kubernetes/ssl/apiserver.pem /etc/ssl/etcd/apiserver.pem +sudo cp /etc/kubernetes/ssl/apiserver-key.pem /etc/ssl/etcd/apiserver-key.pem +sudo chmod +x /opt/bin/* +sudo systemctl daemon-reload +sudo systemctl stop kubelet +sudo docker pull {{cnf["dockers"]["container"]["hyperkube"]["fullname"]}} +sudo systemctl start kubelet +sudo systemctl enable kubelet diff --git a/src/ClusterBootstrap/template/master/pre-upgrade.sh b/src/ClusterBootstrap/template/master/pre-upgrade.sh new file mode 100755 index 000000000..fec6cb3ef --- /dev/null +++ b/src/ClusterBootstrap/template/master/pre-upgrade.sh @@ -0,0 +1,24 @@ +sudo systemctl stop kubelet +sudo timeout 10 docker rm -f $(docker ps -a | grep 'k8s_kube\|k8s_POD' | awk '{print $1}') +sudo mkdir -p /etc/kubernetes +sudo mkdir -p /opt/addons +sudo rm -r /etc/kubernetes +sudo rm -r /opt/addons +sudo systemctl daemon-reload + +# pre deployment +sudo mkdir -p /etc/kubernetes +sudo mkdir -p /etc/kubernetes/manifests +sudo mkdir -p /etc/kubernetes/ssl/ +sudo mkdir -p /etc/kubernetes/pki/ +sudo mkdir -p /opt/addons +sudo mkdir -p /opt/bin +sudo chown -R $USER /etc/kubernetes +sudo chown -R $USER /etc/flannel +sudo chown -R $USER /opt/bin +sudo chown -R $USER /opt/addons +{% if "kubelogdir" in cnf %} +sudo mkdir -p {{cnf["kubelogdir"]}}/kubescheduler +sudo mkdir -p {{cnf["kubelogdir"]}}/kubeapiserver +sudo chown -R $USER {{cnf["kubelogdir"]}} +{% endif %} diff --git a/src/ClusterBootstrap/template/master/upgrade.list b/src/ClusterBootstrap/template/master/upgrade.list new file mode 100755 index 000000000..45962fbc6 --- /dev/null +++ b/src/ClusterBootstrap/template/master/upgrade.list @@ -0,0 +1,20 @@ +./deploy/ssl/apiserver/ca.pem,/etc/kubernetes/ssl/ca.pem +./deploy/ssl/apiserver/ca-key.pem,/etc/kubernetes/ssl/ca-key.pem +./deploy/ssl/apiserver/apiserver.pem,/etc/kubernetes/ssl/apiserver.pem +./deploy/ssl/apiserver/apiserver-key.pem,/etc/kubernetes/ssl/apiserver-key.pem +./deploy/ssl/aggregator/ca.crt,/etc/kubernetes/pki/ca.crt +./deploy/ssl/aggregator/ca.key,/etc/kubernetes/pki/ca.key +./deploy/ssl/aggregator/proxy-client.crt,/etc/kubernetes/pki/proxy-client.crt +./deploy/ssl/aggregator/proxy-client.key,/etc/kubernetes/pki/proxy-client.key +./deploy/master/basicauth,/etc/kubernetes/basicauth +./deploy/master/worker-kubeconfig.yaml,/etc/kubernetes/worker-kubeconfig.yaml +./deploy/master/kubelet.service,/etc/systemd/system/kubelet.service +./deploy/master/kube-apiserver.yaml,/etc/kubernetes/manifests/kube-apiserver.yaml +./deploy/master/kube-controller-manager.yaml,/etc/kubernetes/manifests/kube-controller-manager.yaml +./deploy/master/kube-scheduler.yaml,/etc/kubernetes/manifests/kube-scheduler.yaml +./deploy/bin/kubelet,/opt/bin/kubelet +./deploy/bin/kubectl,/opt/bin/kubectl +./deploy/kube-addons,/opt/addons/kube-addons +./deploy/master/dns-kubeconfig.yaml,/etc/kubernetes/dns-kubeconfig.yaml +./deploy/services/clusterroles/clusterrolebindings.yaml,/etc/kubernetes/clusterroles/clusterrolebindings.yaml +./deploy/services/clusterroles/clusterroles.yaml,/etc/kubernetes/clusterroles/clusterroles.yaml From e8ac8a0997271c925c7c2d41bbfb5615d6cbf2eb Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 8 Aug 2019 05:39:59 +0000 Subject: [PATCH 529/595] robust: fix query ssh port failed on some case --- src/ClusterManager/endpoint_manager.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index 38ab435d3..c64a14c33 100755 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -21,7 +21,10 @@ from config import config, GetStoragePath, GetWorkPath from DataHandler import DataHandler +from job_deployer import JobDeployer + logger = logging.getLogger(__name__) +deployer = JobDeployer() def is_ssh_server_ready(pod_name): @@ -34,8 +37,12 @@ def is_ssh_server_ready(pod_name): def query_ssh_port(pod_name): bash_script = "grep ^Port /etc/ssh/sshd_config | cut -d' ' -f2" - ssh_port = k8sUtils.kubectl_exec("exec %s %s" % (pod_name, " -- " + bash_script)) - return int(ssh_port) + status_code, output = deployer.pod_exec(pod_name, ["/bin/bash", "-c", bash_script]) + if status_code != 0: + raise RuntimeError("Query ssh port failed: {}".format(pod_name)) + if not output: + return 22 + return int(output) def start_ssh_server(pod_name, user_name, host_network=False, ssh_port=22): From 42f303b10385619b390fa4667a41844be60b74ed Mon Sep 17 00:00:00 2001 From: Di Xu Date: Thu, 8 Aug 2019 08:42:54 +0000 Subject: [PATCH 530/595] add description --- src/ClusterBootstrap/deploy.py | 12 ++++++++---- src/ClusterBootstrap/params.py | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 477182371..b7f3c9006 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -3672,11 +3672,11 @@ def run_command( args, command, nargs, parser ): upgrade_masters() elif command == "upgrade_workers": gen_configs() - upgrade_workers() + upgrade_workers(nargs) elif command == "upgrade": gen_configs() upgrade_masters() - upgrade_workers() + upgrade_workers(nargs) elif command in scriptblocks: run_script_blocks(args.verbose, scriptblocks[command]) else: @@ -3714,7 +3714,7 @@ def upgrade_worker_node(nodeIP): utils.SSH_exec_script(config["ssh_cert"],worker_ssh_user, nodeIP, "./deploy/kubelet/post-worker-upgrade.sh") -def upgrade_workers(hypekube_url="gcr.io/google-containers/hyperkube:v1.15.2"): +def upgrade_workers(nargs, hypekube_url="gcr.io/google-containers/hyperkube:v1.15.2"): config["dockers"]["external"]["hyperkube"]["fullname"] = hypekube_url config["dockers"]["container"]["hyperkube"]["fullname"] = hypekube_url @@ -3730,7 +3730,8 @@ def upgrade_workers(hypekube_url="gcr.io/google-containers/hyperkube:v1.15.2"): workerNodes = get_worker_nodes(config["clusterId"], False) workerNodes = limit_nodes(workerNodes) for node in workerNodes: - upgrade_worker_node(node) + if in_list(node, nargs): + upgrade_worker_node(node) os.system("rm ./deploy/kubelet/options.env") os.system("rm ./deploy/kubelet/kubelet.service") @@ -3899,6 +3900,9 @@ def upgrade_masters(hypekube_url="gcr.io/google-containers/hyperkube:v1.15.2"): listmac display mac address of the cluster notes checkconfig display config items rendertemplate template_file target_file + upgrade_masters Upgrade the master nodes. + upgrade_workers [nodes] Upgrade the worker nodes. If no additional node is specified, all nodes will be updated. + upgrade [nodes] Upgrade the cluster and nodes. If no additional node is specified, all nodes will be updated. ''') ) parser.add_argument("-y", "--yes", help="Answer yes automatically for all prompt", diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 5fafa11a6..66c5a9ac7 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -577,7 +577,7 @@ }, "external": { # These dockers are to be built by additional add ons. - "hyperkube": {"fullname":"dlws/hyperkube:v1.9.0"}, + "hyperkube": {"fullname":"gcr.io/google-containers/hyperkube:v1.15.2"}, "freeflow": {"fullname":"dlws/freeflow:0.18"}, "podinfra": {"fullname":"dlws/pause-amd64:3.0"}, "nvidiadriver": {"fullname":"dlws/nvidia_driver:375.20"}, From 22be76d192d5951ed863757723077ff4bf68e02a Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 9 Aug 2019 06:15:10 +0000 Subject: [PATCH 531/595] Allow to set user quota in vc level. Set the user_quote to 4 GPUs in vc metadata: {"P40":..., user_quota": "4"} --- src/ClusterManager/job_manager.py | 36 +++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index cfe395338..555373617 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -170,11 +170,35 @@ def KillJob(job_id, desiredState="killed"): return False -def ApproveJob(job_id): - dataHandler = DataHandler() - dataHandler.UpdateJobTextField(job_id, "jobStatus", "queued") - dataHandler.Close() - return True +def ApproveJob(job): + try: + job_id = job["jobId"] + vcName = job["vcName"] + jobParams = json.loads(base64.b64decode(job["jobParams"])) + total_gpus = GetJobTotalGpu(jobParams) + + dataHandler = DataHandler() + vcList = dataHandler.ListVCs() + vc = None + for item in vcList: + if item["vcName"] == vcName: + vc = item + break + if vc is None: + logging.warning("Vc not exising! job {}, vc {}".format(job_id, vcName)) + return False + metadata = json.loads(vc["metadata"]) + + if "user_quota" in metadata and int(metadata["user_quota"]) < total_gpus: + logging.info("Job {} excesses the user quota: {} > {}. Will need approve from admin.".format(job_id, total_gpus, metadata["user_quota"])) + return False + else: + dataHandler.UpdateJobTextField(job_id, "jobStatus", "queued") + return True + except Exception as e: + logging.warning(e, exc_info=True) + finally: + dataHandler.Close() UnusualJobs = {} @@ -414,7 +438,7 @@ def Run(): elif job["jobStatus"] == "scheduling" or job["jobStatus"] == "running": UpdateJobStatus(job, notifier) elif job["jobStatus"] == "unapproved": - ApproveJob(job["jobId"]) + ApproveJob(job) except Exception as e: logging.warning(e, exc_info=True) except Exception as e: From 4e536c4297bdb3cf80d1252ba7f44f7c9da75baf Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 9 Aug 2019 06:05:21 +0000 Subject: [PATCH 532/595] check if *_manager is hanging and restart accordingly --- src/ClusterManager/cluster_manager.py | 88 ++++++++++++++++++++++---- src/ClusterManager/command_manager.py | 6 +- src/ClusterManager/endpoint_manager.py | 5 +- src/ClusterManager/job_manager.py | 5 +- src/ClusterManager/joblog_manager.py | 5 +- src/ClusterManager/node_manager.py | 6 +- src/ClusterManager/user_manager.py | 6 +- 7 files changed, 102 insertions(+), 19 deletions(-) diff --git a/src/ClusterManager/cluster_manager.py b/src/ClusterManager/cluster_manager.py index 83d2ad81f..87d7cecad 100755 --- a/src/ClusterManager/cluster_manager.py +++ b/src/ClusterManager/cluster_manager.py @@ -5,8 +5,11 @@ import logging.config import sys import time +import datetime import argparse import threading +import traceback +import signal from prometheus_client.twisted import MetricsResource from prometheus_client import Histogram @@ -19,7 +22,7 @@ manager_iteration_histogram = Histogram("manager_iteration_latency_seconds", "latency for manager to iterate", - buckets=(2.5, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, float("inf")), + buckets=(2.5, 5.0, 10.0, 20.0, 40.0, 80.0, 160.0, float("inf")), labelnames=("name",)) @@ -50,39 +53,98 @@ def create_log(logdir="/var/log/dlworkspace"): logging_config["handlers"]["file"]["filename"] = logdir + "/clustermanager.log" logging.config.dictConfig(logging_config) +def dumpstacks(signal, frame): + id2name = dict([(th.ident, th.name) for th in threading.enumerate()]) + code = [] + for threadId, stack in sys._current_frames().items(): + code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""), threadId)) + for filename, lineno, name, line in traceback.extract_stack(stack): + code.append('File: "%s", line %d, in %s' % (filename, lineno, name)) + if line: + code.append(" %s" % (line.strip())) + print "\n".join(code) + sys.stdout.flush() + sys.stderr.flush() + +def register_stack_trace_dump(): + signal.signal(signal.SIGTRAP, dumpstacks) + +def update_file_modification_time(path): + if not os.path.isfile(path): + f = open(path, "w") + f.close() + + mod_time = time.mktime(datetime.datetime.now().timetuple()) + os.utime(path, (mod_time, mod_time)) + +def get_elapsed_seconds(path): + mtime = datetime.datetime.fromtimestamp(os.path.getmtime(path)) + return (datetime.datetime.now() - mtime).seconds def Run(args): + register_stack_trace_dump() create_log() cwd = os.path.dirname(__file__) - cmds = [ + cmds = { + "job_manager": ["python", os.path.join(cwd, "job_manager.py"), "--port", str(args.j)], + "user_manager": ["python", os.path.join(cwd, "user_manager.py"), "--port", str(args.u)], + "node_manager": ["python", os.path.join(cwd, "node_manager.py"), "--port", str(args.n)], + "joblog_manager": ["python", os.path.join(cwd, "joblog_manager.py"), "--port", str(args.l)], + "command_manager": ["python", os.path.join(cwd, "command_manager.py"), "--port", str(args.c)], + "endpoint_manager": ["python", os.path.join(cwd, "endpoint_manager.py"), "--port", str(args.e)], - ] + } FNULL = open(os.devnull, "w") - childs = [None] * len(cmds) + childs = {} while True: - for i, cmd in enumerate(cmds): - child = childs[i] - if child is None or child.poll() is not None: - if child is not None: - logger.info("%s is dead restart it", cmd) - try: - childs[i] = subprocess32.Popen(cmd, stdin=FNULL, close_fds=True) - except Exception as e: - logger.exception("caught exception when trying to start %s, ignore", cmd) + try: + work(cmds, childs, FNULL) + except Exception as e: + logger.exception("caught exception while doing work") time.sleep(60) +def work(cmds, childs, FNULL): + for key, cmd in cmds.items(): + child = childs.get(key) + need_start = False + + if child is None or child.poll() is not None: + if child is not None: + logger.info("%s is dead restart it", cmd) + need_start = True + else: + sec = get_elapsed_seconds(key) + if sec <= args.tictoc: + continue + logger.info("%s did not update file for %d seconds, restart it", + key, sec) + child.send_signal(signal.SIGTRAP) # try to print their stacktrace + time.sleep(1) + child.kill() + sys.stdout.flush() + sys.stderr.flush() + need_start = True + + if need_start: + update_file_modification_time(key) + try: + childs[key] = subprocess32.Popen(cmd, stdin=FNULL) + except Exception as e: + logger.exception("caught exception when trying to start %s, ignore", cmd) + if __name__ == "__main__": parser = argparse.ArgumentParser() + parser.add_argument("--tictoc", help="how many seconds to wait until kill subprocess", type=int, default=600) parser.add_argument("-j", help="port of job_manager", type=int, default=9200) parser.add_argument("-u", help="port of user_manager", type=int, default=9201) parser.add_argument("-n", help="port of node_manager", type=int, default=9202) diff --git a/src/ClusterManager/command_manager.py b/src/ClusterManager/command_manager.py index 452eedd96..86458001a 100755 --- a/src/ClusterManager/command_manager.py +++ b/src/ClusterManager/command_manager.py @@ -31,7 +31,7 @@ import logging -from cluster_manager import setup_exporter_thread, manager_iteration_histogram +from cluster_manager import setup_exporter_thread, manager_iteration_histogram, register_stack_trace_dump, update_file_modification_time logger = logging.getLogger(__name__) @@ -52,8 +52,12 @@ def create_log(logdir = '/var/log/dlworkspace'): logging.config.dictConfig(logging_config) def Run(): + register_stack_trace_dump() create_log() + while True: + update_file_modification_time("command_manager") + with manager_iteration_histogram.labels("command_manager").time(): try: dataHandler = DataHandler() diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index 38ab435d3..15c8c2315 100755 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -14,7 +14,7 @@ import logging.config import argparse -from cluster_manager import setup_exporter_thread, manager_iteration_histogram +from cluster_manager import setup_exporter_thread, manager_iteration_histogram, register_stack_trace_dump, update_file_modification_time sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../utils")) import k8sUtils @@ -248,9 +248,12 @@ def create_log(logdir = '/var/log/dlworkspace'): def Run(): + register_stack_trace_dump() create_log() while True: + update_file_modification_time("endpoint_manager") + with manager_iteration_histogram.labels("endpoint_manager").time(): # start endpoints start_endpoints() diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index cfe395338..e18720c7a 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -41,7 +41,7 @@ from job_deployer import JobDeployer from job_role import JobRole -from cluster_manager import setup_exporter_thread, manager_iteration_histogram +from cluster_manager import setup_exporter_thread, manager_iteration_histogram, register_stack_trace_dump, update_file_modification_time def all_pods_not_existing(job_id): @@ -385,11 +385,14 @@ def TakeJobActions(jobs): def Run(): + register_stack_trace_dump() notifier = notify.Notifier(config.get("job-manager")) notifier.start() create_log() while True: + update_file_modification_time("job_manager") + with manager_iteration_histogram.labels("job_manager").time(): try: config["racks"] = k8sUtils.get_node_labels("rack") diff --git a/src/ClusterManager/joblog_manager.py b/src/ClusterManager/joblog_manager.py index 9f86641f8..b43232a3b 100755 --- a/src/ClusterManager/joblog_manager.py +++ b/src/ClusterManager/joblog_manager.py @@ -32,7 +32,7 @@ from config import config, GetStoragePath from DataHandler import DataHandler -from cluster_manager import setup_exporter_thread, manager_iteration_histogram +from cluster_manager import setup_exporter_thread, manager_iteration_histogram, register_stack_trace_dump, update_file_modification_time logger = logging.getLogger(__name__) @@ -150,10 +150,13 @@ def update_job_logs(): def Run(): + register_stack_trace_dump() create_log() logging.info("start to update job logs ...") while True: + update_file_modification_time("joblog_manager") + with manager_iteration_histogram.labels("joblog_manager").time(): try: update_job_logs() diff --git a/src/ClusterManager/node_manager.py b/src/ClusterManager/node_manager.py index f8f315608..fb0de3193 100755 --- a/src/ClusterManager/node_manager.py +++ b/src/ClusterManager/node_manager.py @@ -39,7 +39,7 @@ from config import config from DataHandler import DataHandler -from cluster_manager import setup_exporter_thread, manager_iteration_histogram +from cluster_manager import setup_exporter_thread, manager_iteration_histogram, register_stack_trace_dump, update_file_modification_time def create_log(logdir = '/var/log/dlworkspace'): @@ -243,10 +243,14 @@ def get_cluster_status(): def Run(): + register_stack_trace_dump() create_log() logging.info("start to update nodes usage information ...") config["cluster_status"] = None + while True: + update_file_modification_time("node_manager") + with manager_iteration_histogram.labels("node_manager").time(): try: get_cluster_status() diff --git a/src/ClusterManager/user_manager.py b/src/ClusterManager/user_manager.py index 2e01da521..eb85a79a1 100755 --- a/src/ClusterManager/user_manager.py +++ b/src/ClusterManager/user_manager.py @@ -34,7 +34,7 @@ from config import config from DataHandler import DataHandler -from cluster_manager import setup_exporter_thread, manager_iteration_histogram +from cluster_manager import setup_exporter_thread, manager_iteration_histogram, register_stack_trace_dump, update_file_modification_time def create_log(logdir = '/var/log/dlworkspace'): @@ -81,9 +81,13 @@ def set_user_directory(): os.system("chmod 644 "+authorized_keyspath) def Run(): + register_stack_trace_dump() create_log() logging.info("start to update user directory...") + while True: + update_file_modification_time("user_manager") + with manager_iteration_histogram.labels("user_manager").time(): try: set_user_directory() From d46dbb984177d8b301b0138e7b05a0e86a6d1801 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 14 Aug 2019 03:11:30 +0000 Subject: [PATCH 533/595] add selector to DaemonSet --- .../services/nvidia-device-plugin/nvidia-device-plugin.yaml | 3 +++ src/ClusterBootstrap/template/kube-addons/weave.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml b/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml index 5f1657366..b59186270 100755 --- a/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml +++ b/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml @@ -4,6 +4,9 @@ metadata: name: nvidia-device-plugin-daemonset namespace: kube-system spec: + selector: + matchLabels: + name: nvidia-device-plugin-ds template: metadata: # Mark this pod as a critical add-on; when enabled, the critical add-on scheduler diff --git a/src/ClusterBootstrap/template/kube-addons/weave.yaml b/src/ClusterBootstrap/template/kube-addons/weave.yaml index e7576c7f0..cd3b9faf1 100755 --- a/src/ClusterBootstrap/template/kube-addons/weave.yaml +++ b/src/ClusterBootstrap/template/kube-addons/weave.yaml @@ -149,6 +149,9 @@ items: name: weave-net namespace: kube-system spec: + selector: + matchLabels: + name: weave-net template: metadata: labels: From d127ed8975bc3aa6bf5af818bf59a79b40d4286c Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 14 Aug 2019 03:54:49 +0000 Subject: [PATCH 534/595] change proxy to kube-proxy --- src/ClusterBootstrap/template/kube-addons/kube-proxy.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClusterBootstrap/template/kube-addons/kube-proxy.json b/src/ClusterBootstrap/template/kube-addons/kube-proxy.json index f8538fcb8..194b4e33f 100755 --- a/src/ClusterBootstrap/template/kube-addons/kube-proxy.json +++ b/src/ClusterBootstrap/template/kube-addons/kube-proxy.json @@ -27,7 +27,7 @@ { "command": [ "/hyperkube", - "proxy", + "kube-proxy", "--kubeconfig=/etc/kubernetes/worker-kubeconfig.yaml" ], "image": "{{cnf["dockers"]["container"]["hyperkube"]["fullname"]}}", From a94351533b117f7de73501cd2315dba2c7773698 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 14 Aug 2019 04:17:41 +0000 Subject: [PATCH 535/595] use 1.11 nvidia device plugin --- .../nvidia-device-plugin/nvidia-device-plugin.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml b/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml index b59186270..912cb7655 100755 --- a/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml +++ b/src/ClusterBootstrap/services/nvidia-device-plugin/nvidia-device-plugin.yaml @@ -7,6 +7,8 @@ spec: selector: matchLabels: name: nvidia-device-plugin-ds + updateStrategy: + type: RollingUpdate template: metadata: # Mark this pod as a critical add-on; when enabled, the critical add-on scheduler @@ -22,8 +24,11 @@ spec: # This, along with the annotation above marks this pod as a critical add-on. - key: CriticalAddonsOnly operator: Exists + - key: nvidia.com/gpu + operator: Exists + effect: NoSchedule containers: - - image: nvidia/k8s-device-plugin:1.9 + - image: nvidia/k8s-device-plugin:1.11 name: nvidia-device-plugin-ctr securityContext: allowPrivilegeEscalation: false From a295f4ff8220e4a78db2aafc5cade612de717bb8 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 14 Aug 2019 05:09:03 +0000 Subject: [PATCH 536/595] upgrade nvidia driver --- .../scripts/prepare_ubuntu.sh | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh index 5f41b2ae8..e9f13f0ac 100755 --- a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh +++ b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh @@ -79,12 +79,35 @@ if lspci | grep -qE "[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F].[0-9] (3D|VG # chmod +x /tmp/NVIDIA-Linux-x86_64-$NVIDIA_VERSION.run # sudo bash /tmp/NVIDIA-Linux-x86_64-$NVIDIA_VERSION.run -a -s + sudo systemctl stop kubelet + + echo kill all containers so we could remove old nvidia drivers + timeout 10 docker kill $(docker ps -a -q) + + lsmod | grep -qE "^nvidia" && + { + echo ======== NVIDIA driver is running, uninstall it ========= + DEP_MODS=`lsmod | tr -s " " | grep -E "^nvidia" | cut -f 4 -d " "` + for mod in ${DEP_MODS//,/ } + do + sudo rmmod $mod || + { + echo "The driver $mod is still in use, can't unload it." + exit 1 + } + done + sudo rmmod nvidia || + { + echo "The driver nvidia is still in use, can't unload it." + exit 1 + } + } + sudo add-apt-repository -y ppa:graphics-drivers/ppa - sudo apt-get purge -y nvidia* - sudo apt-get update - sudo apt-get install -y nvidia-driver-415 - + sudo apt-get purge -y nvidia* + sudo apt-get update + sudo apt-get install -y nvidia-driver-430 sudo apt install -y nvidia-modprobe From abf20fbbf86cec5f9d63ef4dcb070be8b2b00374 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 16 Aug 2019 09:45:57 +0000 Subject: [PATCH 537/595] upgrade cni --- src/ClusterBootstrap/deploy.py | 8 +- src/ClusterBootstrap/params.py | 4 +- .../kube-addons/heapster-deployment.json | 100 ------------------ .../template/kube-addons/heapster-svc.json | 23 ---- .../template/kube-addons/nvidia-driver.json | 87 --------------- .../template/kube-addons/weave.yaml | 43 ++++---- .../template/kubelet/10-weave.conf | 5 + .../template/kubelet/deploy.list | 15 --- .../template/kubelet/kube-proxy.yaml | 37 ------- .../template/kubelet/post-worker-deploy.sh | 15 --- .../template/kubelet/pre-worker-deploy.sh | 40 ------- .../template/kubelet/pre-worker-upgrade.sh | 2 + .../template/kubelet/ubuntu/deploy.list | 9 +- .../kubelet/ubuntu/pre-worker-deploy.sh | 1 + .../template/kubelet/upgrade.list | 14 +++ .../template/master/deploy.list | 26 ----- .../template/master/kube-proxy.yaml | 25 ----- .../template/master/post-master-deploy.sh | 16 --- .../template/master/pre-master-deploy.sh | 19 ---- .../template/master/pre-upgrade.sh | 2 + .../template/master/ubuntu/deploy.list | 9 +- .../template/master/upgrade.list | 14 +++ 22 files changed, 77 insertions(+), 437 deletions(-) delete mode 100755 src/ClusterBootstrap/template/kube-addons/heapster-deployment.json delete mode 100644 src/ClusterBootstrap/template/kube-addons/heapster-svc.json delete mode 100755 src/ClusterBootstrap/template/kube-addons/nvidia-driver.json create mode 100644 src/ClusterBootstrap/template/kubelet/10-weave.conf delete mode 100755 src/ClusterBootstrap/template/kubelet/deploy.list delete mode 100755 src/ClusterBootstrap/template/kubelet/kube-proxy.yaml delete mode 100755 src/ClusterBootstrap/template/kubelet/post-worker-deploy.sh delete mode 100755 src/ClusterBootstrap/template/kubelet/pre-worker-deploy.sh delete mode 100755 src/ClusterBootstrap/template/master/deploy.list delete mode 100755 src/ClusterBootstrap/template/master/kube-proxy.yaml delete mode 100755 src/ClusterBootstrap/template/master/post-master-deploy.sh delete mode 100755 src/ClusterBootstrap/template/master/pre-master-deploy.sh diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index b7f3c9006..1382bd4a9 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -876,18 +876,14 @@ def deploy_master(kubernetes_master): def get_cni_binary(): os.system("mkdir -p ./deploy/bin") - urllib.urlretrieve ("http://ccsdatarepo.westus.cloudapp.azure.com/data/containernetworking/cni-amd64-v0.5.2.tgz", "./deploy/bin/cni-amd64-v0.5.2.tgz") + urllib.urlretrieve("https://xudifsd.org/cni-v0.7.1.tgz", "./deploy/bin/cni-v0.7.1.tgz") # TODO, change the source url if verbose: print "Extracting CNI binaries" - os.system("tar -zxvf ./deploy/bin/cni-amd64-v0.5.2.tgz -C ./deploy/bin") + os.system("tar -zxvf ./deploy/bin/cni-v0.7.1.tgz -C ./deploy/bin") def get_kubectl_binary(force = False): get_hyperkube_docker(force = force) - #os.system("mkdir -p ./deploy/bin") - urllib.urlretrieve ("http://ccsdatarepo.westus.cloudapp.azure.com/data/kube/kubelet/kubelet", "./deploy/bin/kubelet-old") - #urllib.urlretrieve ("http://ccsdatarepo.westus.cloudapp.azure.com/data/kube/kubelet/kubectl", "./deploy/bin/kubectl") - #os.system("chmod +x ./deploy/bin/*") get_cni_binary() def get_hyperkube_docker(force = False) : diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 66c5a9ac7..a0b4e56cc 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -581,8 +581,8 @@ "freeflow": {"fullname":"dlws/freeflow:0.18"}, "podinfra": {"fullname":"dlws/pause-amd64:3.0"}, "nvidiadriver": {"fullname":"dlws/nvidia_driver:375.20"}, - "weave":{"fullname":"dlws/weave:2.2.0"}, - "weave-npc":{"fullname":"dlws/weave-npc:2.2.0"}, + "weave":{"fullname":"docker.io/weaveworks/weave-kube:2.5.2"}, + "weave-npc":{"fullname":"docker.io/weaveworks/weave-npc:2.5.2"}, "k8s-dashboard":{"fullname":"dlws/kubernetes-dashboard-amd64:v1.5.1"}, "kube-dns":{"fullname":"dlws/k8s-dns-kube-dns-amd64:1.14.8"}, "kube-dnsmasq":{"fullname":"dlws/k8s-dns-dnsmasq-nanny-amd64:1.14.8"}, diff --git a/src/ClusterBootstrap/template/kube-addons/heapster-deployment.json b/src/ClusterBootstrap/template/kube-addons/heapster-deployment.json deleted file mode 100755 index c08be655b..000000000 --- a/src/ClusterBootstrap/template/kube-addons/heapster-deployment.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "apiVersion": "apps/v1", - "kind": "Deployment", - "metadata": { - "labels": { - "k8s-app": "heapster", - "kubernetes.io/cluster-service": "true", - "version": "v1.2.0" - }, - "name": "heapster-v1.2.0", - "namespace": "kube-system" - }, - "spec": { - "replicas": 1, - "selector": { - "matchLabels": { - "k8s-app": "heapster", - "version": "v1.2.0" - } - }, - "template": { - "metadata": { - "annotations": { - "scheduler.alpha.kubernetes.io/critical-pod": "", - "scheduler.alpha.kubernetes.io/tolerations": "[{\"key\":\"CriticalAddonsOnly\", \"operator\":\"Exists\"}]" - }, - "labels": { - "k8s-app": "heapster", - "version": "v1.2.0" - } - }, - "spec": { - "containers": [ - { - "command": [ - "/heapster", - "--source=kubernetes.summary_api:''" - ], - "image": "{{cnf["dockers"]["container"]["heapster"]["fullname"]}}", - "name": "heapster", - "resources": { - "limits": { - "cpu": "80m", - "memory": "140Mi" - }, - "requests": { - "cpu": "80m", - "memory": "140Mi" - } - } - }, - { - "command": [ - "/pod_nanny", - "--cpu=80m", - "--extra-cpu=0.5m", - "--memory=140Mi", - "--extra-memory=4Mi", - "--threshold=5", - "--deployment=heapster-v1.2.0-beta.1", - "--container=heapster", - "--poll-period=300000", - "--estimator=exponential" - ], - "env": [ - { - "name": "MY_POD_NAME", - "valueFrom": { - "fieldRef": { - "fieldPath": "metadata.name" - } - } - }, - { - "name": "MY_POD_NAMESPACE", - "valueFrom": { - "fieldRef": { - "fieldPath": "metadata.namespace" - } - } - } - ], - "image": "dlws/addon-resizer:1.6", - "name": "heapster-nanny", - "resources": { - "limits": { - "cpu": "50m", - "memory": "90Mi" - }, - "requests": { - "cpu": "50m", - "memory": "90Mi" - } - } - } - ] - } - } - } -} diff --git a/src/ClusterBootstrap/template/kube-addons/heapster-svc.json b/src/ClusterBootstrap/template/kube-addons/heapster-svc.json deleted file mode 100644 index 8dd242862..000000000 --- a/src/ClusterBootstrap/template/kube-addons/heapster-svc.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "labels": { - "kubernetes.io/cluster-service": "true", - "kubernetes.io/name": "Heapster" - }, - "name": "heapster", - "namespace": "kube-system" - }, - "spec": { - "ports": [ - { - "port": 80, - "targetPort": 8082 - } - ], - "selector": { - "k8s-app": "heapster" - } - } -} diff --git a/src/ClusterBootstrap/template/kube-addons/nvidia-driver.json b/src/ClusterBootstrap/template/kube-addons/nvidia-driver.json deleted file mode 100755 index 56fba5486..000000000 --- a/src/ClusterBootstrap/template/kube-addons/nvidia-driver.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "apiVersion": "apps/v1", - "kind": "DaemonSet", - "metadata": { - "labels": { - "component": "nvidia-driver", - "tier": "node" - }, - "name": "nvidia-driver", - "namespace": "kube-system" - }, - "spec": { - "selector": { - "matchLabels": { - "component": "nvidia-driver" - } - }, - "template": { - "metadata": { - "labels": { - "component": "nvidia-driver", - "tier": "node" - } - }, - "spec": { - "containers": [ - { - "command": [ - "/opt/init_service.sh" - ], - "image": "mlcloudreg.westus.cloudapp.azure.com:5000/nvidia_driver:GeForce375.20_kube", - "name": "kube-proxy", - "imagePullPolicy": "Always", - "securityContext": { - "privileged": true - }, - "volumeMounts": [ - { - "mountPath": "/opt/nvidia-docker-volume", - "name": "nvidia-docker-volume" - }, - { - "mountPath": "/opt/nvidia-driver", - "name": "nvidia-driver" - }, - { - "mountPath": "/opt/nvidia-docker", - "name": "nvidia-docker" - }, - { - "mountPath": "/dev", - "name": "dev" - } - ] - } - ], - "hostNetwork": true, - "volumes": [ - { - "hostPath": { - "path": "/var/lib/nvidia-docker" - }, - "name": "nvidia-docker-volume" - }, - { - "hostPath": { - "path": "/opt/nvidia-driver" - }, - "name": "nvidia-driver" - }, - { - "hostPath": { - "path": "/opt/nvidia-docker" - }, - "name": "nvidia-docker" - }, - { - "hostPath": { - "path": "/dev" - }, - "name": "dev" - } - ] - } - } - } -} diff --git a/src/ClusterBootstrap/template/kube-addons/weave.yaml b/src/ClusterBootstrap/template/kube-addons/weave.yaml index cd3b9faf1..9ef1d9021 100755 --- a/src/ClusterBootstrap/template/kube-addons/weave.yaml +++ b/src/ClusterBootstrap/template/kube-addons/weave.yaml @@ -9,8 +9,8 @@ items: cloud.weave.works/launcher-info: |- { "original-request": { - "url": "/k8s/v1.8/net.yaml?k8s-version=1.9", - "date": "Tue Mar 06 2018 01:14:36 GMT+0000 (UTC)" + "url": "/k8s/v1.10/net.yaml?k8s-version=1.15", + "date": "Thu Aug 15 2019 05:48:37 GMT+0000 (UTC)" }, "email-address": "support@weave.works" } @@ -25,14 +25,13 @@ items: cloud.weave.works/launcher-info: |- { "original-request": { - "url": "/k8s/v1.8/net.yaml?k8s-version=1.9", - "date": "Tue Mar 06 2018 01:14:36 GMT+0000 (UTC)" + "url": "/k8s/v1.10/net.yaml?k8s-version=1.15", + "date": "Thu Aug 15 2019 05:48:37 GMT+0000 (UTC)" }, "email-address": "support@weave.works" } labels: name: weave-net - namespace: kube-system rules: - apiGroups: - '' @@ -52,6 +51,13 @@ items: - get - list - watch + - apiGroups: + - '' + resources: + - nodes/status + verbs: + - patch + - update - apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: @@ -60,14 +66,13 @@ items: cloud.weave.works/launcher-info: |- { "original-request": { - "url": "/k8s/v1.8/net.yaml?k8s-version=1.9", - "date": "Tue Mar 06 2018 01:14:36 GMT+0000 (UTC)" + "url": "/k8s/v1.10/net.yaml?k8s-version=1.15", + "date": "Thu Aug 15 2019 05:48:37 GMT+0000 (UTC)" }, "email-address": "support@weave.works" } labels: name: weave-net - namespace: kube-system roleRef: kind: ClusterRole name: weave-net @@ -84,8 +89,8 @@ items: cloud.weave.works/launcher-info: |- { "original-request": { - "url": "/k8s/v1.8/net.yaml?k8s-version=1.9", - "date": "Tue Mar 06 2018 01:14:36 GMT+0000 (UTC)" + "url": "/k8s/v1.10/net.yaml?k8s-version=1.15", + "date": "Thu Aug 15 2019 05:48:37 GMT+0000 (UTC)" }, "email-address": "support@weave.works" } @@ -116,8 +121,8 @@ items: cloud.weave.works/launcher-info: |- { "original-request": { - "url": "/k8s/v1.8/net.yaml?k8s-version=1.9", - "date": "Tue Mar 06 2018 01:14:36 GMT+0000 (UTC)" + "url": "/k8s/v1.10/net.yaml?k8s-version=1.15", + "date": "Thu Aug 15 2019 05:48:37 GMT+0000 (UTC)" }, "email-address": "support@weave.works" } @@ -140,8 +145,8 @@ items: cloud.weave.works/launcher-info: |- { "original-request": { - "url": "/k8s/v1.8/net.yaml?k8s-version=1.9", - "date": "Tue Mar 06 2018 01:14:36 GMT+0000 (UTC)" + "url": "/k8s/v1.10/net.yaml?k8s-version=1.15", + "date": "Thu Aug 15 2019 05:48:37 GMT+0000 (UTC)" }, "email-address": "support@weave.works" } @@ -152,6 +157,7 @@ items: selector: matchLabels: name: weave-net + minReadySeconds: 5 template: metadata: labels: @@ -162,20 +168,17 @@ items: command: - /home/weave/launch.sh env: - - name: IPALLOC_RANGE - value: {{cnf["pod_ip_range"]}} - name: HOSTNAME valueFrom: fieldRef: apiVersion: v1 fieldPath: spec.nodeName - image: '{{cnf["dockers"]["container"]["weave"]["fullname"]}}' - livenessProbe: + image: '{{cnf["dockers"]["container"]["weave"]["fullname"]}}' + readinessProbe: httpGet: host: 127.0.0.1 path: /status port: 6784 - initialDelaySeconds: 30 resources: requests: cpu: 10m @@ -197,7 +200,6 @@ items: - name: xtables-lock mountPath: /run/xtables.lock - name: weave-npc - args: [] env: - name: HOSTNAME valueFrom: @@ -244,5 +246,6 @@ items: - name: xtables-lock hostPath: path: /run/xtables.lock + type: FileOrCreate updateStrategy: type: RollingUpdate diff --git a/src/ClusterBootstrap/template/kubelet/10-weave.conf b/src/ClusterBootstrap/template/kubelet/10-weave.conf new file mode 100644 index 000000000..9eceb84da --- /dev/null +++ b/src/ClusterBootstrap/template/kubelet/10-weave.conf @@ -0,0 +1,5 @@ +{ + "name": "weave", + "type": "weave-net", + "hairpinMode": true +} diff --git a/src/ClusterBootstrap/template/kubelet/deploy.list b/src/ClusterBootstrap/template/kubelet/deploy.list deleted file mode 100755 index b45bb68a0..000000000 --- a/src/ClusterBootstrap/template/kubelet/deploy.list +++ /dev/null @@ -1,15 +0,0 @@ -./deploy/master/40-ExecStartPre-symlink.conf,/etc/systemd/system/flanneld.service.d/40-ExecStartPre-symlink.conf -./deploy/master/40-flannel.conf,/etc/systemd/system/docker.service.d/40-flannel.conf -./deploy/kubelet/options.env,/etc/flannel/options.env -./deploy/kubelet/kubelet.service,/etc/systemd/system/kubelet.service -./deploy/kubelet/worker-kubeconfig.yaml,/etc/kubernetes/worker-kubeconfig.yaml -./deploy/kubelet/dns-kubeconfig.yaml,/etc/kubernetes/dns-kubeconfig.yaml -./deploy/kubelet/kubelet.sh,/opt/kubelet.sh -./deploy/bin/kubelet,/opt/bin/kubelet -./deploy/ssl/ca/ca.pem,/etc/kubernetes/ssl/ca.pem -./deploy/ssl/kubelet/apiserver.pem,/etc/kubernetes/ssl/worker.pem -./deploy/ssl/kubelet/apiserver-key.pem,/etc/kubernetes/ssl/worker-key.pem -./deploy/kubelet/report.sh,/opt/report.sh -./deploy/kubelet/reportcluster.service,/etc/systemd/system/reportcluster.service -./deploy/kubelet/nodelist.yaml,/etc/kubernetes/nodes/nodelist.yaml -./deploy/kubelet/daemon.json,/etc/docker/daemon.json \ No newline at end of file diff --git a/src/ClusterBootstrap/template/kubelet/kube-proxy.yaml b/src/ClusterBootstrap/template/kubelet/kube-proxy.yaml deleted file mode 100755 index 5880fa78b..000000000 --- a/src/ClusterBootstrap/template/kubelet/kube-proxy.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: kube-proxy - namespace: kube-system -spec: - hostNetwork: true - containers: - - name: kube-proxy - image: {{cnf["dockers"]["container"]["hyperkube"]["fullname"]}} - command: - - /proxy - - --master={{cnf["api_servers"]}} - - --kubeconfig=/etc/kubernetes/worker-kubeconfig.yaml - - --proxy-mode=iptables - securityContext: - privileged: true - volumeMounts: - - mountPath: /etc/ssl/certs - name: "ssl-certs" - - mountPath: /etc/kubernetes/worker-kubeconfig.yaml - name: "kubeconfig" - readOnly: true - - mountPath: /etc/kubernetes/ssl - name: "etc-kube-ssl" - readOnly: true - volumes: - - name: "ssl-certs" - hostPath: - path: "/usr/share/ca-certificates" - - name: "kubeconfig" - hostPath: - path: "/etc/kubernetes/worker-kubeconfig.yaml" - - name: "etc-kube-ssl" - hostPath: - path: "/etc/kubernetes/ssl" - diff --git a/src/ClusterBootstrap/template/kubelet/post-worker-deploy.sh b/src/ClusterBootstrap/template/kubelet/post-worker-deploy.sh deleted file mode 100755 index c1f0ba1c4..000000000 --- a/src/ClusterBootstrap/template/kubelet/post-worker-deploy.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -sudo cp /etc/kubernetes/ssl/ca.pem /etc/ssl/etcd/ca.pem -sudo cp /etc/kubernetes/ssl/worker.pem /etc/ssl/etcd/worker.pem -sudo cp /etc/kubernetes/ssl/worker-key.pem /etc/ssl/etcd/worker-key.pem -sudo chmod +x /opt/bin/kubelet -sudo systemctl daemon-reload -sudo systemctl stop kubelet -sudo systemctl stop docker -sudo systemctl stop flanneld -sudo systemctl start flanneld -sudo systemctl start docker -sudo systemctl start kubelet -sudo systemctl start rpc-statd -sudo systemctl enable flanneld -sudo systemctl enable kubelet \ No newline at end of file diff --git a/src/ClusterBootstrap/template/kubelet/pre-worker-deploy.sh b/src/ClusterBootstrap/template/kubelet/pre-worker-deploy.sh deleted file mode 100755 index a2e322b22..000000000 --- a/src/ClusterBootstrap/template/kubelet/pre-worker-deploy.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -sudo systemctl stop kubelet -sudo docker rm -f $(docker ps -a | grep 'k8s_kube\|k8s_POD' | awk '{print $1}') -sudo systemctl stop docker -sudo systemctl stop flanneld - -sudo rm /etc/systemd/system/flanneld.service.d/40-ExecStartPre-symlink.conf -sudo rm /etc/systemd/system/docker.service.d/40-flannel.conf -sudo rm /etc/flannel/options.env -sudo systemctl disable kubelet -sudo systemctl disable reportcluster -sudo systemctl disable bootstrap -sudo systemctl disable checkinternet -sudo systemctl disable nvidia-docker -sudo systemctl disable nvidia-driver - -sudo rm /etc/systemd/system/kubelet.service -sudo rm /etc/systemd/system/reportcluster.service -sudo rm /etc/systemd/system/bootstrap.service -sudo rm /etc/systemd/system/checkinternet.service -sudo rm /etc/systemd/system/nvidia-docker.service -sudo rm /etc/systemd/system/nvidia-driver.service - - -sudo rm -r /etc/kubernetes -sudo rm /opt/kubelet.sh -sudo rm /opt/bin/kubelet -sudo rm -r /etc/kubernetes -sudo rm -r /etc/systemd/system/reportcluster.service - -sudo mkdir -p /etc/kubernetes -sudo mkdir -p /etc/systemd/system/flanneld.service.d -sudo mkdir -p /etc/systemd/system/docker.service.d -sudo mkdir -p /etc/flannel -sudo mkdir -p /etc/kubernetes/manifests -sudo mkdir -p /etc/kubernetes/ssl/ -sudo mkdir -p /etc/ssl/etcd -sudo mkdir -p /opt/bin -{{'sudo mkdir -p '~cnf["kubeletlogdir"]~'/kubelet' if "kubeletlogdir" in cnf}} - diff --git a/src/ClusterBootstrap/template/kubelet/pre-worker-upgrade.sh b/src/ClusterBootstrap/template/kubelet/pre-worker-upgrade.sh index 61096cf3c..58115a847 100755 --- a/src/ClusterBootstrap/template/kubelet/pre-worker-upgrade.sh +++ b/src/ClusterBootstrap/template/kubelet/pre-worker-upgrade.sh @@ -10,10 +10,12 @@ sudo rm -r /etc/kubernetes sudo rm /opt/kubelet.sh sudo rm /opt/bin/kubelet sudo rm -r /etc/kubernetes +sudo rm -rf /opt/cni sudo mkdir -p /etc/kubernetes sudo mkdir -p /etc/kubernetes/manifests sudo mkdir -p /etc/kubernetes/ssl/ sudo mkdir -p /etc/ssl/etcd sudo mkdir -p /opt/bin +sudo mkdir -p /opt/cni/bin {{'sudo mkdir -p '~cnf["kubeletlogdir"]~'/kubelet' if "kubeletlogdir" in cnf}} diff --git a/src/ClusterBootstrap/template/kubelet/ubuntu/deploy.list b/src/ClusterBootstrap/template/kubelet/ubuntu/deploy.list index 71ca7de92..74d608fef 100755 --- a/src/ClusterBootstrap/template/kubelet/ubuntu/deploy.list +++ b/src/ClusterBootstrap/template/kubelet/ubuntu/deploy.list @@ -20,8 +20,11 @@ ./deploy/bin/ipvlan,/opt/cni/bin/ipvlan ./deploy/bin/bridge,/opt/cni/bin/bridge ./deploy/bin/tuning,/opt/cni/bin/tuning -./deploy/bin/noop,/opt/cni/bin/noop ./deploy/bin/host-local,/opt/cni/bin/host-local -./deploy/bin/cnitool,/opt/cni/bin/cnitool ./deploy/bin/flannel,/opt/cni/bin/flannel -./deploy/kubelet/daemon.json,/etc/docker/daemon.json \ No newline at end of file +./deploy/bin/host-device,/opt/cni/bin/host-device +./deploy/bin/portmap,/opt/cni/bin/portmap +./deploy/bin/sample,/opt/cni/bin/sample +./deploy/bin/vlan,/opt/cni/bin/vlan +./deploy/kubelet/daemon.json,/etc/docker/daemon.json +./deploy/kubelet/10-weave.conf,/etc/cni/net.d/10-weave.conf diff --git a/src/ClusterBootstrap/template/kubelet/ubuntu/pre-worker-deploy.sh b/src/ClusterBootstrap/template/kubelet/ubuntu/pre-worker-deploy.sh index 3523fdbac..d69bce752 100755 --- a/src/ClusterBootstrap/template/kubelet/ubuntu/pre-worker-deploy.sh +++ b/src/ClusterBootstrap/template/kubelet/ubuntu/pre-worker-deploy.sh @@ -13,5 +13,6 @@ sudo rm -r /etc/kubernetes/manifests/* sudo rm -r /etc/kubernetes/ssl/* sudo rm -r /etc/ssl/etcd/* sudo rm -r /opt/addons/kube-addons/* +sudo rm -rf /etc/cni/net.d sudo chown -R $USER /etc/kubernetes sudo chown -R $USER /opt/addons/kube-addons diff --git a/src/ClusterBootstrap/template/kubelet/upgrade.list b/src/ClusterBootstrap/template/kubelet/upgrade.list index 41880d455..69a332f42 100755 --- a/src/ClusterBootstrap/template/kubelet/upgrade.list +++ b/src/ClusterBootstrap/template/kubelet/upgrade.list @@ -8,3 +8,17 @@ ./deploy/ssl/kubelet/apiserver.pem,/etc/kubernetes/ssl/worker.pem ./deploy/ssl/kubelet/apiserver-key.pem,/etc/kubernetes/ssl/worker-key.pem ./deploy/kubelet/nodelist.yaml,/etc/kubernetes/nodes/nodelist.yaml +./deploy/bin/macvlan,/opt/cni/bin/macvlan +./deploy/bin/dhcp,/opt/cni/bin/dhcp +./deploy/bin/loopback,/opt/cni/bin/loopback +./deploy/bin/ptp,/opt/cni/bin/ptp +./deploy/bin/ipvlan,/opt/cni/bin/ipvlan +./deploy/bin/bridge,/opt/cni/bin/bridge +./deploy/bin/tuning,/opt/cni/bin/tuning +./deploy/bin/host-local,/opt/cni/bin/host-local +./deploy/bin/flannel,/opt/cni/bin/flannel +./deploy/bin/host-device,/opt/cni/bin/host-device +./deploy/bin/portmap,/opt/cni/bin/portmap +./deploy/bin/sample,/opt/cni/bin/sample +./deploy/bin/vlan,/opt/cni/bin/vlan +./deploy/kubelet/10-weave.conf,/etc/cni/net.d/10-weave.conf diff --git a/src/ClusterBootstrap/template/master/deploy.list b/src/ClusterBootstrap/template/master/deploy.list deleted file mode 100755 index 1fae61239..000000000 --- a/src/ClusterBootstrap/template/master/deploy.list +++ /dev/null @@ -1,26 +0,0 @@ -./deploy/ssl/apiserver/ca.pem,/etc/kubernetes/ssl/ca.pem -./deploy/ssl/apiserver/ca-key.pem,/etc/kubernetes/ssl/ca-key.pem -./deploy/ssl/apiserver/apiserver.pem,/etc/kubernetes/ssl/apiserver.pem -./deploy/ssl/apiserver/apiserver-key.pem,/etc/kubernetes/ssl/apiserver-key.pem -./deploy/ssl/aggregator/ca.crt,/etc/kubernetes/pki/ca.crt -./deploy/ssl/aggregator/ca.key,/etc/kubernetes/pki/ca.key -./deploy/ssl/aggregator/proxy-client.crt,/etc/kubernetes/pki/proxy-client.crt -./deploy/ssl/aggregator/proxy-client.key,/etc/kubernetes/pki/proxy-client.key -./deploy/master/basicauth,/etc/kubernetes/basicauth -./deploy/master/worker-kubeconfig.yaml,/etc/kubernetes/worker-kubeconfig.yaml -./deploy/master/40-ExecStartPre-symlink.conf,/etc/systemd/system/flanneld.service.d/40-ExecStartPre-symlink.conf -./deploy/master/40-flannel.conf,/etc/systemd/system/docker.service.d/40-flannel.conf -./deploy/master/options.env,/etc/flannel/options.env -./deploy/master/kubelet.service,/etc/systemd/system/kubelet.service -./deploy/master/kube-apiserver.yaml,/etc/kubernetes/manifests/kube-apiserver.yaml -./deploy/master/kube-controller-manager.yaml,/etc/kubernetes/manifests/kube-controller-manager.yaml -./deploy/master/kube-scheduler.yaml,/etc/kubernetes/manifests/kube-scheduler.yaml -./deploy/bin/kubelet,/opt/bin/kubelet -./deploy/bin/kubectl,/opt/bin/kubectl -./deploy/kube-addons,/opt/addons/kube-addons -./deploy/WebUI/appsettings.json,/etc/WebUI/appsettings.json -./deploy/RestfulAPI/config.yaml,/etc/RestfulAPI/config.yaml -./deploy/master/restapi-kubeconfig.yaml,/etc/kubernetes/restapi-kubeconfig.yaml -./deploy/master/dns-kubeconfig.yaml,/etc/kubernetes/dns-kubeconfig.yaml -./deploy/services/clusterroles/clusterrolebindings.yaml,/etc/kubernetes/clusterroles/clusterrolebindings.yaml -./deploy/services/clusterroles/clusterroles.yaml,/etc/kubernetes/clusterroles/clusterroles.yaml \ No newline at end of file diff --git a/src/ClusterBootstrap/template/master/kube-proxy.yaml b/src/ClusterBootstrap/template/master/kube-proxy.yaml deleted file mode 100755 index 90138eb5f..000000000 --- a/src/ClusterBootstrap/template/master/kube-proxy.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: kube-proxy - namespace: kube-system -spec: - hostNetwork: true - containers: - - name: kube-proxy - image: {{cnf["dockers"]["container"]["hyperkube"]["fullname"]}} - command: - - /hyperkube - - kube-proxy - - --master=http://127.0.0.1:8080 - - --proxy-mode=iptables - securityContext: - privileged: true - volumeMounts: - - mountPath: /etc/ssl/certs - name: ssl-certs-host - readOnly: true - volumes: - - hostPath: - path: /usr/share/ca-certificates - name: ssl-certs-host diff --git a/src/ClusterBootstrap/template/master/post-master-deploy.sh b/src/ClusterBootstrap/template/master/post-master-deploy.sh deleted file mode 100755 index 5b43be742..000000000 --- a/src/ClusterBootstrap/template/master/post-master-deploy.sh +++ /dev/null @@ -1,16 +0,0 @@ -sudo cp /etc/kubernetes/ssl/ca.pem /etc/ssl/etcd/ca.pem -sudo cp /etc/kubernetes/ssl/ca-key.pem /etc/ssl/etcd//ca-key.pem -sudo cp /etc/kubernetes/ssl/apiserver.pem /etc/ssl/etcd/apiserver.pem -sudo cp /etc/kubernetes/ssl/apiserver-key.pem /etc/ssl/etcd/apiserver-key.pem -sudo chmod +x /opt/bin/* -sudo systemctl daemon-reload -sudo systemctl stop flanneld -sudo systemctl stop kubelet -sudo systemctl start flanneld -sudo systemctl stop docker -sudo systemctl start docker -sudo docker pull {{cnf["dockers"]["container"]["hyperkube"]["fullname"]}} -sudo systemctl start kubelet -sudo systemctl start rpc-statd -sudo systemctl enable flanneld -sudo systemctl enable kubelet diff --git a/src/ClusterBootstrap/template/master/pre-master-deploy.sh b/src/ClusterBootstrap/template/master/pre-master-deploy.sh deleted file mode 100755 index a90bf7612..000000000 --- a/src/ClusterBootstrap/template/master/pre-master-deploy.sh +++ /dev/null @@ -1,19 +0,0 @@ -sudo mkdir -p /etc/kubernetes -sudo mkdir -p /etc/systemd/system/flanneld.service.d -sudo mkdir -p /etc/systemd/system/docker.service.d -sudo mkdir -p /etc/flannel -sudo mkdir -p /etc/kubernetes/manifests -sudo mkdir -p /etc/kubernetes/ssl/ -sudo mkdir -p /etc/kubernetes/pki/ -sudo mkdir -p /etc/ssl/etcd -sudo mkdir -p /opt/addons -sudo mkdir -p /opt/bin -sudo chown -R $USER /etc/kubernetes -sudo chown -R $USER /etc/flannel -sudo chown -R $USER /opt/bin -sudo chown -R $USER /opt/addons -{% if "kubelogdir" in cnf %} -sudo mkdir -p {{cnf["kubelogdir"]}}/kubescheduler -sudo mkdir -p {{cnf["kubelogdir"]}}/kubeapiserver -sudo chown -R $USER {{cnf["kubelogdir"]}} -{% endif %} diff --git a/src/ClusterBootstrap/template/master/pre-upgrade.sh b/src/ClusterBootstrap/template/master/pre-upgrade.sh index fec6cb3ef..8de62e320 100755 --- a/src/ClusterBootstrap/template/master/pre-upgrade.sh +++ b/src/ClusterBootstrap/template/master/pre-upgrade.sh @@ -4,6 +4,7 @@ sudo mkdir -p /etc/kubernetes sudo mkdir -p /opt/addons sudo rm -r /etc/kubernetes sudo rm -r /opt/addons +sudo rm -r /opt/cni sudo systemctl daemon-reload # pre deployment @@ -13,6 +14,7 @@ sudo mkdir -p /etc/kubernetes/ssl/ sudo mkdir -p /etc/kubernetes/pki/ sudo mkdir -p /opt/addons sudo mkdir -p /opt/bin +sudo mkdir -p /opt/cni/bin sudo chown -R $USER /etc/kubernetes sudo chown -R $USER /etc/flannel sudo chown -R $USER /opt/bin diff --git a/src/ClusterBootstrap/template/master/ubuntu/deploy.list b/src/ClusterBootstrap/template/master/ubuntu/deploy.list index a6823776c..e6919ef38 100755 --- a/src/ClusterBootstrap/template/master/ubuntu/deploy.list +++ b/src/ClusterBootstrap/template/master/ubuntu/deploy.list @@ -19,6 +19,7 @@ ./deploy/WebUI/appsettings.json,/etc/WebUI/appsettings.json ./deploy/RestfulAPI/config.yaml,/etc/RestfulAPI/config.yaml ./deploy/master/restapi-kubeconfig.yaml,/etc/kubernetes/restapi-kubeconfig.yaml +./deploy/services/clusterroles/clusterrolebindings.yaml,/etc/kubernetes/clusterroles/clusterrolebindings.yaml ./deploy/bin/macvlan,/opt/cni/bin/macvlan ./deploy/bin/dhcp,/opt/cni/bin/dhcp ./deploy/bin/loopback,/opt/cni/bin/loopback @@ -26,9 +27,11 @@ ./deploy/bin/ipvlan,/opt/cni/bin/ipvlan ./deploy/bin/bridge,/opt/cni/bin/bridge ./deploy/bin/tuning,/opt/cni/bin/tuning -./deploy/bin/noop,/opt/cni/bin/noop ./deploy/bin/host-local,/opt/cni/bin/host-local -./deploy/bin/cnitool,/opt/cni/bin/cnitool ./deploy/bin/flannel,/opt/cni/bin/flannel -./deploy/services/clusterroles/clusterrolebindings.yaml,/etc/kubernetes/clusterroles/clusterrolebindings.yaml +./deploy/bin/host-device,/opt/cni/bin/host-device +./deploy/bin/portmap,/opt/cni/bin/portmap +./deploy/bin/sample,/opt/cni/bin/sample +./deploy/bin/vlan,/opt/cni/bin/vlan ./deploy/services/clusterroles/clusterroles.yaml,/etc/kubernetes/clusterroles/clusterroles.yaml +./deploy/kubelet/10-weave.conf,/etc/cni/net.d/10-weave.conf diff --git a/src/ClusterBootstrap/template/master/upgrade.list b/src/ClusterBootstrap/template/master/upgrade.list index 45962fbc6..c2507c6ab 100755 --- a/src/ClusterBootstrap/template/master/upgrade.list +++ b/src/ClusterBootstrap/template/master/upgrade.list @@ -18,3 +18,17 @@ ./deploy/master/dns-kubeconfig.yaml,/etc/kubernetes/dns-kubeconfig.yaml ./deploy/services/clusterroles/clusterrolebindings.yaml,/etc/kubernetes/clusterroles/clusterrolebindings.yaml ./deploy/services/clusterroles/clusterroles.yaml,/etc/kubernetes/clusterroles/clusterroles.yaml +./deploy/bin/macvlan,/opt/cni/bin/macvlan +./deploy/bin/dhcp,/opt/cni/bin/dhcp +./deploy/bin/loopback,/opt/cni/bin/loopback +./deploy/bin/ptp,/opt/cni/bin/ptp +./deploy/bin/ipvlan,/opt/cni/bin/ipvlan +./deploy/bin/bridge,/opt/cni/bin/bridge +./deploy/bin/tuning,/opt/cni/bin/tuning +./deploy/bin/host-local,/opt/cni/bin/host-local +./deploy/bin/flannel,/opt/cni/bin/flannel +./deploy/bin/host-device,/opt/cni/bin/host-device +./deploy/bin/portmap,/opt/cni/bin/portmap +./deploy/bin/sample,/opt/cni/bin/sample +./deploy/bin/vlan,/opt/cni/bin/vlan +./deploy/kubelet/10-weave.conf,/etc/cni/net.d/10-weave.conf From a7b5b2525880ac1726e06531971ae231f5e51789 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 16 Aug 2019 09:47:08 +0000 Subject: [PATCH 538/595] add comment --- src/ClusterBootstrap/deploy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 1382bd4a9..1a4559fdc 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -876,7 +876,7 @@ def deploy_master(kubernetes_master): def get_cni_binary(): os.system("mkdir -p ./deploy/bin") - urllib.urlretrieve("https://xudifsd.org/cni-v0.7.1.tgz", "./deploy/bin/cni-v0.7.1.tgz") # TODO, change the source url + urllib.urlretrieve("https://xudifsd.org/cni-v0.7.1.tgz", "./deploy/bin/cni-v0.7.1.tgz") # TODO, change the source url. This tar file contains binary build from https://github.com/containernetworking/cni which used by weave if verbose: print "Extracting CNI binaries" os.system("tar -zxvf ./deploy/bin/cni-v0.7.1.tgz -C ./deploy/bin") From 514077f7142b5fd4769fd8237555906c92e7c4fc Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 19 Aug 2019 08:04:44 +0000 Subject: [PATCH 539/595] add per vc gpu usage dashboard --- .../per-vc-gpu-usage-dashboard.json | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 src/ClusterBootstrap/services/monitor/grafana-config/per-vc-gpu-usage-dashboard.json diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/per-vc-gpu-usage-dashboard.json b/src/ClusterBootstrap/services/monitor/grafana-config/per-vc-gpu-usage-dashboard.json new file mode 100644 index 000000000..f5b6737c8 --- /dev/null +++ b/src/ClusterBootstrap/services/monitor/grafana-config/per-vc-gpu-usage-dashboard.json @@ -0,0 +1,190 @@ +{ + "dashboard": { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [], + "refresh": "30s", + "rows": [ + { + "collapse": false, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 0, + "id": 1, + "legend": { + "avg": false, + "current": true, + "max": false, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(k8s_vc_gpu_total{vc_name=\"$vc_name\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Total", + "refId": "A" + }, + { + "expr": "sum(k8s_vc_gpu_available{vc_name=\"$vc_name\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Available", + "refId": "B" + }, + { + "expr": "sum(k8s_vc_gpu_preemptive_availabe{vc_name=\"$vc_name\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Preemptive Available", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Per VC GPU Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": { + "tags": [], + "text": "platform", + "value": "platform" + }, + "datasource": "PM", + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "vc_name", + "options": [], + "query": "label_values(k8s_vc_gpu_total, vc_name)", + "refresh": 1, + "regex": "", + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Per VC Gpu statistic", + "version": 0 + } +} From a2e6646a4bac51bbe3de02ed0a22f41474464a9f Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Mon, 19 Aug 2019 18:18:03 +0800 Subject: [PATCH 540/595] Add template API in rest server --- src/RestAPI/dlwsrestapi.py | 62 +++++++++++++++++++++++++++++++++++ src/utils/DataHandler.py | 32 +++++++++++++++++- src/utils/MySQLDataHandler.py | 62 +++++++++++++++++++++++++++++++++++ src/utils/SQLDataHandler.py | 53 ++++++++++++++++++++++++++++++ 4 files changed, 208 insertions(+), 1 deletion(-) diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index 08c3f4adc..9b981f4e1 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -1211,6 +1211,68 @@ def endpoint_exist(endpoint_id): ## api.add_resource(Endpoint, '/endpoints') + +class Templates(Resource): + def get(self): + parser = reqparse.RequestParser() + parser.add_argument('userName', location="args") + args = parser.parse_args() + userName = args["userName"] + + dataHandler = DataHandler() + ret = dataHandler.GetTemplates("master") + ret += dataHandler.GetTemplates("user:%s" % (userName,)) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp + + def post(self): + parser = reqparse.RequestParser() + parser.add_argument('userName', location="args") + parser.add_argument('templateName', location="args") + args = parser.parse_args() + userName = args["userName"] + templateName = args["templateName"] + + scope = 'user:%s' % (userName,) + template_json = request.json + + if template_json is None: + return jsonify(result=False, message="Invalid JSON") + + dataHandler = DataHandler() + ret = {} + ret["result"] = dataHandler.UpdateTemplate(templateName, scope, json.dumps(template_json)) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp + + def delete(self): + parser = reqparse.RequestParser() + parser.add_argument('userName', location="args") + parser.add_argument('templateName', location="args") + args = parser.parse_args() + userName = args["userName"] + templateName = args["templateName"] + + scope = 'user:%s' % (userName,) + + dataHandler = DataHandler() + ret = {} + ret["result"] = dataHandler.DeleteTemplate(templateName, scope) + resp = jsonify(ret) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + + return resp + +api.add_resource(Templates, '/templates') + + @app.route("/metrics") def metrics(): return Response(prometheus_client.generate_latest(), mimetype=CONTENT_TYPE_LATEST) diff --git a/src/utils/DataHandler.py b/src/utils/DataHandler.py index 618389061..a8870a909 100755 --- a/src/utils/DataHandler.py +++ b/src/utils/DataHandler.py @@ -64,4 +64,34 @@ def GetAllPendingJobs(vcName): finally: dataHandler.Close() return ret - \ No newline at end of file + + + @staticmethod + def GetTemplates(scope): + dataHandler = DataHandler() + ret = None + try: + ret = dataHandler.GetTemplates(scope) + finally: + dataHandler.Close() + return ret + + @staticmethod + def UpdateTemplate(name, scope, json): + dataHandler = DataHandler() + ret = None + try: + ret = dataHandler.UpdateTemplate(name, scope, json) + finally: + dataHandler.Close() + return ret + + @staticmethod + def DeleteTemplate(name, scope): + dataHandler = DataHandler() + ret = None + try: + ret = dataHandler.DeleteTemplate(name, scope) + finally: + dataHandler.Close() + return ret diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index 2496fc571..1294ff6ff 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -51,6 +51,7 @@ def __init__(self): self.storagetablename = "storage" self.clusterstatustablename = "clusterstatus" self.commandtablename = "commands" + self.templatetablename = "templates" server = config["mysql"]["hostname"] username = config["mysql"]["username"] password = config["mysql"]["password"] @@ -240,6 +241,25 @@ def CreateTable(self): self.conn.commit() cursor.close() + + sql = """ + CREATE TABLE IF NOT EXISTS `%s` + ( + `id` INT NOT NULL AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `scope` VARCHAR(255) NOT NULL COMMENT '"master", "vc:vcname" or "user:username"', + `json` TEXT NOT NULL, + `time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + CONSTRAINT name_scope UNIQUE(`name`, `scope`) + ) + """ % (self.templatetablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + @record def AddStorage(self, vcName, url, storageType, metadata, defaultMountPath): try: @@ -918,6 +938,48 @@ def GetALLJobsCount(self): return ret + @record + def GetTemplates(self, scope): + cursor = self.conn.cursor() + query = "SELECT `name`, `json` FROM `%s` WHERE `scope` = '%s'" % (self.templatetablename, scope) + cursor.execute(query) + ret = [] + for name, json in cursor: + record = {} + record["name"] = name + record["json"] = json + ret.append(record) + self.conn.commit() + cursor.close() + return ret + + @record + def UpdateTemplate(self, name, scope, json): + try: + cursor = self.conn.cursor() + query = "INSERT INTO `" + self.templatetablename + "`(`name`, `scope`, `json`) VALUES(%s, %s, %s) ON DUPLICATE KEY UPDATE `json` = %s" + cursor.execute(query, (name, scope, json, json)) + self.conn.commit() + cursor.close() + return True + except Exception as e: + logger.error('Exception: %s', str(e)) + return False + + @record + def DeleteTemplate(self, name, scope): + try: + cursor = self.conn.cursor() + query = "DELETE FROM `" + self.templatetablename + "` WHERE `name` = %s and `scope` = %s" + cursor.execute(query, (name, scope)) + self.conn.commit() + cursor.close() + return True + except Exception as e: + logger.error('Exception: %s', str(e)) + return False + + def __del__(self): logger.debug("********************** deleted a DataHandler instance *******************") self.Close() diff --git a/src/utils/SQLDataHandler.py b/src/utils/SQLDataHandler.py index f352ebea4..1fc888f73 100755 --- a/src/utils/SQLDataHandler.py +++ b/src/utils/SQLDataHandler.py @@ -160,6 +160,7 @@ def __init__(self): self.storagetablename = "storage-%s" % config["clusterId"] self.clusterstatustablename = "clusterstatus-%s" % config["clusterId"] self.commandtablename = "commands-%s" % config["clusterId"] + self.templatetablename = "templates" self.CreateTable() elapsed = timeit.default_timer() - start_time @@ -348,6 +349,26 @@ def CreateTable(self): self.conn.commit() cursor.close() + + sql = """ + if not exists (select * from sysobjects where name='%s' and xtype='U') + CREATE TABLE [dbo].[%s] + ( + [id] INT IDENTITY (1, 1) NOT NULL, + [name] VARCHAR(255) NOT NULL, + [scope] VARCHAR(255) NOT NULL COMMENT '"master", "vc:vcname" or "user:username"', + [json] NTEXT NOT NULL, + [time] DATETIME DEFAULT (getdate()) NOT NULL, + PRIMARY KEY CLUSTERED ([id] ASC), + CONSTRAINT name_scope UNIQUE (name, scope) + ) + """ % (self.templatetablename, self.templatetablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + @record def AddStorage(self, vcName, url, storageType, metadata, defaultMountPath): try: @@ -935,6 +956,38 @@ def Close(self): ### !!! DataHandler is not threadsafe object, a same object cannot be used in multiple threads self.conn = SQLConnManager.ReturnConnection(self.conn) + @record + def GetTemplates(self, scope): + cursor = self.conn.cursor() + query = "SELECT [name], [json] FROM [%s] WHERE [scope] = '%s'" % (self.templatetablename, scope) + cursor.execute(query) + ret = [] + for name, json in cursor: + record = {} + record["name"] = name + record["json"] = json + ret.append(record) + self.conn.commit() + cursor.close() + return ret + + @record + def UpdateTemplate(self, name, scope, json): + raise NotImplementedError("It's hard to do UPSERT in SQL Server") + + @record + def DeleteTemplate(self, name, scope): + try: + cursor = self.conn.cursor() + query = "DELETE FROM [" + self.templatetablename + "] WHERE [name] = '%s' and [scope] = '%s'" + cursor.execute(query, (name, scope)) + self.conn.commit() + cursor.close() + return True + except Exception as e: + logger.error('Exception: %s', str(e)) + return False + if __name__ == '__main__': TEST_INSERT_JOB = False TEST_QUERY_JOB_LIST = False From 2b44a33fa8a481050491e63960fa0b407909e2f6 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 20 Aug 2019 04:25:04 +0000 Subject: [PATCH 541/595] change cni url --- src/ClusterBootstrap/deploy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 1a4559fdc..2d4e0340d 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -876,7 +876,8 @@ def deploy_master(kubernetes_master): def get_cni_binary(): os.system("mkdir -p ./deploy/bin") - urllib.urlretrieve("https://xudifsd.org/cni-v0.7.1.tgz", "./deploy/bin/cni-v0.7.1.tgz") # TODO, change the source url. This tar file contains binary build from https://github.com/containernetworking/cni which used by weave + # This tar file contains binary build from https://github.com/containernetworking/cni which used by weave + urllib.urlretrieve("https://github.com/microsoft/DLWorkspace/releases/download/v1.2.0/cni-v0.7.1.tgz", "./deploy/bin/cni-v0.7.1.tgz") if verbose: print "Extracting CNI binaries" os.system("tar -zxvf ./deploy/bin/cni-v0.7.1.tgz -C ./deploy/bin") From a9e8876860aac6d2e382414f0f6615207d2e2860 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 20 Aug 2019 08:36:09 +0000 Subject: [PATCH 542/595] use noninteractive mode --- .../scripts/prepare_ubuntu.sh | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh index e9f13f0ac..c69f14d4a 100755 --- a/src/ClusterBootstrap/scripts/prepare_ubuntu.sh +++ b/src/ClusterBootstrap/scripts/prepare_ubuntu.sh @@ -1,9 +1,18 @@ #!/bin/bash + +set -x + +# https://unix.stackexchange.com/questions/146283/how-to-prevent-prompt-that-ask-to-restart-services-when-installing-libpq-dev +export DEBIAN_FRONTEND=noninteractive + +sudo killall apt-get +sudo killall dpkg +sudo dpkg --configure -a + # Install python on CoreOS base image # Docker environment for development of DL workspace sudo apt-get update -y -sudo apt-get upgrade -y -sudo apt-get install -y --no-install-recommends \ +yes | sudo apt-get install -y --no-install-recommends \ apt-utils \ software-properties-common \ build-essential \ @@ -24,7 +33,7 @@ sudo apt-get install -y --no-install-recommends \ nfs-common -sudo apt-get install -y bison curl parted +yes | sudo apt-get install -y bison curl parted # Install docker which docker @@ -40,10 +49,10 @@ sudo add-apt-repository \ $(lsb_release -cs) \ stable" sudo apt-get update -sudo apt-get install -y docker-ce +yes | sudo apt-get install -y docker-ce fi -sudo pip install --upgrade pip +yes | sudo pip install --upgrade pip # pip doesn't install python for root account, causing issues. # sudo pip install setuptools # sudo pip install pyyaml jinja2 argparse @@ -107,9 +116,9 @@ if lspci | grep -qE "[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F].[0-9] (3D|VG sudo apt-get purge -y nvidia* sudo apt-get update - sudo apt-get install -y nvidia-driver-430 + yes | sudo apt-get install -y nvidia-driver-430 - sudo apt install -y nvidia-modprobe + yes | sudo apt install -y nvidia-modprobe sudo rm -r /opt/nvidia-driver || true @@ -123,7 +132,7 @@ if lspci | grep -qE "[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F].[0-9] (3D|VG curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list sudo apt-get update - sudo apt-get install -y nvidia-docker2 + yes | sudo apt-get install -y nvidia-docker2 sudo pkill -SIGHUP dockerd # Test nvidia-smi From e6995e241d1ed8581c8c3c9d8b1086b8804597e0 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 20 Aug 2019 10:07:13 +0000 Subject: [PATCH 543/595] add script to disable kernel auto updates --- .../scripts/disable_kernel_auto_updates.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/ClusterBootstrap/scripts/disable_kernel_auto_updates.sh diff --git a/src/ClusterBootstrap/scripts/disable_kernel_auto_updates.sh b/src/ClusterBootstrap/scripts/disable_kernel_auto_updates.sh new file mode 100644 index 000000000..6a2969b04 --- /dev/null +++ b/src/ClusterBootstrap/scripts/disable_kernel_auto_updates.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +set -x + +# Remove kernel updates + +sed 's/"${distro_id}:${distro_codename}";/\/\/"${distro_id}:${distro_codename}";/g' /etc/apt/apt.conf.d/50unattended-upgrades | sed 's/"${distro_id}:${distro_codename}-security";/\/\/"${distro_id}:${distro_codename}-security";/g' | sed 's/"${distro_id}ESM:${distro_codename}";/\/\/"${distro_id}ESM:${distro_codename}";/g' > /tmp/50unattended-upgrades + +sudo cp /tmp/50unattended-upgrades /etc/apt/apt.conf.d/50unattended-upgrades + +# Disable periodic unattended-update +sed 's/APT::Periodic::Unattended-Upgrade "1";/APT::Periodic::Unattended-Upgrade "0";/g' /etc/apt/apt.conf.d/20auto-upgrades > /tmp/20auto-upgrades + +sudo cp /tmp/20auto-upgrades /etc/apt/apt.conf.d/20auto-upgrades From 0a6a434f6c1717ee1a355225e3e9e201f253ab25 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Wed, 21 Aug 2019 19:09:53 +0800 Subject: [PATCH 544/595] Add vc level templates --- src/RestAPI/dlwsrestapi.py | 41 ++++++++++++++++++++++++++++++++---- src/utils/DataHandler.py | 2 +- src/utils/JobRestAPIUtils.py | 1 + 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index 9b981f4e1..a681d40a9 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -1215,13 +1215,16 @@ def endpoint_exist(endpoint_id): class Templates(Resource): def get(self): parser = reqparse.RequestParser() + parser.add_argument('vcName', location="args") parser.add_argument('userName', location="args") args = parser.parse_args() + vcName = args["vcName"] userName = args["userName"] dataHandler = DataHandler() - ret = dataHandler.GetTemplates("master") - ret += dataHandler.GetTemplates("user:%s" % (userName,)) + ret = dataHandler.GetTemplates("master") or [] + ret += dataHandler.GetTemplates("vc:" + vcName) or [] + ret += dataHandler.GetTemplates("user:" + userName) or [] resp = jsonify(ret) resp.headers["Access-Control-Allow-Origin"] = "*" resp.headers["dataType"] = "json" @@ -1230,13 +1233,28 @@ def get(self): def post(self): parser = reqparse.RequestParser() + parser.add_argument('vcName', location="args") parser.add_argument('userName', location="args") + parser.add_argument('database', location="args") parser.add_argument('templateName', location="args") args = parser.parse_args() + vcName = args["vcName"] userName = args["userName"] + database = args["database"] templateName = args["templateName"] - scope = 'user:%s' % (userName,) + if database == 'master': + if AuthorizationManager.HasAccess(userName, ResourceType.Cluster, "", Permission.Admin): + scope = 'master' + else: + return 'access denied', 403; + elif database == 'vc': + if AuthorizationManager.HasAccess(userName, ResourceType.VC, vcName, Permission.Admin): + scope = 'vc:' + vcName + else: + return 'access denied', 403; + else: + scope = 'user:' + userName template_json = request.json if template_json is None: @@ -1253,13 +1271,28 @@ def post(self): def delete(self): parser = reqparse.RequestParser() + parser.add_argument('vcName', location="args") parser.add_argument('userName', location="args") + parser.add_argument('database', location="args") parser.add_argument('templateName', location="args") args = parser.parse_args() + vcName = args["vcName"] userName = args["userName"] + database = args["database"] templateName = args["templateName"] - scope = 'user:%s' % (userName,) + if database == 'master': + if AuthorizationManager.HasAccess(userName, ResourceType.Cluster, "", Permission.Admin): + scope = 'master' + else: + return 'access denied', 403; + elif database == 'vc': + if AuthorizationManager.HasAccess(userName, ResourceType.VC, vcName, Permission.Admin): + scope = 'vc:' + vcName + else: + return 'access denied', 403; + else: + scope = 'user:' + userName dataHandler = DataHandler() ret = {} diff --git a/src/utils/DataHandler.py b/src/utils/DataHandler.py index a8870a909..b6148ab69 100755 --- a/src/utils/DataHandler.py +++ b/src/utils/DataHandler.py @@ -69,7 +69,7 @@ def GetAllPendingJobs(vcName): @staticmethod def GetTemplates(scope): dataHandler = DataHandler() - ret = None + ret = [] try: ret = dataHandler.GetTemplates(scope) finally: diff --git a/src/utils/JobRestAPIUtils.py b/src/utils/JobRestAPIUtils.py index 850d0bd2c..497e6e4a6 100755 --- a/src/utils/JobRestAPIUtils.py +++ b/src/utils/JobRestAPIUtils.py @@ -488,6 +488,7 @@ def ListVCs(userName): vcList = DataManager.ListVCs() for vc in vcList: if AuthorizationManager.HasAccess(userName, ResourceType.VC, vc["vcName"], Permission.User): + vc['admin'] = AuthorizationManager.HasAccess(userName, ResourceType.VC, vc["vcName"], Permission.Admin) ret.append(vc) # web portal (client) can filter out Default VC return ret From 51023b574ab1a9d4454552c45cacf3951ec8f29f Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 22 Aug 2019 09:53:52 +0800 Subject: [PATCH 545/595] Upgrade kubernetes client, and remove 'dry_run'. (#1) --- src/ClusterManager/job_deployer.py | 13 ++++--------- src/ClusterManager/requirements.txt | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/ClusterManager/job_deployer.py b/src/ClusterManager/job_deployer.py index ac80c22a1..d50c337fa 100644 --- a/src/ClusterManager/job_deployer.py +++ b/src/ClusterManager/job_deployer.py @@ -43,48 +43,43 @@ def __init__(self): self.pretty = "pretty_example" @record - def create_pod(self, body, dry_run=None): + def create_pod(self, body): api_response = self.v1.create_namespaced_pod( namespace=self.namespace, body=body, pretty=self.pretty, - dry_run=dry_run, ) return api_response @record - def delete_pod(self, name, grace_period_seconds=None, dry_run=None): + def delete_pod(self, name, grace_period_seconds=None): body = client.V1DeleteOptions() body.grace_period_seconds = grace_period_seconds - body.dry_run = dry_run api_response = self.v1.delete_namespaced_pod( name=name, namespace=self.namespace, pretty=self.pretty, body=body, grace_period_seconds=grace_period_seconds, - dry_run=dry_run, ) return api_response @record - def create_service(self, body, dry_run=None): + def create_service(self, body): api_response = self.v1.create_namespaced_service( namespace=self.namespace, body=body, pretty=self.pretty, - dry_run=dry_run, ) return api_response @record - def delete_service(self, name, dry_run=None): + def delete_service(self, name): api_response = self.v1.delete_namespaced_service( name=name, namespace=self.namespace, pretty=self.pretty, body=client.V1DeleteOptions(), - dry_run=dry_run, ) return api_response diff --git a/src/ClusterManager/requirements.txt b/src/ClusterManager/requirements.txt index f1363b2db..07245f179 100644 --- a/src/ClusterManager/requirements.txt +++ b/src/ClusterManager/requirements.txt @@ -1,5 +1,5 @@ marshmallow==2.19.5 -kubernetes==9.0.0 +kubernetes==10.0.0 PyYAML>=5.1.1 prometheus-client==0.7.1 twisted==19.2.1 From 54a6e463de46371a4db534f9339a612cac11ce2e Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 21 Aug 2019 10:04:41 +0000 Subject: [PATCH 546/595] add gpu reporter --- src/ClusterBootstrap/params.py | 2 +- .../services/monitor/prometheus.yaml | 16 ++ src/docker-images/gpu-reporter/Dockerfile | 7 + src/docker-images/gpu-reporter/reporter.py | 202 ++++++++++++++++++ 4 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 src/docker-images/gpu-reporter/Dockerfile create mode 100644 src/docker-images/gpu-reporter/reporter.py diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 5fafa11a6..7ced1c03e 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -19,7 +19,7 @@ "influxdb_rpc_port": "8088", "influxdb_data_path": "/var/lib/influxdb", - "prometheus": { "port": 9091 }, + "prometheus": { "port": 9091, "reporter": {"port": 9092} }, "job-exporter": { "port": 9102 }, "node-exporter": { "port": 9100 }, "watchdog": { "port": 9101 }, diff --git a/src/ClusterBootstrap/services/monitor/prometheus.yaml b/src/ClusterBootstrap/services/monitor/prometheus.yaml index bd4c0a203..4920b9520 100644 --- a/src/ClusterBootstrap/services/monitor/prometheus.yaml +++ b/src/ClusterBootstrap/services/monitor/prometheus.yaml @@ -79,6 +79,10 @@ spec: labels: task: monitoring app: prometheus + annotations: + prometheus.io/scrape: "true" + prometheus.io/path: "/metrics" + prometheus.io/port: '{{cnf["prometheus"]["reporter"]["port"]}}' spec: nodeSelector: prometheus: active @@ -115,6 +119,18 @@ spec: mountPath: /etc/prometheus-alert - name: prometheus-data mountPath: /prometheus-data + - name: gpu-reporter + image: {{cnf["worker-dockerregistry"]}}{{cnf["dockerprefix"]}}gpu-reporter:{{cnf["dockertag"]}} + args: + - 'python' + - '/gpu-reporter/reporter.py' + - '--prometheus_url' + - 'http://localhost:{{cnf["prometheus"]["port"]}}' + - '--port' + - '{{cnf["prometheus"]["reporter"]["port"]}}' + ports: + - name: reporter + containerPort: {{cnf["prometheus"]["reporter"]["port"]}} volumes: - name: config-volume configMap: diff --git a/src/docker-images/gpu-reporter/Dockerfile b/src/docker-images/gpu-reporter/Dockerfile new file mode 100644 index 000000000..6aaf95913 --- /dev/null +++ b/src/docker-images/gpu-reporter/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.7 + +RUN pip3 install requests flask prometheus_client + +WORKDIR /gpu-reporter + +COPY * /gpu-reporter/ diff --git a/src/docker-images/gpu-reporter/reporter.py b/src/docker-images/gpu-reporter/reporter.py new file mode 100644 index 000000000..39c22a748 --- /dev/null +++ b/src/docker-images/gpu-reporter/reporter.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 + +import time +import urllib.parse +import threading +import argparse +import logging +import datetime +import timeit +import collections +import faulthandler +import signal + +import requests + +import flask +from flask import Flask +from flask import request +from flask import Response + +import prometheus_client +from prometheus_client import Histogram + +logger = logging.getLogger(__name__) + +prometheus_request_histogram = Histogram("reporter_req_latency_seconds", + "latency for reporter requesting prometheus (seconds)", + buckets=(.05, .075, .1, .25, .5, .75, 1.0, 2.5, 5.0, + 7.5, 10.0, 12.5, 15.0, 17.5, 20.0, float("inf"))) + +reporter_iteration_histogram = Histogram("reporter_iteration_seconds", + "latency for reporter to iterate one pass (seconds)", + buckets=(.05, .075, .1, .25, .5, .75, 1.0, 2.5, 5.0, + 7.5, 10.0, 12.5, 15.0, 17.5, 20.0, float("inf"))) + +class AtomicRef(object): + """ a thread safe way to store and get object, + should not modify data get from this ref + """ + def __init__(self): + self.data = None + self.lock = threading.RLock() + + def set(self, data): + with self.lock: + self.data = data + + def get(self): + with self.lock: + return self.data + +def walk_json_field_safe(obj, *fields): + """ for example a=[{"a": {"b": 2}}] + walk_json_field_safe(a, 0, "a", "b") will get 2 + walk_json_field_safe(a, 0, "not_exist") will get None + """ + try: + for f in fields: + obj = obj[f] + return obj + except: + return None + + +def request_with_error_handling(url, timeout=15): + try: + response = requests.get(url, allow_redirects=True, timeout=timeout) + response.raise_for_status() + return response.json() + except Exception as e: + logger.exception(e) + return None + + +def get_monthly_idleness(prometheus_url): + IDLENESS_THRESHOLD = 0 + STEP_MINUTE = 5 + + step_seconds = STEP_MINUTE * 60 + + now = datetime.datetime.now() + delta = datetime.timedelta(days=31) + one_month_ago = int(datetime.datetime.timestamp(now - delta)) + now = int(datetime.datetime.timestamp(now)) + + args = urllib.parse.urlencode({ + "query": "task_gpu_percent", + "start": str(one_month_ago), + "end": str(now), + "step": str(STEP_MINUTE) + "m", + }) + + url = urllib.parse.urljoin(prometheus_url, + "/prometheus/api/v1/query_range") + "?" + args + + start = timeit.default_timer() + obj = request_with_error_handling(url) + elapsed = timeit.default_timer() - start + prometheus_request_histogram.observe(elapsed) + logger.info("request spent %.2fs", elapsed) + + if walk_json_field_safe(obj, "status") != "success": + logger.warning("requesting %s failed, body is %s", url, obj) + return None + + metrics = walk_json_field_safe(obj, "data", "result") + + default = lambda : {"booked": 0, "idle": 0} + + # the first level is vc, the second level is user + result = collections.defaultdict(lambda : collections.defaultdict(default)) + + for metric in metrics: + username = walk_json_field_safe(metric, "metric", "username") + vc_name = walk_json_field_safe(metric, "metric", "vc_name") + if username is None or vc_name is None: + logger.warning("username or vc_name is missing for metric %s", + walk_json_field_safe(metric, "metric")) + continue + + values = walk_json_field_safe(metric, "values") + if values is None or len(values) == 0: + continue + + booked_seconds = values[-1][0] - values[0][0] + step_seconds + idleness_seconds = 0 + + for time, utils in values: + utils = float(utils) + if utils <= IDLENESS_THRESHOLD: + idleness_seconds += step_seconds + + result[vc_name][username]["booked"] += booked_seconds + result[vc_name][username]["idle"] += idleness_seconds + + return result + + +def refresher(prometheus_url, atomic_ref): + while True: + with reporter_iteration_histogram.time(): + try: + result = get_monthly_idleness(prometheus_url) + if result is not None: + atomic_ref.set(result) + except Exception: + logger.exception("caught exception while refreshing") + time.sleep(5 * 60) + + +def serve(prometheus_url, port): + app = Flask(__name__) + + atomic_ref = AtomicRef() + + t = threading.Thread( + target=refresher, + name="refresher", + args=(prometheus_url, atomic_ref), + daemon=True) + t.start() + + @app.route("/gpu_idle", methods=["GET"]) + def get_gpu_idleness(): + vc_name = request.args.get("vc") + if vc_name is None: + return Response("should provide vc parameter", 400) + + result = atomic_ref.get() + if result is None or result.get(vc_name) is None: + return flask.jsonify({}) + + return flask.jsonify(result[vc_name]) + + @app.route("/metrics") + def metrics(): + return Response(prometheus_client.generate_latest(), + mimetype="text/plain; version=0.0.4; charset=utf-8") + + app.run(host="0.0.0.0", port=port, debug=False, use_reloader=False) + +def register_stack_trace_dump(): + faulthandler.register(signal.SIGTRAP, all_threads=True, chain=False) + +def main(args): + register_stack_trace_dump() + serve(args.prometheus_url, args.port) + +if __name__ == "__main__": + logging.basicConfig(format="%(asctime)s - %(levelname)s - %(filename)s:%(lineno)s - %(message)s", + level=logging.INFO) + + parser = argparse.ArgumentParser() + parser.add_argument("--prometheus_url", "-p", required=True, + help="Prometheus url, eg: http://127.0.0.1:9091") + + parser.add_argument("--port", type=int, default=9092, + help="port to listen") + + args = parser.parse_args() + + main(args) From 1d117cc886344abef46af329ea87f24b29f93d92 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Thu, 22 Aug 2019 03:25:09 +0000 Subject: [PATCH 547/595] add gpu fragmentation dashboard --- .../gpu-fragmentation-dashboard.json | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 src/ClusterBootstrap/services/monitor/grafana-config/gpu-fragmentation-dashboard.json diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/gpu-fragmentation-dashboard.json b/src/ClusterBootstrap/services/monitor/grafana-config/gpu-fragmentation-dashboard.json new file mode 100644 index 000000000..7f6264bf0 --- /dev/null +++ b/src/ClusterBootstrap/services/monitor/grafana-config/gpu-fragmentation-dashboard.json @@ -0,0 +1,149 @@ +{"dashboard": { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [], + "refresh": "30s", + "rows": [ + { + "collapse": false, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 0, + "id": 1, + "legend": { + "avg": false, + "current": true, + "max": false, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "count_values(\"gpu_available\", k8s_node_gpu_available)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{'{{'}} gpu_available {{'}}'}} available gpus", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Node count with certain number of available gpu", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": "Node count", + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "GPU fragmentation", + "version": 0 +}} From 21f534a863acbee9a0bb11a2e39fac2d75cd276d Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 13 Aug 2019 03:25:36 +0000 Subject: [PATCH 548/595] cleanup --- src/ClusterManager/ResourceInfo.py | 8 +++--- src/ClusterManager/job_manager.py | 44 ++++++++++++++---------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/ClusterManager/ResourceInfo.py b/src/ClusterManager/ResourceInfo.py index fa5a833d7..08e9cdd9c 100644 --- a/src/ClusterManager/ResourceInfo.py +++ b/src/ClusterManager/ResourceInfo.py @@ -1,11 +1,11 @@ import math class ResourceInfo: - def __init__(self, res = {}, tag = ""): + def __init__(self, res={}): self.CategoryToCountMap = {} - self.BlockedCategories = set() #not included in serialized form + self.BlockedCategories = set() # not included in serialized form for key in res: - self.CategoryToCountMap[tag + ("_" if tag else "") + key] = int(res[key]) + self.CategoryToCountMap[key] = int(res[key]) def ToSerializable(self): return self.CategoryToCountMap @@ -38,7 +38,7 @@ def Add(self, otherResourceInfo): self.CategoryToCountMap[key] += otherResourceInfo.CategoryToCountMap[key] return self - def CanSatisfy(self, otherResourceInfo): + def CanSatisfy(self, otherResourceInfo): for key in otherResourceInfo.CategoryToCountMap: if (otherResourceInfo.CategoryToCountMap[key] > 0) and ((key in self.BlockedCategories) or (key not in self.CategoryToCountMap) \ or (self.CategoryToCountMap[key] < otherResourceInfo.CategoryToCountMap[key])): diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index f1f64d83b..7761be6a8 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -320,28 +320,27 @@ def create_log(logdir = '/var/log/dlworkspace'): logging.config.dictConfig(logging_config) -def JobInfoSorter(elem): - return elem["sortKey"] - - def TakeJobActions(jobs): dataHandler = DataHandler() vcList = dataHandler.ListVCs() - clusterStatus, dummy = dataHandler.GetClusterStatus() + clusterStatus, _ = dataHandler.GetClusterStatus() dataHandler.Close() - globalTotalRes = ResourceInfo(clusterStatus["gpu_capacity"]) - globalReservedRes = ResourceInfo(clusterStatus["gpu_reserved"]) + cluster_gpu_capacity = clusterStatus["gpu_capacity"] + cluster_gpu_reserved = clusterStatus["gpu_reserved"] + globalTotalRes = ResourceInfo(cluster_gpu_capacity) + globalReservedRes = ResourceInfo(cluster_gpu_reserved) + vc_resources = {} localResInfo = ResourceInfo() globalResInfo = ResourceInfo.Difference(globalTotalRes, globalReservedRes) for vc in vcList: - vcTotalRes = ResourceInfo(json.loads(vc["quota"]), vc["vcName"]) - clusterTotalRes = ResourceInfo(clusterStatus["gpu_capacity"], vc["vcName"]) - clusterReservedRes = ResourceInfo(clusterStatus["gpu_reserved"], vc["vcName"]) + vcTotalRes = ResourceInfo(json.loads(vc["quota"])) + clusterTotalRes = ResourceInfo(clusterStatus["gpu_capacity"]) + clusterReservedRes = ResourceInfo(clusterStatus["gpu_reserved"]) vcReservedRes = clusterReservedRes.GetFraction(vcTotalRes, clusterTotalRes) - localResInfo.Add(ResourceInfo.Difference(vcTotalRes, vcReservedRes)) + vc_resources[vc["vcName"]] = ResourceInfo.Difference(vcTotalRes, vcReservedRes) jobsInfo = [] for job in jobs: @@ -352,7 +351,6 @@ def TakeJobActions(jobs): jobGpuType = "any" if "gpuType" in singleJobInfo["jobParams"]: jobGpuType = singleJobInfo["jobParams"]["gpuType"] - singleJobInfo["localResInfo"] = ResourceInfo({jobGpuType : GetJobTotalGpu(singleJobInfo["jobParams"])}, job["vcName"]) singleJobInfo["globalResInfo"] = ResourceInfo({jobGpuType : GetJobTotalGpu(singleJobInfo["jobParams"])}) singleJobInfo["sortKey"] = str(job["jobTime"]) if singleJobInfo["jobParams"]["preemptionAllowed"]: @@ -362,26 +360,26 @@ def TakeJobActions(jobs): singleJobInfo["allowed"] = False jobsInfo.append(singleJobInfo) - jobsInfo.sort(key=JobInfoSorter) + jobsInfo.sort(key=lambda x: x["sortKey"]) - logging.info("TakeJobActions : local resources : %s" % (localResInfo.CategoryToCountMap)) + logging.info("TakeJobActions : local resources : %s" % (vc_resources)) logging.info("TakeJobActions : global resources : %s" % (globalResInfo.CategoryToCountMap)) for sji in jobsInfo: - logging.info("TakeJobActions : job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["localResInfo"].CategoryToCountMap, sji["sortKey"])) + logging.info("TakeJobActions : job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["globalResInfo"].CategoryToCountMap, sji["sortKey"])) + vc_name = sji["job"]["vcName"] + vc_resource = vc_resources[vc_name] + if sji["jobParams"]["preemptionAllowed"]: - localResInfo.UnblockResourceCategory(sji["localResInfo"]) + vc_resource.UnblockResourceCategory(sji["globalResInfo"]) - if (localResInfo.CanSatisfy(sji["localResInfo"])): - localResInfo.Subtract(sji["localResInfo"]) + if (vc_resource.CanSatisfy(sji["globalResInfo"])): + vc_resource.Subtract(sji["globalResInfo"]) globalResInfo.Subtract(sji["globalResInfo"]) sji["allowed"] = True - logging.info("TakeJobActions : local assignment : %s : %s" % (sji["jobParams"]["jobName"], sji["localResInfo"].CategoryToCountMap)) + logging.info("TakeJobActions : local assignment : %s : %s" % (sji["jobParams"]["jobName"], sji["globalResInfo"].CategoryToCountMap)) elif not sji["jobParams"]["preemptionAllowed"]: - localResInfo.BlockResourceCategory(sji["localResInfo"]) #FIFO scheduling - - #logging.info("TakeJobActions : local resources : %s" % (localResInfo.CategoryToCountMap)) - #logging.info("TakeJobActions : global resources : %s" % (globalResInfo.CategoryToCountMap)) + globalResInfo.BlockResourceCategory(sji["globalResInfo"]) #FIFO scheduling for sji in jobsInfo: if (sji["jobParams"]["preemptionAllowed"] and sji["allowed"] == False): From e10214d080195ed6242ad23f58d01628bc002dd2 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 14 Aug 2019 05:57:14 +0000 Subject: [PATCH 549/595] cleanup --- src/ClusterManager/ResourceInfo.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/ClusterManager/ResourceInfo.py b/src/ClusterManager/ResourceInfo.py index 08e9cdd9c..4641c6194 100644 --- a/src/ClusterManager/ResourceInfo.py +++ b/src/ClusterManager/ResourceInfo.py @@ -10,12 +10,6 @@ def __init__(self, res={}): def ToSerializable(self): return self.CategoryToCountMap - def TotalCount(self): - count = 0 - for key in self.CategoryToCountMap: - count += self.CategoryToCountMap[key] - return count - @staticmethod def Difference(resInfo1, resInfo2): diff = ResourceInfo() From a78892d78136de0acaf0d78457e9c6244dd62bc2 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 14 Aug 2019 07:11:21 +0000 Subject: [PATCH 550/595] refactoring --- src/ClusterManager/job_manager.py | 45 ++++++++++++++++++------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 7761be6a8..f093a3e44 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -12,7 +12,6 @@ sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),"../storage")) sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),"../utils")) -from JobRestAPIUtils import GetJobTotalGpu from jobs_tensorboard import GenTensorboardMeta import k8sUtils import joblog_manager @@ -170,6 +169,13 @@ def KillJob(job_id, desiredState="killed"): return False +def GetJobTotalGpu(jobParams): + numWorkers = 1 + if "numpsworker" in jobParams: + numWorkers = int(jobParams["numpsworker"]) + return int(jobParams["resourcegpu"]) * numWorkers + + def ApproveJob(job): try: job_id = job["jobId"] @@ -344,16 +350,19 @@ def TakeJobActions(jobs): jobsInfo = [] for job in jobs: - if job["jobStatus"] == "queued" or job["jobStatus"] == "scheduling" or job["jobStatus"] == "running": + if job["jobStatus"] in ["queued", "scheduling", "running"]: singleJobInfo = {} singleJobInfo["job"] = job - singleJobInfo["jobParams"] = json.loads(base64.b64decode(job["jobParams"])) + job_params = json.loads(base64.b64decode(job["jobParams"])) + singleJobInfo["preemptionAllowed"] = job_params["preemptionAllowed"] + singleJobInfo["jobName"] = job_params["jobName"] + singleJobInfo["jobId"] = job_params["jobId"] jobGpuType = "any" - if "gpuType" in singleJobInfo["jobParams"]: - jobGpuType = singleJobInfo["jobParams"]["gpuType"] - singleJobInfo["globalResInfo"] = ResourceInfo({jobGpuType : GetJobTotalGpu(singleJobInfo["jobParams"])}) + if "gpuType" in job_params: + jobGpuType = job_params["gpuType"] + singleJobInfo["globalResInfo"] = ResourceInfo({jobGpuType : GetJobTotalGpu(job_params)}) singleJobInfo["sortKey"] = str(job["jobTime"]) - if singleJobInfo["jobParams"]["preemptionAllowed"]: + if singleJobInfo["preemptionAllowed"]: singleJobInfo["sortKey"] = "1_" + singleJobInfo["sortKey"] else: singleJobInfo["sortKey"] = "0_" + singleJobInfo["sortKey"] @@ -366,40 +375,40 @@ def TakeJobActions(jobs): logging.info("TakeJobActions : global resources : %s" % (globalResInfo.CategoryToCountMap)) for sji in jobsInfo: - logging.info("TakeJobActions : job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["globalResInfo"].CategoryToCountMap, sji["sortKey"])) + logging.info("TakeJobActions : job : %s : %s : %s" % (sji["jobName"], sji["globalResInfo"].CategoryToCountMap, sji["sortKey"])) vc_name = sji["job"]["vcName"] vc_resource = vc_resources[vc_name] - if sji["jobParams"]["preemptionAllowed"]: + if sji["preemptionAllowed"]: vc_resource.UnblockResourceCategory(sji["globalResInfo"]) if (vc_resource.CanSatisfy(sji["globalResInfo"])): vc_resource.Subtract(sji["globalResInfo"]) globalResInfo.Subtract(sji["globalResInfo"]) sji["allowed"] = True - logging.info("TakeJobActions : local assignment : %s : %s" % (sji["jobParams"]["jobName"], sji["globalResInfo"].CategoryToCountMap)) - elif not sji["jobParams"]["preemptionAllowed"]: + logging.info("TakeJobActions : local assignment : %s : %s" % (sji["jobName"], sji["globalResInfo"].CategoryToCountMap)) + elif not sji["preemptionAllowed"]: globalResInfo.BlockResourceCategory(sji["globalResInfo"]) #FIFO scheduling for sji in jobsInfo: - if (sji["jobParams"]["preemptionAllowed"] and sji["allowed"] == False): + if sji["preemptionAllowed"] and (sji["allowed"] is False): if globalResInfo.CanSatisfy(sji["globalResInfo"]): - logging.info("TakeJobActions : job : %s : %s" % (sji["jobParams"]["jobName"], sji["globalResInfo"].CategoryToCountMap)) + logging.info("TakeJobActions : job : %s : %s" % (sji["jobName"], sji["globalResInfo"].CategoryToCountMap)) # Strict FIFO policy not required for global (bonus) tokens since these jobs are anyway pre-emptible. globalResInfo.Subtract(sji["globalResInfo"]) sji["allowed"] = True - logging.info("TakeJobActions : global assignment : %s : %s" % (sji["jobParams"]["jobName"], sji["globalResInfo"].CategoryToCountMap)) + logging.info("TakeJobActions : global assignment : %s : %s" % (sji["jobName"], sji["globalResInfo"].CategoryToCountMap)) logging.info("TakeJobActions : global resources : %s" % (globalResInfo.CategoryToCountMap)) for sji in jobsInfo: try: - if sji["job"]["jobStatus"] == "queued" and sji["allowed"] == True: + if sji["job"]["jobStatus"] == "queued" and (sji["allowed"] is True): SubmitJob(sji["job"]) - logging.info("TakeJobActions : submitting job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["jobParams"]["jobId"], sji["sortKey"])) - elif sji["jobParams"]["preemptionAllowed"] and (sji["job"]["jobStatus"] == "scheduling" or sji["job"]["jobStatus"] == "running") and sji["allowed"] == False: + logging.info("TakeJobActions : submitting job : %s : %s : %s" % (sji["jobName"], sji["jobId"], sji["sortKey"])) + elif sji["preemptionAllowed"] and (sji["job"]["jobStatus"] == "scheduling" or sji["job"]["jobStatus"] == "running") and (sji["allowed"] is False): KillJob(sji["job"]["jobId"], "queued") - logging.info("TakeJobActions : pre-empting job : %s : %s : %s" % (sji["jobParams"]["jobName"], sji["jobParams"]["jobId"], sji["sortKey"])) + logging.info("TakeJobActions : pre-empting job : %s : %s : %s" % (sji["jobName"], sji["jobId"], sji["sortKey"])) except Exception as e: logging.error("Process job failed {}".format(sji["job"]), exc_info=True) From c8d2031482886ee9cc1aa869c95db6211a2b14b2 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 14 Aug 2019 07:35:50 +0000 Subject: [PATCH 551/595] code cleanup --- src/ClusterManager/ResourceInfo.py | 17 +---------------- src/ClusterManager/job_manager.py | 5 ----- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/ClusterManager/ResourceInfo.py b/src/ClusterManager/ResourceInfo.py index 4641c6194..2b1c145ad 100644 --- a/src/ClusterManager/ResourceInfo.py +++ b/src/ClusterManager/ResourceInfo.py @@ -3,7 +3,6 @@ class ResourceInfo: def __init__(self, res={}): self.CategoryToCountMap = {} - self.BlockedCategories = set() # not included in serialized form for key in res: self.CategoryToCountMap[key] = int(res[key]) @@ -34,8 +33,7 @@ def Add(self, otherResourceInfo): def CanSatisfy(self, otherResourceInfo): for key in otherResourceInfo.CategoryToCountMap: - if (otherResourceInfo.CategoryToCountMap[key] > 0) and ((key in self.BlockedCategories) or (key not in self.CategoryToCountMap) \ - or (self.CategoryToCountMap[key] < otherResourceInfo.CategoryToCountMap[key])): + if (otherResourceInfo.CategoryToCountMap[key] > 0) and ((key not in self.CategoryToCountMap) or (self.CategoryToCountMap[key] < otherResourceInfo.CategoryToCountMap[key])): return False return True @@ -44,16 +42,3 @@ def Subtract(self, otherResourceInfo): if otherResourceInfo.CategoryToCountMap[key] > 0: self.CategoryToCountMap[key] -= otherResourceInfo.CategoryToCountMap[key] return self - - def BlockResourceCategory(self, resourceInfo): - for key in resourceInfo.CategoryToCountMap: - self.BlockedCategories.add(key) - return self - - def UnblockResourceCategory(self, resourceInfo): - for key in resourceInfo.CategoryToCountMap: - if key in self.BlockedCategories: - self.BlockedCategories.remove(key) - return self - - diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index f093a3e44..bbfb2700c 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -379,16 +379,11 @@ def TakeJobActions(jobs): vc_name = sji["job"]["vcName"] vc_resource = vc_resources[vc_name] - if sji["preemptionAllowed"]: - vc_resource.UnblockResourceCategory(sji["globalResInfo"]) - if (vc_resource.CanSatisfy(sji["globalResInfo"])): vc_resource.Subtract(sji["globalResInfo"]) globalResInfo.Subtract(sji["globalResInfo"]) sji["allowed"] = True logging.info("TakeJobActions : local assignment : %s : %s" % (sji["jobName"], sji["globalResInfo"].CategoryToCountMap)) - elif not sji["preemptionAllowed"]: - globalResInfo.BlockResourceCategory(sji["globalResInfo"]) #FIFO scheduling for sji in jobsInfo: if sji["preemptionAllowed"] and (sji["allowed"] is False): From 6cf1f859dd20470a31e8b13c65b6560cab44997e Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Wed, 14 Aug 2019 07:52:43 +0000 Subject: [PATCH 552/595] cleanup --- src/ClusterManager/job_manager.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index bbfb2700c..3df009eda 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -355,7 +355,6 @@ def TakeJobActions(jobs): singleJobInfo["job"] = job job_params = json.loads(base64.b64decode(job["jobParams"])) singleJobInfo["preemptionAllowed"] = job_params["preemptionAllowed"] - singleJobInfo["jobName"] = job_params["jobName"] singleJobInfo["jobId"] = job_params["jobId"] jobGpuType = "any" if "gpuType" in job_params: @@ -375,7 +374,7 @@ def TakeJobActions(jobs): logging.info("TakeJobActions : global resources : %s" % (globalResInfo.CategoryToCountMap)) for sji in jobsInfo: - logging.info("TakeJobActions : job : %s : %s : %s" % (sji["jobName"], sji["globalResInfo"].CategoryToCountMap, sji["sortKey"])) + logging.info("TakeJobActions : job : %s : %s : %s" % (sji["jobId"], sji["globalResInfo"].CategoryToCountMap, sji["sortKey"])) vc_name = sji["job"]["vcName"] vc_resource = vc_resources[vc_name] @@ -383,16 +382,16 @@ def TakeJobActions(jobs): vc_resource.Subtract(sji["globalResInfo"]) globalResInfo.Subtract(sji["globalResInfo"]) sji["allowed"] = True - logging.info("TakeJobActions : local assignment : %s : %s" % (sji["jobName"], sji["globalResInfo"].CategoryToCountMap)) + logging.info("TakeJobActions : local assignment : %s : %s" % (sji["jobId"], sji["globalResInfo"].CategoryToCountMap)) for sji in jobsInfo: if sji["preemptionAllowed"] and (sji["allowed"] is False): if globalResInfo.CanSatisfy(sji["globalResInfo"]): - logging.info("TakeJobActions : job : %s : %s" % (sji["jobName"], sji["globalResInfo"].CategoryToCountMap)) + logging.info("TakeJobActions : job : %s : %s" % (sji["jobId"], sji["globalResInfo"].CategoryToCountMap)) # Strict FIFO policy not required for global (bonus) tokens since these jobs are anyway pre-emptible. globalResInfo.Subtract(sji["globalResInfo"]) sji["allowed"] = True - logging.info("TakeJobActions : global assignment : %s : %s" % (sji["jobName"], sji["globalResInfo"].CategoryToCountMap)) + logging.info("TakeJobActions : global assignment : %s : %s" % (sji["jobId"], sji["globalResInfo"].CategoryToCountMap)) logging.info("TakeJobActions : global resources : %s" % (globalResInfo.CategoryToCountMap)) @@ -400,10 +399,10 @@ def TakeJobActions(jobs): try: if sji["job"]["jobStatus"] == "queued" and (sji["allowed"] is True): SubmitJob(sji["job"]) - logging.info("TakeJobActions : submitting job : %s : %s : %s" % (sji["jobName"], sji["jobId"], sji["sortKey"])) + logging.info("TakeJobActions : submitting job : %s : %s" % (sji["jobId"], sji["sortKey"])) elif sji["preemptionAllowed"] and (sji["job"]["jobStatus"] == "scheduling" or sji["job"]["jobStatus"] == "running") and (sji["allowed"] is False): KillJob(sji["job"]["jobId"], "queued") - logging.info("TakeJobActions : pre-empting job : %s : %s : %s" % (sji["jobName"], sji["jobId"], sji["sortKey"])) + logging.info("TakeJobActions : pre-empting job : %s : %s" % (sji["jobId"], sji["sortKey"])) except Exception as e: logging.error("Process job failed {}".format(sji["job"]), exc_info=True) From 4cedbb52a715e2513a36839b3c4839a41d571042 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 15 Aug 2019 07:46:48 +0000 Subject: [PATCH 553/595] Support manually adjust job priority in queuing. Insert a priority record in the table 'job_priorities', priority value ranges from 0-65535, 0 for the highest priority. --- src/ClusterManager/job_manager.py | 26 ++++++++++++++++++++++++-- src/utils/MySQLDataHandler.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 3df009eda..2c93fd571 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -326,6 +326,24 @@ def create_log(logdir = '/var/log/dlworkspace'): logging.config.dictConfig(logging_config) +def get_priority_dict(): + try: + dataHandler = DataHandler() + priority_dict = dataHandler.get_job_priority() + return priority_dict + except Exception as e: + logging.warning("Fetch job priority dict failed!", exc_info=True) + return {} + finally: + dataHandler.Close() + + +def get_job_priority(priority_dict, job_id): + if job_id in priority_dict.keys(): + return priority_dict[job_id] + return 65535 + + def TakeJobActions(jobs): dataHandler = DataHandler() vcList = dataHandler.ListVCs() @@ -341,6 +359,9 @@ def TakeJobActions(jobs): localResInfo = ResourceInfo() globalResInfo = ResourceInfo.Difference(globalTotalRes, globalReservedRes) + priority_dict = get_priority_dict() + logging.info("Job priority dict: {}".format(priority_dict)) + for vc in vcList: vcTotalRes = ResourceInfo(json.loads(vc["quota"])) clusterTotalRes = ResourceInfo(clusterStatus["gpu_capacity"]) @@ -361,10 +382,11 @@ def TakeJobActions(jobs): jobGpuType = job_params["gpuType"] singleJobInfo["globalResInfo"] = ResourceInfo({jobGpuType : GetJobTotalGpu(job_params)}) singleJobInfo["sortKey"] = str(job["jobTime"]) + priority = get_job_priority(priority_dict, singleJobInfo["jobId"]) if singleJobInfo["preemptionAllowed"]: - singleJobInfo["sortKey"] = "1_" + singleJobInfo["sortKey"] + singleJobInfo["sortKey"] = "1_{:06d}_{}".format(priority, singleJobInfo["sortKey"]) else: - singleJobInfo["sortKey"] = "0_" + singleJobInfo["sortKey"] + singleJobInfo["sortKey"] = "0_{:06d}_{}".format(priority, singleJobInfo["sortKey"]) singleJobInfo["allowed"] = False jobsInfo.append(singleJobInfo) diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index 1294ff6ff..9d318fd5e 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -52,6 +52,7 @@ def __init__(self): self.clusterstatustablename = "clusterstatus" self.commandtablename = "commands" self.templatetablename = "templates" + self.jobprioritytablename = "job_priorities" server = config["mysql"]["hostname"] username = config["mysql"]["username"] password = config["mysql"]["password"] @@ -260,6 +261,24 @@ def CreateTable(self): self.conn.commit() cursor.close() + + sql = """ + CREATE TABLE IF NOT EXISTS `%s` + ( + `id` INT NOT NULL AUTO_INCREMENT, + `jobId` varchar(50) NOT NULL, + `priority` INT NOT NULL, + `time` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT identityName_jobId UNIQUE(`jobId`) + ) + """ % (self.jobprioritytablename) + + cursor = self.conn.cursor() + cursor.execute(sql) + self.conn.commit() + cursor.close() + @record def AddStorage(self, vcName, url, storageType, metadata, defaultMountPath): try: @@ -979,6 +998,17 @@ def DeleteTemplate(self, name, scope): logger.error('Exception: %s', str(e)) return False + def get_job_priority(self): + cursor = self.conn.cursor() + query = "select jobId, priority from {} where jobId in (select jobId from {} where jobStatus in (\"queued\", \"scheduling\", \"running\"))".format(self.jobprioritytablename, self.jobtablename) + cursor.execute(query) + priority_dict = {} + for job_id, priority in cursor: + priority_dict[job_id] = priority + self.conn.commit() + cursor.close() + + return priority_dict def __del__(self): logger.debug("********************** deleted a DataHandler instance *******************") From 384163b39c2c13dc8762b0161a3d0560220b94b5 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Mon, 19 Aug 2019 06:34:00 +0000 Subject: [PATCH 554/595] add api /jobs/priorites --- src/RestAPI/dlwsrestapi.py | 25 +++++++++++++++++++++++++ src/utils/JobRestAPIUtils.py | 21 ++++++++++++++++++--- src/utils/MySQLDataHandler.py | 10 ++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index a681d40a9..afa426975 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -1306,6 +1306,31 @@ def delete(self): api.add_resource(Templates, '/templates') +class JobPriority(Resource): + def get(self): + job_priorites = JobRestAPIUtils.get_job_priorities() + resp = jsonify(job_priorites) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + return resp + + def post(self): + payload = request.get_json(silent=True) + success = JobRestAPIUtils.update_job_priorites(payload) + http_status = 200 if success else 400 + + job_priorites = JobRestAPIUtils.get_job_priorities() + resp = jsonify(job_priorites) + resp.headers["Access-Control-Allow-Origin"] = "*" + resp.headers["dataType"] = "json" + resp.status_code = http_status + return resp + +## +## Actually setup the Api resource routing here +## +api.add_resource(JobPriority, '/jobs/priorites') + @app.route("/metrics") def metrics(): return Response(prometheus_client.generate_latest(), mimetype=CONTENT_TYPE_LATEST) diff --git a/src/utils/JobRestAPIUtils.py b/src/utils/JobRestAPIUtils.py index 497e6e4a6..6d8b885aa 100755 --- a/src/utils/JobRestAPIUtils.py +++ b/src/utils/JobRestAPIUtils.py @@ -73,7 +73,7 @@ def SubmitJob(jobParamsJsonStr): jobParams["preemptionAllowed"] = False else: jobParams["preemptionAllowed"] = ToBool(jobParams["preemptionAllowed"]) - + if "jobId" not in jobParams or jobParams["jobId"] == "": #jobParams["jobId"] = jobParams["jobName"] + "-" + str(uuid.uuid4()) #jobParams["jobId"] = jobParams["jobName"] + "-" + str(time.time()) @@ -495,7 +495,7 @@ def ListVCs(userName): def GetVC(userName, vcName): - ret = None + ret = None clusterStatus, dummy = DataManager.GetClusterStatus() clusterTotalRes = ResourceInfo(clusterStatus["gpu_capacity"]) @@ -526,7 +526,7 @@ def GetVC(userName, vcName): vc["gpu_used"] = vcConsumedRes.ToSerializable() vc["gpu_unschedulable"] = vcReservedRes.ToSerializable() vc["gpu_avaliable"] = vcAvailableRes.ToSerializable() - vc["AvaliableJobNum"] = len(jobs) + vc["AvaliableJobNum"] = len(jobs) vc["node_status"] = clusterStatus["node_status"] vc["user_status"] = [] for user_name, user_gpu in user_status.iteritems(): @@ -580,6 +580,21 @@ def update_job(job_id, field, value): dataHandler.UpdateJobTextField(job_id, field, value) dataHandler.Close() + +def get_job_priorities(): + dataHandler = DataHandler() + job_priorites = dataHandler.get_job_priority() + dataHandler.Close() + return job_priorites + + +def update_job_priorites(job_priorites): + dataHandler = DataHandler() + success = dataHandler.update_job_priority(job_priorites) + dataHandler.Close() + return success + + if __name__ == '__main__': TEST_SUB_REG_JOB = False TEST_JOB_STATUS = True diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index 9d318fd5e..b967636c1 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -1010,6 +1010,16 @@ def get_job_priority(self): return priority_dict + @record + def update_job_priority(self, job_priorites): + cursor = self.conn.cursor() + for job_id, priority in job_priorites.items(): + query = "INSERT INTO {0}(jobId, priority, time) VALUES('{1}', {2}, SYSDATE()) ON DUPLICATE KEY UPDATE jobId='{1}', priority='{2}' ".format(self.jobprioritytablename, job_id, priority) + cursor.execute(query) + self.conn.commit() + cursor.close() + return True + def __del__(self): logger.debug("********************** deleted a DataHandler instance *******************") self.Close() From 6e60f943b3d1d2823ce0d788cc09dff61f13a692 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 27 Aug 2019 04:10:51 +0000 Subject: [PATCH 555/595] ib topology awareness --- src/Jobs_Templete/pod.yaml.template | 63 ++++++----------------------- 1 file changed, 13 insertions(+), 50 deletions(-) diff --git a/src/Jobs_Templete/pod.yaml.template b/src/Jobs_Templete/pod.yaml.template index 4bb58c510..af3cc2652 100755 --- a/src/Jobs_Templete/pod.yaml.template +++ b/src/Jobs_Templete/pod.yaml.template @@ -63,9 +63,9 @@ spec: values: - "worker" topologyKey: "kubernetes.io/hostname" - {% endif %} + {% else %} preferredDuringSchedulingIgnoredDuringExecution: - - weight: 50 + - weight: 50 # For regular jobs, distributed jobs will consume all gpus in node podAffinityTerm: labelSelector: matchExpressions: @@ -74,64 +74,27 @@ spec: values: - "job" topologyKey: "kubernetes.io/hostname" - {% if job["gpuLimit"]|int == 1 %} - - weight: 30 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: gpu-request - operator: In - values: - - "3" - topologyKey: "kubernetes.io/hostname" - - weight: 29 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: gpu-request - operator: In - values: - - "1" - topologyKey: "kubernetes.io/hostname" - - weight: 28 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: gpu-request - operator: In - values: - - "2" - topologyKey: "kubernetes.io/hostname" - {% elif job["gpuLimit"]|int == 2 %} - - weight: 30 - podAffinityTerm: - labelSelector: - matchExpressions: - - key: gpu-request - operator: In - values: - - "2" - topologyKey: "kubernetes.io/hostname" - - weight: 29 + - weight: 100 # For distributed jobs, try to cluster pod of same job into one region podAffinityTerm: labelSelector: matchExpressions: - - key: gpu-request + - key: jobId operator: In values: - - "1" - topologyKey: "kubernetes.io/hostname" - {% elif job["gpuLimit"]|int == 3 %} - - weight: 30 + - "{{ job["jobId"] }}" + topologyKey: "failure-domain.beta.kubernetes.io/region" + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 50 podAffinityTerm: labelSelector: matchExpressions: - - key: "gpu-request" + - key: jobId operator: In values: - - "1" - topologyKey: "kubernetes.io/hostname" - {% endif %} + - "{{ job["jobId"] }}" + topologyKey: "failure-domain.beta.kubernetes.io/zone" + {% endif %} {% if job["dnsPolicy"] %} dnsPolicy: {{ job["dnsPolicy" ]}} {% endif %} From 514cdfe7faa0fe5cac8e3cf107971c3ec455d029 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Tue, 27 Aug 2019 06:15:41 +0000 Subject: [PATCH 556/595] always use full qulified domain name --- src/ClusterBootstrap/deploy.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 2d4e0340d..d0b18c9f8 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -505,25 +505,10 @@ def check_node_availability(ipAddress): #status = sock.connect_ex((ipAddress,22)) return status == 0 -# check if on same domain -def is_cur_on_same_domain(): - if "network" in config and "domain" in config["network"]: - hostname = subprocess.check_output("hostname").strip() - try: - output = subprocess.check_output("nslookup {0}.{1}".format(hostname, config["network"]["domain"]), shell=True) - if "name:" in output.lower(): - return True - except: - pass - return False - # Get domain of the node def get_domain(): if "network" in config and "domain" in config["network"] and len(config["network"]["domain"]) > 0 : - if is_cur_on_same_domain(): - domain = "" - else: - domain = "."+config["network"]["domain"] + domain = "."+config["network"]["domain"] else: domain = "" return domain From cf1b70965695135782148f9a9f9a222dee4885bb Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 28 Aug 2019 05:03:52 +0000 Subject: [PATCH 557/595] adapt k8s 1.15 changes on date --- src/utils/k8sUtils.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/utils/k8sUtils.py b/src/utils/k8sUtils.py index 943288238..af4fba87b 100755 --- a/src/utils/k8sUtils.py +++ b/src/utils/k8sUtils.py @@ -31,6 +31,11 @@ logger = logging.getLogger(__name__) +def localize_time(date): + if type(date) == str: + date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ") + return pytz.utc.localize(date).isoformat() + def curl_get(url): curl = pycurl.Curl() curl.setopt(pycurl.URL, url) @@ -304,14 +309,14 @@ def get_pod_status(pod): ret += ":\n" + containerStatus["state"]["terminated"]["message"] podstatus["message"] = ret if "finishedAt" in containerStatus["state"]["terminated"]: - podstatus["finishedAt"] = pytz.utc.localize(containerStatus["state"]["terminated"]["finishedAt"]).isoformat() + podstatus["finishedAt"] = localize_time(containerStatus["state"]["terminated"]["finishedAt"]) if "startedAt" in containerStatus["state"]["terminated"]: - podstatus["startedAt"] = pytz.utc.localize(containerStatus["state"]["terminated"]["startedAt"]).isoformat() + podstatus["startedAt"] = localize_time(containerStatus["state"]["terminated"]["startedAt"]) elif "state" in containerStatus and "running" in containerStatus["state"] and "startedAt" in containerStatus["state"]["running"]: - podstatus["message"] = "started at: " + pytz.utc.localize(containerStatus["state"]["running"]["startedAt"]).isoformat() + podstatus["message"] = "started at: " + localize_time(containerStatus["state"]["running"]["startedAt"]) if "startedAt" in containerStatus["state"]["running"]: - podstatus["startedAt"] = pytz.utc.localize(containerStatus["state"]["running"]["startedAt"]).isoformat() + podstatus["startedAt"] = localize_time(containerStatus["state"]["running"]["startedAt"]) if "finishedAt" not in podstatus: podstatus["finishedAt"] = datetime.now(get_localzone()).isoformat() From d501f1296a4c54fedd8bcc5baf4b9ae307025652 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 28 Aug 2019 05:51:30 +0000 Subject: [PATCH 558/595] generate dlws-scripts in pre-render --- .gitignore | 1 + src/ClusterBootstrap/deploy.py | 4 ---- src/ClusterBootstrap/services/jobmanager/launch_order | 2 ++ src/ClusterBootstrap/services/jobmanager/pre-render.sh | 7 +++++++ src/{Jobs_Templete => init-scripts}/bootstrap.sh | 0 src/{Jobs_Templete => init-scripts}/init_user.sh | 0 src/{Jobs_Templete => init-scripts}/setup_ssh_config.sh | 0 src/{Jobs_Templete => init-scripts}/setup_sshd.sh | 0 8 files changed, 10 insertions(+), 4 deletions(-) create mode 100755 src/ClusterBootstrap/services/jobmanager/launch_order create mode 100755 src/ClusterBootstrap/services/jobmanager/pre-render.sh rename src/{Jobs_Templete => init-scripts}/bootstrap.sh (100%) rename src/{Jobs_Templete => init-scripts}/init_user.sh (100%) rename src/{Jobs_Templete => init-scripts}/setup_ssh_config.sh (100%) rename src/{Jobs_Templete => init-scripts}/setup_sshd.sh (100%) diff --git a/.gitignore b/.gitignore index 1d62c6528..3ca6b31b6 100755 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,4 @@ cluster-autoscaler src/ClusterBootstrap/services/monitor/grafana-config.yaml src/ClusterBootstrap/services/monitor/prometheus-alerting.yaml src/ClusterBootstrap/services/monitor/alert-templates.yaml +src/ClusterBootstrap/services/jobmanager/dlws-scripts.yaml diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index d0b18c9f8..691a7ae3e 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -2810,10 +2810,6 @@ def start_one_kube_service(fname): except Exception as e: pass - if fname == "./deploy/services/jobmanager/jobmanager.yaml": - # recreate the configmap dlws-scripts - run_kubectl( ["create configmap dlws-scripts --from-file=../Jobs_Templete/ -o yaml --dry-run | ./deploy/bin/kubectl apply -f -"] ) - run_kubectl( ["create", "-f", fname ] ) def stop_one_kube_service(fname): diff --git a/src/ClusterBootstrap/services/jobmanager/launch_order b/src/ClusterBootstrap/services/jobmanager/launch_order new file mode 100755 index 000000000..526994c7c --- /dev/null +++ b/src/ClusterBootstrap/services/jobmanager/launch_order @@ -0,0 +1,2 @@ +dlws-scripts.yaml +jobmanager.yaml diff --git a/src/ClusterBootstrap/services/jobmanager/pre-render.sh b/src/ClusterBootstrap/services/jobmanager/pre-render.sh new file mode 100755 index 000000000..898c3d143 --- /dev/null +++ b/src/ClusterBootstrap/services/jobmanager/pre-render.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +dir=`dirname $0` + +dlws_scripts_file_name=${dir}/dlws-scripts.yaml + +${dir}/../../deploy/bin/kubectl create configmap dlws-scripts --from-file=${dir}/../../../init-scripts --dry-run -o yaml > $dlws_scripts_file_name diff --git a/src/Jobs_Templete/bootstrap.sh b/src/init-scripts/bootstrap.sh similarity index 100% rename from src/Jobs_Templete/bootstrap.sh rename to src/init-scripts/bootstrap.sh diff --git a/src/Jobs_Templete/init_user.sh b/src/init-scripts/init_user.sh similarity index 100% rename from src/Jobs_Templete/init_user.sh rename to src/init-scripts/init_user.sh diff --git a/src/Jobs_Templete/setup_ssh_config.sh b/src/init-scripts/setup_ssh_config.sh similarity index 100% rename from src/Jobs_Templete/setup_ssh_config.sh rename to src/init-scripts/setup_ssh_config.sh diff --git a/src/Jobs_Templete/setup_sshd.sh b/src/init-scripts/setup_sshd.sh similarity index 100% rename from src/Jobs_Templete/setup_sshd.sh rename to src/init-scripts/setup_sshd.sh From db96ee3995d577ab39865e16173073abe4472c5b Mon Sep 17 00:00:00 2001 From: Di Xu Date: Thu, 29 Aug 2019 04:28:39 +0000 Subject: [PATCH 559/595] add gpu retired page alert --- .../services/monitor/alerting/gpu.rules | 16 +++++++++++----- .../services/monitor/alerting/k8s.rules | 4 ++-- .../services/monitor/alerting/node.rules | 8 ++++---- .../services/monitor/alerting/services.rules | 6 +++--- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/ClusterBootstrap/services/monitor/alerting/gpu.rules b/src/ClusterBootstrap/services/monitor/alerting/gpu.rules index a233fdf8a..1c6f7b0dd 100644 --- a/src/ClusterBootstrap/services/monitor/alerting/gpu.rules +++ b/src/ClusterBootstrap/services/monitor/alerting/gpu.rules @@ -3,24 +3,30 @@ groups: rules: - alert: NvidiaSmiLatencyTooLarge expr: histogram_quantile(0.95, sum(rate(cmd_nvidia_smi_latency_seconds_bucket[5m])) by (le, instance)) > 40 - for: 10m + for: 30m annotations: summary: "95th nvidia-smi call latency is larger than 40s in {{$labels.instance}}, should check the gpu status manually" - alert: NvidiaSmiEccError - expr: nvidiasmi_ecc_error_count{type="volatile_single"} > 0 - for: 10m + expr: nvidiasmi_ecc_error_count{type="volatile_double"} > 0 + for: 30m annotations: summary: "nvidia card from {{$labels.instance}} minor number {{$labels.minor_number}} has {{$labels.type}} ecc error, count {{$value}}" - alert: NvidiaMemoryLeak expr: nvidiasmi_memory_leak_count > 0 - for: 10m + for: 30m annotations: summary: "found nvidia memory leak from {{$labels.instance}} minor number {{$labels.minor_number}}" - alert: NvidiaZombieProcess expr: zombie_process_count{command="nvidia-smi"} > 0 - for: 10m + for: 30m annotations: summary: "found nvidia zombie process in {{$labels.instance}}" + + - alert: NvidiaRetiredPage + expr: sum (nvidiasmi_retired_page_count) by (instance, minor_number) > 60 + for: 30m + annotations: + summary: "gpu retired page from {{$labels.instance}} {{$labels.minor_number}} exceed threshold, may need to replace this gpu" diff --git a/src/ClusterBootstrap/services/monitor/alerting/k8s.rules b/src/ClusterBootstrap/services/monitor/alerting/k8s.rules index 675ae3f78..17c3ba277 100644 --- a/src/ClusterBootstrap/services/monitor/alerting/k8s.rules +++ b/src/ClusterBootstrap/services/monitor/alerting/k8s.rules @@ -3,12 +3,12 @@ groups: rules: - alert: k8sApiServerNotOk expr: k8s_api_server_count{error!="ok"} > 0 - for: 10m + for: 30m annotations: summary: "api server in {{$labels.host_ip}} is {{$labels.error}}" - alert: k8sDockerDaemonNotOk expr: docker_daemon_count{error!="ok"} > 0 - for: 10m + for: 30m annotations: summary: "docker daemon in {{$labels.ip}} is {{$labels.error}}" diff --git a/src/ClusterBootstrap/services/monitor/alerting/node.rules b/src/ClusterBootstrap/services/monitor/alerting/node.rules index ff7a689ac..d182ca241 100644 --- a/src/ClusterBootstrap/services/monitor/alerting/node.rules +++ b/src/ClusterBootstrap/services/monitor/alerting/node.rules @@ -3,19 +3,19 @@ groups: rules: - alert: NodeFilesystemUsage expr: node_filesystem_avail_bytes{mountpoint=~"/host-root.*", device=~"/dev.*"} / node_filesystem_size_bytes * 100 <= 20 - for: 10m + for: 30m annotations: summary: "Free space in {{$labels.device}} from {{$labels.instance}} is less than 20% (current value is: {{ $value }})" - alert: NodeMemoryUsage expr: (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes+node_memory_Buffers_bytes+node_memory_Cached_bytes )) / node_memory_MemTotal_bytes * 100 > 95 - for: 10m + for: 30m annotations: summary: "Memory usage in {{$labels.instance}} is above 95% (current value is: {{ $value }})" - alert: NodeCPUUsage expr: (100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)) > 98 - for: 10m + for: 30m annotations: summary: "CPU usage in {{$labels.instance}} is above 98% (current value is: {{ $value }})" @@ -27,7 +27,7 @@ groups: - alert: NodeOutOfDisk expr: pai_node_count{out_of_disk="true"} > 0 - for: 10m + for: 30m annotations: summary: "{{$labels.name}} is out of disk" diff --git a/src/ClusterBootstrap/services/monitor/alerting/services.rules b/src/ClusterBootstrap/services/monitor/alerting/services.rules index 9b8332e74..288477496 100644 --- a/src/ClusterBootstrap/services/monitor/alerting/services.rules +++ b/src/ClusterBootstrap/services/monitor/alerting/services.rules @@ -3,13 +3,13 @@ groups: rules: - alert: ServicePodNotRunning expr: pai_pod_count{phase!="running"} > 0 - for: 10m + for: 30m annotations: summary: "{{$labels.name}} in {{$labels.host_ip}} not running detected" - alert: ServicePodNotReady expr: pai_pod_count{phase="running", ready="false"} > 0 - for: 10m + for: 30m labels: type: pai_service annotations: @@ -17,7 +17,7 @@ groups: - alert: ServiceNotUp expr: up != 1 - for: 10m + for: 30m labels: type: pai_service annotations: From 76bad75b67a7dca90ade022e6d665c624829a4e7 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 29 Aug 2019 06:41:27 +0000 Subject: [PATCH 560/595] fix typo --- src/RestAPI/dlwsrestapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/RestAPI/dlwsrestapi.py b/src/RestAPI/dlwsrestapi.py index afa426975..b1d59af86 100755 --- a/src/RestAPI/dlwsrestapi.py +++ b/src/RestAPI/dlwsrestapi.py @@ -1329,7 +1329,7 @@ def post(self): ## ## Actually setup the Api resource routing here ## -api.add_resource(JobPriority, '/jobs/priorites') +api.add_resource(JobPriority, '/jobs/priorities') @app.route("/metrics") def metrics(): From 54291765ed0f601ea589c69cd00f246a48ebf4ca Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 29 Aug 2019 06:41:53 +0000 Subject: [PATCH 561/595] change default job priority to 100 --- src/ClusterManager/job_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 2c93fd571..c76850513 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -341,7 +341,7 @@ def get_priority_dict(): def get_job_priority(priority_dict, job_id): if job_id in priority_dict.keys(): return priority_dict[job_id] - return 65535 + return 100 def TakeJobActions(jobs): From 33857acb6babf324dc0277c9939cb0208e71d1d5 Mon Sep 17 00:00:00 2001 From: Zhe Date: Thu, 29 Aug 2019 20:32:11 +0000 Subject: [PATCH 562/595] devbox, docs, WebUI --- docs/deployment/Azure/FAQ.md | 94 ++++++++----- docs/deployment/Azure/Readme.md | 61 +++++--- docs/deployment/Azure/auto_scale.md | 60 ++++---- docs/deployment/Azure/configure.md | 131 ++++++++++-------- src/ClusterBootstrap/install_prerequisites.sh | 84 +++++------ src/ClusterBootstrap/params.py | 19 ++- .../template/WebUI/userconfig.json | 1 + src/docker-images/WebUI/Dockerfile | 57 +++++++- 8 files changed, 319 insertions(+), 188 deletions(-) diff --git a/docs/deployment/Azure/FAQ.md b/docs/deployment/Azure/FAQ.md index 4debf3dcf..e21b2f534 100755 --- a/docs/deployment/Azure/FAQ.md +++ b/docs/deployment/Azure/FAQ.md @@ -1,32 +1,64 @@ -# Frequently Asked Questions (FAQ) for Azure Cluster Deployment. - -Please refer to [this](../knownissues/Readme.md) for more general deployment issues. - -## After setup, I cannot visit the deployed DL Workspace portal. - -* Please wait a few minutes after the deployment script runs through to allow the portal container to be pulled and scheduled for execution. - -## I can't execute Spark job on Azure. - -The current default deployment procedure on Azure doesn't deploy HDFS/Spark. So Spark job execution is not available. - -## For 'az login', when I type in the device code, the web page prompt me again for the code. - -It seems that sometime the browser (Edge, Chrome) cache another identity not intended to be used with az login. To get around, please start the browser in (in-private) or (incognito) mode, you may then enter the proper device code. - -## I have launched a job (e.g., TensorFlow-iPython-GPU). However, I am unable to access the endpoint with error - - ```This site can’t be reached - ....cloudapp.azure.com refused to connect. - ``` - - Please check the docker image of the job you are running. Sometime, the iPython (or SSH server) hasn't been properly started, which caused the endpoint to be not accessible. - -## I notice that my azure command is failing. - -Azure CLI may time out after inactivity. You may need to re-login via 'az login'. - -## Common configuration errors. - -* "merge_config( config["azure_cluster"], tmpconfig["azure_cluster"][config["azure_cluster"]["cluster_name"]], verbose )" +# Frequently Asked Questions (FAQ) for Azure Cluster Deployment. + +Please refer to [this](../knownissues/Readme.md) for more general deployment issues. + +## After setup, I cannot visit the deployed DL Workspace portal. + +* Please wait a few minutes after the deployment script runs through to allow the portal container to be pulled and scheduled for execution. + +## sudo ./az_tools.py create failed. + +* Check whether your subscription is correct. Always execute ```az account list | grep -A5 -B5 '"isDefault": true'``` to double check. + +## Lost connection at the very first step of deploying infra node to Azure, or ```./deploy.py runscriptonall ./scripts/prepare_vm_disk.sh``` + +* Check whether hostname and source address in config.yaml are correctly set. Also try to make sure that you can ssh to the node. + +## I cannot ssh to the node when my devbox is a physical server instead of a virtual one. + +* Source IP address in config.yaml should probably be public IP, which could be derived by ```curl ifconfig.me```, instead of private IP you use to ssh to the devbox deriving from ```hostname -I```. If you cannot even ssh to the node after creating it, try to first set a new rule in Azure portal, allowing any source and destination IP, and set destination portal ranges to 22. Then ssh to the node, and type ```who``` to get the actual IP that is used to login to the node. Delete the temporary rule and in Networking setting, add /16 to valid source IP, where is the ```who``` IP with last two numbers set to 0. (e.g., 167.220.2.105 to 167.220.0.0/16) + +## How do I know the node has been deployed? + +* you can log into the master node: ```./deploy.py connect master``` + +## I could not build docker image/No such image/An image does not exist locally with the tag/The repository XXX does not have a Release file + +* check whether your docker is able to correctly resolve dns. First try on your devbox to ping a certain website, then do it in docker, such as `docker run -it busybox`, + if the former setting can ping but not the later one, try to figure out whether your devbox need to visit public Internet via some private DNS. Then edit it in `/etc/docker/daemon.json` on your devbox. refer to [this article](https://medium.com/@faithfulanere/solved-docker-build-could-not-resolve-archive-ubuntu-com-apt-get-fails-to-install-anything-9ea4dfdcdcf2) + use `systemd-resolve --status` to get more info about DNS if it is not managed by network-manager + +## I can connect master/infra node, but the UI is not working (cannot access from browser), how to debug? + +* login to the master node, and use ```docker ps | grep web``` to get the ID corresponding to Web UI, then use ```docker logs --follow ``` to figure out what happened. + a better way is to use ```sudo docker logs --tail 100 --follow $(sudo docker ps | grep webui | awk '{print $1}') ``` since the ID would change everytime the docker is restarted. + Everytime after modifying /etc/WebUI/userconfig.json etc., remember to restart that docker image: ```docker rm -f ``` + +## finished all deployment, but not able to connect to master node via ```./deploy.py connect master```, ssh denied even with ``` ssh -i deploy/sshkey/id_rsa core@```. + +* need to change owner ```sudo chown -R : DLWorkspace/```, can check ownership using ```ls -l``` + +## I can't execute Spark job on Azure. + +* The current default deployment procedure on Azure doesn't deploy HDFS/Spark. So Spark job execution is not available. + +## For 'az login', when I type in the device code, the web page prompt me again for the code. + +* It seems that sometime the browser (Edge, Chrome) cache another identity not intended to be used with az login. To get around, please start the browser in (in-private) or (incognito) mode, you may then enter the proper device code. + +## I have launched a job (e.g., TensorFlow-iPython-GPU). However, I am unable to access the endpoint with error + + ```This site can’t be reached + ....cloudapp.azure.com refused to connect. + ``` + + Please check the docker image of the job you are running. Sometime, the iPython (or SSH server) hasn't been properly started, which caused the endpoint to be not accessible. + +## I notice that my azure command is failing. + +Azure CLI may time out after inactivity. You may need to re-login via 'az login'. + +## Common configuration errors. + +* "merge_config( config["azure_cluster"], tmpconfig["azure_cluster"][config["azure_cluster"]["cluster_name"]], verbose )" Please check if the cluster_name used in azure_cluster is the same as the DL workspace cluster name. \ No newline at end of file diff --git a/docs/deployment/Azure/Readme.md b/docs/deployment/Azure/Readme.md index 726de1e0a..ef4babe2c 100755 --- a/docs/deployment/Azure/Readme.md +++ b/docs/deployment/Azure/Readme.md @@ -4,20 +4,36 @@ This document describes the procedure to deploy a DL Workspace cluster on Azure. Please note that the procedure below doesn't deploy HDFS/Spark on DLWorkspace cluster on Azure (Spark job execution is not available on Azure Cluster). -1. Follow [this document](../../DevEnvironment/Readme.md) to setup the dev environment of DLWorkspace. Login to your Azure subscription on your dev machine via: +Prerequisite steps: +First require the manager to add you into a subscription group., then either +1. go to that group from Azure Portal and add ubuntu server from resources, this virtual server is your devbox, or +2. if you have a physical machine, install ubuntu server system(18.04) on that and use it as your devbox +then use the devbox to deploy node on cloud. +Workflow: +1. Please [configure](configure.md) your azure cluster. Put config.yaml under src/ClusterBootstrap + +2. change directory to src/ClusterBootstrap on devbox, and install prerequisite packages: +``` +cd src/ClusterBootstrap/ +sudo ./install_prerequisites.sh ``` +3. login to Azure, setup proper subscription and confirm +``` +SUBSCRIPTION_NAME="" az login +az account set --subscription "${SUBSCRIPTION_NAME}" +az account list | grep -A5 -B5 '"isDefault": true' ``` - -2. Please [configure](configure.md) your azure cluster. - -3. Set proper [authentication](../authentication/Readme.md). - -4. Initial cluster and generate certificates and keys: +configure your location, should be the same as you specified in config.yaml file: +```AZ_LOCATION=""``` +execute this command, log out(exit) and log in back +```sudo usermod -aG docker zhe_ms``` +4. Initiate cluster and generate certificates and keys: ``` -./deploy.py -y build + ./deploy.py -y build ``` + 5. Create Azure Cluster: ``` ./az_tools.py create @@ -40,9 +56,10 @@ Please note that if you are not Microsoft user, you should remove the ``` where machine1 is your azure infrastructure node. (you may get the address by ./deploy.py display) - The script block execute the following command in sequences: (you do NOT need to run the following commands if you have run step 5) - 1. Setup basic tools on the Ubuntu image. + This command sequetially execute following steps: + 1. Setup basic tools on VM and on the Ubuntu image. ``` + ./deploy.py runscriptonall ./scripts/prepare_vm_disk.sh ./deploy.py runscriptonall ./scripts/prepare_ubuntu.sh ``` @@ -57,16 +74,28 @@ Please note that if you are not Microsoft user, you should remove the ./deploy.py -y kubernetes labels ``` - 4. Build and deploy jobmanager, restfulapi, and webportal. Mount storage. + 4. Start Nvidia device plugins: ``` + ./deploy.py kubernetes start nvidia-device-plugin + ``` + + 5. Build and deploy jobmanager, restfulapi, and webportal. Mount storage. + ``` + ./deploy.py webui ./deploy.py docker push restfulapi ./deploy.py docker push webui - ./deploy.py webui ./deploy.py mount ./deploy.py kubernetes start jobmanager restfulapi webportal ``` -8. If you run into a deployment issue, please check [here](FAQ.md) first. - -9. If you want to deploy a DLWorkspace cluster that can be autoscaled (i.e., automatically create/release VM when needed), please follow the following additional steps. - +8. Manually connect to the infrastructure/master node: + ```./deploy.py connect master``` + On master node(log in from devbox by ./deploy.py connect master), manually add ```"Grafana": "",``` to /etc/WebUI/userconfig.json, under "Restapi" entry. + Restart the WebUI docker: + login to the master node, and use + ```docker ps | grep web``` + to get the ID corresponding to Web UI, then restart that docker image: + ```docker rm -f ``` + wait for minutes for it to restart (can follow by using ```docker logs --follow ```) and visit the infra node from web browser. + +9. If you run into a deployment issue, please check [here](FAQ.md) first. \ No newline at end of file diff --git a/docs/deployment/Azure/auto_scale.md b/docs/deployment/Azure/auto_scale.md index 8816c721e..0570b487c 100755 --- a/docs/deployment/Azure/auto_scale.md +++ b/docs/deployment/Azure/auto_scale.md @@ -1,31 +1,31 @@ -# The following describe the procedures to autoscale a DLWorkspace cluster (i.e., automatically create/release VM when needed). - -1. Download auto_scaler binary - ``` - wget https://github.com/DLWorkspace/autoscaler/releases/download/v1.9.0/cluster-autoscaler - ``` - -2. Setup azure running environment and login (via az login) - -3. For the Azure machine types supported, please check the document at: - ``` - src/ClusterBootstrap/templates/machine-types/azure/machineTypes.yaml - ``` - - A sample template is as follows. Please fill in additional worker VM SKUs if you need. - - ``` - --- - Standard_NC6: - cpu: 6 - memoryInMb: 56339 - gpu: 1 - Standard_D3_v2: - cpu: 4 - memoryInMb: 14339 - ``` - -4. Start auto_scaler: - ``` - ./cluster-autoscaler --v=5 --stderrthreshold=error --logtostderr=true --cloud-provider=aztools --skip-nodes-with-local-storage=false --nodes=0:10:Standard_NC6 --nodes=0:10:Standard_D3_v2 --leader-elect=false --scale-down-enabled=true --kubeconfig=./deploy/kubeconfig/kubeconfig.yaml --expander=least-waste +# The following describe the procedures to autoscale a DLWorkspace cluster (i.e., automatically create/release VM when needed). + +1. Download auto_scaler binary + ``` + wget https://github.com/DLWorkspace/autoscaler/releases/download/v1.9.0/cluster-autoscaler + ``` + +2. Setup azure running environment and login (via az login) + +3. For the Azure machine types supported, please check the document at: + ``` + src/ClusterBootstrap/templates/machine-types/azure/machineTypes.yaml + ``` + + A sample template is as follows. Please fill in additional worker VM SKUs if you need. + + ``` + --- + Standard_NC6: + cpu: 6 + memoryInMb: 56339 + gpu: 1 + Standard_D3_v2: + cpu: 4 + memoryInMb: 14339 + ``` + +4. Start auto_scaler: + ``` + ./cluster-autoscaler --v=5 --stderrthreshold=error --logtostderr=true --cloud-provider=aztools --skip-nodes-with-local-storage=false --nodes=0:10:Standard_NC6 --nodes=0:10:Standard_D3_v2 --leader-elect=false --scale-down-enabled=true --kubeconfig=./deploy/kubeconfig/kubeconfig.yaml --expander=least-waste ``` \ No newline at end of file diff --git a/docs/deployment/Azure/configure.md b/docs/deployment/Azure/configure.md index 6e9222b85..7d98fec08 100755 --- a/docs/deployment/Azure/configure.md +++ b/docs/deployment/Azure/configure.md @@ -1,58 +1,73 @@ -# Configuration: Azure Cluster - -For more customized configuration, please refer to the [Configuration Section](../configuration/Readme.md). - -## Azure Cluster specific configuration - -We have greatly simplified Azure Cluster Configuration. As a minimum, you will only need to create a config.yaml file under src/ClusterBootstrap, with the cluster name. - -### Cluster Name - -Cluster name must be unique, and should be specified as: - -``` -cluster_name: [your cluster name] -``` - - -### Authentication -If you are not building a cluster for Microsoft employee usage, you will also need to configure [Authentication](../authentication/Readme.md). - -### Additional configuration. - -You may provide/change the specification of the deployed Azure cluster by adding the following information on config.yaml file: - -``` -azure_cluster: - <>: - "infra_node_num": 1, - "worker_node_num": 2, - "azure_location": "westus2", - "infra_vm_size" : "Standard_D1_v2", - "worker_vm_size": "Standard_NC6", -``` - -* infra_node_num: should be odd (1, 3 or 5), number of infrastructure node for the deployment. 3 infrastructure nodes tolerate 1 failure, and 5 infrastructure nodes tolerate 2 failures. However, more infrastructure nodes (and more failure tolerance) will reduce performance of the node. - -* worker_node_num: number of worker node used for deployment. - -* azure_location: - -Please use the following to find all available azure locations. -``` -az account list-locations -``` - -* infra_vm_size, worker_vm_size: infrastructure and worker VM size. - -Usually, a CPU VM will be used for infra_vm_size, and a GPU VM will be used for worker_vm_size. Please use the following to find all available Azure VM size. -``` -az vm list-sizes --location westus2 -``` - -* Configure MySql as follows: - -``` -datasource: MySQL -mysql_password: <> -``` +# Configuration: Azure Cluster + +For more customized configuration, please refer to the [Configuration Section](../configuration/Readme.md). + +## Azure Cluster specific configuration + +We have greatly simplified Azure Cluster Configuration. As a minimum, you will only need to create a config.yaml file under src/ClusterBootstrap, with the cluster name. + +### Cluster Name + +Cluster name must be unique, and should be specified as: + +``` +cluster_name: +``` + + +### Authentication +If you are not building a cluster for Microsoft employee usage, you will also need to configure [Authentication](../authentication/Readme.md). + +### Additional configuration. + +You may provide/change the specification of the deployed Azure cluster by adding the following information on config.yaml file: + +``` +cluster_name: exitedtoad +#vm size: westus:Standard_DS1, westus2:Standard_DS1_v2 +azure_cluster: + : + "infra_node_num": 1 + "worker_node_num": 2 + "azure_location": "westus2" + "infra_vm_size": "Standard_DS1_v2" + "worker_vm_size": "Standard_B2s" +datasource: MySQL +webuiport: 80 +mysql_password: +DeployAuthentications : ["Corp"] +WinbindServers: [] +cloud_config: + default_admin_username: core + dev_network: + source_addresses_prefixes: + # These are the dev box of the cluster, only the machine in the IP address below will have access to the cluster. + - "/32" +``` + +* cluster_name: a name without underscore or numbers (purely consisting of lower case letters) is recommended. + +* infra_node_num: should be odd (1, 3 or 5), number of infrastructure node for the deployment. 3 infrastructure nodes tolerate 1 failure, and 5 infrastructure nodes tolerate 2 failures. However, more infrastructure nodes (and more failure tolerance) will reduce performance of the node. + +* worker_node_num: number of worker node used for deployment. + +* azure_location: + +Please use the following to find all available azure locations. +``` +az account list-locations +``` + +* infra_vm_size, worker_vm_size: infrastructure and worker VM size. + +Usually, a CPU VM will be used for infra_vm_size, and a GPU VM will be used for worker_vm_size. Please use the following to find all available Azure VM size. +``` +az vm list-sizes --location westus2 +``` + +* Configure MySql as follows: + +``` +datasource: MySQL +mysql_password: <> +``` diff --git a/src/ClusterBootstrap/install_prerequisites.sh b/src/ClusterBootstrap/install_prerequisites.sh index 38ef3f909..81ffdf3b8 100755 --- a/src/ClusterBootstrap/install_prerequisites.sh +++ b/src/ClusterBootstrap/install_prerequisites.sh @@ -1,42 +1,42 @@ -#!/bin/bash - -sudo apt-get update -sudo apt-get install -y --no-install-recommends \ - apt-utils \ - software-properties-common \ - git \ - curl \ - python-pip \ - wget \ - cpio \ - mkisofs \ - apt-transport-https \ - openssh-client \ - ca-certificates - -# Install docker -curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - - -sudo add-apt-repository \ - "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ - $(lsb_release -cs) \ - stable" - docker-ce - -AZ_REPO=$(lsb_release -cs) -echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $AZ_REPO main" | \ - sudo tee /etc/apt/sources.list.d/azure-cli.list - -sudo apt-key --keyring /etc/apt/trusted.gpg.d/Microsoft.gpg adv \ - --keyserver packages.microsoft.com \ - --recv-keys BC528686B50D79E339D3721CEB3E94ADBE1229CF - -sudo apt-get update -sudo apt-get install -y --no-install-recommends \ -sudo apt-get install azure-cli - -pip install --upgrade pip -pip install setuptools && pip install pyyaml && pip install jinja2 - -sudo echo "dockerd > /dev/null 2>&1 &" | cat >> /etc/bash.bashrc - +#!/bin/bash +sudo apt-get update +sudo apt-get install -y --no-install-recommends \ + apt-utils \ + software-properties-common \ + git \ + curl \ + python-dev \ + python-pip \ + wget \ + cpio \ + mkisofs \ + apt-transport-https \ + openssh-client \ + ca-certificates \ + network-manager + +sudo apt-get install libcurl4-openssl-dev libssl-dev gcc libnss3-dev libgnutls28-dev +sudo apt-get install -y python-subprocess32 +# Install docker +# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - + +# sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" docker-ce +sudo apt install docker.io + +AZ_REPO=$(lsb_release -cs) +echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $AZ_REPO main" | \ + sudo tee /etc/apt/sources.list.d/azure-cli.list + +sudo apt-key --keyring /etc/apt/trusted.gpg.d/Microsoft.gpg adv \ + --keyserver packages.microsoft.com \ + --recv-keys BC528686B50D79E339D3721CEB3E94ADBE1229CF + +sudo apt-get update +sudo apt-get install -y --no-install-recommends +curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash + +# pip install --upgrade pip +sudo pip install setuptools && pip install pyyaml && pip install jinja2 && pip install requests && pip install tzlocal && pip install pycurl + +sudo echo "dockerd > /dev/null 2>&1 &" | cat >> /etc/bash.bashrc +./gene_loc_dns.sh diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 79fd5bef9..a9be03ce1 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -43,7 +43,7 @@ "mysql_data_path": "/var/lib/mysql", "datasource": "AzureSQL", - + "defalt_virtual_cluster_name": "platform", # Discover server is used to find IP address of the host, it need to be a well-known IP address # that is pingable. "discoverserver": "4.2.2.1", @@ -359,7 +359,7 @@ "CCSAdmins": { # The match is in C# Regex Language, please refer to : # https://msdn.microsoft.com/en-us/library/az24scfc(v=vs.110).aspx - "Allowed": ["jinl@microsoft.com", "hongzl@microsoft.com", "sanjeevm@microsoft.com"], + "Allowed": ["jinl@microsoft.com", "hongzl@microsoft.com", "sanjeevm@microsoft.com","zhexu@microsoft.com"], "uid": "900000000-999999999", "gid": "508953967" }, @@ -387,7 +387,7 @@ }, "WebUIregisterGroups": ["MicrosoftUsers", "Live", "Gmail"], - "WebUIauthorizedGroups": [], # [ "MicrosoftUsers", "Live", "Gmail" ], + "WebUIauthorizedGroups": ["MicrosoftUsers"], # [ "MicrosoftUsers", "Live", "Gmail" ], "WebUIadminGroups": ["CCSAdmins"], # Selectively deploy (turn on) one or more authenticatin methods. @@ -397,7 +397,7 @@ "DeployAuthentications": ["Corp", "Live", "Gmail"], # You should remove WinBindServers if you will use # UserGroups for authentication. - "WinbindServers": ["http://onenet40.redmond.corp.microsoft.com/domaininfo/GetUserId?userName={0}"], + # "WinbindServers": ["http://onenet40.redmond.corp.microsoft.com/domaininfo/GetUserId?userName={0}"], "workFolderAccessPoint": "", "dataFolderAccessPoint": "", @@ -609,6 +609,11 @@ "tcp_port_for_pods": "30000-49999", "tcp_port_ranges": "80 443 30000-49999 25826", "udp_port_ranges": "25826", + "inter_connect": { + "tcp_port_ranges": "22 1443 2379 3306 5000 8086 10250", + # Need to white list dev machines to connect + # "source_addresses_prefixes": [ "52.151.0.0/16"] + }, "dev_network": { "tcp_port_ranges": "22 1443 2379 3306 5000 8086", # Need to white list dev machines to connect @@ -634,15 +639,15 @@ "webui", "docker push restfulapi", "docker push webui", - "nginx fqdn", - "nginx config", + # "nginx fqdn", + # "nginx config", "mount", "kubernetes start mysql", "kubernetes start jobmanager", "kubernetes start restfulapi", "kubernetes start webportal", "kubernetes start cloudmonitor", - "kubernetes start nginx", + # "kubernetes start nginx", "kubernetes start custommetrics", # TODO(harry): we cannot distinguish gce aws from azure, so add the same providerID # This will not break current deployment. diff --git a/src/ClusterBootstrap/template/WebUI/userconfig.json b/src/ClusterBootstrap/template/WebUI/userconfig.json index b0ac02965..b54603d14 100755 --- a/src/ClusterBootstrap/template/WebUI/userconfig.json +++ b/src/ClusterBootstrap/template/WebUI/userconfig.json @@ -21,6 +21,7 @@ "RegisterGroups": {{cnf["WebUIregisterGroups"]}}, "ClusterId": "{{cnf["clusterId"]}}", "Restapi": "{{cnf["restapi"]}}", + "Grafana": "", "WorkFolderAccessPoint": "{{cnf["workFolderAccessPoint"]}}", "DataFolderAccessPoint": "{{cnf["dataFolderAccessPoint"]}}", "smbUsername":"{{cnf["smbUsername"]}}", diff --git a/src/docker-images/WebUI/Dockerfile b/src/docker-images/WebUI/Dockerfile index 4c1056527..29572cc44 100755 --- a/src/docker-images/WebUI/Dockerfile +++ b/src/docker-images/WebUI/Dockerfile @@ -1,50 +1,99 @@ FROM ubuntu:16.04 + MAINTAINER Hongzhi Li + + + RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF + RUN echo "deb http://download.mono-project.com/repo/debian wheezy main" | tee /etc/apt/sources.list.d/mono-xamarin.list + RUN echo "deb http://download.mono-project.com/repo/debian wheezy-apache24-compat main" | tee -a /etc/apt/sources.list.d/mono-xamarin.list + RUN apt-get update + RUN apt-get install -y apt-transport-https + + RUN sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ xenial main" > /etc/apt/sources.list.d/dotnetdev.list' + RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 417A0893 + RUN apt-get install -y apt-transport-https + RUN apt-get update -RUN apt-get install -y dotnet-dev-1.0.0-preview2.1-003177 -RUN apt-get install -y dotnet-dev-1.0.0-preview2-003156 + +RUN apt-get install -y dotnet-dev-1.0.0-preview2.1-003177 --allow-unauthenticated + +RUN apt-get install -y dotnet-dev-1.0.0-preview2-003156 --allow-unauthenticated + + + RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + git \ + wget \ + vim \ + python-dev \ + python-numpy \ + python-pip \ + python-yaml \ - locales + + locales + + + RUN apt-get install -y bison curl nfs-common + RUN pip install --upgrade pip; + + RUN pip install setuptools; + RUN locale-gen en_US.UTF-8 + RUN update-locale LANG=en_US.UTF-8 + + RUN pip install flask + RUN pip install flask.restful + + #this ssh key is used to download DLWorkspace from github. It has read-only access to github repo. + RUN apt-get install -y --no-install-recommends ssh + + WORKDIR /usr/WebPortal + + ADD WebPortal /usr/WebPortal + COPY run.sh . + RUN chmod +x run.sh -CMD ./run.sh + +CMD ./run.sh \ No newline at end of file From c2d66b3324e920530cda6442b68d03cbf39c576e Mon Sep 17 00:00:00 2001 From: Zhe Date: Thu, 29 Aug 2019 23:52:19 +0000 Subject: [PATCH 563/595] auto_deployment_after_DLTS --- src/ClusterBootstrap/az_params.py | 3 +- src/ClusterBootstrap/az_tools.py | 28 ++++++++--- src/ClusterBootstrap/deploy.py | 78 +++++++++++++++++++++++++++---- src/utils/MySQLDataHandler.py | 14 +++++- 4 files changed, 105 insertions(+), 18 deletions(-) diff --git a/src/ClusterBootstrap/az_params.py b/src/ClusterBootstrap/az_params.py index 790ad805e..2835daf97 100755 --- a/src/ClusterBootstrap/az_params.py +++ b/src/ClusterBootstrap/az_params.py @@ -2,10 +2,11 @@ "azure_cluster" : { "infra_node_num": 1, "worker_node_num": 2, + "nfs_node_num": 0, "azure_location": "westus2", "infra_vm_size" : "Standard_D1_v2", "worker_vm_size": "Standard_NC6", - "vm_image" : "UbuntuLTS", + "vm_image" : "Canonical:UbuntuServer:18.04-LTS:18.04.201907221", "vm_storage_sku" : "Premium_LRS", # "udp_port_ranges": "" # Use file_share_name to create Azure file share diff --git a/src/ClusterBootstrap/az_tools.py b/src/ClusterBootstrap/az_tools.py index 168bf164e..2a24b7637 100755 --- a/src/ClusterBootstrap/az_tools.py +++ b/src/ClusterBootstrap/az_tools.py @@ -307,7 +307,6 @@ def create_vnet(): def create_nsg(): - # print config["cloud_config"]["dev_network"] if "source_addresses_prefixes" in config["cloud_config"]["dev_network"]: source_addresses_prefixes = config["cloud_config"][ "dev_network"]["source_addresses_prefixes"] @@ -422,7 +421,7 @@ def create_cluster(arm_vm_password=None): create_sql() if arm_vm_password is not None: - # dev box + # dev box, used in extreme condition when there's only one public IP available, then would use dev in cluster to bridge-connect all of them create_vm_param(0, False, True, config["azure_cluster"]["infra_vm_size"], True, arm_vm_password) for i in range(int(config["azure_cluster"]["infra_node_num"])): @@ -431,7 +430,10 @@ def create_cluster(arm_vm_password=None): for i in range(int(config["azure_cluster"]["worker_node_num"])): create_vm_param(i, True, False, config["azure_cluster"]["worker_vm_size"], arm_vm_password is not None, arm_vm_password) - + # create nfs server if specified. + for i in range(int(config["azure_cluster"]["nfs_node_num"])): + create_vm_param(i, True, False, config["azure_cluster"]["worker_vm_size"], + arm_vm_password is not None, arm_vm_password) def create_vm_param(i, isWorker, isDev, vm_size, no_az=False, arm_vm_password=None): if isWorker: @@ -526,7 +528,7 @@ def vm_interconnects(): --access allow """ % ( config["azure_cluster"]["resource_group_name"], config["azure_cluster"]["nsg_name"], - config["cloud_config"]["dev_network"]["tcp_port_ranges"], + config["cloud_config"]["inter_connect"]["tcp_port_ranges"], portinfo ) if verbose: @@ -600,6 +602,19 @@ def get_disk_from_vm(vmname): return output.split("/")[-1].strip('\n') +def AZvmsize2GPU(vmsize): + vmsz2GPU = {"Standard_NC6s_v2":"P100","Standard_NC12s_v2":"P100","Standard_NC24s_v2":"P100","Standard_NC24rs_v2":"P100", + "Standard_NC6s_v3":"V100","Standard_NC12s_v3":"V100","Standard_NC24s_v3":"V100","Standard_NC24rs_v3":"V100","Standard_ND40s_v2":"V100", + "Standard_ND6s":"P40","Standard_ND12s":"P40","Standard_ND24s":"P40","Standard_ND24rs":"P40", + "Standard_NV6":"M60","Standard_NV12":"M60","Standard_NV24":"M60","Standard_NV12s_v3":"M60","Standard_NV24s_v3":"M60","Standard_NV48s_v3":"M60"} + return vmsz2GPU.get(vmsize, "NULL") + +def AZvmsize2GPUcnt(): + vmsz2GPU = {"Standard_NC6s_v2":"1","Standard_NC12s_v2":"2","Standard_NC24s_v2":"4","Standard_NC24rs_v2":"4", + "Standard_NC6s_v3":"1","Standard_NC12s_v3":"2","Standard_NC24s_v3":"4","Standard_NC24rs_v3":"4","Standard_ND40s_v2":"8", + "Standard_ND6s":"1","Standard_ND12s":"2","Standard_ND24s":"4","Standard_ND24rs":"4", + "Standard_NV6":"1","Standard_NV12":"2","Standard_NV24":"4","Standard_NV12s_v3":"1","Standard_NV24s_v3":"2","Standard_NV48s_v3":"4"} + return vmsz2GPU.get(vmsize, 0) def gen_cluster_config(output_file_name, output_file=True, no_az=False): bSQLOnly = (config["azure_cluster"]["infra_node_num"] <= 0) @@ -674,11 +689,11 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): if isNewlyScaledMachine(vmname): cc["machines"][vmname] = { "role": "worker", "scaled": True, - "node-group": vm["vmSize"]} + "node-group": vm["vmSize"],"gpu-type":AZvmsize2GPU(vm["vmSize"])} else: cc["machines"][vmname] = { "role": "worker", - "node-group": vm["vmSize"]} + "node-group": vm["vmSize"],"gpu-type":AZvmsize2GPU(vm["vmSize"])} if not bSQLOnly: # Require explicit authorization setting. @@ -709,7 +724,6 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): return cc - def isNewlyScaledMachine(vmName): scaler_config_file = os.path.join(dirpath, "deploy/scaler.yaml") if os.path.exists(scaler_config_file): diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 691a7ae3e..84e8976ab 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -505,10 +505,25 @@ def check_node_availability(ipAddress): #status = sock.connect_ex((ipAddress,22)) return status == 0 +# check if on same domain +def is_cur_on_same_domain(): + if "network" in config and "domain" in config["network"]: + hostname = subprocess.check_output("hostname").strip() + try: + output = subprocess.check_output("nslookup {0}.{1}".format(hostname, config["network"]["domain"]), shell=True) + if "name:" in output.lower(): + return True + except: + pass + return False + # Get domain of the node def get_domain(): if "network" in config and "domain" in config["network"] and len(config["network"]["domain"]) > 0 : - domain = "."+config["network"]["domain"] + if is_cur_on_same_domain(): + domain = "" + else: + domain = "."+config["network"]["domain"] else: domain = "" return domain @@ -2413,6 +2428,11 @@ def exec_on_all_with_output(nodes, args, supressWarning = False): print "Node: " + node print output +def exec_on_rand_master(args, supressWarning = False): + nodes = get_ETCD_master_nodes(config["clusterId"]) + master_node = random.choice(nodes) + exec_on_all_with_output([master_node], args, supressWarning) + # run a shell script on one remote node def run_script(node, args, sudo = False, supressWarning = False): if ".py" in args[0]: @@ -2440,6 +2460,11 @@ def run_script_on_all(nodes, args, sudo = False, supressWarning = False): for node in nodes: run_script( node, args, sudo = sudo, supressWarning = supressWarning) +def run_script_on_rand_master(nargs, args): + nodes = get_ETCD_master_nodes(config["clusterId"]) + master_node = random.choice(nodes) + run_script_on_all([master_node], nargs, sudo = args.sudo ) + def copy_to_all(nodes, src, dst): for node in nodes: rmt_cp(node, src, dst) @@ -2755,14 +2780,11 @@ def kubernetes_label_nodes( verb, servicelists, force ): kubernetes_label_node(cmdoptions, nodename, label+"-") -# Label kubernete nodes with gpu types. +# Label kubernete nodes with gpu types.skip for CPU workers def kubernetes_label_GpuTypes(): - nodes = get_nodes(config["clusterId"]) - gpuType_config = fetch_config(config, ["gpuType_config"]) - for gpuType, nodes in gpuType_config: - for node in nodes: - nodename = kubernetes_get_node_name(node) - kubernetes_label_node("--overwrite", nodename, "gpuType="+gpuType) + for nodename,nodeInfo in config["machines"].items(): + if nodeInfo["role"] == "worker" and nodeInfo["gpu-type"] != "NULL": + kubernetes_label_node("--overwrite", nodename, "gpuType="+nodeInfo["gpu-type"]) def kubernetes_patch_nodes_provider (provider, scaledOnly): @@ -2810,6 +2832,10 @@ def start_one_kube_service(fname): except Exception as e: pass + if fname == "./deploy/services/jobmanager/jobmanager.yaml": + # recreate the configmap dlws-scripts + run_kubectl( ["create configmap dlws-scripts --from-file=../Jobs_Templete/ -o yaml --dry-run | ./deploy/bin/kubectl apply -f -"] ) + run_kubectl( ["create", "-f", fname ] ) def stop_one_kube_service(fname): @@ -2908,6 +2934,23 @@ def run_docker_image( imagename, native = False, sudo = False ): else: run_docker( matches[0], prompt = imagename, dockerConfig = dockerConfig, sudo = sudo ) +def gen_dns_config_script(): + dnsf = open("./scripts/dns.sh","w") + dnsf.write("sudo systemctl disable systemd-resolved.service\nsudo systemctl stop systemd-resolved\necho \"dns=default\" | sudo tee -a /etc/NetworkManager/NetworkManager.conf") + dnsf.write("sudo rm /etc/resolv.conf\necho \"nameserver 8.8.8.8\" | sudo tee -a /etc/resolv.conf\n") + dnsf.write("echo \"search {}.cloudapp.azure.com\" | sudo tee -a /etc/resolv.conf\n".format(config["azure_cluster"][config["cluster_name"]]["azure_location"])) + dnsf.write("sudo chattr -e /etc/resolv.conf\nsudo chattr +i /etc/resolv.conf\n") + dnsf.close() + +def gen_pass_secret_script(): + psf= open("./scripts/pass_secret.sh","w") + cmds = [] + for regi_name, regi_cred in config["registry_credential"].items(): + psf.write("docker login {} -u {} -p {}\n".format(regi_name, regi_cred["username"], regi_cred["password"])) + psf.write("sudo ln -s /opt/bin/kubectl /usr/bin/kubectl\n") + psf.write("kubectl create secret generic regcred --from-file=.dockerconfigjson=/home/"+config["cloud_config"]["default_admin_username"]+"/.docker/config.json --type=kubernetes.io/dockerconfigjson --dry-run -o yaml | kubectl apply -f -\n") + psf.close() + def run_command( args, command, nargs, parser ): # If necessary, show parsed arguments. # print args @@ -3088,6 +3131,11 @@ def run_command( args, command, nargs, parser ): print "Error: build target %s is not recognized. " % nargs[0] exit() + elif command == "dnssetup": + os.system("./gene_loc_dns.sh") + nodes = get_nodes(config["clusterId"]) + run_script_on_all(nodes, "./scripts/dns.sh", sudo = args.sudo ) + elif command == "sshkey": if len(nargs) >=1 and nargs[0] == "install": install_ssh_key(nargs[1:]) @@ -3376,6 +3424,9 @@ def run_command( args, command, nargs, parser ): nodes = get_nodes(config["clusterId"]) run_script_on_all(nodes, nargs, sudo = args.sudo ) + elif command == "runscriptonrandmaster" and len(nargs)>=1: + run_script_on_rand_master(nargs, args) + elif command == "runscriptonscaleup" and len(nargs)>=1: nodes = get_scaled_nodes(config["clusterId"]) run_script_on_all(nodes, nargs, sudo = args.sudo ) @@ -3577,6 +3628,17 @@ def run_command( args, command, nargs, parser ): print "Error: kubernetes need a subcommand." exit() + elif command == "gpulabel": + kubernetes_label_GpuTypes() + + elif command == "gen_scripts": + # print(config["azure_cluster"].keys()) + gen_dns_config_script() + gen_pass_secret_script() + + elif command == "setconfigmap": + os.system('./deploy/bin/kubectl create configmap dlws-scripts --from-file=../Jobs_Templete -o yaml --dry-run | ./deploy.py kubectl apply -f -') + elif command == "download": if len(nargs)>=1: if nargs[0] == "kubectl" or nargs[0] == "kubelet": diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index b967636c1..3d835b64d 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -14,6 +14,10 @@ from prometheus_client import Histogram +sys.path.append("../ClusterBootstrap") + +from az_tools import AZvmsize2GPU, AZvmsize2GPUcnt + logger = logging.getLogger(__name__) data_handler_fn_histogram = Histogram("datahandler_fn_latency_seconds", @@ -182,7 +186,12 @@ def CreateTable(self): self.conn.commit() cursor.close() + # when the VC has vm of same GPU type but different VMsizes, e.g., when VC has Standard_NC6s_v3 and Standard_NC12s_v3 both? + # impossible since there's no way to do it with current config mechanism + worker_cnt = int(config["azure_cluster"]["worker_node_num"]) + n_gpu_pernode = AZvmsize2GPUcnt(config["azure_cluster"]["worker_vm_size"]) + gpu_type = AZvmsize2GPU(config["azure_cluster"]["worker_vm_size"]) sql = """ CREATE TABLE IF NOT EXISTS `%s` ( @@ -195,7 +204,8 @@ def CreateTable(self): PRIMARY KEY (`id`), CONSTRAINT `hierarchy` FOREIGN KEY (`parent`) REFERENCES `%s` (`vcName`) ) - """ % (self.vctablename, self.vctablename) + AS SELECT %s AS vcName, NULL AS parent, '{\"%s\":%s}' AS quota, '{\"%s\":{\"num_gpu_per_node\":%s}}' AS metadata; + """ % (self.vctablename, self.vctablename, config['defalt_virtual_cluster_name'], gpu_type, num_gpu_per_node*worker_cnt, gpu_type,num_gpu_per_node) cursor = self.conn.cursor() cursor.execute(sql) @@ -1053,4 +1063,4 @@ def Close(self): dataHandler.CreateTable() if CREATE_DB: - dataHandler.CreateDatabase() + dataHandler.CreateDatabase() \ No newline at end of file From 9deccf580b2edfa51e6a2d4e9a8fbafefd897483 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 30 Aug 2019 02:22:47 +0000 Subject: [PATCH 564/595] make gpu-reporter support CORS --- src/docker-images/gpu-reporter/Dockerfile | 2 +- src/docker-images/gpu-reporter/reporter.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/docker-images/gpu-reporter/Dockerfile b/src/docker-images/gpu-reporter/Dockerfile index 6aaf95913..dc85ef551 100644 --- a/src/docker-images/gpu-reporter/Dockerfile +++ b/src/docker-images/gpu-reporter/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.7 -RUN pip3 install requests flask prometheus_client +RUN pip3 install requests flask prometheus_client flask-cors WORKDIR /gpu-reporter diff --git a/src/docker-images/gpu-reporter/reporter.py b/src/docker-images/gpu-reporter/reporter.py index 39c22a748..34e636d8e 100644 --- a/src/docker-images/gpu-reporter/reporter.py +++ b/src/docker-images/gpu-reporter/reporter.py @@ -17,6 +17,7 @@ from flask import Flask from flask import request from flask import Response +from flask_cors import CORS import prometheus_client from prometheus_client import Histogram @@ -150,6 +151,7 @@ def refresher(prometheus_url, atomic_ref): def serve(prometheus_url, port): app = Flask(__name__) + CORS(app) atomic_ref = AtomicRef() From de09fb2230a2cc3bfee47e6987ddca937b163830 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 30 Aug 2019 07:16:49 +0000 Subject: [PATCH 565/595] support config kill through config.yaml --- .gitignore | 1 + .../services/monitor/alerting/jobs.rules | 10 ----- .../services/monitor/config_alerting.py | 38 +++++++++++++++++++ .../services/monitor/pre-render.sh | 9 ++++- 4 files changed, 47 insertions(+), 11 deletions(-) create mode 100755 src/ClusterBootstrap/services/monitor/config_alerting.py diff --git a/.gitignore b/.gitignore index 3ca6b31b6..6b028ec3a 100755 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,4 @@ src/ClusterBootstrap/services/monitor/grafana-config.yaml src/ClusterBootstrap/services/monitor/prometheus-alerting.yaml src/ClusterBootstrap/services/monitor/alert-templates.yaml src/ClusterBootstrap/services/jobmanager/dlws-scripts.yaml +src/ClusterBootstrap/services/monitor/alerting/kill-idle.rules diff --git a/src/ClusterBootstrap/services/monitor/alerting/jobs.rules b/src/ClusterBootstrap/services/monitor/alerting/jobs.rules index 7a6160384..7f095f2e4 100644 --- a/src/ClusterBootstrap/services/monitor/alerting/jobs.rules +++ b/src/ClusterBootstrap/services/monitor/alerting/jobs.rules @@ -6,13 +6,3 @@ groups: for: 4h labels: type: idle_gpu - - alert: kill-idle-jobs-email - expr: avg(task_gpu_percent) by (user_email, job_name, vc_name) == 0 - for: 8h - labels: - type: kill_idle_job_email - - alert: kill-idle-jobs - expr: avg(task_gpu_percent) by (user_email, job_name, vc_name) == 0 - for: 8h - labels: - type: reaper diff --git a/src/ClusterBootstrap/services/monitor/config_alerting.py b/src/ClusterBootstrap/services/monitor/config_alerting.py new file mode 100755 index 000000000..5efff9963 --- /dev/null +++ b/src/ClusterBootstrap/services/monitor/config_alerting.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +import os +import sys +import yaml + +headers = """ +groups: + - name: kill-idle + rules: +""" + +kill_template = """ + - alert: kill-idle-jobs-email-%s + for: %dh + expr: avg(task_gpu_percent{vc_name="%s"}) by (user_email, job_name, vc_name) == 0 + labels: + type: kill_idle_job_email + - alert: kill-idle-jobs-%s + for: %dh + expr: avg(task_gpu_percent{vc_name="%s"}) by (user_email, job_name, vc_name) == 0 + labels: + type: reaper +""" + +def config_kill_rule(m): + for vc_name, hour in m.items(): + print(kill_template % (vc_name, hour, vc_name, vc_name, hour, vc_name)) + +def extract_relevant_config(config_map): + return config_map.get("prometheus", {}).get("alerting", {}).get("kill-idle", {}) + +if __name__ == "__main__": + with open(sys.argv[1]) as f: + config = yaml.load(f.read()) + + print(headers) + config_kill_rule(extract_relevant_config(config)) diff --git a/src/ClusterBootstrap/services/monitor/pre-render.sh b/src/ClusterBootstrap/services/monitor/pre-render.sh index 3bef7333e..cf783b746 100755 --- a/src/ClusterBootstrap/services/monitor/pre-render.sh +++ b/src/ClusterBootstrap/services/monitor/pre-render.sh @@ -2,10 +2,17 @@ dir=`dirname $0` +kill_idle_rule=${dir}/alerting/kill-idle.rules + grafana_file_name=${dir}/grafana-config.yaml alert_tmpl_file_name=${dir}/alert-templates.yaml prometheus_file_name=${dir}/prometheus-alerting.yaml +rm $kill_idle_rule $grafana_file_name $alert_tmpl_file_name $prometheus_file_name 2> /dev/null + +# config kill rules +${dir}/config_alerting.py "${dir}/../../config.yaml" > $kill_idle_rule + # create configmap for i in `find ${dir}/grafana-config/ -type f -regex ".*json" ` ; do echo --from-file=$i @@ -13,4 +20,4 @@ done | xargs ${dir}/../../deploy/bin/kubectl --namespace=kube-system create conf ${dir}/../../deploy/bin/kubectl --namespace=kube-system create configmap alert-templates --from-file=${dir}/alert-templates --dry-run -o yaml > $alert_tmpl_file_name -${dir}/../../deploy/bin/kubectl --namespace=kube-system create configmap prometheus-alert --from-file=${dir}/alerting --dry-run -o yaml >> $prometheus_file_name +${dir}/../../deploy/bin/kubectl --namespace=kube-system create configmap prometheus-alert --from-file=${dir}/alerting --dry-run -o yaml > $prometheus_file_name From 17333b2e01117427c49f129d3b38c4dc1d2f35c9 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 30 Aug 2019 07:43:33 +0000 Subject: [PATCH 566/595] support config notifier through config.yaml --- .../template/RestfulAPI/config.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/ClusterBootstrap/template/RestfulAPI/config.yaml b/src/ClusterBootstrap/template/RestfulAPI/config.yaml index 1624912fb..c5e9c5320 100755 --- a/src/ClusterBootstrap/template/RestfulAPI/config.yaml +++ b/src/ClusterBootstrap/template/RestfulAPI/config.yaml @@ -31,3 +31,16 @@ webportal_node: {{cnf["webportal_node"]}} datasource : {{cnf["datasource"]}} kube_custom_scheduler: {{cnf["kube_custom_scheduler"]}} WinbindServers: {{cnf["WinbindServers"]}} + +{% if cnf["job-manager"] %} +job-manager: + {% if cnf["job-manager"]["notifier"] %} + notifier: + {% if cnf["job-manager"]["notifier"]["cluster"] %} + cluster: {{ cnf["job-manager"]["notifier"]["cluster"] }} + {% endif %} + {% if cnf["job-manager"]["notifier"]["alert-manager-url"] %} + alert-manager-url: {{ cnf["job-manager"]["notifier"]["alert-manager-url"] }} + {% endif %} + {% endif %} +{% endif %} From af92e1a441d36ba5a109d07a8617fb565091798e Mon Sep 17 00:00:00 2001 From: Zhe Date: Fri, 30 Aug 2019 20:34:32 +0000 Subject: [PATCH 567/595] bug fixing --- src/ClusterBootstrap/az_tools.py | 2 +- src/utils/MySQLDataHandler.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ClusterBootstrap/az_tools.py b/src/ClusterBootstrap/az_tools.py index 084dfce6f..e1d2e78f7 100755 --- a/src/ClusterBootstrap/az_tools.py +++ b/src/ClusterBootstrap/az_tools.py @@ -609,7 +609,7 @@ def AZvmsize2GPU(vmsize): "Standard_NV6":"M60","Standard_NV12":"M60","Standard_NV24":"M60","Standard_NV12s_v3":"M60","Standard_NV24s_v3":"M60","Standard_NV48s_v3":"M60"} return vmsz2GPU.get(vmsize, "NULL") -def AZvmsize2GPUcnt(): +def AZvmsize2GPUcnt(vmsize): vmsz2GPU = {"Standard_NC6s_v2": 1,"Standard_NC12s_v2": 2,"Standard_NC24s_v2": 4,"Standard_NC24rs_v2": 4, "Standard_NC6s_v3": 1,"Standard_NC12s_v3": 2,"Standard_NC24s_v3": 4,"Standard_NC24rs_v3": 4,"Standard_ND40s_v2": 8, "Standard_ND6s": 1,"Standard_ND12s": 2,"Standard_ND24s": 4,"Standard_ND24rs": 4, diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index 3d835b64d..e1c3562da 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -205,7 +205,7 @@ def CreateTable(self): CONSTRAINT `hierarchy` FOREIGN KEY (`parent`) REFERENCES `%s` (`vcName`) ) AS SELECT %s AS vcName, NULL AS parent, '{\"%s\":%s}' AS quota, '{\"%s\":{\"num_gpu_per_node\":%s}}' AS metadata; - """ % (self.vctablename, self.vctablename, config['defalt_virtual_cluster_name'], gpu_type, num_gpu_per_node*worker_cnt, gpu_type,num_gpu_per_node) + """ % (self.vctablename, self.vctablename, config['defalt_virtual_cluster_name'], gpu_type, n_gpu_pernode*worker_cnt, gpu_type,n_gpu_pernode) cursor = self.conn.cursor() cursor.execute(sql) @@ -1063,4 +1063,4 @@ def Close(self): dataHandler.CreateTable() if CREATE_DB: - dataHandler.CreateDatabase() \ No newline at end of file + dataHandler.CreateDatabase() From 62253326d9f35fed1d50c2da7050e69358301f80 Mon Sep 17 00:00:00 2001 From: Zhe Date: Fri, 30 Aug 2019 20:42:55 +0000 Subject: [PATCH 568/595] try auto --- src/ClusterBootstrap/params.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index a9be03ce1..5df160b5e 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -631,10 +631,12 @@ "runscriptonall ./scripts/prepare_vm_disk.sh", "nfs-server create", "runscriptonall ./scripts/prepare_ubuntu.sh", + "gen_scripts", "runscriptonall ./scripts/dns.sh", "-y deploy", "-y updateworker", "-y kubernetes labels", + "-y gpulabel", "kubernetes start nvidia-device-plugin", "webui", "docker push restfulapi", @@ -651,7 +653,9 @@ "kubernetes start custommetrics", # TODO(harry): we cannot distinguish gce aws from azure, so add the same providerID # This will not break current deployment. - "-y kubernetes patchprovider aztools" + "-y kubernetes patchprovider aztools", + "setconfigmap", + "--sudo runscriptonrandmaster ./scripts/pass_secret.sh", ], "azure_uncordon": [ "runscriptonall ./scripts/prepare_vm_disk.sh", From e81182d06c54ce5ec27ae882d4e01762bad3a8e8 Mon Sep 17 00:00:00 2001 From: Zhe Date: Fri, 30 Aug 2019 20:42:55 +0000 Subject: [PATCH 569/595] try auto --- src/ClusterBootstrap/params.py | 6 +++++- src/utils/MySQLDataHandler.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index a9be03ce1..5df160b5e 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -631,10 +631,12 @@ "runscriptonall ./scripts/prepare_vm_disk.sh", "nfs-server create", "runscriptonall ./scripts/prepare_ubuntu.sh", + "gen_scripts", "runscriptonall ./scripts/dns.sh", "-y deploy", "-y updateworker", "-y kubernetes labels", + "-y gpulabel", "kubernetes start nvidia-device-plugin", "webui", "docker push restfulapi", @@ -651,7 +653,9 @@ "kubernetes start custommetrics", # TODO(harry): we cannot distinguish gce aws from azure, so add the same providerID # This will not break current deployment. - "-y kubernetes patchprovider aztools" + "-y kubernetes patchprovider aztools", + "setconfigmap", + "--sudo runscriptonrandmaster ./scripts/pass_secret.sh", ], "azure_uncordon": [ "runscriptonall ./scripts/prepare_vm_disk.sh", diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index e1c3562da..b42f79293 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -204,7 +204,7 @@ def CreateTable(self): PRIMARY KEY (`id`), CONSTRAINT `hierarchy` FOREIGN KEY (`parent`) REFERENCES `%s` (`vcName`) ) - AS SELECT %s AS vcName, NULL AS parent, '{\"%s\":%s}' AS quota, '{\"%s\":{\"num_gpu_per_node\":%s}}' AS metadata; + AS SELECT \'%s\' AS vcName, NULL AS parent, '{\\\"%s\\\":%s}' AS quota, '{\\\"%s\\\":{\\\"num_gpu_per_node\\\":%s}}' AS metadata; """ % (self.vctablename, self.vctablename, config['defalt_virtual_cluster_name'], gpu_type, n_gpu_pernode*worker_cnt, gpu_type,n_gpu_pernode) cursor = self.conn.cursor() From d0f45f7948c1a16801602d1d8ae5901612fcdf5e Mon Sep 17 00:00:00 2001 From: Zhe Xu Date: Sat, 31 Aug 2019 12:33:28 -0700 Subject: [PATCH 570/595] fix template config for restful API --- src/ClusterBootstrap/deploy.py | 6 +++--- .../template/RestfulAPI/config.yaml | 4 ++++ src/utils/MySQLDataHandler.py | 21 +++++++++++++++---- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 6e0897ca8..054b94a7b 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -2946,9 +2946,9 @@ def gen_pass_secret_script(): psf= open("./scripts/pass_secret.sh","w") cmds = [] for regi_name, regi_cred in config["registry_credential"].items(): - psf.write("docker login {} -u {} -p {}\n".format(regi_name, regi_cred["username"], regi_cred["password"])) - #psf.write("sudo ln -s /opt/bin/kubectl /usr/bin/kubectl\n") - psf.write("/opt/bin/kubectl create secret generic regcred --from-file=.dockerconfigjson=/home/"+config["cloud_config"]["default_admin_username"]+"/.docker/config.json --type=kubernetes.io/dockerconfigjson --dry-run -o yaml | kubectl apply -f -\n") + psf.write("docker login {} -u {} -p {}\n".format(regi_name, regi_cred["username"], regi_cred["password"])) + psf.write("chown -R {}:{} /home/{}/.docker/\n".format(config["cloud_config"]["default_admin_username"],config["cloud_config"]["default_admin_username"],config["cloud_config"]["default_admin_username"])) + psf.write("/opt/bin/kubectl create secret generic regcred --from-file=.dockerconfigjson=/home/"+config["cloud_config"]["default_admin_username"]+"/.docker/config.json --type=kubernetes.io/dockerconfigjson --dry-run -o yaml | /opt/bin/kubectl apply -f -\n") psf.close() def run_command( args, command, nargs, parser ): diff --git a/src/ClusterBootstrap/template/RestfulAPI/config.yaml b/src/ClusterBootstrap/template/RestfulAPI/config.yaml index 1624912fb..8fd47fde1 100755 --- a/src/ClusterBootstrap/template/RestfulAPI/config.yaml +++ b/src/ClusterBootstrap/template/RestfulAPI/config.yaml @@ -31,3 +31,7 @@ webportal_node: {{cnf["webportal_node"]}} datasource : {{cnf["datasource"]}} kube_custom_scheduler: {{cnf["kube_custom_scheduler"]}} WinbindServers: {{cnf["WinbindServers"]}} +azure_cluster : + worker_node_num : {{cnf["azure_cluster"][cnf["cluster_name"]]["worker_node_num"]}} + worker_vm_size : {{cnf["azure_cluster"][cnf["cluster_name"]]["worker_vm_size"]}} +defalt_virtual_cluster_name: {{cnf["defalt_virtual_cluster_name"]}} diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index b42f79293..4c7e28139 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -14,10 +14,23 @@ from prometheus_client import Histogram -sys.path.append("../ClusterBootstrap") - -from az_tools import AZvmsize2GPU, AZvmsize2GPUcnt - +# sys.path.append("../ClusterBootstrap") + +# from az_tools import AZvmsize2GPU, AZvmsize2GPUcnt +def AZvmsize2GPU(vmsize): + vmsz2GPU = {"Standard_NC6s_v2":"P100","Standard_NC12s_v2":"P100","Standard_NC24s_v2":"P100","Standard_NC24rs_v2":"P100", + "Standard_NC6s_v3":"V100","Standard_NC12s_v3":"V100","Standard_NC24s_v3":"V100","Standard_NC24rs_v3":"V100","Standard_ND40s_v2":"V100", + "Standard_ND6s":"P40","Standard_ND12s":"P40","Standard_ND24s":"P40","Standard_ND24rs":"P40", + "Standard_NV6":"M60","Standard_NV12":"M60","Standard_NV24":"M60","Standard_NV12s_v3":"M60","Standard_NV24s_v3":"M60","Standard_NV48s_v3":"M60"} + return vmsz2GPU.get(vmsize, "NULL") + +def AZvmsize2GPUcnt(vmsize): + vmsz2GPU = {"Standard_NC6s_v2": 1,"Standard_NC12s_v2": 2,"Standard_NC24s_v2": 4,"Standard_NC24rs_v2": 4, + "Standard_NC6s_v3": 1,"Standard_NC12s_v3": 2,"Standard_NC24s_v3": 4,"Standard_NC24rs_v3": 4,"Standard_ND40s_v2": 8, + "Standard_ND6s": 1,"Standard_ND12s": 2,"Standard_ND24s": 4,"Standard_ND24rs": 4, + "Standard_NV6": 1,"Standard_NV12": 2,"Standard_NV24": 4,"Standard_NV12s_v3": 1,"Standard_NV24s_v3": 2,"Standard_NV48s_v3": 4} + return vmsz2GPU.get(vmsize, 0) + logger = logging.getLogger(__name__) data_handler_fn_histogram = Histogram("datahandler_fn_latency_seconds", From f0c7201f445f70beffe733102306e31698652160 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 4 Sep 2019 07:03:43 +0000 Subject: [PATCH 571/595] add storage usage dashboard --- .../storage-usage-dashboard.json | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 src/ClusterBootstrap/services/monitor/grafana-config/storage-usage-dashboard.json diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/storage-usage-dashboard.json b/src/ClusterBootstrap/services/monitor/grafana-config/storage-usage-dashboard.json new file mode 100644 index 000000000..85b1c8c56 --- /dev/null +++ b/src/ClusterBootstrap/services/monitor/grafana-config/storage-usage-dashboard.json @@ -0,0 +1,149 @@ +{"dashboard": { + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [], + "rows": [ + { + "collapse": false, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 0, + "id": 1, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "min ((node_filesystem_size_bytes - node_filesystem_free_bytes{fstype=\"nfs4\"}) / node_filesystem_size_bytes) by (device) * 100", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{ '{{' }} device {{ '}}' }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Storage Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percent", + "label": null, + "logBase": 1, + "max": "100", + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Storage usage", + "version": 0 +}} From a69cb52d3ccfc1a32754d4ac0f4398e9a940fd16 Mon Sep 17 00:00:00 2001 From: "REDMOND\\anbhu" Date: Wed, 4 Sep 2019 18:33:48 -0700 Subject: [PATCH 572/595] Disable cache --- src/utils/DataHandler.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/utils/DataHandler.py b/src/utils/DataHandler.py index b6148ab69..586ea9474 100755 --- a/src/utils/DataHandler.py +++ b/src/utils/DataHandler.py @@ -20,7 +20,6 @@ def GetClusterStatus(): @staticmethod - @fcache() def ListVCs(): dataHandler = DataHandler() ret = None @@ -32,7 +31,6 @@ def ListVCs(): @staticmethod - @fcache() def GetResourceAcl(resourceAclPath): dataHandler = DataHandler() ret = None @@ -44,7 +42,6 @@ def GetResourceAcl(resourceAclPath): @staticmethod - @fcache() def GetIdentityInfo(identityName): dataHandler = DataHandler() ret = None From fde798bb8c90ebf961717488e18a792e029d9065 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Thu, 5 Sep 2019 07:58:43 +0000 Subject: [PATCH 573/595] user level quota --- src/ClusterManager/job_manager.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index c76850513..6f15cf5d1 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -181,7 +181,7 @@ def ApproveJob(job): job_id = job["jobId"] vcName = job["vcName"] jobParams = json.loads(base64.b64decode(job["jobParams"])) - total_gpus = GetJobTotalGpu(jobParams) + job_total_gpus = GetJobTotalGpu(jobParams) dataHandler = DataHandler() vcList = dataHandler.ListVCs() @@ -195,12 +195,21 @@ def ApproveJob(job): return False metadata = json.loads(vc["metadata"]) - if "user_quota" in metadata and int(metadata["user_quota"]) < total_gpus: - logging.info("Job {} excesses the user quota: {} > {}. Will need approve from admin.".format(job_id, total_gpus, metadata["user_quota"])) - return False - else: - dataHandler.UpdateJobTextField(job_id, "jobStatus", "queued") - return True + if "user_quota" in metadata: + user_running_jobs = dataHandler.GetJobList(job["userName"], vcName, status="running,queued,scheduling", op=("=", "or")) + running_gpus = 0 + for running_job in user_running_jobs: + running_jobParams = json.loads(base64.b64decode(running_job["jobParams"])) + running_job_total_gpus = GetJobTotalGpu(running_jobParams) + running_gpus += running_job_total_gpus + + logging.info("Job {} require {}, using {}, with user quote of {}.".format(job_id, job_total_gpus, running_gpus, metadata["user_quota"])) + if int(metadata["user_quota"]) < (running_gpus + job_total_gpus): + logging.info("Job {} excesses the user quota: {} + {} > {}. Will need approve from admin.".format(job_id, running_gpus, job_total_gpus, metadata["user_quota"])) + return False + + dataHandler.UpdateJobTextField(job_id, "jobStatus", "queued") + return True except Exception as e: logging.warning(e, exc_info=True) finally: From 55aebfb1f4687c13fe830503f2008390f9fd0feb Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Fri, 6 Sep 2019 02:48:12 +0000 Subject: [PATCH 574/595] after resume, job will be "unapproved" state --- src/utils/JobRestAPIUtils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/JobRestAPIUtils.py b/src/utils/JobRestAPIUtils.py index 6d8b885aa..b7fe2c2d9 100755 --- a/src/utils/JobRestAPIUtils.py +++ b/src/utils/JobRestAPIUtils.py @@ -310,10 +310,10 @@ def ApproveJob(userName, jobId): def ResumeJob(userName, jobId): dataHandler = DataHandler() ret = False - jobs = dataHandler.GetJob(jobId=jobId) + jobs = dataHandler.GetJob(jobId=jobId) if len(jobs) == 1: if jobs[0]["userName"] == userName or AuthorizationManager.HasAccess(userName, ResourceType.VC, jobs[0]["vcName"], Permission.Collaborator): - ret = dataHandler.UpdateJobTextField(jobId,"jobStatus","queued") + ret = dataHandler.UpdateJobTextField(jobId, "jobStatus", "unapproved") dataHandler.Close() return ret From 87d6bfb63ca6e2523f0c795d64db0b7b2ae2dfe4 Mon Sep 17 00:00:00 2001 From: Zhe Xu Date: Fri, 6 Sep 2019 17:25:26 +0000 Subject: [PATCH 575/595] format correction and doc update, mapping file for Azure, jinja rendering for dns setting and secret passing script --- docs/deployment/Azure/FAQ.md | 8 +-- docs/deployment/Azure/Readme.md | 16 +++--- docs/deployment/Azure/configure.md | 21 ++++--- src/ClusterBootstrap/az_tools.py | 8 ++- src/ClusterBootstrap/deploy.py | 17 +----- src/ClusterBootstrap/params.py | 7 +-- .../template/dns/dns.sh.template | 7 +++ .../template/secret/pass_secret.sh.template | 5 ++ src/utils/MySQLDataHandler.py | 24 ++------ src/utils/sku_mapping.yaml | 57 +++++++++++++++++++ 10 files changed, 112 insertions(+), 58 deletions(-) create mode 100755 src/ClusterBootstrap/template/dns/dns.sh.template create mode 100755 src/ClusterBootstrap/template/secret/pass_secret.sh.template create mode 100644 src/utils/sku_mapping.yaml diff --git a/docs/deployment/Azure/FAQ.md b/docs/deployment/Azure/FAQ.md index e21b2f534..139abe035 100755 --- a/docs/deployment/Azure/FAQ.md +++ b/docs/deployment/Azure/FAQ.md @@ -20,23 +20,23 @@ Please refer to [this](../knownissues/Readme.md) for more general deployment iss ## How do I know the node has been deployed? -* you can log into the master node: ```./deploy.py connect master``` +* You can log into the master node: ```./deploy.py connect master``` ## I could not build docker image/No such image/An image does not exist locally with the tag/The repository XXX does not have a Release file -* check whether your docker is able to correctly resolve dns. First try on your devbox to ping a certain website, then do it in docker, such as `docker run -it busybox`, +* Check whether your docker is able to correctly resolve dns. First try on your devbox to ping a certain website, then do it in docker, such as `docker run -it busybox`, if the former setting can ping but not the later one, try to figure out whether your devbox need to visit public Internet via some private DNS. Then edit it in `/etc/docker/daemon.json` on your devbox. refer to [this article](https://medium.com/@faithfulanere/solved-docker-build-could-not-resolve-archive-ubuntu-com-apt-get-fails-to-install-anything-9ea4dfdcdcf2) use `systemd-resolve --status` to get more info about DNS if it is not managed by network-manager ## I can connect master/infra node, but the UI is not working (cannot access from browser), how to debug? -* login to the master node, and use ```docker ps | grep web``` to get the ID corresponding to Web UI, then use ```docker logs --follow ``` to figure out what happened. +* Login to the master node, and use ```docker ps | grep web``` to get the ID corresponding to Web UI, then use ```docker logs --follow ``` to figure out what happened. a better way is to use ```sudo docker logs --tail 100 --follow $(sudo docker ps | grep webui | awk '{print $1}') ``` since the ID would change everytime the docker is restarted. Everytime after modifying /etc/WebUI/userconfig.json etc., remember to restart that docker image: ```docker rm -f ``` ## finished all deployment, but not able to connect to master node via ```./deploy.py connect master```, ssh denied even with ``` ssh -i deploy/sshkey/id_rsa core@```. -* need to change owner ```sudo chown -R : DLWorkspace/```, can check ownership using ```ls -l``` +* Need to change owner ```sudo chown -R : DLWorkspace/```, can check ownership using ```ls -l``` ## I can't execute Spark job on Azure. diff --git a/docs/deployment/Azure/Readme.md b/docs/deployment/Azure/Readme.md index ef4babe2c..d009ab424 100755 --- a/docs/deployment/Azure/Readme.md +++ b/docs/deployment/Azure/Readme.md @@ -13,25 +13,25 @@ then use the devbox to deploy node on cloud. Workflow: 1. Please [configure](configure.md) your azure cluster. Put config.yaml under src/ClusterBootstrap -2. change directory to src/ClusterBootstrap on devbox, and install prerequisite packages: +2. Change directory to src/ClusterBootstrap on devbox, and install prerequisite packages: ``` cd src/ClusterBootstrap/ sudo ./install_prerequisites.sh ``` -3. login to Azure, setup proper subscription and confirm +3. Login to Azure, setup proper subscription and confirm ``` SUBSCRIPTION_NAME="" az login az account set --subscription "${SUBSCRIPTION_NAME}" az account list | grep -A5 -B5 '"isDefault": true' ``` -configure your location, should be the same as you specified in config.yaml file: +Configure your location, should be the same as you specified in config.yaml file: ```AZ_LOCATION=""``` -execute this command, log out(exit) and log in back +Execute this command, log out(exit) and log in back ```sudo usermod -aG docker zhe_ms``` 4. Initiate cluster and generate certificates and keys: ``` - ./deploy.py -y build +./deploy.py -y build ``` 5. Create Azure Cluster: @@ -92,10 +92,10 @@ Please note that if you are not Microsoft user, you should remove the ```./deploy.py connect master``` On master node(log in from devbox by ./deploy.py connect master), manually add ```"Grafana": "",``` to /etc/WebUI/userconfig.json, under "Restapi" entry. Restart the WebUI docker: - login to the master node, and use + Login to the master node, and use ```docker ps | grep web``` to get the ID corresponding to Web UI, then restart that docker image: ```docker rm -f ``` - wait for minutes for it to restart (can follow by using ```docker logs --follow ```) and visit the infra node from web browser. + Wait for minutes for it to restart (can follow by using ```docker logs --follow ```) and visit the infra node from web browser. -9. If you run into a deployment issue, please check [here](FAQ.md) first. \ No newline at end of file +9. If you run into a deployment issue, please check [here](FAQ.md) first. \ No newline at end of file diff --git a/docs/deployment/Azure/configure.md b/docs/deployment/Azure/configure.md index 7d98fec08..92532d09d 100755 --- a/docs/deployment/Azure/configure.md +++ b/docs/deployment/Azure/configure.md @@ -1,6 +1,6 @@ # Configuration: Azure Cluster -For more customized configuration, please refer to the [Configuration Section](../configuration/Readme.md). +For more customized configuration, please refer to the [Configuration Section](../configuration/Readme.md) and [Azure doc](https://docs.microsoft.com/en-us/cli/azure/vm?view=azure-cli-latest). ## Azure Cluster specific configuration @@ -31,25 +31,30 @@ azure_cluster: "worker_node_num": 2 "azure_location": "westus2" "infra_vm_size": "Standard_DS1_v2" - "worker_vm_size": "Standard_B2s" + "worker_vm_size": "Standard_NC6" + "vm_image" : "Canonical:UbuntuServer:18.04-LTS:18.04.201907221" datasource: MySQL webuiport: 80 mysql_password: -DeployAuthentications : ["Corp"] -WinbindServers: [] cloud_config: default_admin_username: core dev_network: source_addresses_prefixes: # These are the dev box of the cluster, only the machine in the IP address below will have access to the cluster. - "/32" +registry_credential: + : + username: + password: ``` -* cluster_name: a name without underscore or numbers (purely consisting of lower case letters) is recommended. +* cluster_name: A name without underscore or numbers (purely consisting of lower case letters) is recommended. -* infra_node_num: should be odd (1, 3 or 5), number of infrastructure node for the deployment. 3 infrastructure nodes tolerate 1 failure, and 5 infrastructure nodes tolerate 2 failures. However, more infrastructure nodes (and more failure tolerance) will reduce performance of the node. +* infra_node_num: Should be odd (1, 3 or 5), number of infrastructure node for the deployment. 3 infrastructure nodes tolerate 1 failure, and 5 infrastructure nodes tolerate 2 failures. However, more infrastructure nodes (and more failure tolerance) will reduce performance of the node. -* worker_node_num: number of worker node used for deployment. +* worker_node_num: Number of worker node used for deployment. + +* vm_image: Used to fix the image version if the changing LTS is breaking the consistency of the deployment. * azure_location: @@ -71,3 +76,5 @@ az vm list-sizes --location westus2 datasource: MySQL mysql_password: <> ``` + +* registry_credential: defines your access to certain dockers. A docker image name consists of three parts - registry name, image name, and image tag. If your job needs a certain private docker, then use 0. the registry name of that docker, 1. your user name and 2. your password to specify your access to it. \ No newline at end of file diff --git a/src/ClusterBootstrap/az_tools.py b/src/ClusterBootstrap/az_tools.py index e1d2e78f7..5e12a1e66 100755 --- a/src/ClusterBootstrap/az_tools.py +++ b/src/ClusterBootstrap/az_tools.py @@ -683,17 +683,21 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): vm_list = get_vm_list_by_grp() else: vm_list = get_vm_list_by_enum() + + sku_mapping_cnfn = "../utils/sku_mapping.yaml" + f = open(sku_mapping_cnfn) + sku_mapping = yaml.load(f) for vm in vm_list: vmname = vm["name"] if "-worker" in vmname: if isNewlyScaledMachine(vmname): cc["machines"][vmname] = { "role": "worker", "scaled": True, - "node-group": vm["vmSize"],"gpu-type":AZvmsize2GPU(vm["vmSize"])} + "node-group": vm["vmSize"],"gpu-type":sku_mapping[vm["vmSize"]]["gpu-type"]} else: cc["machines"][vmname] = { "role": "worker", - "node-group": vm["vmSize"],"gpu-type":AZvmsize2GPU(vm["vmSize"])} + "node-group": vm["vmSize"],"gpu-type":sku_mapping[vm["vmSize"]]["gpu-type"]} if not bSQLOnly: # Require explicit authorization setting. diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 054b94a7b..d85cb778e 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -2935,21 +2935,10 @@ def run_docker_image( imagename, native = False, sudo = False ): run_docker( matches[0], prompt = imagename, dockerConfig = dockerConfig, sudo = sudo ) def gen_dns_config_script(): - dnsf = open("./scripts/dns.sh","w") - dnsf.write("sudo systemctl disable systemd-resolved.service\nsudo systemctl stop systemd-resolved\necho \"dns=default\" | sudo tee -a /etc/NetworkManager/NetworkManager.conf") - dnsf.write("sudo rm /etc/resolv.conf\necho \"nameserver 8.8.8.8\" | sudo tee -a /etc/resolv.conf\n") - dnsf.write("echo \"search {}.cloudapp.azure.com\" | sudo tee -a /etc/resolv.conf\n".format(config["azure_cluster"][config["cluster_name"]]["azure_location"])) - dnsf.write("sudo chattr -e /etc/resolv.conf\nsudo chattr +i /etc/resolv.conf\n") - dnsf.close() + utils.render_template("./template/dns/dns.sh.template", "deploy/kubeconfig/kubeconfig.yaml", config) def gen_pass_secret_script(): - psf= open("./scripts/pass_secret.sh","w") - cmds = [] - for regi_name, regi_cred in config["registry_credential"].items(): - psf.write("docker login {} -u {} -p {}\n".format(regi_name, regi_cred["username"], regi_cred["password"])) - psf.write("chown -R {}:{} /home/{}/.docker/\n".format(config["cloud_config"]["default_admin_username"],config["cloud_config"]["default_admin_username"],config["cloud_config"]["default_admin_username"])) - psf.write("/opt/bin/kubectl create secret generic regcred --from-file=.dockerconfigjson=/home/"+config["cloud_config"]["default_admin_username"]+"/.docker/config.json --type=kubernetes.io/dockerconfigjson --dry-run -o yaml | /opt/bin/kubectl apply -f -\n") - psf.close() + utils.render_template("./template/dns/pass.sh.template", "scripts/pass.sh", config) def run_command( args, command, nargs, parser ): # If necessary, show parsed arguments. @@ -3631,7 +3620,7 @@ def run_command( args, command, nargs, parser ): elif command == "gpulabel": kubernetes_label_GpuTypes() - elif command == "gen_scripts": + elif command == "genscripts": # print(config["azure_cluster"].keys()) gen_dns_config_script() gen_pass_secret_script() diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 5df160b5e..7b1bf2da4 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -359,7 +359,7 @@ "CCSAdmins": { # The match is in C# Regex Language, please refer to : # https://msdn.microsoft.com/en-us/library/az24scfc(v=vs.110).aspx - "Allowed": ["jinl@microsoft.com", "hongzl@microsoft.com", "sanjeevm@microsoft.com","zhexu@microsoft.com"], + "Allowed": ["hongzl@microsoft.com", "anbhu@microsoft.com", "jachzh@microsoft.com","zhexu@microsoft.com","dixu@microsoft.com","qixcheng@microsoft.com","jingzhao@microsoft.com","hayua@microsoft.com"], "uid": "900000000-999999999", "gid": "508953967" }, @@ -387,7 +387,7 @@ }, "WebUIregisterGroups": ["MicrosoftUsers", "Live", "Gmail"], - "WebUIauthorizedGroups": ["MicrosoftUsers"], # [ "MicrosoftUsers", "Live", "Gmail" ], + "WebUIauthorizedGroups": [], # [ "MicrosoftUsers", "Live", "Gmail" ], "WebUIadminGroups": ["CCSAdmins"], # Selectively deploy (turn on) one or more authenticatin methods. @@ -397,7 +397,6 @@ "DeployAuthentications": ["Corp", "Live", "Gmail"], # You should remove WinBindServers if you will use # UserGroups for authentication. - # "WinbindServers": ["http://onenet40.redmond.corp.microsoft.com/domaininfo/GetUserId?userName={0}"], "workFolderAccessPoint": "", "dataFolderAccessPoint": "", @@ -631,7 +630,7 @@ "runscriptonall ./scripts/prepare_vm_disk.sh", "nfs-server create", "runscriptonall ./scripts/prepare_ubuntu.sh", - "gen_scripts", + "genscripts", "runscriptonall ./scripts/dns.sh", "-y deploy", "-y updateworker", diff --git a/src/ClusterBootstrap/template/dns/dns.sh.template b/src/ClusterBootstrap/template/dns/dns.sh.template new file mode 100755 index 000000000..8bd5bcc82 --- /dev/null +++ b/src/ClusterBootstrap/template/dns/dns.sh.template @@ -0,0 +1,7 @@ +sudo systemctl disable systemd-resolved.service +sudo systemctl stop systemd-resolved +echo "dns=default" | sudo tee -a /etc/NetworkManager/NetworkManager.conf +sudo rm /etc/resolv.conf +echo "nameserver 8.8.8.8" | sudo tee -a /etc/resolv.conf +echo 'search {{cnf["azure_cluster"][cnf["cluster_name"]]["azure_location"].cloudapp.azure.com}}' | sudo tee -a /etc/resolv.conf +sudo service network-manager restart \ No newline at end of file diff --git a/src/ClusterBootstrap/template/secret/pass_secret.sh.template b/src/ClusterBootstrap/template/secret/pass_secret.sh.template new file mode 100755 index 000000000..e04b3fc52 --- /dev/null +++ b/src/ClusterBootstrap/template/secret/pass_secret.sh.template @@ -0,0 +1,5 @@ +{% for regi_name, regi_cred in cnf["registry_credential"].items() %} +docker login {{ regi_name }} -u {{ regi_cred["username"] }} -p {{ regi_cred["password"] }} +{% endfor %} +chown -R {{cnf["cloud_config"]["default_admin_username"]}}:{{cnf["cloud_config"]["default_admin_username"]}} /home/{{cnf["cloud_config"]["default_admin_username"]}}/.docker/ +/opt/bin/kubectl create secret generic regcred --from-file=.dockerconfigjson=/home/{{cnf["cloud_config"]["default_admin_username"]}}/.docker/config.json --type=kubernetes.io/dockerconfigjson --dry-run -o yaml | /opt/bin/kubectl apply -f - \ No newline at end of file diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index 4c7e28139..740fa3863 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -13,23 +13,6 @@ from config import global_vars from prometheus_client import Histogram - -# sys.path.append("../ClusterBootstrap") - -# from az_tools import AZvmsize2GPU, AZvmsize2GPUcnt -def AZvmsize2GPU(vmsize): - vmsz2GPU = {"Standard_NC6s_v2":"P100","Standard_NC12s_v2":"P100","Standard_NC24s_v2":"P100","Standard_NC24rs_v2":"P100", - "Standard_NC6s_v3":"V100","Standard_NC12s_v3":"V100","Standard_NC24s_v3":"V100","Standard_NC24rs_v3":"V100","Standard_ND40s_v2":"V100", - "Standard_ND6s":"P40","Standard_ND12s":"P40","Standard_ND24s":"P40","Standard_ND24rs":"P40", - "Standard_NV6":"M60","Standard_NV12":"M60","Standard_NV24":"M60","Standard_NV12s_v3":"M60","Standard_NV24s_v3":"M60","Standard_NV48s_v3":"M60"} - return vmsz2GPU.get(vmsize, "NULL") - -def AZvmsize2GPUcnt(vmsize): - vmsz2GPU = {"Standard_NC6s_v2": 1,"Standard_NC12s_v2": 2,"Standard_NC24s_v2": 4,"Standard_NC24rs_v2": 4, - "Standard_NC6s_v3": 1,"Standard_NC12s_v3": 2,"Standard_NC24s_v3": 4,"Standard_NC24rs_v3": 4,"Standard_ND40s_v2": 8, - "Standard_ND6s": 1,"Standard_ND12s": 2,"Standard_ND24s": 4,"Standard_ND24rs": 4, - "Standard_NV6": 1,"Standard_NV12": 2,"Standard_NV24": 4,"Standard_NV12s_v3": 1,"Standard_NV24s_v3": 2,"Standard_NV48s_v3": 4} - return vmsz2GPU.get(vmsize, 0) logger = logging.getLogger(__name__) @@ -203,8 +186,11 @@ def CreateTable(self): # impossible since there's no way to do it with current config mechanism worker_cnt = int(config["azure_cluster"]["worker_node_num"]) - n_gpu_pernode = AZvmsize2GPUcnt(config["azure_cluster"]["worker_vm_size"]) - gpu_type = AZvmsize2GPU(config["azure_cluster"]["worker_vm_size"]) + sku_mapping_cnfn = "./sku_mapping.yaml" + f = open(sku_mapping_cnfn) + sku_mapping = yaml.load(f) + n_gpu_pernode = sku_mapping[config["azure_cluster"]["worker_vm_size"]]["gpu-count"] + gpu_type = sku_mapping[config["azure_cluster"]["worker_vm_size"]]["gpu-type"] sql = """ CREATE TABLE IF NOT EXISTS `%s` ( diff --git a/src/utils/sku_mapping.yaml b/src/utils/sku_mapping.yaml new file mode 100644 index 000000000..4858f719a --- /dev/null +++ b/src/utils/sku_mapping.yaml @@ -0,0 +1,57 @@ +Standard_ND6s: + gpu-type: P40 + gpu-count: 1 +Standard_NV24: + gpu-type: M60 + gpu-count: 4 +Standard_ND12s: + gpu-type: P40 + gpu-count: 2 +Standard_ND24rs: + gpu-type: P40 + gpu-count: 4 +Standard_NV12: + gpu-type: M60 + gpu-count: 2 +Standard_NV48s_v3: + gpu-type: M60 + gpu-count: 4 +Standard_ND40s_v2: + gpu-type: V100 + gpu-count: 8 +Standard_NC6s_v3: + gpu-type: V100 + gpu-count: 1 +Standard_NC6s_v2: + gpu-type: P100 + gpu-count: 1 +Standard_ND24s: + gpu-type: P40 + gpu-count: 4 +Standard_NV24s_v3: + gpu-type: M60 + gpu-count: 2 +Standard_NV6: + gpu-type: M60 + gpu-count: 1 +Standard_NV12s_v3: + gpu-type: M60 + gpu-count: 1 +Standard_NC24s_v2: + gpu-type: P100 + gpu-count: 4 +Standard_NC24s_v3: + gpu-type: V100 + gpu-count: 4 +Standard_NC12s_v3: + gpu-type: V100 + gpu-count: 2 +Standard_NC12s_v2: + gpu-type: P100 + gpu-count: 2 +Standard_NC24rs_v3: + gpu-type: V100 + gpu-count: 4 +Standard_NC24rs_v2: + gpu-type: P100 + gpu-count: 4 From 20f711e01f528a2e6e1f0efc37dca1d8d90b19cc Mon Sep 17 00:00:00 2001 From: Zhe Xu Date: Sat, 7 Sep 2019 01:13:58 +0000 Subject: [PATCH 576/595] apply nsg machine creation Azure --- src/ClusterBootstrap/az_params.py | 31 +++---- src/ClusterBootstrap/az_tools.py | 137 +++++++++++++++++++----------- src/ClusterBootstrap/deploy.py | 32 +++++-- src/ClusterBootstrap/params.py | 9 +- 4 files changed, 135 insertions(+), 74 deletions(-) diff --git a/src/ClusterBootstrap/az_params.py b/src/ClusterBootstrap/az_params.py index 2835daf97..a8cb49b1c 100755 --- a/src/ClusterBootstrap/az_params.py +++ b/src/ClusterBootstrap/az_params.py @@ -1,15 +1,16 @@ -default_az_parameters = { - "azure_cluster" : { - "infra_node_num": 1, - "worker_node_num": 2, - "nfs_node_num": 0, - "azure_location": "westus2", - "infra_vm_size" : "Standard_D1_v2", - "worker_vm_size": "Standard_NC6", - "vm_image" : "Canonical:UbuntuServer:18.04-LTS:18.04.201907221", - "vm_storage_sku" : "Premium_LRS", - # "udp_port_ranges": "" - # Use file_share_name to create Azure file share - # "file_share_name" : "files", - }, -} +default_az_parameters = { + "azure_cluster" : { + "infra_node_num": 1, + "worker_node_num": 2, + "nfs_node_num": 0, + "azure_location": "westus2", + "infra_vm_size" : "Standard_D1_v2", + "worker_vm_size": "Standard_NC6", + "nfs_vm_size": "Standard_D1_v2", + "vm_image" : "Canonical:UbuntuServer:18.04-LTS:18.04.201907221", + "vm_storage_sku" : "Premium_LRS", + # "udp_port_ranges": "" + # Use file_share_name to create Azure file share + # "file_share_name" : "files", + }, +} diff --git a/src/ClusterBootstrap/az_tools.py b/src/ClusterBootstrap/az_tools.py index 5e12a1e66..b4de2da9c 100755 --- a/src/ClusterBootstrap/az_tools.py +++ b/src/ClusterBootstrap/az_tools.py @@ -76,6 +76,9 @@ def update_config(config, genSSH=True): config["azure_cluster"]["nsg_name"] = config[ "azure_cluster"]["cluster_name"] + "-nsg" + if int(config["azure_cluster"]["nfs_node_num"]) > 0: + config["azure_cluster"]["nfs_nsg_name"] = config[ + "azure_cluster"]["cluster_name"] + "-nfs-nsg" config["azure_cluster"]["sql_server_name"] = config[ "azure_cluster"]["cluster_name"] + "sqlserver" config["azure_cluster"]["sql_admin_name"] = config[ @@ -145,7 +148,9 @@ def create_vm_pwd(vmname, vm_ip, vm_size, use_private_ip, pwd): output = utils.exec_cmd_local(cmd) print(output) -def create_vm(vmname, vm_ip, bIsWorker, vm_size): +def create_vm(vmname, vm_ip, role, vm_size): + specify_priv_IP = role in ["worker","nfs"] + nsg = "nfs_nsg_name" if role == "nfs" else "nsg_name" cmd = """ az vm create --resource-group %s \ --name %s \ @@ -170,10 +175,10 @@ def create_vm(vmname, vm_ip, bIsWorker, vm_size): config["azure_cluster"]["azure_location"], vm_size, config["azure_cluster"]["vnet_name"], - config["azure_cluster"]["nsg_name"], + config["azure_cluster"][nsg], config["cloud_config"]["default_admin_username"], config["azure_cluster"]["vm_storage_sku"], - config["azure_cluster"]["sshkey"]) if not bIsWorker else """ + config["azure_cluster"]["sshkey"]) if not specify_priv_IP else """ az vm create --resource-group %s \ --name %s \ --image %s \ @@ -195,7 +200,7 @@ def create_vm(vmname, vm_ip, bIsWorker, vm_size): config["azure_cluster"]["azure_location"], vm_size, config["azure_cluster"]["vnet_name"], - config["azure_cluster"]["nsg_name"], + config["azure_cluster"][nsg], config["cloud_config"]["default_admin_username"], config["azure_cluster"]["vm_storage_sku"], config["azure_cluster"]["sshkey"]) @@ -367,7 +372,7 @@ def create_nsg(): --name allowdevtcp \ --protocol tcp \ --priority 900 \ - --destination-port-range %s \ + --destination-port-ranges %s \ --source-address-prefixes %s \ --access allow """ % ( config["azure_cluster"]["resource_group_name"], @@ -378,6 +383,51 @@ def create_nsg(): output = utils.exec_cmd_local(cmd) print(output) +def create_nfs_nsg(): + cmd = """ + az network nsg create \ + --resource-group %s \ + --name %s + """ % ( config["azure_cluster"]["resource_group_name"], + config["azure_cluster"]["nfs_nsg_name"]) + if verbose: + print(cmd) + output = utils.exec_cmd_local(cmd) + print(output) + + cmd = """ + az network nsg rule create \ + --resource-group %s \ + --nsg-name %s \ + --name allow_ssh\ + --priority 900 \ + --destination-port-ranges %s \ + --source-address-prefixes %s \ + --access allow + """ % ( config["azure_cluster"]["resource_group_name"], + config["azure_cluster"]["nfs_nsg_name"], + config["cloud_config"]["nfs_ssh"]["port"], + " ".join(config["cloud_config"]["nfs_ssh"]["source_ips"]), + ) + output = utils.exec_cmd_local(cmd) + print(output) + + cmd = """ + az network nsg rule create \ + --resource-group %s \ + --nsg-name %s \ + --name allow_share \ + --priority 1000 \ + --source-address-prefixes %s \ + --destination-port-ranges \'*\' \ + --access allow + """ % ( config["azure_cluster"]["resource_group_name"], + config["azure_cluster"]["nfs_nsg_name"], + " ".join(config["cloud_config"]["nfs_share"]["source_ips"]), + ) + output = utils.exec_cmd_local(cmd) + print(output) + def delete_group(): cmd = """ @@ -389,13 +439,13 @@ def delete_group(): print(output) -def get_vm_ip(i, bIsWorker, bIsDev): +def get_vm_ip(i, role): vnet_range = config["cloud_config"]["vnet_range"] vnet_ip = vnet_range.split("/")[0] vnet_ips = vnet_ip.split(".") - if bIsWorker: + if role in ["worker", "nfs"]: return vnet_ips[0] + "." + vnet_ips[1] + "." + "1" + "." + str(i + 1) - elif bIsDev: + elif role == "dev": return vnet_ips[0] + "." + vnet_ips[1] + "." + "255" + "." + str(int(config["azure_cluster"]["infra_node_num"]) + 1) else: # 192.168.0 is reserved. @@ -416,44 +466,41 @@ def create_cluster(arm_vm_password=None): create_vnet() print "creating network security group..." create_nsg() + if int(config["azure_cluster"]["nfs_node_num"]) > 0: + create_nfs_nsg() if useSqlAzure(): print "creating sql server and database..." create_sql() if arm_vm_password is not None: # dev box, used in extreme condition when there's only one public IP available, then would use dev in cluster to bridge-connect all of them - create_vm_param(0, False, True, config["azure_cluster"]["infra_vm_size"], + create_vm_param(0, "dev", config["azure_cluster"]["infra_vm_size"], True, arm_vm_password) for i in range(int(config["azure_cluster"]["infra_node_num"])): - create_vm_param(i, False, False, config["azure_cluster"]["infra_vm_size"], + create_vm_param(i, "infra", config["azure_cluster"]["infra_vm_size"], arm_vm_password is not None, arm_vm_password) for i in range(int(config["azure_cluster"]["worker_node_num"])): - create_vm_param(i, True, False, config["azure_cluster"]["worker_vm_size"], + create_vm_param(i, "worker", config["azure_cluster"]["worker_vm_size"], arm_vm_password is not None, arm_vm_password) # create nfs server if specified. for i in range(int(config["azure_cluster"]["nfs_node_num"])): - create_vm_param(i, True, False, config["azure_cluster"]["worker_vm_size"], + create_vm_param(i, "nfs", config["azure_cluster"]["nfs_vm_size"], arm_vm_password is not None, arm_vm_password) -def create_vm_param(i, isWorker, isDev, vm_size, no_az=False, arm_vm_password=None): - if isWorker: - if no_az: - vmname = "%s-worker%02d" % (config["azure_cluster"] - ["cluster_name"], i+1) - else: - vmname = "%s-worker-%s" % (config["azure_cluster"] - ["cluster_name"], random_str(6)) - elif not isDev: +def create_vm_param(i, role, vm_size, no_az=False, arm_vm_password=None): + if role in ["worker","nfs"]: + vmname = "{}-{}".format(config["azure_cluster"]["cluster_name"], role) + ("{:02d}".format(i+1) if no_az else '-'+random_str(6)) + elif role == "infra": vmname = "%s-infra%02d" % (config["azure_cluster"] ["cluster_name"], i + 1) - else: + elif role == "dev": vmname = "%s-dev" % (config["azure_cluster"]["cluster_name"]) print "creating VM %s..." % vmname - vm_ip = get_vm_ip(i, isWorker, isDev) + vm_ip = get_vm_ip(i, role) if arm_vm_password is not None: - create_vm_pwd(vmname, vm_ip, vm_size, not isWorker, arm_vm_password) + create_vm_pwd(vmname, vm_ip, vm_size, not role in ["worker","nfs"], arm_vm_password) else: - create_vm(vmname, vm_ip, isWorker, vm_size) + create_vm(vmname, vm_ip, role, vm_size) return vmname @@ -523,7 +570,7 @@ def vm_interconnects(): --name tcpinterconnect \ --protocol tcp \ --priority 850 \ - --destination-port-range %s \ + --destination-port-ranges %s \ --source-address-prefixes %s \ --access allow """ % ( config["azure_cluster"]["resource_group_name"], @@ -602,20 +649,6 @@ def get_disk_from_vm(vmname): return output.split("/")[-1].strip('\n') -def AZvmsize2GPU(vmsize): - vmsz2GPU = {"Standard_NC6s_v2":"P100","Standard_NC12s_v2":"P100","Standard_NC24s_v2":"P100","Standard_NC24rs_v2":"P100", - "Standard_NC6s_v3":"V100","Standard_NC12s_v3":"V100","Standard_NC24s_v3":"V100","Standard_NC24rs_v3":"V100","Standard_ND40s_v2":"V100", - "Standard_ND6s":"P40","Standard_ND12s":"P40","Standard_ND24s":"P40","Standard_ND24rs":"P40", - "Standard_NV6":"M60","Standard_NV12":"M60","Standard_NV24":"M60","Standard_NV12s_v3":"M60","Standard_NV24s_v3":"M60","Standard_NV48s_v3":"M60"} - return vmsz2GPU.get(vmsize, "NULL") - -def AZvmsize2GPUcnt(vmsize): - vmsz2GPU = {"Standard_NC6s_v2": 1,"Standard_NC12s_v2": 2,"Standard_NC24s_v2": 4,"Standard_NC24rs_v2": 4, - "Standard_NC6s_v3": 1,"Standard_NC12s_v3": 2,"Standard_NC24s_v3": 4,"Standard_NC24rs_v3": 4,"Standard_ND40s_v2": 8, - "Standard_ND6s": 1,"Standard_ND12s": 2,"Standard_ND24s": 4,"Standard_ND24rs": 4, - "Standard_NV6": 1,"Standard_NV12": 2,"Standard_NV24": 4,"Standard_NV12s_v3": 1,"Standard_NV24s_v3": 2,"Standard_NV48s_v3": 4} - return vmsz2GPU.get(vmsize, 0) - def gen_cluster_config(output_file_name, output_file=True, no_az=False): bSQLOnly = (config["azure_cluster"]["infra_node_num"] <= 0) if useAzureFileshare() and not no_az: @@ -674,8 +707,7 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): for i in range(int(config["azure_cluster"]["infra_node_num"])): vmname = "%s-infra%02d" % (config["azure_cluster"] ["cluster_name"], i + 1) - cc["machines"][vmname] = { - "role": "infrastructure", "private-ip": get_vm_ip(i, False, False)} + cc["machines"][vmname] = {"role": "infrastructure", "private-ip": get_vm_ip(i, "infra")} # Generate the workers in machines. vm_list = [] @@ -698,7 +730,12 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): cc["machines"][vmname] = { "role": "worker", "node-group": vm["vmSize"],"gpu-type":sku_mapping[vm["vmSize"]]["gpu-type"]} - + for vm in vm_list: + vmname = vm["name"] + if "-nfs" in vmname: + cc["machines"][vmname] = { + "role": "nfs", + "node-group": vm["vmSize"]} if not bSQLOnly: # Require explicit authorization setting. # cc["WinbindServers"] = [] @@ -715,7 +752,7 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): cc["mountpoints"]["rootshare"]["accesskey"] = file_share_key else: cc["mountpoints"]["rootshare"]["type"] = "nfs" - cc["mountpoints"]["rootshare"]["server"] = get_vm_ip(0, False, False) + cc["mountpoints"]["rootshare"]["server"] = get_vm_ip(0, "sql") cc["mountpoints"]["rootshare"]["filesharename"] = "/data/share" cc["mountpoints"]["rootshare"][ "curphysicalmountpoint"] = "/mntdlws/nfs" @@ -755,12 +792,12 @@ def get_vm_list_by_grp(): # simply enumerate to get vm list def get_vm_list_by_enum(): vm_list = [] - for i in range(int(config["azure_cluster"]["worker_node_num"])): - vminfo = {} - vminfo["name"] = "%s-worker%02d" % (config["azure_cluster"] - ["cluster_name"], i + 1) - vminfo["vmSize"] = config["azure_cluster"]["worker_vm_size"] - vm_list.append(vminfo) + for role in ["worker","nfs"]: + for i in range(int(config["azure_cluster"]["{}_node_num".format(role)])): + vminfo = {} + vminfo["name"] = "{}-{}{:02d}".format(config["azure_cluster"]["cluster_name"], role, i + 1) + vminfo["vmSize"] = config["azure_cluster"]["{}_vm_size".format(role)] + vm_list.append(vminfo) return vm_list def random_str(length): diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index d85cb778e..6223b8d25 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -621,6 +621,19 @@ def get_worker_nodes_from_config(clusterId): config["worker_node"] = Nodes return Nodes +def get_nodes_by_role(role): + """ + role: "infrastructure", "worker", or "nfs" + this function aims to deprecate get_worker_nodes_from_config and get_ETCD_master_nodes_from_config + """ + Nodes = get_nodes_from_config(role) + if role == "infrastructure": + config["etcd_node"] = Nodes + config["kubernetes_master_node"] = Nodes + else: + config["{}_node".format(role)] = Nodes + return Nodes + def get_worker_nodes(clusterId, isScaledOnly): nodes = [] if "worker_node" in config and len(config["worker_node"]) > 0: @@ -628,7 +641,8 @@ def get_worker_nodes(clusterId, isScaledOnly): if "useclusterfile" not in config or not config["useclusterfile"]: nodes = get_worker_nodes_from_cluster_report(clusterId) else: - nodes = get_worker_nodes_from_config(clusterId) + print("from console") + nodes = get_nodes_by_role("worker") #get_worker_nodes_from_config(clusterId) if isScaledOnly: return get_scaled_nodes_from_config() @@ -650,7 +664,7 @@ def limit_nodes(nodes): return nodes def get_nodes(clusterId): - nodes = get_ETCD_master_nodes(clusterId) + get_worker_nodes(clusterId, False) + nodes = get_ETCD_master_nodes(clusterId) + get_worker_nodes(clusterId, False) + get_nodes_by_role("nfs") nodes = limit_nodes(nodes) return nodes @@ -666,10 +680,12 @@ def check_master_ETCD_status(): print "Checking Available Nodes for Deployment..." get_ETCD_master_nodes(config["clusterId"]) get_worker_nodes(config["clusterId"], False) + get_nodes_by_role("nfs") print "===============================================" print "Activate Master Node(s): %s\n %s \n" % (len(config["kubernetes_master_node"]),",".join(config["kubernetes_master_node"])) print "Activate ETCD Node(s):%s\n %s \n" % (len(config["etcd_node"]),",".join(config["etcd_node"])) print "Activate Worker Node(s):%s\n %s \n" % (len(config["worker_node"]),",".join(config["worker_node"])) + print "Activate NFS Node(s):%s\n %s \n" % (len(config["nfs_node"]),",".join(config["nfs_node"])) def clean_deployment(): print "===============================================" @@ -3061,15 +3077,15 @@ def run_command( args, command, nargs, parser ): elif command == "connect": check_master_ETCD_status() - if len(nargs) < 1 or nargs[0] == "master": + role2connect = nargs[0] + print(role2connect, config["ssh_cert"], config["admin_username"]) + if len(nargs) < 1 or role2connect == "master": nodes = config["kubernetes_master_node"] - elif nargs[0] == "etcd": - nodes = config["etcd_node"] - elif nargs[0] == "worker": - nodes = config["worker_node"] + elif role2connect in ["etcd","worker","nfs"]: + nodes = config["{}_node".format(role2connect)] else: parser.print_help() - print "ERROR: must connect to either master, etcd or worker nodes" + print "ERROR: must connect to either master, etcd, nfs or worker nodes" exit() if len(nodes) == 0: parser.print_help() diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 7b1bf2da4..2900f2937 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -617,7 +617,14 @@ "tcp_port_ranges": "22 1443 2379 3306 5000 8086", # Need to white list dev machines to connect # "source_addresses_prefixes": [ "52.151.0.0/16"] - } + }, + "nfs_share": { + "source_ips": "104.44.112.0/24 131.107.0.0/16", + }, + "nfs_ssh": { + "source_ips": "131.107.0.0/16 104.44.0.0/16", + "port": "22", + }, }, "vc_config":{ "VC-Default":["*"], From 7663b6ea2434ee3096ec846e1541a79589e85394 Mon Sep 17 00:00:00 2001 From: "REDMOND\\anbhu" Date: Mon, 9 Sep 2019 17:18:03 -0700 Subject: [PATCH 577/595] Allo 0 GPU job to go through --- src/ClusterManager/job_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 6f15cf5d1..c88842e74 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -204,7 +204,7 @@ def ApproveJob(job): running_gpus += running_job_total_gpus logging.info("Job {} require {}, using {}, with user quote of {}.".format(job_id, job_total_gpus, running_gpus, metadata["user_quota"])) - if int(metadata["user_quota"]) < (running_gpus + job_total_gpus): + if job_total_gpus > 0 and int(metadata["user_quota"]) < (running_gpus + job_total_gpus): logging.info("Job {} excesses the user quota: {} + {} > {}. Will need approve from admin.".format(job_id, running_gpus, job_total_gpus, metadata["user_quota"])) return False From 639b15f3584aeddb708498f65eb387f30861a618 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 10 Sep 2019 03:54:58 +0000 Subject: [PATCH 578/595] log more info about pod --- src/ClusterManager/job_manager.py | 5 +++++ src/ClusterManager/job_role.py | 10 +++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index c88842e74..ee0257521 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -314,6 +314,11 @@ def check_job_status(job_id): statuses = [job_role.status() for job_role in job_roles] logging.info("Job: {}, status: {}".format(job_id, statuses)) + details = [] + for job_role in job_roles: + details.append(job_role.pod_details().to_dict()) + logging.info("Job {}, details: {}".format(job_id, details)) + if "Failed" in statuses: return "Failed" if "Unknown" in statuses: diff --git a/src/ClusterManager/job_role.py b/src/ClusterManager/job_role.py index f4e61e9f8..66ad9b2ad 100644 --- a/src/ClusterManager/job_role.py +++ b/src/ClusterManager/job_role.py @@ -41,13 +41,13 @@ def status(self): return "NotFound" assert(len(pods) == 1) - pod = pods[0] - phase = pod.status.phase + self.pod = pods[0] + phase = self.pod.status.phase # !!! Pod is running, doesn't mean "Role" is ready and running. if(phase == "Running"): # Found that phase won't turn into "Unkonwn" even when we get 'unknown' from kubectl - if pod.status.reason == "NodeLost": + if self.pod.status.reason == "NodeLost": return "Unknown" # Check if the user command had been ran. @@ -56,6 +56,10 @@ def status(self): return phase + # TODO should call after status(), or the self.pod would be None + def pod_details(self): + return self.pod + def isFileExisting(self, file): deployer = JobDeployer() status_code, _ = deployer.pod_exec(self.pod_name, ["/bin/sh", "-c", "ls -lrt {}".format(file)]) From f0e8152431296928ab82992664ba76402716f531 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 10 Sep 2019 06:44:35 +0000 Subject: [PATCH 579/595] ignore preemptible GPUs --- src/ClusterManager/job_manager.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index ee0257521..5cf094fbd 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -200,10 +200,13 @@ def ApproveJob(job): running_gpus = 0 for running_job in user_running_jobs: running_jobParams = json.loads(base64.b64decode(running_job["jobParams"])) + # ignore preemptible GPUs + if "preemptionAllowed" in running_jobParams and running_jobParams["preemptionAllowed"] is True: + continue running_job_total_gpus = GetJobTotalGpu(running_jobParams) running_gpus += running_job_total_gpus - logging.info("Job {} require {}, using {}, with user quote of {}.".format(job_id, job_total_gpus, running_gpus, metadata["user_quota"])) + logging.info("Job {} require {}, used quota (exclude preemptible GPUs) {}, with user quota of {}.".format(job_id, job_total_gpus, running_gpus, metadata["user_quota"])) if job_total_gpus > 0 and int(metadata["user_quota"]) < (running_gpus + job_total_gpus): logging.info("Job {} excesses the user quota: {} + {} > {}. Will need approve from admin.".format(job_id, running_gpus, job_total_gpus, metadata["user_quota"])) return False From c3fb7837b94c385cdd85f1a92784b02d357b81f4 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 10 Sep 2019 06:52:24 +0000 Subject: [PATCH 580/595] auto approve preemptible job, preemptible GPUs are not take in count of quota --- src/ClusterManager/job_manager.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ClusterManager/job_manager.py b/src/ClusterManager/job_manager.py index 5cf094fbd..86fe8433b 100755 --- a/src/ClusterManager/job_manager.py +++ b/src/ClusterManager/job_manager.py @@ -184,6 +184,12 @@ def ApproveJob(job): job_total_gpus = GetJobTotalGpu(jobParams) dataHandler = DataHandler() + + if "preemptionAllowed" in jobParams and jobParams["preemptionAllowed"] is True: + logging.info("Job {} preemptible, approve!".format(job_id)) + dataHandler.UpdateJobTextField(job_id, "jobStatus", "queued") + return True + vcList = dataHandler.ListVCs() vc = None for item in vcList: From 3e8efaffadbca172818667f812b5cc04190365d4 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Tue, 10 Sep 2019 07:04:18 +0000 Subject: [PATCH 581/595] default mount home-folder for all jobs --- src/ClusterManager/dist_pod_template.py | 1 + src/ClusterManager/pod_template.py | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ClusterManager/dist_pod_template.py b/src/ClusterManager/dist_pod_template.py index 493764ab9..c2d6ddff7 100644 --- a/src/ClusterManager/dist_pod_template.py +++ b/src/ClusterManager/dist_pod_template.py @@ -101,6 +101,7 @@ def generate_pods(self, job): job.data_path = params["dataPath"] # TODO user's mountpoints first, but should after 'job_path' job.add_mountpoints(job.job_path_mountpoint()) + job.add_mountpoints({"name": "home", "containerPath": "/home/{}".format(job.get_alias()), "hostPath": job.get_homefolder_hostpath(), "enabled": True}) if "mountpoints" in params: job.add_mountpoints(params["mountpoints"]) job.add_mountpoints(job.work_path_mountpoint()) diff --git a/src/ClusterManager/pod_template.py b/src/ClusterManager/pod_template.py index 0de62e5e4..ecd228fcc 100644 --- a/src/ClusterManager/pod_template.py +++ b/src/ClusterManager/pod_template.py @@ -85,6 +85,7 @@ def generate_pods(self, job): job.data_path = params["dataPath"] # TODO user's mountpoints first, but should after 'job_path' job.add_mountpoints(job.job_path_mountpoint()) + job.add_mountpoints({"name": "home", "containerPath": "/home/{}".format(job.get_alias()), "hostPath": job.get_homefolder_hostpath(), "enabled": True}) if "mountpoints" in params: job.add_mountpoints(params["mountpoints"]) job.add_mountpoints(job.work_path_mountpoint()) From c84444a3dd5c2dce110e99869fdd30931626bfac Mon Sep 17 00:00:00 2001 From: "REDMOND\\anbhu" Date: Tue, 10 Sep 2019 12:05:42 -0700 Subject: [PATCH 582/595] Update Get Pending Jobs --- src/utils/DataHandler.py | 2 +- src/utils/JobRestAPIUtils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/DataHandler.py b/src/utils/DataHandler.py index 586ea9474..80a25ee0a 100755 --- a/src/utils/DataHandler.py +++ b/src/utils/DataHandler.py @@ -57,7 +57,7 @@ def GetAllPendingJobs(vcName): dataHandler = DataHandler() ret = None try: - ret = dataHandler.GetJobList("all",vcName,None, "running,queued,scheduling,unapproved", ("=","or")) + ret = dataHandler.GetJobList("all",vcName,None, "running,queued,scheduling,unapproved,pausing,paused", ("=","or")) finally: dataHandler.Close() return ret diff --git a/src/utils/JobRestAPIUtils.py b/src/utils/JobRestAPIUtils.py index b7fe2c2d9..8cf542694 100755 --- a/src/utils/JobRestAPIUtils.py +++ b/src/utils/JobRestAPIUtils.py @@ -228,7 +228,7 @@ def GetJobList(userName, vcName, jobOwner, num=None): if jobOwner != "all" or not hasAccessOnAllJobs: jobs = jobs + GetUserPendingJobs(userName, vcName) - jobs = jobs + dataHandler.GetJobList(userName,vcName,num, "running,queued,scheduling,unapproved", ("<>","and")) + jobs = jobs + dataHandler.GetJobList(userName,vcName,num, "running,queued,scheduling,unapproved,pausing,paused", ("<>","and")) else: jobs = GetUserPendingJobs(jobOwner, vcName) From 0889e6e0f0f98da83548be296e652aeb1bd29ed2 Mon Sep 17 00:00:00 2001 From: "REDMOND\\anbhu" Date: Tue, 10 Sep 2019 19:38:10 -0700 Subject: [PATCH 583/595] Only consider running jobs as active jobs --- src/utils/JobRestAPIUtils.py | 4 +++- src/utils/MySQLDataHandler.py | 2 +- src/utils/SQLDataHandler.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/utils/JobRestAPIUtils.py b/src/utils/JobRestAPIUtils.py index 8cf542694..133c563fc 100755 --- a/src/utils/JobRestAPIUtils.py +++ b/src/utils/JobRestAPIUtils.py @@ -509,8 +509,10 @@ def GetVC(userName, vcName): vcTotalRes = ResourceInfo(json.loads(vc["quota"])) vcConsumedRes = ResourceInfo() jobs = DataManager.GetAllPendingJobs(vcName) + num_active_jobs = 0 for job in jobs: if job["jobStatus"] == "running": + num_active_jobs += 1 username = job["userName"] jobParam = json.loads(base64.b64decode(job["jobParams"])) if "gpuType" in jobParam and not jobParam["preemptionAllowed"]: @@ -526,7 +528,7 @@ def GetVC(userName, vcName): vc["gpu_used"] = vcConsumedRes.ToSerializable() vc["gpu_unschedulable"] = vcReservedRes.ToSerializable() vc["gpu_avaliable"] = vcAvailableRes.ToSerializable() - vc["AvaliableJobNum"] = len(jobs) + vc["AvaliableJobNum"] = num_active_jobs vc["node_status"] = clusterStatus["node_status"] vc["user_status"] = [] for user_name, user_gpu in user_status.iteritems(): diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index b967636c1..824fb754e 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -934,7 +934,7 @@ def GetUsers(self): @record def GetActiveJobsCount(self): cursor = self.conn.cursor() - query = "SELECT count(ALL id) as c FROM `%s` where `jobStatus` <> 'error' and `jobStatus` <> 'failed' and `jobStatus` <> 'finished' and `jobStatus` <> 'killed' " % (self.jobtablename) + query = "SELECT count(ALL id) as c FROM `%s` where `jobStatus` = 'running'" % (self.jobtablename) cursor.execute(query) ret = 0 for c in cursor: diff --git a/src/utils/SQLDataHandler.py b/src/utils/SQLDataHandler.py index 1fc888f73..8e6d95938 100755 --- a/src/utils/SQLDataHandler.py +++ b/src/utils/SQLDataHandler.py @@ -925,7 +925,7 @@ def GetUsers(self): @record def GetActiveJobsCount(self): cursor = self.conn.cursor() - query = "SELECT count(ALL id) as c FROM [%s] where [jobStatus] <> 'error' and [jobStatus] <> 'failed' and [jobStatus] <> 'finished' and [jobStatus] <> 'killed' " % (self.jobtablename) + query = "SELECT count(ALL id) as c FROM [%s] where [jobStatus] = 'running'" % (self.jobtablename) cursor.execute(query) ret = 0 for c in cursor: From a8389c3e4587fbac9a85829e9af0371c442fd2f5 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Wed, 11 Sep 2019 03:28:50 +0000 Subject: [PATCH 584/595] change gpu statistic dashboard --- .../cluster-gpu-statistic-dashboard.json | 346 ++++++++++++++++-- 1 file changed, 321 insertions(+), 25 deletions(-) diff --git a/src/ClusterBootstrap/services/monitor/grafana-config/cluster-gpu-statistic-dashboard.json b/src/ClusterBootstrap/services/monitor/grafana-config/cluster-gpu-statistic-dashboard.json index 60dd045da..685277ee2 100644 --- a/src/ClusterBootstrap/services/monitor/grafana-config/cluster-gpu-statistic-dashboard.json +++ b/src/ClusterBootstrap/services/monitor/grafana-config/cluster-gpu-statistic-dashboard.json @@ -19,7 +19,6 @@ "hideControls": false, "id": null, "links": [], - "refresh": "30s", "rows": [ { "collapse": false, @@ -31,21 +30,21 @@ "dashLength": 10, "dashes": false, "datasource": null, - "fill": 1, + "fill": 0, "id": 1, "legend": { - "avg": true, - "current": true, - "max": true, - "min": true, + "avg": false, + "current": false, + "max": false, + "min": false, "show": true, "total": false, - "values": true + "values": false }, "lines": true, "linewidth": 1, "links": [], - "nullPointMode": "connected", + "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, @@ -57,18 +56,18 @@ "steppedLine": false, "targets": [ { - "expr": "(sum(k8s_node_gpu_total) - sum(k8s_node_gpu_available) - sum(k8s_node_gpu_reserved)) / sum(k8s_node_gpu_total) * 100", + "expr": "count (task_gpu_percent) by (username)", "format": "time_series", - "instant": false, + "hide": false, "intervalFactor": 2, - "legendFormat": "Allocation Rate", + "legendFormat": "{{ '{{' }} username {{ '}}' }}", "refId": "A" } ], "thresholds": [], "timeFrom": null, "timeShift": null, - "title": "Cluster wide GPU allocation rate", + "title": "Per user allocation", "tooltip": { "shared": true, "sort": 0, @@ -84,11 +83,11 @@ }, "yaxes": [ { - "format": "percent", + "format": "short", "label": null, "logBase": 1, "max": null, - "min": null, + "min": "0", "show": true }, { @@ -119,21 +118,130 @@ "dashLength": 10, "dashes": false, "datasource": null, - "fill": 1, + "fill": 0, "id": 2, "legend": { - "avg": true, - "current": true, - "max": true, - "min": true, + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "count (task_gpu_percent) by (vc_name)", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{ '{{' }} vc_name {{ '}}' }} allocation", + "refId": "A" + }, + { + "expr": "count(task_gpu_percent)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Cluster wide allocation", + "refId": "B" + }, + { + "expr": "k8s_vc_gpu_total", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ '{{' }} vc_name {{ '}}' }} quota", + "refId": "C" + }, + { + "expr": "sum(k8s_vc_gpu_total)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Cluster wide quota", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Per VC allocation & quota", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + }, + { + "collapse": false, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 0, + "id": 3, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, "show": true, "total": false, - "values": true + "values": false }, "lines": true, "linewidth": 1, "links": [], - "nullPointMode": "connected", + "nullPointMode": "null", "percentage": false, "pointradius": 5, "points": false, @@ -145,17 +253,111 @@ "steppedLine": false, "targets": [ { - "expr": "avg(task_gpu_percent)", + "expr": "count (task_gpu_percent) by (vc_name) / sum (k8s_vc_gpu_total) by (vc_name)", "format": "time_series", "intervalFactor": 2, - "legendFormat": "Avg Util", + "legendFormat": "{{ '{{' }} vc_name {{ '}}' }}", "refId": "A" + }, + { + "expr": "count (task_gpu_percent) / sum (k8s_vc_gpu_total)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Cluster wide", + "refId": "B" } ], "thresholds": [], "timeFrom": null, "timeShift": null, - "title": "Cluster wide avg util", + "title": "Per VC allocation rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": "1", + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + }, + { + "collapse": false, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 0, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg (task_gpu_percent) by (username)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ '{{' }} username {{ '}}' }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Per user GPU utils", "tooltip": { "shared": true, "sort": 0, @@ -174,8 +376,102 @@ "format": "percent", "label": null, "logBase": 1, + "max": "100", + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, "max": null, "min": null, + "show": false + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + }, + { + "collapse": false, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fill": 0, + "id": 5, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg (task_gpu_percent) by (vc_name)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ '{{' }} vc_name {{ '}}' }}", + "refId": "A" + }, + { + "expr": "avg (task_gpu_percent)", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "Cluster wide", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "Per VC GPU utils", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percent", + "label": null, + "logBase": 1, + "max": "100", + "min": "0", "show": true }, { @@ -204,7 +500,7 @@ "list": [] }, "time": { - "from": "now/w", + "from": "now-6h", "to": "now" }, "timepicker": { From a755be8488e689b9f477cb616e91586b34aae8a3 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Wed, 11 Sep 2019 15:28:03 +0800 Subject: [PATCH 585/595] Expose priority of more statuses of job . --- src/utils/MySQLDataHandler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index b967636c1..29dfb4c99 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -998,9 +998,10 @@ def DeleteTemplate(self, name, scope): logger.error('Exception: %s', str(e)) return False + @record def get_job_priority(self): cursor = self.conn.cursor() - query = "select jobId, priority from {} where jobId in (select jobId from {} where jobStatus in (\"queued\", \"scheduling\", \"running\"))".format(self.jobprioritytablename, self.jobtablename) + query = "select jobId, priority from {} where jobId in (select jobId from {} where jobStatus in (\"queued\", \"scheduling\", \"running\", \"unapproved\", \"pausing\", \"paused\"))".format(self.jobprioritytablename, self.jobtablename) cursor.execute(query) priority_dict = {} for job_id, priority in cursor: From 5262a6a8e3bece8a2c44469225ef6b4eaf198691 Mon Sep 17 00:00:00 2001 From: Zhe Xu Date: Wed, 11 Sep 2019 16:51:11 +0000 Subject: [PATCH 586/595] NFS auto deployment attempt --- src/ClusterBootstrap/az_tools.py | 41 +++++++++++++++---- src/ClusterBootstrap/deploy.py | 19 ++++++--- src/ClusterBootstrap/params.py | 7 ++++ .../template/nfs/nfs_config.sh.template | 23 +++++++++++ 4 files changed, 76 insertions(+), 14 deletions(-) create mode 100755 src/ClusterBootstrap/template/nfs/nfs_config.sh.template diff --git a/src/ClusterBootstrap/az_tools.py b/src/ClusterBootstrap/az_tools.py index b4de2da9c..b290d5e95 100755 --- a/src/ClusterBootstrap/az_tools.py +++ b/src/ClusterBootstrap/az_tools.py @@ -716,9 +716,13 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): else: vm_list = get_vm_list_by_enum() + vm_ips = get_vm_private_ip() + vm_ips = sorted(vm_ips, key = lambda x:x['name']) + sku_mapping_cnfn = "../utils/sku_mapping.yaml" - f = open(sku_mapping_cnfn) - sku_mapping = yaml.load(f) + with open(sku_mapping_cnfn) as f: + sku_mapping = yaml.load(f) + for vm in vm_list: vmname = vm["name"] if "-worker" in vmname: @@ -730,12 +734,17 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): cc["machines"][vmname] = { "role": "worker", "node-group": vm["vmSize"],"gpu-type":sku_mapping[vm["vmSize"]]["gpu-type"]} + nfs_nodes = [] for vm in vm_list: vmname = vm["name"] if "-nfs" in vmname: cc["machines"][vmname] = { "role": "nfs", "node-group": vm["vmSize"]} + + # Dilemma : Before the servers got created, you don't know there name, cannot specify which server does a mountpoint config group belongs to + nfs_ips = [rec['privateIP'][0] for rec in vm_ips if "-nfs" in rec['name']] + print(nfs_ips) if not bSQLOnly: # Require explicit authorization setting. # cc["WinbindServers"] = [] @@ -751,13 +760,17 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): if file_share_key is not None: cc["mountpoints"]["rootshare"]["accesskey"] = file_share_key else: - cc["mountpoints"]["rootshare"]["type"] = "nfs" - cc["mountpoints"]["rootshare"]["server"] = get_vm_ip(0, "sql") - cc["mountpoints"]["rootshare"]["filesharename"] = "/data/share" - cc["mountpoints"]["rootshare"][ - "curphysicalmountpoint"] = "/mntdlws/nfs" - cc["mountpoints"]["rootshare"]["mountpoints"] = "" - + nfs_svr_cnt = min(len(nfs_ips), len(config["cloud_config"]["nfs_svr_setup"])) + if len(nfs_ips) < nfs_svr_cnt: + print("Warning: More NFS config than #. of server, only first {} taken".format(nfs_svr_cnt)) + for cnfid, nfscnf in enumerate(config["cloud_config"]["nfs_svr_setup"][:nfs_svr_cnt]): + # if "server" not in nfscnf: + # nfscnf["server"] = nfs_ips[cnfid] + for mntname, mntcnf in nfscnf["mnt_point"].items(): + cc["mountpoints"][mntname] = mntcnf + cc["mountpoints"][mntname]["type"] = "nfs" + cc["mountpoints"][mntname]["server"] = nfs_ips[cnfid] + if output_file: print yaml.dump(cc, default_flow_style=False) with open(output_file_name, 'w') as outfile: @@ -789,6 +802,16 @@ def get_vm_list_by_grp(): return utils.json_loads_byteified(output) +def get_vm_private_ip(): + cmd = """ + az vm list-ip-addresses -g %s --output json --query '[].{name:virtualMachine.name, privateIP:virtualMachine.network.privateIpAddresses}' + + """ % (config["azure_cluster"]["resource_group_name"]) + if verbose: + print(cmd) + output = utils.exec_cmd_local(cmd) + return utils.json_loads_byteified(output) + # simply enumerate to get vm list def get_vm_list_by_enum(): vm_list = [] diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 6223b8d25..39a2123d8 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -794,6 +794,7 @@ def gen_configs(): config["ssh_cert"] = expand_path("./deploy/sshkey/id_rsa") config["etcd_user"] = config["admin_username"] + config["nfs_user"] = config["admin_username"] config["kubernetes_master_ssh_user"] = config["admin_username"] #config["api_servers"] = ",".join(["https://"+x for x in config["kubernetes_master_node"]]) @@ -825,6 +826,7 @@ def get_ssh_config(): if "ssh_cert" in config: config["ssh_cert"] = expand_path(config["ssh_cert"]) config["etcd_user"] = config["admin_username"] + config["nfs_user"] = config["admin_username"] config["kubernetes_master_ssh_user"] = config["admin_username"] add_ssh_key() @@ -1102,11 +1104,18 @@ def deploy_ETCD(): utils.SSH_exec_script( config["ssh_cert"], etcd_server_user, etcd_servers[0], "./deploy/etcd/init_network.sh") def create_nfs_server(): - etcd_servers = config["etcd_node"] - etcd_server_user = config["etcd_user"] - os.system( "mkdir -p ./deploy/scripts") - utils.render_template("./scripts/setup_nfs_server.sh","./deploy/scripts/setup_nfs_server.sh",config) - utils.SSH_exec_script( config["ssh_cert"], etcd_server_user, etcd_servers[0], "./deploy/scripts/setup_nfs_server.sh") + """ + we assume there's only 1 cluster. + """ + etcd_server_user = config["nfs_user"] + nfs_servers = config["nfs_node"] if int(config["azure_cluster"][config["cluster_name"]]["nfs_node_num"]) > 0 else config["etcd_node"] + for serverID, nfs_cnf in config["cloud_config"]["nfs_svr_setup"].items(): + nfs_cnf["cloud_config"] = {"vnet_range":config["cloud_config"]["vnet_range"], "samba_range": config["cloud_config"]["samba_range"]} + nfs_server = nfs_servers[serverID] + utils.render_template("./template/nfs/nfs_config.sh.template","./deploy/scripts/setup_nfs_server.sh",nfs_cnf) + os.system("cat ./deploy/scripts/setup_nfs_server.sh") + # utils.SSH_exec_script( config["ssh_cert"], etcd_server_user, nfs_server, "./deploy/scripts/setup_nfs_server.sh") + def create_ISO(): imagename = "./deploy/iso/dlworkspace-cluster-deploy-"+config["cluster_name"]+".iso" diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 2900f2937..d5cf8f193 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -625,6 +625,13 @@ "source_ips": "131.107.0.0/16 104.44.0.0/16", "port": "22", }, + "nfs_svr_setup": [ + # 0:{ + # "vg_disks": {"dlts-data-lvm": "/dev/sdc"}, + # "logical_vol": {"dlts-data-lvm-vol1":{"volgrp":"dlts-data-lvm", "percentage":100, "mnt":"/infradata"}}, + # "mnt_point": {"rootshare":{"curphysicalmountpoint":"/mntdlws/infranfs","filesharename":"/infradata/share","mountpoints":"''"}} + ], + "samba_range": "104.44.112.0/24", }, "vc_config":{ "VC-Default":["*"], diff --git a/src/ClusterBootstrap/template/nfs/nfs_config.sh.template b/src/ClusterBootstrap/template/nfs/nfs_config.sh.template new file mode 100755 index 000000000..9f8ef63fb --- /dev/null +++ b/src/ClusterBootstrap/template/nfs/nfs_config.sh.template @@ -0,0 +1,23 @@ +{% for volgrp, disks in cnf["vg_disks"].items() %} +sudo vgcreate {{ volgrp }} {{disks}} +{% endfor %} +{% for lv, lv_param in cnf["logical_vol"].items() %} +sudo lvcreate -l {{lv_param["percentage"]}}%FREE -n {{lv}} {{lv_param["volgrp"]}} +sudo mkfs.ext4 /dev/mapper/{{lv_param["volgrp"] | replace('-','--')}}-{{lv | replace('-','--')}} +sudo mkdir -p {{lv_param["mnt"]}} +sudo mount /dev/{{lv_param["volgrp"]}}/{{lv}} {{lv_param["mnt"]}} +echo "UUID=$(sudo blkid | grep {{lv | replace('-','--')}} | sed -n 's/.*UUID=\"\(.*\)\" TYPE.*/\1/p') /data ext4 defaults,discard 0 0" | sudo tee -a /etc/fstab +{% endfor %} +# setup NFS service +sudo apt-get update +sudo apt-get install -y nfs-kernel-server + +{% for mnt_name, mnt_setting in cnf["mnt_point"].items() %} +sudo mkdir -p {{mnt_setting["filesharename"]}} +sudo chown nobody:nogroup {{mnt_setting["filesharename"]}} +echo "{{mnt_setting["filesharename"]}} {{cnf["cloud_config"]["vnet_range"]}}(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports +echo "{{mnt_setting["filesharename"]}} {{cnf["cloud_config"]["samba_range"]}}(rw,fsid=1,nohide,insecure,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports +{% endfor %} + +sudo systemctl restart nfs-kernel-server.service +sudo exportfs -a \ No newline at end of file From b93fbd9c2cc5b3f4ab81413424b1adec147d74b6 Mon Sep 17 00:00:00 2001 From: Zhe Xu Date: Wed, 11 Sep 2019 21:08:57 +0000 Subject: [PATCH 587/595] bug fixing and corner cases, adding runscript on role --- src/ClusterBootstrap/az_tools.py | 25 ++++++++++------- src/ClusterBootstrap/deploy.py | 46 ++++++++++++++++++++++---------- src/ClusterBootstrap/params.py | 14 +++++----- 3 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/ClusterBootstrap/az_tools.py b/src/ClusterBootstrap/az_tools.py index b290d5e95..3f703b959 100755 --- a/src/ClusterBootstrap/az_tools.py +++ b/src/ClusterBootstrap/az_tools.py @@ -716,8 +716,8 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): else: vm_list = get_vm_list_by_enum() - vm_ips = get_vm_private_ip() - vm_ips = sorted(vm_ips, key = lambda x:x['name']) + vm_ip_names = get_vm_private_ip() + vm_ip_names = sorted(vm_ip_names, key = lambda x:x['name']) sku_mapping_cnfn = "../utils/sku_mapping.yaml" with open(sku_mapping_cnfn) as f: @@ -743,8 +743,8 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): "node-group": vm["vmSize"]} # Dilemma : Before the servers got created, you don't know there name, cannot specify which server does a mountpoint config group belongs to - nfs_ips = [rec['privateIP'][0] for rec in vm_ips if "-nfs" in rec['name']] - print(nfs_ips) + nfs_ip_names = [rec for rec in vm_ip_names if "-nfs" in rec['name']] + # print(nfs_ips) if not bSQLOnly: # Require explicit authorization setting. # cc["WinbindServers"] = [] @@ -760,16 +760,18 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): if file_share_key is not None: cc["mountpoints"]["rootshare"]["accesskey"] = file_share_key else: - nfs_svr_cnt = min(len(nfs_ips), len(config["cloud_config"]["nfs_svr_setup"])) - if len(nfs_ips) < nfs_svr_cnt: + nfs_svr_cnt = min(len(nfs_ip_names), len(config["cloud_config"]["nfs_svr_setup"])) + if len(nfs_ip_names) < nfs_svr_cnt: print("Warning: More NFS config than #. of server, only first {} taken".format(nfs_svr_cnt)) + print(config["cloud_config"]["nfs_svr_setup"]) for cnfid, nfscnf in enumerate(config["cloud_config"]["nfs_svr_setup"][:nfs_svr_cnt]): - # if "server" not in nfscnf: - # nfscnf["server"] = nfs_ips[cnfid] for mntname, mntcnf in nfscnf["mnt_point"].items(): + if mntname in cc["mountpoints"]: + print("Warning, duplicated mountpoints item name, skipping") cc["mountpoints"][mntname] = mntcnf cc["mountpoints"][mntname]["type"] = "nfs" - cc["mountpoints"][mntname]["server"] = nfs_ips[cnfid] + cc["mountpoints"][mntname]["server"] = nfs_ip_names[cnfid]['privateIP'][0] + cc["mountpoints"][mntname]["servername"] = nfs_ip_names[cnfid]['name'] if output_file: print yaml.dump(cc, default_flow_style=False) @@ -837,6 +839,7 @@ def delete_cluster(): def run_command(args, command, nargs, parser): if command == "create": + # print config["azure_cluster"]["infra_vm_size"] create_cluster(args.arm_password) vm_interconnects() @@ -1002,7 +1005,9 @@ def run_command(args, command, nargs, parser): config_file = os.path.join(dirpath, "config.yaml") if os.path.exists(config_file): - tmpconfig = yaml.load(open(config_file)) + with open(config_file) as cf: + tmpconfig = yaml.load(cf) + assert tmpconfig["cluster_name"] in tmpconfig["azure_cluster"] merge_config(config, tmpconfig, verbose) if tmpconfig is not None and "cluster_name" in tmpconfig: config["azure_cluster"]["cluster_name"] = tmpconfig["cluster_name"] diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 39a2123d8..4f3dbfc51 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -58,7 +58,7 @@ verbose = False nocache = False limitnodes = None - +allroles = {"infra", "infrastructure", "worker", "nfs", "sql"} # default search for all partitions of hdb, hdc, hdd, and sdb, sdc, sdd @@ -530,6 +530,7 @@ def get_domain(): # Get a list of nodes from cluster.yaml def get_nodes_from_config(machinerole): + machinerole = "infrastructure" if machinerole == "infra" else machinerole if "machines" not in config: return [] else: @@ -621,17 +622,19 @@ def get_worker_nodes_from_config(clusterId): config["worker_node"] = Nodes return Nodes -def get_nodes_by_role(role): +def get_nodes_by_roles(roles): """ role: "infrastructure", "worker", or "nfs" this function aims to deprecate get_worker_nodes_from_config and get_ETCD_master_nodes_from_config """ - Nodes = get_nodes_from_config(role) - if role == "infrastructure": - config["etcd_node"] = Nodes - config["kubernetes_master_node"] = Nodes - else: - config["{}_node".format(role)] = Nodes + Nodes = [] + for role in roles: + Nodes += get_nodes_from_config(role) + if role == "infrastructure" or role == "infra": + config["etcd_node"] = Nodes + config["kubernetes_master_node"] = Nodes + else: + config["{}_node".format(role)] = Nodes return Nodes def get_worker_nodes(clusterId, isScaledOnly): @@ -642,7 +645,7 @@ def get_worker_nodes(clusterId, isScaledOnly): nodes = get_worker_nodes_from_cluster_report(clusterId) else: print("from console") - nodes = get_nodes_by_role("worker") #get_worker_nodes_from_config(clusterId) + nodes = get_nodes_by_roles(["worker"]) #get_worker_nodes_from_config(clusterId) if isScaledOnly: return get_scaled_nodes_from_config() @@ -664,7 +667,7 @@ def limit_nodes(nodes): return nodes def get_nodes(clusterId): - nodes = get_ETCD_master_nodes(clusterId) + get_worker_nodes(clusterId, False) + get_nodes_by_role("nfs") + nodes = get_ETCD_master_nodes(clusterId) + get_worker_nodes(clusterId, False) + get_nodes_by_roles(["nfs"]) nodes = limit_nodes(nodes) return nodes @@ -680,7 +683,7 @@ def check_master_ETCD_status(): print "Checking Available Nodes for Deployment..." get_ETCD_master_nodes(config["clusterId"]) get_worker_nodes(config["clusterId"], False) - get_nodes_by_role("nfs") + get_nodes_by_roles(["nfs"]) print "===============================================" print "Activate Master Node(s): %s\n %s \n" % (len(config["kubernetes_master_node"]),",".join(config["kubernetes_master_node"])) print "Activate ETCD Node(s):%s\n %s \n" % (len(config["etcd_node"]),",".join(config["etcd_node"])) @@ -1109,12 +1112,13 @@ def create_nfs_server(): """ etcd_server_user = config["nfs_user"] nfs_servers = config["nfs_node"] if int(config["azure_cluster"][config["cluster_name"]]["nfs_node_num"]) > 0 else config["etcd_node"] - for serverID, nfs_cnf in config["cloud_config"]["nfs_svr_setup"].items(): + for serverID, nfs_cnf in enumerate(config["cloud_config"]["nfs_svr_setup"]): nfs_cnf["cloud_config"] = {"vnet_range":config["cloud_config"]["vnet_range"], "samba_range": config["cloud_config"]["samba_range"]} nfs_server = nfs_servers[serverID] utils.render_template("./template/nfs/nfs_config.sh.template","./deploy/scripts/setup_nfs_server.sh",nfs_cnf) - os.system("cat ./deploy/scripts/setup_nfs_server.sh") - # utils.SSH_exec_script( config["ssh_cert"], etcd_server_user, nfs_server, "./deploy/scripts/setup_nfs_server.sh") + # os.system("cat ./deploy/scripts/setup_nfs_server.sh") + # print(nfs_server) + utils.SSH_exec_script( config["ssh_cert"], etcd_server_user, nfs_server, "./deploy/scripts/setup_nfs_server.sh") def create_ISO(): @@ -3436,8 +3440,22 @@ def run_command( args, command, nargs, parser ): elif command == "runscriptonall" and len(nargs)>=1: nodes = get_nodes(config["clusterId"]) + # print(nodes) run_script_on_all(nodes, nargs, sudo = args.sudo ) + elif command == "runscriptonroles": + assert len(nargs)>=1 + nodeset, scripts_start = [], 0 + for ni, arg in enumerate(nargs): + scripts_start = ni + if arg in allroles: + nodeset += arg, + else: + break + nodes = get_nodes_by_roles(nodeset) + # print(nodes) + run_script_on_all(nodes, nargs[scripts_start:], sudo = args.sudo ) + elif command == "runscriptonrandmaster" and len(nargs)>=1: run_script_on_rand_master(nargs, args) diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index d5cf8f193..f69fd2bc5 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -619,17 +619,17 @@ # "source_addresses_prefixes": [ "52.151.0.0/16"] }, "nfs_share": { - "source_ips": "104.44.112.0/24 131.107.0.0/16", + "source_ips": ["104.44.112.0/24", "131.107.0.0/16"], }, "nfs_ssh": { - "source_ips": "131.107.0.0/16 104.44.0.0/16", + "source_ips": ["131.107.0.0/16", "104.44.0.0/16"], "port": "22", }, "nfs_svr_setup": [ - # 0:{ - # "vg_disks": {"dlts-data-lvm": "/dev/sdc"}, - # "logical_vol": {"dlts-data-lvm-vol1":{"volgrp":"dlts-data-lvm", "percentage":100, "mnt":"/infradata"}}, - # "mnt_point": {"rootshare":{"curphysicalmountpoint":"/mntdlws/infranfs","filesharename":"/infradata/share","mountpoints":"''"}} + # { + # "vg_disks": {"dlts-data-lvm": "/dev/sdc"}, + # "logical_vol": {"dlts-data-lvm-vol1":{"volgrp":"dlts-data-lvm", "percentage":100, "mnt":"/infradata"}}, + # "mnt_point": {"rootshare":{"curphysicalmountpoint":"/mntdlws/infranfs","filesharename":"/infradata/share","mountpoints":"''"}}} ], "samba_range": "104.44.112.0/24", }, @@ -641,7 +641,7 @@ # These are super scripts scriptblocks = { "azure": [ - "runscriptonall ./scripts/prepare_vm_disk.sh", + "runscriptonroles infra worker ./scripts/prepare_vm_disk.sh", "nfs-server create", "runscriptonall ./scripts/prepare_ubuntu.sh", "genscripts", From 8f099c285adc5e5c1d199dffac70d511cdedbce3 Mon Sep 17 00:00:00 2001 From: Zhe Xu Date: Wed, 11 Sep 2019 22:53:58 +0000 Subject: [PATCH 588/595] bug fix --- src/ClusterBootstrap/deploy.py | 2 +- src/ClusterBootstrap/params.py | 4 ++-- src/ClusterBootstrap/template/dns/dns.sh.template | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 4f3dbfc51..5e7117a3a 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -2967,7 +2967,7 @@ def gen_dns_config_script(): utils.render_template("./template/dns/dns.sh.template", "deploy/kubeconfig/kubeconfig.yaml", config) def gen_pass_secret_script(): - utils.render_template("./template/dns/pass.sh.template", "scripts/pass.sh", config) + utils.render_template("./template/secret/pass_secret.sh.template", "scripts/pass.sh", config) def run_command( args, command, nargs, parser ): # If necessary, show parsed arguments. diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index f69fd2bc5..80b3859b7 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -643,9 +643,9 @@ "azure": [ "runscriptonroles infra worker ./scripts/prepare_vm_disk.sh", "nfs-server create", - "runscriptonall ./scripts/prepare_ubuntu.sh", + "runscriptonroles infra worker ./scripts/prepare_ubuntu.sh", "genscripts", - "runscriptonall ./scripts/dns.sh", + "runscriptonroles infra worker ./scripts/dns.sh", "-y deploy", "-y updateworker", "-y kubernetes labels", diff --git a/src/ClusterBootstrap/template/dns/dns.sh.template b/src/ClusterBootstrap/template/dns/dns.sh.template index 8bd5bcc82..36c384c12 100755 --- a/src/ClusterBootstrap/template/dns/dns.sh.template +++ b/src/ClusterBootstrap/template/dns/dns.sh.template @@ -3,5 +3,5 @@ sudo systemctl stop systemd-resolved echo "dns=default" | sudo tee -a /etc/NetworkManager/NetworkManager.conf sudo rm /etc/resolv.conf echo "nameserver 8.8.8.8" | sudo tee -a /etc/resolv.conf -echo 'search {{cnf["azure_cluster"][cnf["cluster_name"]]["azure_location"].cloudapp.azure.com}}' | sudo tee -a /etc/resolv.conf +echo 'search {{cnf["azure_cluster"][cnf["cluster_name"]]["azure_location"]}}.cloudapp.azure.com' | sudo tee -a /etc/resolv.conf sudo service network-manager restart \ No newline at end of file From a6a2645d83b2f8a4a977799e1ccb201708cb533d Mon Sep 17 00:00:00 2001 From: Zhe Xu Date: Fri, 13 Sep 2019 18:03:40 +0000 Subject: [PATCH 589/595] default nfs rule, bug fix --- src/ClusterBootstrap/az_tools.py | 140 ++++++++++++------ src/ClusterBootstrap/deploy.py | 52 ++++--- .../template/nfs/nfs_config.sh.template | 10 +- 3 files changed, 134 insertions(+), 68 deletions(-) diff --git a/src/ClusterBootstrap/az_tools.py b/src/ClusterBootstrap/az_tools.py index 3f703b959..d3dd7846f 100755 --- a/src/ClusterBootstrap/az_tools.py +++ b/src/ClusterBootstrap/az_tools.py @@ -34,6 +34,7 @@ from params import * verbose = False +no_execution = False # These are the default configuration parameter @@ -145,8 +146,9 @@ def create_vm_pwd(vmname, vm_ip, vm_size, use_private_ip, pwd): if verbose: print(cmd) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) def create_vm(vmname, vm_ip, role, vm_size): specify_priv_IP = role in ["worker","nfs"] @@ -208,8 +210,9 @@ def create_vm(vmname, vm_ip, role, vm_size): # --public-ip-address-allocation static \ if verbose: print(cmd) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) def create_group(): @@ -218,8 +221,9 @@ def create_group(): """ % (config["azure_cluster"]["resource_group_name"], config["azure_cluster"]["azure_location"]) if verbose: print(cmd) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) def create_sql(): @@ -236,8 +240,9 @@ def create_sql(): config["azure_cluster"]["sql_admin_password"]) if verbose: print(cmd) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) cmd = """ az sql server firewall-rule create --resource-group %s \ @@ -249,8 +254,9 @@ def create_sql(): config["azure_cluster"]["sql_server_name"]) if verbose: print(cmd) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) def create_storage_account(): @@ -266,8 +272,9 @@ def create_storage_account(): config["azure_cluster"]["azure_location"]) if verbose: print(cmd) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) def create_file_share(): @@ -278,8 +285,9 @@ def create_file_share(): --query 'connectionString' \ -o tsv """ % (config["azure_cluster"]["storage_account_name"], config["azure_cluster"]["resource_group_name"]) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) cmd = """ az storage share create \ @@ -289,8 +297,9 @@ def create_file_share(): """ % (config["azure_cluster"]["file_share_name"], output) if verbose: print(cmd) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) def create_vnet(): @@ -307,8 +316,9 @@ def create_vnet(): config["cloud_config"]["vnet_range"]) if verbose: print(cmd) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) def create_nsg(): @@ -328,8 +338,9 @@ def create_nsg(): config["azure_cluster"]["nsg_name"]) if verbose: print(cmd) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) if "tcp_port_ranges" in config["cloud_config"]: cmd = """ @@ -345,8 +356,9 @@ def create_nsg(): config["azure_cluster"]["nsg_name"], config["cloud_config"]["tcp_port_ranges"] ) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) if "udp_port_ranges" in config["cloud_config"]: cmd = """ @@ -362,8 +374,9 @@ def create_nsg(): config["azure_cluster"]["nsg_name"], config["cloud_config"]["udp_port_ranges"] ) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) cmd = """ az network nsg rule create \ @@ -380,8 +393,9 @@ def create_nsg(): config["cloud_config"]["dev_network"]["tcp_port_ranges"], source_addresses_prefixes ) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) def create_nfs_nsg(): cmd = """ @@ -392,8 +406,9 @@ def create_nfs_nsg(): config["azure_cluster"]["nfs_nsg_name"]) if verbose: print(cmd) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) cmd = """ az network nsg rule create \ @@ -409,8 +424,9 @@ def create_nfs_nsg(): config["cloud_config"]["nfs_ssh"]["port"], " ".join(config["cloud_config"]["nfs_ssh"]["source_ips"]), ) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) cmd = """ az network nsg rule create \ @@ -425,8 +441,9 @@ def create_nfs_nsg(): config["azure_cluster"]["nfs_nsg_name"], " ".join(config["cloud_config"]["nfs_share"]["source_ips"]), ) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) def delete_group(): @@ -435,8 +452,9 @@ def delete_group(): """ % (config["azure_cluster"]["resource_group_name"]) if verbose: print(cmd) - output = utils.exec_cmd_local(cmd) - print(output) + if not no_execution: + output = utils.exec_cmd_local(cmd) + print(output) def get_vm_ip(i, role): @@ -454,6 +472,7 @@ def get_vm_ip(i, role): def create_cluster(arm_vm_password=None): bSQLOnly = (config["azure_cluster"]["infra_node_num"] <= 0) + assert int(config["azure_cluster"]["nfs_node_num"]) >= len(config["azure_cluster"]["nfs_suffix"]) print "creating resource group..." create_group() if not bSQLOnly: @@ -484,7 +503,11 @@ def create_cluster(arm_vm_password=None): arm_vm_password is not None, arm_vm_password) # create nfs server if specified. for i in range(int(config["azure_cluster"]["nfs_node_num"])): - create_vm_param(i, "nfs", config["azure_cluster"]["nfs_vm_size"], + if i < len(config["azure_cluster"]["nfs_suffix"]): + create_vm_role_suffix(i, "nfs", config["azure_cluster"]["nfs_vm_size"], + config["azure_cluster"]["nfs_suffix"][i], arm_vm_password) + else: + create_vm_param(i, "nfs", config["azure_cluster"]["nfs_vm_size"], arm_vm_password is not None, arm_vm_password) def create_vm_param(i, role, vm_size, no_az=False, arm_vm_password=None): @@ -503,6 +526,15 @@ def create_vm_param(i, role, vm_size, no_az=False, arm_vm_password=None): create_vm(vmname, vm_ip, role, vm_size) return vmname +def create_vm_role_suffix(i, role, vm_size, suffix, arm_vm_password=None): + vmname = "{}-{}-".format(config["azure_cluster"]["cluster_name"], role) + suffix + print "creating VM %s..." % vmname + vm_ip = get_vm_ip(i, role) + if arm_vm_password is not None: + create_vm_pwd(vmname, vm_ip, vm_size, not role in ["worker","nfs"], arm_vm_password) + else: + create_vm(vmname, vm_ip, role, vm_size) + return vmname def useSqlAzure(): if "datasource" in config["azure_cluster"]: @@ -743,13 +775,15 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): "node-group": vm["vmSize"]} # Dilemma : Before the servers got created, you don't know there name, cannot specify which server does a mountpoint config group belongs to - nfs_ip_names = [rec for rec in vm_ip_names if "-nfs" in rec['name']] - # print(nfs_ips) + if int(config["azure_cluster"]["nfs_node_num"]) > 0: + nfs_names2ip = {rec['name']:rec['privateIP'][0] for rec in vm_ip_names if "-nfs" in rec['name']} + else: + nfs_names2ip = {rec['name']:rec['privateIP'][0] for rec in vm_ip_names if "infra" in rec['name']} if not bSQLOnly: # Require explicit authorization setting. # cc["WinbindServers"] = [] # cc["WebUIauthorizedGroups"] = ['MicrosoftUsers'] - cc["mountpoints"] = {"rootshare": {}} + cc["mountpoints"] = {} if useAzureFileshare(): cc["mountpoints"]["rootshare"]["type"] = "azurefileshare" cc["mountpoints"]["rootshare"]["accountname"] = config[ @@ -760,19 +794,29 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): if file_share_key is not None: cc["mountpoints"]["rootshare"]["accesskey"] = file_share_key else: - nfs_svr_cnt = min(len(nfs_ip_names), len(config["cloud_config"]["nfs_svr_setup"])) - if len(nfs_ip_names) < nfs_svr_cnt: - print("Warning: More NFS config than #. of server, only first {} taken".format(nfs_svr_cnt)) - print(config["cloud_config"]["nfs_svr_setup"]) - for cnfid, nfscnf in enumerate(config["cloud_config"]["nfs_svr_setup"][:nfs_svr_cnt]): - for mntname, mntcnf in nfscnf["mnt_point"].items(): + named_nfs_suffix = set(config["azure_cluster"]["nfs_suffixes"] if "nfs_suffixes" in config["azure_cluster"] else []) + used_nfs_suffix = set([nfs_cnf["server_suffix"] for nfs_cnf in config["cloud_config"]["nfs_svr_setup"] if "server_suffix" in nfs_cnf]) + assert (used_nfs_suffix - named_nfs_suffix) == set() and "suffix not in nfs_suffixes list!" + assert len(nfs_names2ip) >= len(config["cloud_config"]["nfs_svr_setup"]) and "More NFS config items than #. of NFS server" + suffix2used_nfs = {suffix: "{}-nfs-{}".format(config["cluster_name"], suffix) for suffix in used_nfs_suffix} + # unused, either node without name suffix or those with suffix but not specified in any nfs_svr_setup item + unused_nfs = sorted([s for s in nfs_names2ip.keys() if s not in suffix2used_nfs.values()]) + unused_ID_cnt = 0 + for nfs_cnf in config["cloud_config"]["nfs_svr_setup"]: + if "server_suffix" in nfs_cnf: + server_name = suffix2used_nfs[nfs_cnf["server_suffix"]] + else: + server_name = unused_nfs[unused_ID_cnt] + unused_ID_cnt += 1 + server_ip = nfs_names2ip[server_name] + for mntname, mntcnf in nfs_cnf["mnt_point"].items(): if mntname in cc["mountpoints"]: - print("Warning, duplicated mountpoints item name, skipping") + print("Warning, duplicated mountpoints item name {}, skipping".format(mntname)) + continue cc["mountpoints"][mntname] = mntcnf cc["mountpoints"][mntname]["type"] = "nfs" - cc["mountpoints"][mntname]["server"] = nfs_ip_names[cnfid]['privateIP'][0] - cc["mountpoints"][mntname]["servername"] = nfs_ip_names[cnfid]['name'] - + cc["mountpoints"][mntname]["server"] = server_ip + cc["mountpoints"][mntname]["servername"] = server_name if output_file: print yaml.dump(cc, default_flow_style=False) with open(output_file_name, 'w') as outfile: diff --git a/src/ClusterBootstrap/deploy.py b/src/ClusterBootstrap/deploy.py index 5e7117a3a..a7747e89b 100755 --- a/src/ClusterBootstrap/deploy.py +++ b/src/ClusterBootstrap/deploy.py @@ -319,8 +319,8 @@ def add_acs_config(command): config["WinbindServers"] = [] config["etcd_node_num"] = config["master_node_num"] config["kube_addons"] = [] # no addons - config["mountpoints"]["rootshare"]["azstoragesku"] = config["azstoragesku"] - config["mountpoints"]["rootshare"]["azfilesharequota"] = config["azfilesharequota"] + # config["mountpoints"]["rootshare"]["azstoragesku"] = config["azstoragesku"] + # config["mountpoints"]["rootshare"]["azfilesharequota"] = config["azfilesharequota"] config["freeflow"] = True config["useclusterfile"] = True @@ -517,7 +517,7 @@ def is_cur_on_same_domain(): pass return False -# Get domain of the node +# Get domain of the node, assigned in add_acs_config (line config["network"]["domain"]) def get_domain(): if "network" in config and "domain" in config["network"] and len(config["network"]["domain"]) > 0 : if is_cur_on_same_domain(): @@ -535,7 +535,6 @@ def get_nodes_from_config(machinerole): return [] else: domain = get_domain() - # print ("Doamin = %s " % domain ) Nodes = [] for nodename in config["machines"]: nodeInfo = config["machines"][nodename] @@ -546,6 +545,9 @@ def get_nodes_from_config(machinerole): Nodes.append(nodename) return sorted(Nodes) +def get_node_full_name(nodename): + return nodename + get_domain() if len(nodename.split("."))<3 else nodename + # Get a list of scaled nodes from cluster.yaml def get_scaled_nodes_from_config(): if "machines" not in config: @@ -667,7 +669,7 @@ def limit_nodes(nodes): return nodes def get_nodes(clusterId): - nodes = get_ETCD_master_nodes(clusterId) + get_worker_nodes(clusterId, False) + get_nodes_by_roles(["nfs"]) + nodes = get_ETCD_master_nodes(clusterId) + get_worker_nodes(clusterId, False) nodes = limit_nodes(nodes) return nodes @@ -1111,13 +1113,29 @@ def create_nfs_server(): we assume there's only 1 cluster. """ etcd_server_user = config["nfs_user"] - nfs_servers = config["nfs_node"] if int(config["azure_cluster"][config["cluster_name"]]["nfs_node_num"]) > 0 else config["etcd_node"] - for serverID, nfs_cnf in enumerate(config["cloud_config"]["nfs_svr_setup"]): + cluster_by_name = config["azure_cluster"][config["cluster_name"]] + nfs_servers = config["nfs_node"] if int(cluster_by_name["nfs_node_num"]) > 0 else config["etcd_node"] + # if we have suffixed server, then it must be external + named_nfs_suffix = set(cluster_by_name["nfs_suffixes"] if "nfs_suffixes" in cluster_by_name else []) + used_nfs_suffix = set([nfs_cnf["server_suffix"] for nfs_cnf in config["cloud_config"]["nfs_svr_setup"] if "server_suffix" in nfs_cnf]) + assert (used_nfs_suffix - named_nfs_suffix) == set() and "suffix not in nfs_suffixes list!" + suffix2used_nfs = {suffix: get_node_full_name("{}-nfs-{}".format(config["cluster_name"], suffix)) for suffix in used_nfs_suffix} + # unused, either node without name suffix or those with suffix but not specified in any nfs_svr_setup item + unused_nfs = sorted([s for s in nfs_servers if s not in suffix2used_nfs.values()]) + unused_ID_cnt = 0 + # print(nfs_servers, suffix2used_nfs, unused_nfs) + + + for nfs_cnf in config["cloud_config"]["nfs_svr_setup"]: nfs_cnf["cloud_config"] = {"vnet_range":config["cloud_config"]["vnet_range"], "samba_range": config["cloud_config"]["samba_range"]} - nfs_server = nfs_servers[serverID] + if "server_suffix" in nfs_cnf: + nfs_server = suffix2used_nfs[nfs_cnf["server_suffix"]] + else: + nfs_server = unused_nfs[unused_ID_cnt] + unused_ID_cnt += 1 utils.render_template("./template/nfs/nfs_config.sh.template","./deploy/scripts/setup_nfs_server.sh",nfs_cnf) # os.system("cat ./deploy/scripts/setup_nfs_server.sh") - # print(nfs_server) + # print("------------------>nfs_server<------------------------"+nfs_server) utils.SSH_exec_script( config["ssh_cert"], etcd_server_user, nfs_server, "./deploy/scripts/setup_nfs_server.sh") @@ -1574,7 +1592,9 @@ def get_mount_fileshares(curNode = None): physicalmountpoint = config["physical-mount-path"] storagemountpoint = config["storage-mount-path"] mountshares = {} + # print(config["mountpoints"]) for k,v in config["mountpoints"].iteritems(): + # print("<<<<<<<<<<<<<<<<<< Date: Sat, 14 Sep 2019 01:08:32 +0000 Subject: [PATCH 590/595] NFS automation test, merged sku_mapping config info --- src/ClusterBootstrap/az_tools.py | 10 ++++----- src/ClusterBootstrap/params.py | 21 +++++++++++++++++++ .../template/RestfulAPI/config.yaml | 1 + src/utils/MySQLDataHandler.py | 4 +--- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/ClusterBootstrap/az_tools.py b/src/ClusterBootstrap/az_tools.py index d3dd7846f..fa4ad631c 100755 --- a/src/ClusterBootstrap/az_tools.py +++ b/src/ClusterBootstrap/az_tools.py @@ -472,7 +472,7 @@ def get_vm_ip(i, role): def create_cluster(arm_vm_password=None): bSQLOnly = (config["azure_cluster"]["infra_node_num"] <= 0) - assert int(config["azure_cluster"]["nfs_node_num"]) >= len(config["azure_cluster"]["nfs_suffix"]) + assert int(config["azure_cluster"]["nfs_node_num"]) >= len(config["azure_cluster"]["nfs_suffixes"]) print "creating resource group..." create_group() if not bSQLOnly: @@ -503,9 +503,9 @@ def create_cluster(arm_vm_password=None): arm_vm_password is not None, arm_vm_password) # create nfs server if specified. for i in range(int(config["azure_cluster"]["nfs_node_num"])): - if i < len(config["azure_cluster"]["nfs_suffix"]): + if i < len(config["azure_cluster"]["nfs_suffixes"]): create_vm_role_suffix(i, "nfs", config["azure_cluster"]["nfs_vm_size"], - config["azure_cluster"]["nfs_suffix"][i], arm_vm_password) + config["azure_cluster"]["nfs_suffixes"][i], arm_vm_password) else: create_vm_param(i, "nfs", config["azure_cluster"]["nfs_vm_size"], arm_vm_password is not None, arm_vm_password) @@ -751,9 +751,7 @@ def gen_cluster_config(output_file_name, output_file=True, no_az=False): vm_ip_names = get_vm_private_ip() vm_ip_names = sorted(vm_ip_names, key = lambda x:x['name']) - sku_mapping_cnfn = "../utils/sku_mapping.yaml" - with open(sku_mapping_cnfn) as f: - sku_mapping = yaml.load(f) + sku_mapping = config["sku_mapping"] for vm in vm_list: vmname = vm["name"] diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 80b3859b7..0a40d8a83 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -635,6 +635,27 @@ }, "vc_config":{ "VC-Default":["*"], + }, + "sku_mapping": { + "Standard_ND6s":{"gpu-type": "P40","gpu-count": 1}, + "Standard_NV24": {"gpu-type": "M60", "gpu-count": 4}, + "Standard_ND12s": {"gpu-type": "P40", "gpu-count": 2}, + "Standard_ND24rs": {"gpu-type": "P40", "gpu-count": 4}, + "Standard_NV12": {"gpu-type": "M60", "gpu-count": 2}, + "Standard_NV48s_v3": {"gpu-type": "M60", "gpu-count": 4}, + "Standard_ND40s_v2": {"gpu-type": "V100", "gpu-count": 8}, + "Standard_NC6s_v3": {"gpu-type": "V100", "gpu-count": 1}, + "Standard_NC6s_v2": {"gpu-type": "P100", "gpu-count": 1}, + "Standard_ND24s": {"gpu-type": "P40", "gpu-count": 4}, + "Standard_NV24s_v3": {"gpu-type": "M60", "gpu-count": 2}, + "Standard_NV6": {"gpu-type": "M60", "gpu-count": 1}, + "Standard_NV12s_v3": {"gpu-type": "M60", "gpu-count": 1}, + "Standard_NC24s_v2": {"gpu-type": "P100", "gpu-count": 4}, + "Standard_NC24s_v3": {"gpu-type": "V100", "gpu-count": 4}, + "Standard_NC12s_v3": {"gpu-type": "V100", "gpu-count": 2}, + "Standard_NC12s_v2": {"gpu-type": "P100", "gpu-count": 2}, + "Standard_NC24rs_v3": {"gpu-type": "V100", "gpu-count": 4}, + "Standard_NC24rs_v2": {"gpu-type": "P100", "gpu-count": 4}, } } diff --git a/src/ClusterBootstrap/template/RestfulAPI/config.yaml b/src/ClusterBootstrap/template/RestfulAPI/config.yaml index 7adb75e07..f75ef7021 100755 --- a/src/ClusterBootstrap/template/RestfulAPI/config.yaml +++ b/src/ClusterBootstrap/template/RestfulAPI/config.yaml @@ -34,6 +34,7 @@ WinbindServers: {{cnf["WinbindServers"]}} azure_cluster : worker_node_num : {{cnf["azure_cluster"][cnf["cluster_name"]]["worker_node_num"]}} worker_vm_size : {{cnf["azure_cluster"][cnf["cluster_name"]]["worker_vm_size"]}} +sku_mapping: {{cnf["sku_mapping"]}} defalt_virtual_cluster_name: {{cnf["defalt_virtual_cluster_name"]}} {% if cnf["job-manager"] %} job-manager: diff --git a/src/utils/MySQLDataHandler.py b/src/utils/MySQLDataHandler.py index cb9990057..07dc40024 100755 --- a/src/utils/MySQLDataHandler.py +++ b/src/utils/MySQLDataHandler.py @@ -186,9 +186,7 @@ def CreateTable(self): # impossible since there's no way to do it with current config mechanism worker_cnt = int(config["azure_cluster"]["worker_node_num"]) - sku_mapping_cnfn = "./sku_mapping.yaml" - f = open(sku_mapping_cnfn) - sku_mapping = yaml.load(f) + sku_mapping = config["sku_mapping"] n_gpu_pernode = sku_mapping[config["azure_cluster"]["worker_vm_size"]]["gpu-count"] gpu_type = sku_mapping[config["azure_cluster"]["worker_vm_size"]]["gpu-type"] sql = """ From 3c8f17e64c510e8319f69da2e63b9f6020e15135 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 15 Sep 2019 05:56:00 +0000 Subject: [PATCH 591/595] basic NFS automation version ready --- src/ClusterBootstrap/az_tools.py | 2 +- src/ClusterBootstrap/params.py | 7 +++---- src/ClusterBootstrap/template/nfs/nfs_config.sh.template | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ClusterBootstrap/az_tools.py b/src/ClusterBootstrap/az_tools.py index fa4ad631c..40cada920 100755 --- a/src/ClusterBootstrap/az_tools.py +++ b/src/ClusterBootstrap/az_tools.py @@ -472,7 +472,7 @@ def get_vm_ip(i, role): def create_cluster(arm_vm_password=None): bSQLOnly = (config["azure_cluster"]["infra_node_num"] <= 0) - assert int(config["azure_cluster"]["nfs_node_num"]) >= len(config["azure_cluster"]["nfs_suffixes"]) + assert int(config["azure_cluster"]["nfs_node_num"]) >= len(config["cloud_config"]["nfs_suffixes"]) print "creating resource group..." create_group() if not bSQLOnly: diff --git a/src/ClusterBootstrap/params.py b/src/ClusterBootstrap/params.py index 0a40d8a83..0979bde91 100755 --- a/src/ClusterBootstrap/params.py +++ b/src/ClusterBootstrap/params.py @@ -625,11 +625,10 @@ "source_ips": ["131.107.0.0/16", "104.44.0.0/16"], "port": "22", }, + "nfs_suffixes":[], "nfs_svr_setup": [ - # { - # "vg_disks": {"dlts-data-lvm": "/dev/sdc"}, - # "logical_vol": {"dlts-data-lvm-vol1":{"volgrp":"dlts-data-lvm", "percentage":100, "mnt":"/infradata"}}, - # "mnt_point": {"rootshare":{"curphysicalmountpoint":"/mntdlws/infranfs","filesharename":"/infradata/share","mountpoints":"''"}}} + { + "mnt_point": {"rootshare":{"curphysicalmountpoint":"/mntdlws/infranfs","filesharename":"/infradata/share","mountpoints":""}}} ], "samba_range": "104.44.112.0/24", }, diff --git a/src/ClusterBootstrap/template/nfs/nfs_config.sh.template b/src/ClusterBootstrap/template/nfs/nfs_config.sh.template index f9c357b56..0568baf20 100755 --- a/src/ClusterBootstrap/template/nfs/nfs_config.sh.template +++ b/src/ClusterBootstrap/template/nfs/nfs_config.sh.template @@ -10,7 +10,7 @@ sudo lvcreate -l {{lv_param["percentage"]}}%FREE -n {{lv}} {{lv_param["volgrp"]} sudo mkfs.ext4 /dev/mapper/{{lv_param["volgrp"] | replace('-','--')}}-{{lv | replace('-','--')}} sudo mkdir -p {{lv_param["mnt"]}} sudo mount /dev/{{lv_param["volgrp"]}}/{{lv}} {{lv_param["mnt"]}} -echo "UUID=$(sudo blkid | grep {{lv | replace('-','--')}} | sed -n 's/.*UUID=\"\(.*\)\" TYPE.*/\1/p') /data ext4 defaults,discard 0 0" | sudo tee -a /etc/fstab +echo "UUID=$(sudo blkid | grep {{lv | replace('-','--')}} | sed -n 's/.*UUID=\"\(.*\)\" TYPE.*/\1/p') {{lv_param["mnt"]}} ext4 defaults,discard 0 0" | sudo tee -a /etc/fstab {% endfor %} {% endif %} @@ -26,4 +26,4 @@ echo "{{mnt_setting["filesharename"]}} {{cnf["cloud_config"]["samba_range"]}}(rw {% endfor %} sudo systemctl restart nfs-kernel-server.service -sudo exportfs -a \ No newline at end of file +sudo exportfs -a From 70a3c61cf5a17711d394411e59f8a0353ebb2332 Mon Sep 17 00:00:00 2001 From: Hao Yuan Date: Mon, 16 Sep 2019 05:59:41 +0000 Subject: [PATCH 592/595] fix sshd server port config for some case --- src/ClusterManager/endpoint_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ClusterManager/endpoint_manager.py b/src/ClusterManager/endpoint_manager.py index 87c38ee3c..d6912ed35 100755 --- a/src/ClusterManager/endpoint_manager.py +++ b/src/ClusterManager/endpoint_manager.py @@ -58,7 +58,7 @@ def start_ssh_server(pod_name, user_name, host_network=False, ssh_port=22): ssh_port = random.randint(40000, 49999) # bash_script = "sed -i '/^Port 22/c Port "+str(ssh_port)+"' /etc/ssh/sshd_config && "+bash_script # TODO refine the script later - bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port 22/Port " + str(ssh_port) + "/\" /etc/ssh/sshd_config && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh/*; chmod 700 .ssh; true) && service ssh restart'" + bash_script = "sudo bash -c 'apt-get update && apt-get install -y openssh-server && sed -i \"s/^Port/#&/\" /etc/ssh/sshd_config && echo \"Port " + str(ssh_port) + "\" >> /etc/ssh/sshd_config && cd /home/" + user_name + " && (chown " + user_name + " -R .ssh; chmod 600 -R .ssh/*; chmod 700 .ssh; true) && service ssh restart'" # TODO setup reasonable timeout # output = k8sUtils.kubectl_exec("exec %s %s" % (jobId, " -- " + bash_script), 1) From 8eb6d43d98729407e853e2c9ff683b27cc348ce4 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 17 Sep 2019 00:25:13 +0000 Subject: [PATCH 593/595] autoshare naming bug fix, nfs nsg alto allow devbox --- src/ClusterBootstrap/az_tools.py | 9 ++- .../bash_step_by_step_deploy.sh | 26 +++++++++ .../template/storage/auto_share/auto_share.py | 5 +- src/utils/sku_mapping.yaml | 57 ------------------- 4 files changed, 38 insertions(+), 59 deletions(-) create mode 100755 src/ClusterBootstrap/bash_step_by_step_deploy.sh delete mode 100644 src/utils/sku_mapping.yaml diff --git a/src/ClusterBootstrap/az_tools.py b/src/ClusterBootstrap/az_tools.py index 40cada920..3f92e2a71 100755 --- a/src/ClusterBootstrap/az_tools.py +++ b/src/ClusterBootstrap/az_tools.py @@ -398,6 +398,12 @@ def create_nsg(): print(output) def create_nfs_nsg(): + if "source_addresses_prefixes" in config["cloud_config"]["dev_network"]: + source_addresses_prefixes = config["cloud_config"][ + "dev_network"]["source_addresses_prefixes"] + else: + print "Please setup source_addresses_prefixes in config.yaml, otherwise, your cluster cannot be accessed" + exit() cmd = """ az network nsg create \ --resource-group %s \ @@ -410,6 +416,7 @@ def create_nfs_nsg(): output = utils.exec_cmd_local(cmd) print(output) + print type(config["cloud_config"]["nfs_ssh"]["source_ips"]), config["cloud_config"]["nfs_ssh"]["source_ips"],type(source_addresses_prefixes), source_addresses_prefixes cmd = """ az network nsg rule create \ --resource-group %s \ @@ -422,7 +429,7 @@ def create_nfs_nsg(): """ % ( config["azure_cluster"]["resource_group_name"], config["azure_cluster"]["nfs_nsg_name"], config["cloud_config"]["nfs_ssh"]["port"], - " ".join(config["cloud_config"]["nfs_ssh"]["source_ips"]), + " ".join(config["cloud_config"]["nfs_ssh"]["source_ips"] + source_addresses_prefixes), ) if not no_execution: output = utils.exec_cmd_local(cmd) diff --git a/src/ClusterBootstrap/bash_step_by_step_deploy.sh b/src/ClusterBootstrap/bash_step_by_step_deploy.sh new file mode 100755 index 000000000..2d43f2754 --- /dev/null +++ b/src/ClusterBootstrap/bash_step_by_step_deploy.sh @@ -0,0 +1,26 @@ +./deploy.py -y build +./az_tools.py create +./az_tools.py genconfig +./deploy.py runscriptonroles infra worker ./scripts/prepare_vm_disk.sh +./deploy.py nfs-server create +./deploy.py runscriptonroles infra worker ./scripts/prepare_ubuntu.sh +./deploy.py genscripts +./deploy.py runscriptonroles infra worker ./scripts/dns.sh +./deploy.py -y deploy +./deploy.py -y updateworker +./deploy.py -y kubernetes labels +./deploy.py -y gpulabel +./deploy.py kubernetes start nvidia-device-plugin +./deploy.py webui +./deploy.py docker push restfulapi +./deploy.py docker push webui +./deploy.py mount +./deploy.py kubernetes start mysql +./deploy.py kubernetes start jobmanager +./deploy.py kubernetes start restfulapi +./deploy.py kubernetes start webportal +./deploy.py kubernetes start cloudmonitor +./deploy.py kubernetes start custommetrics +./deploy.py -y kubernetes patchprovider aztools +./deploy.py setconfigmap +./deploy.py --sudo runscriptonrandmaster ./scripts/pass_secret.sh diff --git a/src/ClusterBootstrap/template/storage/auto_share/auto_share.py b/src/ClusterBootstrap/template/storage/auto_share/auto_share.py index 47f596e88..bf757a855 100755 --- a/src/ClusterBootstrap/template/storage/auto_share/auto_share.py +++ b/src/ClusterBootstrap/template/storage/auto_share/auto_share.py @@ -179,12 +179,15 @@ def mount_fileshare(verbose=True): for k,v in allmountpoints.iteritems(): if "curphysicalmountpoint" in v and istrue(v, "autoshare", True): physicalmountpoint = v["curphysicalmountpoint"] + # gives mounted information only, would not write anything or carry out mount action output = pipe_with_output("mount", "grep %s" % v["curphysicalmountpoint"], verbose=False) umounts = [] existmounts = [] for line in output.splitlines(): words = line.split() - if len(words)>3 and words[1]=="on": + # pitfall: words[2] might be prefix of v["curphysicalmountpoint"], then a mount point would be missed + # so we should check whether they are equal, if so, we know the specified path on NFS node was previously mounted to infra/worker. + if len(words)>3 and words[1]=="on" and words[2] == v["curphysicalmountpoint"]: if verbose: logging.debug( "%s on %s" % (words[0], words[2]) ) # check if mount point exists, automatic create directory if non exist diff --git a/src/utils/sku_mapping.yaml b/src/utils/sku_mapping.yaml deleted file mode 100644 index 4858f719a..000000000 --- a/src/utils/sku_mapping.yaml +++ /dev/null @@ -1,57 +0,0 @@ -Standard_ND6s: - gpu-type: P40 - gpu-count: 1 -Standard_NV24: - gpu-type: M60 - gpu-count: 4 -Standard_ND12s: - gpu-type: P40 - gpu-count: 2 -Standard_ND24rs: - gpu-type: P40 - gpu-count: 4 -Standard_NV12: - gpu-type: M60 - gpu-count: 2 -Standard_NV48s_v3: - gpu-type: M60 - gpu-count: 4 -Standard_ND40s_v2: - gpu-type: V100 - gpu-count: 8 -Standard_NC6s_v3: - gpu-type: V100 - gpu-count: 1 -Standard_NC6s_v2: - gpu-type: P100 - gpu-count: 1 -Standard_ND24s: - gpu-type: P40 - gpu-count: 4 -Standard_NV24s_v3: - gpu-type: M60 - gpu-count: 2 -Standard_NV6: - gpu-type: M60 - gpu-count: 1 -Standard_NV12s_v3: - gpu-type: M60 - gpu-count: 1 -Standard_NC24s_v2: - gpu-type: P100 - gpu-count: 4 -Standard_NC24s_v3: - gpu-type: V100 - gpu-count: 4 -Standard_NC12s_v3: - gpu-type: V100 - gpu-count: 2 -Standard_NC12s_v2: - gpu-type: P100 - gpu-count: 2 -Standard_NC24rs_v3: - gpu-type: V100 - gpu-count: 4 -Standard_NC24rs_v2: - gpu-type: P100 - gpu-count: 4 From 0080c58ce930e859c5630098d6e698819b6a7e92 Mon Sep 17 00:00:00 2001 From: Qixiang Cheng Date: Tue, 17 Sep 2019 17:26:05 +0800 Subject: [PATCH 594/595] Add dashboard --- src/dashboard/.dockerignore | 23 + src/dashboard/.editorconfig | 14 + src/dashboard/.gitignore | 99 + src/dashboard/Dockerfile | 18 + src/dashboard/README.md | 19 + src/dashboard/config/.gitignore | 4 + src/dashboard/package.json | 89 + src/dashboard/public/favicon.ico | Bin 0 -> 3870 bytes src/dashboard/public/index.html | 35 + src/dashboard/server/.eslintrc.yaml | 2 + .../server/api/configurations/config.js | 17 + .../server/api/configurations/logger.js | 15 + .../api/controllers/authenticate/index.js | 82 + .../api/controllers/authenticate/logout.js | 11 + .../server/api/controllers/bootstrap.js | 11 + .../server/api/controllers/cluster/index.js | 9 + .../api/controllers/cluster/job/commands.js | 14 + .../controllers/cluster/job/commands.post.js | 14 + .../api/controllers/cluster/job/endpoints.js | 14 + .../controllers/cluster/job/endpoints.post.js | 13 + .../api/controllers/cluster/job/index.js | 12 + .../controllers/cluster/job/priority.put.js | 13 + .../api/controllers/cluster/job/status.js | 14 + .../api/controllers/cluster/job/status.put.js | 13 + .../api/controllers/cluster/jobs.post.js | 17 + .../server/api/controllers/team/cluster.js | 12 + .../server/api/controllers/team/jobs.js | 49 + .../api/controllers/team/template.delete.js | 21 + .../api/controllers/team/template.put.js | 20 + .../server/api/controllers/team/templates.js | 23 + src/dashboard/server/api/controllers/teams.js | 56 + src/dashboard/server/api/controllers/user.js | 10 + src/dashboard/server/api/index.js | 15 + src/dashboard/server/api/middlewares/body.js | 21 + .../server/api/middlewares/cluster.js | 7 + src/dashboard/server/api/middlewares/user.js | 29 + src/dashboard/server/api/router.js | 77 + src/dashboard/server/api/services/cluster.js | 367 + src/dashboard/server/api/services/service.js | 19 + src/dashboard/server/api/services/user.js | 115 + .../server/api/validator/command.schema.json | 9 + .../server/api/validator/config.schema.json | 66 + .../api/validator/endpoints.schema.json | 32 + src/dashboard/server/api/validator/index.js | 11 + .../server/api/validator/job.schema.json | 8 + .../server/api/validator/priority.schema.json | 9 + .../server/api/validator/status.schema.json | 14 + .../server/api/validator/template.schema.json | 3 + src/dashboard/server/frontend.js | 14 + src/dashboard/server/index.js | 11 + src/dashboard/src/.eslintrc.yaml | 7 + src/dashboard/src/App.test.tsx | 9 + src/dashboard/src/App.tsx | 108 + src/dashboard/src/Configuration/.gitignore | 4 + .../src/Configuration/foldFormat.json | 4 + src/dashboard/src/contexts/Clusters.tsx | 44 + .../src/contexts/MonospacedTheme.tsx | 16 + src/dashboard/src/contexts/Teams.tsx | 86 + src/dashboard/src/contexts/User.tsx | 33 + src/dashboard/src/index.js | 8 + src/dashboard/src/layout/AppBar.tsx | 276 + src/dashboard/src/layout/Content/index.tsx | 60 + src/dashboard/src/layout/Drawer/Context.tsx | 18 + src/dashboard/src/layout/Drawer/index.tsx | 97 + .../components/ServicesChips.tsx | 48 + .../src/pages/ClusterStatus/index.tsx | 458 + .../src/pages/CommonComponents/TabPanel.tsx | 25 + .../src/pages/CommonComponents/a11yProps.ts | 6 + src/dashboard/src/pages/Home/GPUCard.tsx | 282 + src/dashboard/src/pages/Home/index.tsx | 27 + src/dashboard/src/pages/Job/Details/Brief.tsx | 65 + .../src/pages/Job/Details/Context.ts | 14 + .../src/pages/Job/Details/Endpoints.tsx | 251 + src/dashboard/src/pages/Job/Details/Log.tsx | 24 + .../src/pages/Job/Details/Monitor.tsx | 27 + .../src/pages/Job/Details/RunCommand.tsx | 48 + src/dashboard/src/pages/Job/Details/index.tsx | 164 + src/dashboard/src/pages/Job/index.tsx | 59 + src/dashboard/src/pages/Job/useJob.ts | 32 + src/dashboard/src/pages/Jobs/index.tsx | 998 ++ src/dashboard/src/pages/Jobs/useJobs.ts | 37 + src/dashboard/src/pages/Jobs/useJobsAll.ts | 41 + src/dashboard/src/pages/SignIn/image1.jpeg | Bin 0 -> 327019 bytes src/dashboard/src/pages/SignIn/image2.jpeg | Bin 0 -> 401649 bytes src/dashboard/src/pages/SignIn/image3.jpeg | Bin 0 -> 255458 bytes src/dashboard/src/pages/SignIn/index.tsx | 76 + .../src/pages/Submission/DataJob.tsx | 255 + .../src/pages/Submission/Training.tsx | 1025 ++ .../components/ClusterSelectField.tsx | 83 + src/dashboard/src/pages/Submission/index.tsx | 19 + src/dashboard/src/react-app-env.d.ts | 1 + src/dashboard/src/setupProxy.js | 3 + src/dashboard/tsconfig.json | 25 + src/dashboard/yarn.lock | 14259 ++++++++++++++++ 94 files changed, 20712 insertions(+) create mode 100644 src/dashboard/.dockerignore create mode 100644 src/dashboard/.editorconfig create mode 100644 src/dashboard/.gitignore create mode 100644 src/dashboard/Dockerfile create mode 100644 src/dashboard/README.md create mode 100644 src/dashboard/config/.gitignore create mode 100644 src/dashboard/package.json create mode 100644 src/dashboard/public/favicon.ico create mode 100644 src/dashboard/public/index.html create mode 100644 src/dashboard/server/.eslintrc.yaml create mode 100644 src/dashboard/server/api/configurations/config.js create mode 100644 src/dashboard/server/api/configurations/logger.js create mode 100644 src/dashboard/server/api/controllers/authenticate/index.js create mode 100644 src/dashboard/server/api/controllers/authenticate/logout.js create mode 100644 src/dashboard/server/api/controllers/bootstrap.js create mode 100644 src/dashboard/server/api/controllers/cluster/index.js create mode 100644 src/dashboard/server/api/controllers/cluster/job/commands.js create mode 100644 src/dashboard/server/api/controllers/cluster/job/commands.post.js create mode 100644 src/dashboard/server/api/controllers/cluster/job/endpoints.js create mode 100644 src/dashboard/server/api/controllers/cluster/job/endpoints.post.js create mode 100644 src/dashboard/server/api/controllers/cluster/job/index.js create mode 100644 src/dashboard/server/api/controllers/cluster/job/priority.put.js create mode 100644 src/dashboard/server/api/controllers/cluster/job/status.js create mode 100644 src/dashboard/server/api/controllers/cluster/job/status.put.js create mode 100644 src/dashboard/server/api/controllers/cluster/jobs.post.js create mode 100644 src/dashboard/server/api/controllers/team/cluster.js create mode 100644 src/dashboard/server/api/controllers/team/jobs.js create mode 100644 src/dashboard/server/api/controllers/team/template.delete.js create mode 100644 src/dashboard/server/api/controllers/team/template.put.js create mode 100644 src/dashboard/server/api/controllers/team/templates.js create mode 100644 src/dashboard/server/api/controllers/teams.js create mode 100644 src/dashboard/server/api/controllers/user.js create mode 100644 src/dashboard/server/api/index.js create mode 100644 src/dashboard/server/api/middlewares/body.js create mode 100644 src/dashboard/server/api/middlewares/cluster.js create mode 100644 src/dashboard/server/api/middlewares/user.js create mode 100644 src/dashboard/server/api/router.js create mode 100644 src/dashboard/server/api/services/cluster.js create mode 100644 src/dashboard/server/api/services/service.js create mode 100644 src/dashboard/server/api/services/user.js create mode 100644 src/dashboard/server/api/validator/command.schema.json create mode 100644 src/dashboard/server/api/validator/config.schema.json create mode 100644 src/dashboard/server/api/validator/endpoints.schema.json create mode 100644 src/dashboard/server/api/validator/index.js create mode 100644 src/dashboard/server/api/validator/job.schema.json create mode 100644 src/dashboard/server/api/validator/priority.schema.json create mode 100644 src/dashboard/server/api/validator/status.schema.json create mode 100644 src/dashboard/server/api/validator/template.schema.json create mode 100644 src/dashboard/server/frontend.js create mode 100644 src/dashboard/server/index.js create mode 100644 src/dashboard/src/.eslintrc.yaml create mode 100644 src/dashboard/src/App.test.tsx create mode 100644 src/dashboard/src/App.tsx create mode 100644 src/dashboard/src/Configuration/.gitignore create mode 100644 src/dashboard/src/Configuration/foldFormat.json create mode 100644 src/dashboard/src/contexts/Clusters.tsx create mode 100644 src/dashboard/src/contexts/MonospacedTheme.tsx create mode 100644 src/dashboard/src/contexts/Teams.tsx create mode 100644 src/dashboard/src/contexts/User.tsx create mode 100644 src/dashboard/src/index.js create mode 100644 src/dashboard/src/layout/AppBar.tsx create mode 100644 src/dashboard/src/layout/Content/index.tsx create mode 100644 src/dashboard/src/layout/Drawer/Context.tsx create mode 100644 src/dashboard/src/layout/Drawer/index.tsx create mode 100644 src/dashboard/src/pages/ClusterStatus/components/ServicesChips.tsx create mode 100644 src/dashboard/src/pages/ClusterStatus/index.tsx create mode 100644 src/dashboard/src/pages/CommonComponents/TabPanel.tsx create mode 100644 src/dashboard/src/pages/CommonComponents/a11yProps.ts create mode 100644 src/dashboard/src/pages/Home/GPUCard.tsx create mode 100644 src/dashboard/src/pages/Home/index.tsx create mode 100644 src/dashboard/src/pages/Job/Details/Brief.tsx create mode 100644 src/dashboard/src/pages/Job/Details/Context.ts create mode 100644 src/dashboard/src/pages/Job/Details/Endpoints.tsx create mode 100644 src/dashboard/src/pages/Job/Details/Log.tsx create mode 100644 src/dashboard/src/pages/Job/Details/Monitor.tsx create mode 100644 src/dashboard/src/pages/Job/Details/RunCommand.tsx create mode 100644 src/dashboard/src/pages/Job/Details/index.tsx create mode 100644 src/dashboard/src/pages/Job/index.tsx create mode 100644 src/dashboard/src/pages/Job/useJob.ts create mode 100644 src/dashboard/src/pages/Jobs/index.tsx create mode 100644 src/dashboard/src/pages/Jobs/useJobs.ts create mode 100644 src/dashboard/src/pages/Jobs/useJobsAll.ts create mode 100644 src/dashboard/src/pages/SignIn/image1.jpeg create mode 100644 src/dashboard/src/pages/SignIn/image2.jpeg create mode 100644 src/dashboard/src/pages/SignIn/image3.jpeg create mode 100644 src/dashboard/src/pages/SignIn/index.tsx create mode 100644 src/dashboard/src/pages/Submission/DataJob.tsx create mode 100644 src/dashboard/src/pages/Submission/Training.tsx create mode 100644 src/dashboard/src/pages/Submission/components/ClusterSelectField.tsx create mode 100644 src/dashboard/src/pages/Submission/index.tsx create mode 100644 src/dashboard/src/react-app-env.d.ts create mode 100644 src/dashboard/src/setupProxy.js create mode 100644 src/dashboard/tsconfig.json create mode 100644 src/dashboard/yarn.lock diff --git a/src/dashboard/.dockerignore b/src/dashboard/.dockerignore new file mode 100644 index 000000000..4d29575de --- /dev/null +++ b/src/dashboard/.dockerignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/src/dashboard/.editorconfig b/src/dashboard/.editorconfig new file mode 100644 index 000000000..68e74812a --- /dev/null +++ b/src/dashboard/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 80 + +[*.md] +indent_size = 4 +trim_trailing_whitespace = false diff --git a/src/dashboard/.gitignore b/src/dashboard/.gitignore new file mode 100644 index 000000000..cf89a30ab --- /dev/null +++ b/src/dashboard/.gitignore @@ -0,0 +1,99 @@ + +# Created by https://www.gitignore.io/api/node +# Edit at https://www.gitignore.io/?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# End of https://www.gitignore.io/api/node + + +.idea +.vscode diff --git a/src/dashboard/Dockerfile b/src/dashboard/Dockerfile new file mode 100644 index 000000000..d0934b6e8 --- /dev/null +++ b/src/dashboard/Dockerfile @@ -0,0 +1,18 @@ +FROM node:dubnium + +RUN mkdir /usr/src/app +WORKDIR /usr/src/app + +COPY package.json yarn.lock ./ +RUN yarn --frozen-lockfile + +COPY . . +RUN npm run build + +ENV HOST 0.0.0.0 +ENV PORT 80 + +VOLUME /usr/src/app/config +EXPOSE 80 + +CMD npm start --production diff --git a/src/dashboard/README.md b/src/dashboard/README.md new file mode 100644 index 000000000..5518166ae --- /dev/null +++ b/src/dashboard/README.md @@ -0,0 +1,19 @@ +# DLTS dashboard + +A dashboard that support serving multiple DLTS backend. + +## Configuration + +DLTS dashboard is using [config](https://npmjs.com/package/config) to maintain configurations, read [Configuration Files](https://github.com/lorenwest/node-config/wiki/Configuration-Files) for more file-wise details. + +The configuration schema (with description) is maintained in [config.schema.json](./server/api/validator/config.schema.json). + +## Local development + +1. Install [Node.js](https://nodejs.org/), version 10 is recommended. +2. Install [Yarn](https://yarnpkg.com/) for package maintaince. +3. Run `yarn` to install dependencies. +4. Prepare a [configuration](#configuration) file named `local.yaml` in `config` directory, it will be auto ignored by git. +5. For frontend development, run `yarn frontend`; for backend development, run `yarn build` and then `yarn backend`. +6. Open (may be automatically opened by script) to preview. +7. For frontend development, local code changes will automatically refresh the browser; for backend development, local code changes will automatically restart the server. diff --git a/src/dashboard/config/.gitignore b/src/dashboard/config/.gitignore new file mode 100644 index 000000000..9ee94afe4 --- /dev/null +++ b/src/dashboard/config/.gitignore @@ -0,0 +1,4 @@ +# local configurations + +local.* +local-*.* diff --git a/src/dashboard/package.json b/src/dashboard/package.json new file mode 100644 index 000000000..90003018d --- /dev/null +++ b/src/dashboard/package.json @@ -0,0 +1,89 @@ +{ + "name": "dashboard", + "version": "0.1.0", + "private": true, + "dependencies": { + "@material-ui/core": "^4.1.0", + "@material-ui/icons": "^4.1.0", + "@types/luxon": "^1.15.2", + "ajv": "^6.10.2", + "clipboard-copy": "^3.1.0", + "clsx": "^1.0.4", + "config": "^3.2.1", + "js-yaml": "^3.13.1", + "jsonwebtoken": "^8.5.1", + "koa": "^2.7.0", + "koa-bodyparser": "^4.2.1", + "koa-compose": "^4.1.0", + "koa-mount": "^4.0.0", + "koa-pino-logger": "^2.1.3", + "koa-router": "^7.4.0", + "koa-send": "^5.0.0", + "koa-static": "^5.0.0", + "lodash": "^4.17.15", + "luxon": "^1.17.2", + "material-table": "^1.49.0", + "node-fetch": "^2.6.0", + "pino": "^4.0.0", + "query-string": "^6.8.2", + "react": "^16.8.6", + "react-dom": "^16.8.6", + "react-draggable": "^3.3.2", + "react-iframe": "^1.8.0", + "react-router-dom": "^5.0.1", + "react-swipeable-views": "^0.13.3", + "recharts": "^1.6.2", + "typeface-roboto": "^0.0.54", + "typeface-roboto-mono": "^0.0.54", + "use-http": "^0.1.79", + "uuid": "^3.3.2" + }, + "devDependencies": { + "@types/config": "^0.0.34", + "@types/jsonwebtoken": "^8.3.2", + "@types/koa": "^2.0.49", + "@types/koa-bodyparser": "^4.3.0", + "@types/koa-compose": "^3.2.4", + "@types/koa-pino-logger": "^2.1.5", + "@types/koa-router": "^7.0.42", + "@types/lodash": "^4.14.136", + "@types/node": "^12.0.8", + "@types/node-fetch": "^2.3.7", + "@types/react": "^16.8.19", + "@types/react-dom": "^16.8.4", + "@types/react-router-dom": "^4.3.3", + "@types/react-swipeable-views": "^0.13.0", + "@types/recharts": "^1.1.16", + "@types/uuid": "^3.4.5", + "eslint": "^5.16.0", + "eslint-config-standard": "^12.0.0", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-node": "^9.1.0", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-standard": "^4.0.0", + "nodemon": "^1.19.1", + "react-scripts": "^3.0.1", + "typescript": "^3.5.2" + }, + "scripts": { + "start": "node server", + "frontend": "react-scripts start", + "build": "react-scripts build", + "backend": "nodemon server" + }, + "eslintConfig": { + "extends": "eslint:recommended" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/src/dashboard/public/favicon.ico b/src/dashboard/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a11777cc471a4344702741ab1c8a588998b1311a GIT binary patch literal 3870 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ literal 0 HcmV?d00001 diff --git a/src/dashboard/public/index.html b/src/dashboard/public/index.html new file mode 100644 index 000000000..096e68b70 --- /dev/null +++ b/src/dashboard/public/index.html @@ -0,0 +1,35 @@ + + + + + + + + + + Deep Learning Training Service + + + +
+ + + + diff --git a/src/dashboard/server/.eslintrc.yaml b/src/dashboard/server/.eslintrc.yaml new file mode 100644 index 000000000..1eece14ad --- /dev/null +++ b/src/dashboard/server/.eslintrc.yaml @@ -0,0 +1,2 @@ +root: true +extends: standard diff --git a/src/dashboard/server/api/configurations/config.js b/src/dashboard/server/api/configurations/config.js new file mode 100644 index 000000000..c7b1ccaf5 --- /dev/null +++ b/src/dashboard/server/api/configurations/config.js @@ -0,0 +1,17 @@ +const Ajv = require('ajv') +const config = require('config') + +const ajv = new Ajv() + +const schema = require('../validator/config.schema.json') + +module.exports = () => { + const validate = ajv.compile(schema) + const valid = validate(config.util.toObject()) + if (!valid) { + const message = validate.errors.map( + error => `${error.dataPath} ${error.message}` + ).join('\n') + throw Error(message) + } +} diff --git a/src/dashboard/server/api/configurations/logger.js b/src/dashboard/server/api/configurations/logger.js new file mode 100644 index 000000000..52ce68b97 --- /dev/null +++ b/src/dashboard/server/api/configurations/logger.js @@ -0,0 +1,15 @@ +const config = require('config') +const logger = require('koa-pino-logger') + +/** + * @param {import('koa')} app + */ +module.exports = (app) => { + const prettyPrint = app.env === 'development' + const middleware = logger({ prettyPrint }) + + // Log the config + middleware.logger.info(config.util.toObject(), 'App config') + + app.use(middleware) +} diff --git a/src/dashboard/server/api/controllers/authenticate/index.js b/src/dashboard/server/api/controllers/authenticate/index.js new file mode 100644 index 000000000..685fff414 --- /dev/null +++ b/src/dashboard/server/api/controllers/authenticate/index.js @@ -0,0 +1,82 @@ +const config = require('config') +const fetch = require('node-fetch') +const jwt = require('jsonwebtoken') + +const User = require('../../services/user') + +const activeDirectoryConfig = config.get('activeDirectory') + +const OAUTH2_URL = `https://login.microsoftonline.com/${activeDirectoryConfig.tenant}/oauth2` + +/** + * @param {import('koa').Context} context + * @return {string} + */ +const getUriWithoutQuery = context => { + const originalUrl = context.req.originalUrl || context.request.originalUrl || '' + return (context.origin + originalUrl).split('?')[0] +} + +/** + * @param {import('koa').Context} context + * @return {string} + */ +const getAuthenticationUrl = context => { + const params = new URLSearchParams({ + client_id: activeDirectoryConfig.clientId, + response_type: 'code', + redirect_uri: getUriWithoutQuery(context), + response_mode: 'query', + scope: 'openid profile email' + }) + return OAUTH2_URL + '/authorize?' + params +} + +/** + * @param {import('koa').Context} context + * @return {Promise} + */ +const getDecodedIdToken = async context => { + const { code } = context.query + const params = new URLSearchParams({ + client_id: activeDirectoryConfig.clientId, + scope: 'openid profile email', + code, + redirect_uri: getUriWithoutQuery(context), + grant_type: 'authorization_code', + client_secret: activeDirectoryConfig.clientSecret + }) + context.log.info({ body: params.toString() }, 'Token request') + const response = await fetch(OAUTH2_URL + '/token', { + method: 'POST', + body: params + }) + const data = await response.json() + context.log.info({ data }, 'Token response') + + context.assert(data['error'] == null, 502, data['error']) + + return jwt.decode(data['id_token']) +} + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + if (context.query.code != null) { + context.log.info({ query: context.query }, 'Authentication succeessful callback') + const idToken = await getDecodedIdToken(context) + context.log.info(idToken, 'Id token') + + const user = User.fromIdToken(context, idToken) + const data = await user.fillIdFromWinbind() + user.addUserToCluster(data) + + context.cookies.set('token', user.toCookie()) + + return context.redirect('/') + } else if (context.query.error != null) { + context.log.error({ query: context.query }, 'Authentication failed callback') + return context.redirect('/') + } else { + return context.redirect(getAuthenticationUrl(context)) + } +} diff --git a/src/dashboard/server/api/controllers/authenticate/logout.js b/src/dashboard/server/api/controllers/authenticate/logout.js new file mode 100644 index 000000000..2636e0e05 --- /dev/null +++ b/src/dashboard/server/api/controllers/authenticate/logout.js @@ -0,0 +1,11 @@ +const config = require('config') + +const activeDirectoryConfig = config.get('activeDirectory') + +const OAUTH2_URL = `https://login.microsoftonline.com/${activeDirectoryConfig.tenant}/oauth2` + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + context.cookies.set('token') + return context.redirect(OAUTH2_URL + '/logout') +} diff --git a/src/dashboard/server/api/controllers/bootstrap.js b/src/dashboard/server/api/controllers/bootstrap.js new file mode 100644 index 000000000..9ad708b46 --- /dev/null +++ b/src/dashboard/server/api/controllers/bootstrap.js @@ -0,0 +1,11 @@ +/** + * @typedef {Object} State + * @property {import('../services/user')} user + */ + +/** @type {import('koa').Middleware} */ +module.exports = (context) => { + const { user } = context.state + context.type = 'javascript' + context.body = `bootstrap(${JSON.stringify(user)})` +} diff --git a/src/dashboard/server/api/controllers/cluster/index.js b/src/dashboard/server/api/controllers/cluster/index.js new file mode 100644 index 000000000..4fab4565f --- /dev/null +++ b/src/dashboard/server/api/controllers/cluster/index.js @@ -0,0 +1,9 @@ +/** + * @typedef {Object} State + * @property {import('../../services/cluster')} cluster + */ + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + context.body = context.state.cluster.config +} diff --git a/src/dashboard/server/api/controllers/cluster/job/commands.js b/src/dashboard/server/api/controllers/cluster/job/commands.js new file mode 100644 index 000000000..e183eb565 --- /dev/null +++ b/src/dashboard/server/api/controllers/cluster/job/commands.js @@ -0,0 +1,14 @@ +/** + * @typedef {Object} State + * @property {import('../../services/cluster')} cluster + */ + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { cluster } = context.state + const { jobId } = context.params + + const commands = await cluster.getCommands(jobId) + + context.body = commands +} diff --git a/src/dashboard/server/api/controllers/cluster/job/commands.post.js b/src/dashboard/server/api/controllers/cluster/job/commands.post.js new file mode 100644 index 000000000..57a999486 --- /dev/null +++ b/src/dashboard/server/api/controllers/cluster/job/commands.post.js @@ -0,0 +1,14 @@ +/** + * @typedef {Object} State + * @property {import('../../services/cluster')} cluster + */ + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { cluster } = context.state + const { jobId } = context.params + const { command } = context.request.body + + context.body = await cluster.addCommand(jobId, command) + context.status = 201 +} diff --git a/src/dashboard/server/api/controllers/cluster/job/endpoints.js b/src/dashboard/server/api/controllers/cluster/job/endpoints.js new file mode 100644 index 000000000..dc4e669bb --- /dev/null +++ b/src/dashboard/server/api/controllers/cluster/job/endpoints.js @@ -0,0 +1,14 @@ +/** + * @typedef {Object} State + * @property {import('../../services/cluster')} cluster + */ + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { cluster } = context.state + const { jobId } = context.params + + const endpoints = await cluster.getEndpoints(jobId) + + context.body = endpoints +} diff --git a/src/dashboard/server/api/controllers/cluster/job/endpoints.post.js b/src/dashboard/server/api/controllers/cluster/job/endpoints.post.js new file mode 100644 index 000000000..9693867e7 --- /dev/null +++ b/src/dashboard/server/api/controllers/cluster/job/endpoints.post.js @@ -0,0 +1,13 @@ +/** + * @typedef {Object} State + * @property {import('../../services/cluster')} cluster + */ + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { cluster } = context.state + const { jobId } = context.params + const { endpoints } = context.request.body + + context.body = await cluster.addEndpoint(jobId, endpoints) +} diff --git a/src/dashboard/server/api/controllers/cluster/job/index.js b/src/dashboard/server/api/controllers/cluster/job/index.js new file mode 100644 index 000000000..a0214f275 --- /dev/null +++ b/src/dashboard/server/api/controllers/cluster/job/index.js @@ -0,0 +1,12 @@ +/** + * @typedef {Object} State + * @property {import('../../../services/cluster')} cluster + */ + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { cluster } = context.state + const { jobId } = context.params + + context.body = await cluster.getJob(jobId) +} diff --git a/src/dashboard/server/api/controllers/cluster/job/priority.put.js b/src/dashboard/server/api/controllers/cluster/job/priority.put.js new file mode 100644 index 000000000..69a07306d --- /dev/null +++ b/src/dashboard/server/api/controllers/cluster/job/priority.put.js @@ -0,0 +1,13 @@ +/** + * @typedef {Object} State + * @property {import('../../../services/cluster')} cluster + */ + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { cluster } = context.state + const { jobId } = context.params + + const priority = context.request.body.priority + context.body = await cluster.setJobPriorty(jobId, priority) +} diff --git a/src/dashboard/server/api/controllers/cluster/job/status.js b/src/dashboard/server/api/controllers/cluster/job/status.js new file mode 100644 index 000000000..3c2b2e1c1 --- /dev/null +++ b/src/dashboard/server/api/controllers/cluster/job/status.js @@ -0,0 +1,14 @@ +/** + * @typedef {Object} State + * @property {import('../../../services/cluster')} cluster + */ + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { cluster } = context.state + const { jobId } = context.params + + const status = await cluster.getJobStatus(jobId) + + context.body = { status } +} diff --git a/src/dashboard/server/api/controllers/cluster/job/status.put.js b/src/dashboard/server/api/controllers/cluster/job/status.put.js new file mode 100644 index 000000000..0181d49e5 --- /dev/null +++ b/src/dashboard/server/api/controllers/cluster/job/status.put.js @@ -0,0 +1,13 @@ +/** + * @typedef {Object} State + * @property {import('../../../services/cluster')} cluster + */ + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { cluster } = context.state + const { jobId } = context.params + + const status = context.request.body.status + context.body = await cluster.setJobStatus(jobId, status) +} diff --git a/src/dashboard/server/api/controllers/cluster/jobs.post.js b/src/dashboard/server/api/controllers/cluster/jobs.post.js new file mode 100644 index 000000000..4c49121cd --- /dev/null +++ b/src/dashboard/server/api/controllers/cluster/jobs.post.js @@ -0,0 +1,17 @@ +const uuid = require('uuid') + +/** + * @typedef {Object} State + * @property {import('../../services/cluster')} cluster + */ + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { cluster } = context.state + + const job = Object.assign({}, context.request.body) + job['familyToken'] = uuid() + job['isParent'] = 1 + + context.body = await cluster.addJob(job) +} diff --git a/src/dashboard/server/api/controllers/team/cluster.js b/src/dashboard/server/api/controllers/team/cluster.js new file mode 100644 index 000000000..3216518f6 --- /dev/null +++ b/src/dashboard/server/api/controllers/team/cluster.js @@ -0,0 +1,12 @@ +/** + * @typedef {Object} State + * @property {import('../../services/cluster')} cluster + */ + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { cluster } = context.state + const { teamId } = context.params + const team = await cluster.getTeam(teamId) + context.body = team +} diff --git a/src/dashboard/server/api/controllers/team/jobs.js b/src/dashboard/server/api/controllers/team/jobs.js new file mode 100644 index 000000000..3ac00f23d --- /dev/null +++ b/src/dashboard/server/api/controllers/team/jobs.js @@ -0,0 +1,49 @@ +const config = require('config') +const { flatMap, stubArray } = require('lodash') + +const Cluster = require('../../services/cluster') + +const clusterIds = Object.keys(config.get('clusters')) + +const DEFAULT_PRIORITY = 100 + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { teamId } = context.params + const offset = Number(context.query.offset) || 0 + const limit = Number(context.query.limit) || 10 + const all = context.query.user === 'all' + + const getClusterJobs = async id => { + const cluster = new Cluster(context, id) + const [jobs, jobPriorities] = await Promise.all([ + cluster.getJobs(teamId, all), + cluster.getJobsPriority().catch(() => Object.create(null)) // Ignore errors + ]) + + jobs.forEach(job => { + job.cluster = id + if (job['jobId'] in jobPriorities) { + job.priority = jobPriorities[job['jobId']] + } else { + job.priority = DEFAULT_PRIORITY + } + }) + + return jobs + } + + const jobs = flatMap( + await Promise.all( + clusterIds.map( + id => getClusterJobs(id).catch(stubArray)))) // ignore error and return empty array + jobs.sort((jobA, jobB) => { + const jobATime = jobA['jobTime'] + const jobBTime = jobB['jobTime'] + const jobADate = new Date(jobATime || 0) + const jobBDate = new Date(jobBTime || 0) + return jobBDate - jobADate + }) + + context.body = jobs.slice(offset, offset + limit) +} diff --git a/src/dashboard/server/api/controllers/team/template.delete.js b/src/dashboard/server/api/controllers/team/template.delete.js new file mode 100644 index 000000000..0f2c5ed5f --- /dev/null +++ b/src/dashboard/server/api/controllers/team/template.delete.js @@ -0,0 +1,21 @@ +const config = require('config') + +const Cluster = require('../../services/cluster') + +const clusterIds = Object.keys(config.get('clusters')) + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { teamId, templateName } = context.params + const { database } = context.query + + const deleteClusterTemplate = async id => { + const cluster = new Cluster(context, id) + console.log('----> exe!!!') + return cluster.deleteTemplate(database, teamId, templateName) + } + + await Promise.all(clusterIds.map(deleteClusterTemplate)) + + context.status = 204 +} diff --git a/src/dashboard/server/api/controllers/team/template.put.js b/src/dashboard/server/api/controllers/team/template.put.js new file mode 100644 index 000000000..615599a3b --- /dev/null +++ b/src/dashboard/server/api/controllers/team/template.put.js @@ -0,0 +1,20 @@ +const config = require('config') + +const Cluster = require('../../services/cluster') + +const clusterIds = Object.keys(config.get('clusters')) + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { teamId, templateName } = context.params + const { database } = context.query + + const updateClusterTemplate = async id => { + const cluster = new Cluster(context, id) + return cluster.updateTemplate(database, teamId, templateName, context.request.body) + } + + await Promise.all(clusterIds.map(updateClusterTemplate)) + + context.status = 204 +} diff --git a/src/dashboard/server/api/controllers/team/templates.js b/src/dashboard/server/api/controllers/team/templates.js new file mode 100644 index 000000000..d2e513ec1 --- /dev/null +++ b/src/dashboard/server/api/controllers/team/templates.js @@ -0,0 +1,23 @@ +const config = require('config') +const { uniqBy, flatMap, stubArray } = require('lodash') + +const Cluster = require('../../services/cluster') + +const clusterIds = Object.keys(config.get('clusters')) + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const { teamId } = context.params + + const getClusterTemplates = async id => { + const cluster = new Cluster(context, id) + return cluster.getTemplates(teamId) + } + + const templates = flatMap(await Promise.all( + clusterIds.map(id => getClusterTemplates(id).catch(stubArray)) // ignore error and return empty array + )) + const unionedTemplates = uniqBy(templates, 'name') + + context.body = unionedTemplates +} diff --git a/src/dashboard/server/api/controllers/teams.js b/src/dashboard/server/api/controllers/teams.js new file mode 100644 index 000000000..ebf7db92f --- /dev/null +++ b/src/dashboard/server/api/controllers/teams.js @@ -0,0 +1,56 @@ +const config = require('config') +const { flatMap, groupBy, map, stubArray } = require('lodash') + +const Cluster = require('../services/cluster') + +const clusterIds = Object.keys(config.get('clusters')) + +/** + * @param {string} json the JSON text + * @param {any} empty empty object when failed to parse + * @return {any} parsed JSON or the empty object + */ +const tryParseJSON = (json, empty) => { + try { + return JSON.parse(json) + } catch (e) { + return empty + } +} + +/** @type {import('koa').Middleware} */ +module.exports = async context => { + const getClusterTeams = async id => { + const cluster = new Cluster(context, id) + const teams = await cluster.getTeams() + return teams.map(({ vcName, admin, metadata, quota }) => { + const metadataObject = tryParseJSON(metadata, Object.create(null)) + const quotaObject = tryParseJSON(quota, Object.create(null)) + const gpus = Object.create(null) + for (const model of Object.keys(metadataObject)) { + const perNode = metadataObject[model]['num_gpu_per_node'] + gpus[model] = { perNode } + } + for (const model of Object.keys(quotaObject)) { + const quota = quotaObject[model] + if (gpus[model]) { + gpus[model].quota = quota + } else { + gpus[model] = { quota } + } + } + const userQuota = metadataObject['user_quota'] + + return { id, teamId: vcName, admin, gpus, userQuota } + }) + } + + const clusterTeams = flatMap(await Promise.all(clusterIds.map( + id => getClusterTeams(id).catch(stubArray) // ignore error and return empty array + ))) + const teams = map(groupBy(clusterTeams, 'teamId'), (clusters, id) => { + clusters.forEach(cluster => { delete cluster.teamId }) + return { id, clusters } + }) + context.body = teams +} diff --git a/src/dashboard/server/api/controllers/user.js b/src/dashboard/server/api/controllers/user.js new file mode 100644 index 000000000..71d75a68a --- /dev/null +++ b/src/dashboard/server/api/controllers/user.js @@ -0,0 +1,10 @@ +/** + * @typedef {Object} State + * @property {import('../services/user')} user + */ + +/** @type {import('koa').Middleware} */ +module.exports = context => { + const { user } = context.state + context.body = { token: user.token.toString('hex') } +} diff --git a/src/dashboard/server/api/index.js b/src/dashboard/server/api/index.js new file mode 100644 index 000000000..18fc6ffdb --- /dev/null +++ b/src/dashboard/server/api/index.js @@ -0,0 +1,15 @@ +const Koa = require('koa') + +const app = module.exports = new Koa() + +require('./configurations/logger')(app) +require('./configurations/config')(app) + +const router = require('./router') + +app.use(router.routes()) +app.use(router.allowedMethods()) + +if (require.main === module) { + app.listen(process.env.PORT || 3000, process.env.HOST) +} diff --git a/src/dashboard/server/api/middlewares/body.js b/src/dashboard/server/api/middlewares/body.js new file mode 100644 index 000000000..6069b3b7c --- /dev/null +++ b/src/dashboard/server/api/middlewares/body.js @@ -0,0 +1,21 @@ +const compose = require('koa-compose') +const bodyparser = require('koa-bodyparser') + +const validator = require('../validator') + +module.exports = schema => { + return compose([ + bodyparser({ enableTypes: ['json'] }), + /** @type {import('koa').Middleware} */ + (context, next) => { + var valid = validator.validate(schema, context.request.body) + if (!valid) { + const message = validator.errors.map( + error => `${error.dataPath} ${error.message}` + ).join('\n') + return context.throw(400, message) + } + return next() + } + ]) +} diff --git a/src/dashboard/server/api/middlewares/cluster.js b/src/dashboard/server/api/middlewares/cluster.js new file mode 100644 index 000000000..adab3d9ed --- /dev/null +++ b/src/dashboard/server/api/middlewares/cluster.js @@ -0,0 +1,7 @@ +const Cluster = require('../services/cluster') + +/** @type {import('koa-router').IParamMiddleware} */ +module.exports = (id, context, next) => { + context.state.cluster = new Cluster(context, id) + return next() +} diff --git a/src/dashboard/server/api/middlewares/user.js b/src/dashboard/server/api/middlewares/user.js new file mode 100644 index 000000000..df5b85874 --- /dev/null +++ b/src/dashboard/server/api/middlewares/user.js @@ -0,0 +1,29 @@ +const User = require('../services/user') + +/** + * @param {boolean} force + * @return {import('koa').Middleware} + */ +module.exports = (forceAuthenticated = true) => async (context, next) => { + if ('email' in context.query && 'token' in context.query) { + const { email, token } = context.query + const user = context.state.user = User.fromToken(context, email, token) + await user.fillIdFromWinbind() + context.log.info(user, 'Authenticated by token') + } else if (context.cookies.get('token')) { + try { + const token = context.cookies.get('token') + const user = context.state.user = User.fromCookie(context, token) + await user.token + context.log.info(user, 'Authenticated by cookie') + } catch (error) { + context.log.error(error, 'Error in cookie authentication') + } + } + + if (forceAuthenticated) { + context.assert(context.state.user != null, 403) + } + + return next() +} diff --git a/src/dashboard/server/api/router.js b/src/dashboard/server/api/router.js new file mode 100644 index 000000000..3e40dc175 --- /dev/null +++ b/src/dashboard/server/api/router.js @@ -0,0 +1,77 @@ +const Router = require('koa-router') + +const router = module.exports = new Router() + +router.get('/bootstrap.js', + require('./middlewares/user')(false), + require('./controllers/bootstrap')) +router.get('/authenticate', + require('./controllers/authenticate')) +router.get('/authenticate/logout', + require('./controllers/authenticate/logout')) + +router.param('clusterId', + require('./middlewares/cluster')) + +router.get('/teams', + require('./middlewares/user')(), + require('./controllers/teams')) +router.get('/teams/:teamId/clusters/:clusterId', + require('./middlewares/user')(), + require('./controllers/team/cluster')) +router.get('/clusters/:clusterId', + require('./middlewares/user')(), + require('./controllers/cluster')) + +router.get('/teams/:teamId/jobs', + require('./middlewares/user')(), + require('./controllers/team/jobs')) +router.post('/clusters/:clusterId/jobs', + require('./middlewares/user')(), + require('./middlewares/body')('job'), + require('./controllers/cluster/jobs.post')) +router.get('/clusters/:clusterId/jobs/:jobId', + require('./middlewares/user')(), + require('./controllers/cluster/job')) +router.get('/clusters/:clusterId/jobs/:jobId/status', + require('./middlewares/user')(), + require('./controllers/cluster/job/status')) +router.put('/clusters/:clusterId/jobs/:jobId/status', + require('./middlewares/user')(), + require('./middlewares/body')('status'), + require('./controllers/cluster/job/status.put')) +router.put('/clusters/:clusterId/jobs/:jobId/priority', + require('./middlewares/user')(), + require('./middlewares/body')('priority'), + require('./controllers/cluster/job/priority.put')) + +router.get('/clusters/:clusterId/jobs/:jobId/commands', + require('./middlewares/user')(), + require('./controllers/cluster/job/commands')) +router.post('/clusters/:clusterId/jobs/:jobId/commands', + require('./middlewares/user')(), + require('./middlewares/body')('command'), + require('./controllers/cluster/job/commands.post')) + +router.get('/clusters/:clusterId/jobs/:jobId/endpoints', + require('./middlewares/user')(), + require('./controllers/cluster/job/endpoints')) +router.post('/clusters/:clusterId/jobs/:jobId/endpoints', + require('./middlewares/user')(), + require('./middlewares/body')('endpoints'), + require('./controllers/cluster/job/endpoints.post')) + +router.get('/user', + require('./middlewares/user')(), + require('./controllers/user')) + +router.get('/teams/:teamId/templates', + require('./middlewares/user')(), + require('./controllers/team/templates')) +router.put('/teams/:teamId/templates/:templateName', + require('./middlewares/user')(), + require('./middlewares/body')('template'), + require('./controllers/team/template.put')) +router.delete('/teams/:teamId/templates/:templateName', + require('./middlewares/user')(), + require('./controllers/team/template.delete')) diff --git a/src/dashboard/server/api/services/cluster.js b/src/dashboard/server/api/services/cluster.js new file mode 100644 index 000000000..305d1993e --- /dev/null +++ b/src/dashboard/server/api/services/cluster.js @@ -0,0 +1,367 @@ +const config = require('config') +const fetch = require('node-fetch') + +const Service = require('./service') + +const clustersConfig = config.get('clusters') + +/** + * @typedef {Object} State + * @property {import('./user')} user + */ + +/** + * @extends {Service} + */ +class Cluster extends Service { + /** + * @param {import('koa').Context} context + * @param {string} id + */ + constructor (context, id) { + super(context) + this.id = id + this.config = clustersConfig[id] + context.assert(this.config != null, 404, 'Cluster is not found') + } + + /** + * @param {string} teamId + * @param {boolean} all + * @return {Promise} + */ + async getJobs (teamId, all) { + const { user } = this.context.state + const params = new URLSearchParams({ + userName: user.email, + vcName: teamId, + jobOwner: all ? 'all' : user.email + }) + const response = await this.fetch('/ListJobs?' + params) + this.context.assert(response.ok, 502) + const data = await response.json() + const jobs = [].concat( + data['finishedJobs'], + data['queuedJobs'], + data['runningJobs'], + data['visualizationJobs'] + ) + this.context.log.info('Got %d jobs from %s', jobs.length, this.id) + return jobs + } + + /** + * @param {string} jobId + * @return {Promise} + */ + async getJob (jobId) { + const { user } = this.context.state + const params = new URLSearchParams({ + jobId, + userName: user.email + }) + const response = await this.fetch('/GetJobDetail?' + params) + this.context.assert(response.ok, 502) + const job = await response.json() + this.context.log.info({ job }, 'Got job') + return job + } + + /** + * @param {object} job + * @return {Promise} + */ + async addJob (job) { + const response = await this.fetch('/PostJob', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(job) + }) + const text = await response.text() + this.context.log.info({ text }, 'Submit job response text') + this.context.assert(response.ok, response.status, response.statusText) + return text + } + + /** + * @param {object} job + * @return {Promise} + */ + async getJobStatus (jobId) { + const params = new URLSearchParams({ jobId }) + const response = await this.fetch('/GetJobStatus?' + params) + this.context.assert(response.ok, 502) + const data = await response.json() + this.context.assert(data['errorMsg'] == null, 404, data['errorMsg']) + this.context.log.info({ data }, 'Got job status') + return data['jobStatus'] + } + + /** + * @param {string} jobId + * @param {'approved'|'killing'} status + * @return {Promise} + */ + async setJobStatus (jobId, status) { + const { user } = this.context.state + const params = new URLSearchParams({ + jobId, + userName: user.email + }) + if (status === 'approved') { + const response = await this.fetch('/ApproveJob?' + params) + const text = await response.text() + this.context.log.info({ text }, 'Approve job response') + this.context.assert(response.ok, response.status, response.statusText) + return text + } else if (status === 'killing') { + const response = await this.fetch('/KillJob?' + params) + const text = await response.text() + this.context.log.info({ text }, 'Kill job response') + this.context.assert(response.ok, response.status, response.statusText) + return text + } else if (status === 'pausing') { + const response = await this.fetch('/PauseJob?' + params) + const text = await response.text() + this.context.log.info({ text }, 'Pause job response') + this.context.assert(response.ok, response.status, response.statusText) + return text + } else if (status === 'queued') { // resume + const response = await this.fetch('/ResumeJob?' + params) + const text = await response.text() + this.context.log.info({ text }, 'Resume job response') + this.context.assert(response.ok, response.status, response.statusText) + return text + } else { + this.context.throw(400, 'Invalid status') + } + } + + /** + * @return {Promise} + */ + async getJobsPriority () { + const response = await this.fetch('/jobs/priorities') + this.context.assert(response.ok, 502) + const data = await response.json() + this.context.log.info({ data }, 'Got job priorities') + return data + } + + /** + * @param {string} jobId + * @param {number} priority + * @return {Promise} + */ + async setJobPriorty (jobId, priority) { + const body = { [jobId]: priority } + const response = await this.fetch('/jobs/priorities', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body) + }) + const text = await response.text() + this.context.log.info({ text }, 'Set priority %d for job "%s"', priority, jobId) + this.context.assert(response.ok, 502) + return text + } + + /** + * @return {Promise} + */ + async getTeams () { + const { user } = this.context.state + const params = new URLSearchParams({ + userName: user.email + }) + + const response = await this.fetch('/ListVCs?' + params) + const data = await response.json() + this.context.log.info(data, 'Listed VC') + + return data['result'] + } + + /** + * @param {string} teamId + * @return {Promise} + */ + async getTeam (teamId) { + const { user } = this.context.state + const params = new URLSearchParams({ + userName: user.email, + vcName: teamId + }) + const response = await this.fetch('/GetVC?' + params) + this.context.assert(response.ok, 502) + const data = await response.json() + this.context.log.info(data, 'Got VC') + this.context.assert(data != null, 404, 'Team is not found') + return data + } + + /** + * @param {string} jobId + * @return {Promise} + */ + async getCommands (jobId) { + const { user } = this.context.state + const params = new URLSearchParams({ + jobId, + userName: user.email + }) + const response = await this.fetch('/GetCommands?' + params) + this.context.assert(response.ok, 502) + const data = await response.json() + this.context.log.info(data, 'Got commands') + return data + } + + /** + * @param {string} jobId + * @param {string} command + * @return {Promise} + */ + async addCommand (jobId, command) { + const { user } = this.context.state + const params = new URLSearchParams({ + jobId, + userName: user.email, + command + }) + + const response = await this.fetch('/AddCommand?' + params) + const text = await response.text() + this.context.log.info({ text }, 'Added command "%s" to "%s"', command, jobId) + this.context.assert(response.ok, 502) + return text + } + + /** + * @param {string} jobId + * @returns {Promise} + */ + async getEndpoints (jobId) { + const { user } = this.context.state + const params = new URLSearchParams({ + jobId, + userName: user.email + }) + + const response = await this.fetch('/endpoints?' + params) + this.context.assert(response.ok, 502) + const data = await response.json() + this.context.log.info(data, 'Got endpoints') + + return data + } + + /** + * @param {string} jobId + * @param {Array} endpoints + * @returns {Promise} + */ + async addEndpoint (jobId, endpoints) { + const body = { jobId, endpoints } + const response = await this.fetch('/endpoints', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body) + }) + const text = await response.text() + this.context.log.info({ text }, 'Added endpoints %o to "%s"', endpoints, jobId) + this.context.assert(response.ok, 502) + return text + } + + /** + * @param {string} teamId + * @returns {Promise} + */ + async getTemplates (teamId) { + const { user } = this.context.state + const params = new URLSearchParams({ + userName: user.email, + vcName: teamId + }) + const response = await this.fetch('/templates?' + params) + this.context.assert(response.ok, 502) + const data = await response.json() + this.context.log.info({ data }, 'Got templates from %s', this.id) + return data + } + + /** + * @param {string} templateName + * @param {object} template + * @return {Promise} + */ + async updateTemplate (database, teamId, templateName, template) { + const { user } = this.context.state + const params = new URLSearchParams({ + userName: user.email, + vcName: teamId, + database: { + user: 'user', + team: 'vc' + }[database] || 'user', + templateName + }) + const response = await this.fetch('/templates?' + params, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(template) + }) + const text = await response.text() + this.context.log.info({ template, text }, 'Updated template %s in %s', templateName, this.id) + this.context.assert(response.ok, 502) + return text + } + + /** + * @param {string} templateName + * @return {Promise} + */ + async deleteTemplate (database, teamId, templateName) { + const { user } = this.context.state + const params = new URLSearchParams({ + userName: user.email, + vcName: teamId, + database: 'user', + templateName + }) + const response = await this.fetch('/templates?' + params, { + method: 'DELETE' + }) + const text = await response.text() + this.context.log.info({ text }, 'Deleted template %s in %s', templateName, this.id) + this.context.assert(response.ok, 502) + return text + } + + /** + * @private + * @param {string} path + * @param {import('node-fetch').RequestInit} init + * @returns {Promise} + */ + async fetch (path, init) { + const url = new URL(path, this.config.restfulapi) + const begin = Date.now() + this.context.log.info({ url, init }, 'Cluster fetch request') + try { + const response = await fetch(url, init) + const duration = Date.now() - begin + this.context.log.info({ url, init, status: response.status, duration }, 'Cluster fetch response') + return response + } catch (error) { + const duration = Date.now() - begin + this.context.log.error({ url, init, error, duration }, 'Cluster fetch error') + throw error + } + } +} + +module.exports = Cluster diff --git a/src/dashboard/server/api/services/service.js b/src/dashboard/server/api/services/service.js new file mode 100644 index 000000000..a128a7473 --- /dev/null +++ b/src/dashboard/server/api/services/service.js @@ -0,0 +1,19 @@ +/** + * @typedef {Object} State + * @property {import('./user')} user + */ + +/** + * @abstract + * @template State + */ +class Service { + /** + * @param {import('koa').ParameterizedContext} context + */ + constructor (context) { + this.context = context + } +} + +module.exports = Service diff --git a/src/dashboard/server/api/services/user.js b/src/dashboard/server/api/services/user.js new file mode 100644 index 000000000..b2ff3e550 --- /dev/null +++ b/src/dashboard/server/api/services/user.js @@ -0,0 +1,115 @@ +const { createHash } = require('crypto') + +const config = require('config') +const jwt = require('jsonwebtoken') +const fetch = require('node-fetch') + +const Service = require('./service') +const Cluster = require('./cluster') + +const sign = config.get('sign') +const winbind = config.get('winbind') +const masterToken = config.get('masterToken') +const clusterIds = Object.keys(config.get('clusters')) + +class User extends Service { + /** + * @param {import('koa').Context} context + * @param {string} email + */ + constructor (context, email) { + super(context) + this.email = email + } + + /** + * @param {import('koa').Context} context + * @param {object} idToken + * @return {User} + */ + static fromIdToken (context, idToken) { + const user = new User(context, idToken['upn']) + user.givenName = idToken['given_name'] + user.familyName = idToken['family_name'] + return user + } + + /** + * @param {import('koa').Context} context + * @param {object} idToken + * @return {User} + */ + static fromToken (context, email, token) { + const user = new User(context, email) + const expectedToken = user.token + const actualToken = Buffer.from(token, 'hex') + context.assert(expectedToken.equals(actualToken), 403, 'Invalid token') + + return user // No givenName nor familyName here + } + + /** + * @param {import('koa').Context} context + * @param {string} token + * @return {User} + */ + static fromCookie (context, token) { + const payload = jwt.verify(token, sign) + const user = new User(context, payload['email']) + user.givenName = payload['givenName'] + user.familyName = payload['familyName'] + user.uid = payload['uid'] + user.gid = payload['gid'] + return user + } + + get token () { + if (this._token == null) { + const hash = createHash('md5') + hash.update(`${this.email}:${masterToken}`) + this._token = hash.digest() + } + return this._token + } + + async fillIdFromWinbind () { + const params = new URLSearchParams({ userName: this.email }) + const url = `${winbind}/domaininfo/GetUserId?${params}` + this.context.log.info({ url }, 'Winbind request') + const response = await fetch(url) + const data = await response.json() + this.context.log.info({ data }, 'Winbind response') + + this.uid = data['uid'] + this.gid = data['gid'] + + return data + } + + async addUserToCluster (data) { + // Fix groups format + if (Array.isArray(data['groups'])) { + data['groups'] = JSON.stringify(data['groups'].map(e => String(e))) + } + const params = new URLSearchParams(Object.assign({ userName: this.email }, data)) + for (const clusterId of clusterIds) { + new Cluster(this.context, clusterId).fetch('/AddUser?' + params) + } + } + + /** + * @return {string} + */ + toCookie () { + console.log('token is ', this.token) + return jwt.sign({ + email: this.email, + uid: this.uid, + gid: this.gid, + familyName: this.familyName, + givenName: this.givenName + }, sign) + } +} + +module.exports = User diff --git a/src/dashboard/server/api/validator/command.schema.json b/src/dashboard/server/api/validator/command.schema.json new file mode 100644 index 000000000..d42c8b586 --- /dev/null +++ b/src/dashboard/server/api/validator/command.schema.json @@ -0,0 +1,9 @@ +{ + "type": "object", + "properties": { + "command": { + "type": "string" + } + }, + "required": ["command"] +} diff --git a/src/dashboard/server/api/validator/config.schema.json b/src/dashboard/server/api/validator/config.schema.json new file mode 100644 index 000000000..4228e9a31 --- /dev/null +++ b/src/dashboard/server/api/validator/config.schema.json @@ -0,0 +1,66 @@ +{ + "title": "Config", + "type": "object", + "required": ["winbind", "clusters"], + "properties": { + "sign": { + "description": "Sign key for JWT", + "type": "string" + }, + "winbind": { + "title": "WinBind server for get user's id info", + "description": "Will call /domaininfo/GetUserId with userName query to get the user's id info", + "type": "string" + }, + "masterToken": { + "title": "Access token of all users", + "type": "string" + }, + "activeDirectory": { + "title": "Configurations of Azure Active Directory", + "type": "object", + "properties": { + "tenant": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "clientSecret": { + "type": "string" + } + } + }, + "clusters": { + "type": "object", + "patternProperties": { + "[\\w-+]": { + "type": "object", + "required": ["restfulapi"], + "properties": { + "restfulapi": { + "title": "RestfulAPI endpoint of the cluster", + "description": "Exclude the tailing slash", + "type": "string" + }, + "grafana": { + "title": "Grafana endpoint of the cluster", + "description": "Exclude the tailing slash", + "type": "string" + }, + "workStorage": { + "title": "Samba endpoint of the cluster's work directory", + "description": "Starting with file://, exclude the tailing slash", + "type": "string" + }, + "dataStorage": { + "title": "Samba endpoint of the cluster's data directory", + "description": "Starting with file://, exclude the tailing slash", + "type": "string" + } + } + } + } + } + } +} diff --git a/src/dashboard/server/api/validator/endpoints.schema.json b/src/dashboard/server/api/validator/endpoints.schema.json new file mode 100644 index 000000000..fed023b44 --- /dev/null +++ b/src/dashboard/server/api/validator/endpoints.schema.json @@ -0,0 +1,32 @@ +{ + "type": "object", + "properties": { + "endpoints": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "podPort": { + "type": "number" + } + }, + "required": ["name", "podPort"] + }, + { + "enum": [ + "ssh", + "ipython", + "tensorboard" + ] + } + ] + } + } + }, + "required": ["endpoints"] +} diff --git a/src/dashboard/server/api/validator/index.js b/src/dashboard/server/api/validator/index.js new file mode 100644 index 000000000..41325505d --- /dev/null +++ b/src/dashboard/server/api/validator/index.js @@ -0,0 +1,11 @@ +const Ajv = require('ajv') + +const validator = new Ajv() +validator.addSchema(require('./status.schema'), 'status') +validator.addSchema(require('./command.schema'), 'command') +validator.addSchema(require('./endpoints.schema'), 'endpoints') +validator.addSchema(require('./job.schema'), 'job') +validator.addSchema(require('./template.schema'), 'template') +validator.addSchema(require('./priority.schema'), 'priority') + +module.exports = validator diff --git a/src/dashboard/server/api/validator/job.schema.json b/src/dashboard/server/api/validator/job.schema.json new file mode 100644 index 000000000..192dab3ae --- /dev/null +++ b/src/dashboard/server/api/validator/job.schema.json @@ -0,0 +1,8 @@ +{ + "type": "object", + "properties": { + "team": { + "type": "string" + } + } +} diff --git a/src/dashboard/server/api/validator/priority.schema.json b/src/dashboard/server/api/validator/priority.schema.json new file mode 100644 index 000000000..b38b8fef2 --- /dev/null +++ b/src/dashboard/server/api/validator/priority.schema.json @@ -0,0 +1,9 @@ +{ + "type": "object", + "properties": { + "priority": { + "type": "number" + } + }, + "required": ["priority"] +} diff --git a/src/dashboard/server/api/validator/status.schema.json b/src/dashboard/server/api/validator/status.schema.json new file mode 100644 index 000000000..cebd5633e --- /dev/null +++ b/src/dashboard/server/api/validator/status.schema.json @@ -0,0 +1,14 @@ +{ + "type": "object", + "properties": { + "status": { + "enum": [ + "approved", + "killing", + "pausing", + "queued" + ] + } + }, + "required": ["status"] +} diff --git a/src/dashboard/server/api/validator/template.schema.json b/src/dashboard/server/api/validator/template.schema.json new file mode 100644 index 000000000..e6307dc1c --- /dev/null +++ b/src/dashboard/server/api/validator/template.schema.json @@ -0,0 +1,3 @@ +{ + "type": "object" +} diff --git a/src/dashboard/server/frontend.js b/src/dashboard/server/frontend.js new file mode 100644 index 000000000..194d5c1b4 --- /dev/null +++ b/src/dashboard/server/frontend.js @@ -0,0 +1,14 @@ +const compose = require('koa-compose') +const send = require('koa-send') +const serve = require('koa-static') + +const index = (context, next) => { + if (context.method !== 'GET') { return next() } + if (context.accepts('html') !== 'html') { return next() } + return send(context, 'build/index.html') +} + +module.exports = compose([ + serve('build'), + index +]) diff --git a/src/dashboard/server/index.js b/src/dashboard/server/index.js new file mode 100644 index 000000000..7e7f8771a --- /dev/null +++ b/src/dashboard/server/index.js @@ -0,0 +1,11 @@ +const Koa = require('koa') +const mount = require('koa-mount') + +const app = module.exports = new Koa() + +app.use(mount('/api', require('./api'))) +app.use(require('./frontend')) + +if (require.main === module) { + app.listen(process.env.PORT || 3000, process.env.HOST) +} diff --git a/src/dashboard/src/.eslintrc.yaml b/src/dashboard/src/.eslintrc.yaml new file mode 100644 index 000000000..e27f9605e --- /dev/null +++ b/src/dashboard/src/.eslintrc.yaml @@ -0,0 +1,7 @@ +root: true +extends: +- plugin:@typescript-eslint/recommended +- react-app +rules: + '@typescript-eslint/indent': [error, 2] + '@typescript-eslint/explicit-function-return-type': off diff --git a/src/dashboard/src/App.test.tsx b/src/dashboard/src/App.test.tsx new file mode 100644 index 000000000..a754b201b --- /dev/null +++ b/src/dashboard/src/App.test.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import App from './App'; + +it('renders without crashing', () => { + const div = document.createElement('div'); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); +}); diff --git a/src/dashboard/src/App.tsx b/src/dashboard/src/App.tsx new file mode 100644 index 000000000..67e74c4db --- /dev/null +++ b/src/dashboard/src/App.tsx @@ -0,0 +1,108 @@ +import React from 'react'; + +import { BrowserRouter, Redirect, Route, RouteComponentProps, Switch } from "react-router-dom" + +import 'typeface-roboto'; +import 'typeface-roboto-mono'; + +import { Box, CssBaseline, createMuiTheme, CircularProgress } from '@material-ui/core'; +import { ThemeProvider } from "@material-ui/styles"; + +import UserContext, { Provider as UserProvider } from "./contexts/User"; +import { Provider as ClustersProvider } from "./contexts/Clusters"; +import { Provider as TeamProvider } from './contexts/Teams'; + +import AppBar from "./layout/AppBar"; +import Content from "./layout/Content"; +import Drawer from "./layout/Drawer"; +import { Provider as DrawerProvider } from "./layout/Drawer/Context"; +import Training from "./pages/Submission/Training"; + +const Home = React.lazy(() => import('./pages/Home')); +const SignIn = React.lazy(() => import('./pages/SignIn')); +const Submission = React.lazy(() => import('./pages/Submission')); +const Jobs = React.lazy(() => import('./pages/Jobs')); +const Job = React.lazy(() => import('./pages/Job')); +const ClusterStatus = React.lazy( () => import('./pages/ClusterStatus')); + +const theme = createMuiTheme(); + +interface BootstrapProps { + email?: string; + uid?: string; + familyName?: string; + givenName?: string; + _token?: any; +} + +const Loading = ( + + + +); + +const Contexts: React.FC = ({ email, uid, familyName, givenName,_token ,children }) => ( + + + + + + {children} + + + + + +); + +const Layout: React.FC = ({ location, history }) => { + const { email } = React.useContext(UserContext); + + React.useEffect(() => { + if (email === undefined) { + history.replace('/sign-in'); + } + }, [email, history]); + + if (email === undefined) { + return null; + } + + return ( + <> + + + + + + + + + + + + + + + + + + + ); +} + +const App: React.FC = (props) => ( + + + + + + + + + + + +); + +export default App; diff --git a/src/dashboard/src/Configuration/.gitignore b/src/dashboard/src/Configuration/.gitignore new file mode 100644 index 000000000..9ee94afe4 --- /dev/null +++ b/src/dashboard/src/Configuration/.gitignore @@ -0,0 +1,4 @@ +# local configurations + +local.* +local-*.* diff --git a/src/dashboard/src/Configuration/foldFormat.json b/src/dashboard/src/Configuration/foldFormat.json new file mode 100644 index 000000000..21da9b5c0 --- /dev/null +++ b/src/dashboard/src/Configuration/foldFormat.json @@ -0,0 +1,4 @@ +{ + "azureDataStorage": "adl://indexserveplatform-experiment-c09.azuredatalakestore.net/local/users/deepscale/data/qanet", + "nfsDataStorage": "/data/users/deepscale/data/qanet" +} diff --git a/src/dashboard/src/contexts/Clusters.tsx b/src/dashboard/src/contexts/Clusters.tsx new file mode 100644 index 000000000..5b11cd319 --- /dev/null +++ b/src/dashboard/src/contexts/Clusters.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import TeamContext from "./Teams"; +import _ from 'lodash'; +interface Context { + clusters: any []; + selectedCluster?: string; + saveSelectedCluster(team: React.SetStateAction): void; +} + +const Context = React.createContext({ + clusters: [], + selectedCluster: '', + saveSelectedCluster: function(team: React.SetStateAction) {} +}); + +export default Context; + +export const Provider: React.FC = ({ children }) => { + const { teams,selectedTeam } = React.useContext(TeamContext); + const [clusters, setClusters] = React.useState([]); + const [selectedCluster, setSelectedCluster] = React.useState(''); + const saveSelectedCluster = (cluster: React.SetStateAction) => { + setSelectedCluster(cluster); + }; + React.useEffect( ()=>{ + if (teams && selectedTeam) { + const filterClusters = teams.filter((team: any) => team.id === selectedTeam); + + for (let filterCluster of filterClusters) { + setClusters(filterCluster.clusters) + setSelectedCluster(_.map((filterCluster.clusters),'id')[0]); + } + console.log(clusters) + } + + },[selectedTeam, teams]); + + return ( + + ); +}; diff --git a/src/dashboard/src/contexts/MonospacedTheme.tsx b/src/dashboard/src/contexts/MonospacedTheme.tsx new file mode 100644 index 000000000..f100d103a --- /dev/null +++ b/src/dashboard/src/contexts/MonospacedTheme.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +import { createMuiTheme } from "@material-ui/core"; +import { ThemeProvider } from "@material-ui/styles"; + +const theme = createMuiTheme({ + typography: { + fontFamily: `"Roboto-mono", "Menlo", "Consolas", monospace` + } +}); + +export default theme; + +export const Provider: React.FC = ({ children }) => ( + +); diff --git a/src/dashboard/src/contexts/Teams.tsx b/src/dashboard/src/contexts/Teams.tsx new file mode 100644 index 000000000..4c4e07ed4 --- /dev/null +++ b/src/dashboard/src/contexts/Teams.tsx @@ -0,0 +1,86 @@ +import React, { useEffect } from 'react'; +import useFetch from "use-http/dist"; +import { + Box, Button, + Dialog, DialogActions, + DialogContent, + DialogContentText, + DialogTitle +} from "@material-ui/core"; +import {Redirect, Switch} from "react-router"; +import _ from "lodash"; + +interface Context { + teams: any; + selectedTeam: any; + saveSelectedTeam(team: React.SetStateAction): void; +} + +const Context = React.createContext({ + teams: [], + selectedTeam: '', + saveSelectedTeam: function(team: React.SetStateAction) {} +}); + +export default Context; +export const Provider: React.FC = ({ children }) => { + const fetchTeamsUrl = '/api/teams'; + const [teams, setTeams] = useFetch(fetchTeamsUrl, { onMount: true }); + const [selectedTeam, setSelectedTeam] = React.useState(''); + const saveSelectedTeam = (team: React.SetStateAction) => { + setSelectedTeam(team); + localStorage.setItem('team',team.toString()) + window.location.reload() + }; + useEffect(()=> { + if (localStorage.getItem('team')) { + setSelectedTeam((String)(localStorage.getItem('team'))) + } else { + setSelectedTeam(_.map(teams, 'id')[0]); + } + // if (typeof(Storage) !== "undefined" && localStorage.getItem('team') === undefined) { + // setSelectedTeam(_.map(teams, 'id')[0]); + // localStorage.setItem('team',_.map(teams, 'id')[0]) + // } + },[teams]) + const EmptyTeam: React.FC = () => { + const onClick = () => { + return ( + + ) + } + return ( + + + + {"warning"} + + + + {"Your name is number empty team"} + + + + + + + + ) + }; + if (teams !== undefined && teams.length === 0) { + return ( + + ) + } + return ( + + ); +}; diff --git a/src/dashboard/src/contexts/User.tsx b/src/dashboard/src/contexts/User.tsx new file mode 100644 index 000000000..905463292 --- /dev/null +++ b/src/dashboard/src/contexts/User.tsx @@ -0,0 +1,33 @@ +import React from "react"; + +interface Context { + email?: string; + uid?: string; + familyName?: string; + givenName?: string; + token?: any; +} + +const Context = React.createContext({}); + +export default Context; + +interface ProviderProps { + email?: string; + uid?: string; + familyName?: string; + givenName?: string; + token?: any; +} + +export const Provider: React.FC = ({ email, uid,familyName, givenName,token,children }) => { + if (token) { + token = new Buffer(token.data).toString('hex'); + } + return ( + + ); +}; diff --git a/src/dashboard/src/index.js b/src/dashboard/src/index.js new file mode 100644 index 000000000..bbdca23dc --- /dev/null +++ b/src/dashboard/src/index.js @@ -0,0 +1,8 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import App from './App'; + +window.bootstrap = (props) => ReactDOM.render( + React.createElement(App, props), + document.getElementById('root')); diff --git a/src/dashboard/src/layout/AppBar.tsx b/src/dashboard/src/layout/AppBar.tsx new file mode 100644 index 000000000..fe30fea11 --- /dev/null +++ b/src/dashboard/src/layout/AppBar.tsx @@ -0,0 +1,276 @@ +import React, {useEffect, useContext, useCallback} from 'react'; + +import { + AppBar, + Box, + Button, + Grid, + IconButton, + Menu, + MenuItem, + Tooltip, + Typography, + Slide, + Toolbar, + ListItem, + List, + Divider, + ListItemText, + Dialog, + Snackbar, + SnackbarContent, + Breadcrumbs, Hidden +} from '@material-ui/core'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import { + AccountBox, + Check, + ExitToApp, + Group, + MenuRounded, +} from '@material-ui/icons'; +import CloseIcon from '@material-ui/icons/Close'; +import { TransitionProps } from '@material-ui/core/transitions'; +import clsx from 'clsx'; +import DrawerContext from './Drawer/Context'; +import UserContext from '../contexts/User'; +import TeamContext from '../contexts/Teams'; +import { Link } from 'react-router-dom'; +import _ from 'lodash'; +import copy from 'clipboard-copy' +import {green,purple} from "@material-ui/core/colors"; + + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + headerStyle: { + backgroundColor:theme.palette.background.paper, + }, + leftIcon: { + marginRight: theme.spacing(1) + }, + success: { + backgroundColor: green[600], + }, + title: { + padding: theme.spacing(1) + }, + titleLink: { + textDecoration: 'none', + color:purple[50] + }, + appBar: { + zIndex: theme.zIndex.drawer + 1 + }, + userLabel: { + whiteSpace:'nowrap' + } + + }) +); +const Transition = React.forwardRef(function Transition(props, ref) { + return ; +}); +const OpenDrawerButton: React.FC = () => { + const { setOpen, open } = React.useContext(DrawerContext); + const onClick = React.useCallback(() => setOpen(!open), [setOpen, open]); + return ( + + + + + + ); +}; + +let TeamMenu: React.FC; +TeamMenu = () => { + const { teams, saveSelectedTeam, selectedTeam } = React.useContext( + TeamContext + ); + + const [open, setOpen] = React.useState(false); + + const button = React.useRef(null); + + const onButtonClick = React.useCallback(() => setOpen(true), [setOpen]); + const onMenuClose = React.useCallback(() => setOpen(false), [setOpen]); + const onMenuItemClick = React.useCallback( + team => () => { + saveSelectedTeam(team); + setOpen(false); + }, + [saveSelectedTeam] + ); + const styles = useStyles(); + return ( + <> + + + {_.map(teams, 'id').map((team: string) => ( + + {team === selectedTeam ? ( + + ) : ( + + )} + {team} + + ))} + + + ); +}; + +const UserButton: React.FC = () => { + const [openUserProfile, setOpenUserProfile] = React.useState(false); + const [openCopyWarn, setOpenCopyWarn] = React.useState(false); + const { givenName, familyName,email,token } = React.useContext(UserContext); + const styles = useStyles(); + const name = typeof email === 'string' ? email.split('@', 1)[0] : email; + const handleClose = () => { + setOpenUserProfile(false); + } + const handleWarnClose = () => { + setOpenCopyWarn(false); + } + const showUserProfile = () => { + setOpenUserProfile(true); + } + + const handleCopy = useCallback((value) => { + copy(value); + setOpenCopyWarn(true) + },[]) + const classes = useStyles() + return ( +
+ + + + + + + + + {"Account Settings"} + + + + + + + handleCopy(email)}/> + + + + handleCopy(name)}/> + + + + handleCopy(token)}/> + + + + + Copied Success} + /> + + + +
+ ); +}; +const clearLocalStorage = () => { + localStorage.clear() +} +const SignOutButton: React.FC = () => { + return ( + + + + + + ); +}; + +const Title: React.FC = () => { + const styles = useStyles(); + return ( + + + + DLTS + + + + ); +}; + +const DashboardAppBar: React.FC = () => { + const styles = useStyles(); + const { open } = React.useContext(DrawerContext); + return ( + + + + + + + + + + </Grid> + </Hidden> + <Grid item > + <TeamMenu /> + </Grid> + <Grid item style={{ marginLeft:'5' }}> + <UserButton /> + </Grid> + <Grid item> + {' '} + <SignOutButton /> + </Grid> + </Grid> + </Toolbar> + </AppBar> + ); +}; + +export default DashboardAppBar; diff --git a/src/dashboard/src/layout/Content/index.tsx b/src/dashboard/src/layout/Content/index.tsx new file mode 100644 index 000000000..d8030be06 --- /dev/null +++ b/src/dashboard/src/layout/Content/index.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import clsx from 'clsx'; +import { + makeStyles, + useTheme, + Theme, + createStyles +} from '@material-ui/core/styles'; +import {Box, CircularProgress, Toolbar} from '@material-ui/core'; +import DrawerContext from '../Drawer/Context'; +import TeamContext from '../../contexts/Teams'; +const WIDTH = 240; +const useStyles = makeStyles((theme: Theme) => + createStyles({ + content: { + flexGrow: 1, + paddingLeft: theme.spacing(30), + paddingTop: theme.spacing(3), + transition: theme.transitions.create('margin', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen + }), + marginLeft: -WIDTH + }, + contentShift: { + transition: theme.transitions.create('margin', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen + }), + marginLeft: 0 + } + }) +); +const Loading = ( + <Box flex={1} display="flex" alignItems="center" justifyContent="center"> + <CircularProgress/> + </Box> +); +const Content: React.FC = ({ children }) => { + const { open } = React.useContext(DrawerContext); + const { teams } = React.useContext(TeamContext); + const classes = useStyles(); + if (teams === undefined) { + return Loading; + } else { + return ( + <Box + className={clsx(classes.content, { + [classes.contentShift]: open + })} + > + <Toolbar /> + {children} + </Box> + ) + } + +}; + +export default Content; diff --git a/src/dashboard/src/layout/Drawer/Context.tsx b/src/dashboard/src/layout/Drawer/Context.tsx new file mode 100644 index 000000000..65613eba1 --- /dev/null +++ b/src/dashboard/src/layout/Drawer/Context.tsx @@ -0,0 +1,18 @@ +import React from "react"; + +const Context = React.createContext({ + open: false, + setOpen(open: boolean) { this.open = open; } +}); + +export default Context; + +export const Provider: React.FC = ({ children }) => { + const [open, setOpen] = React.useState(false); + return ( + <Context.Provider + value={{ open, setOpen }} + children={children} + /> + ); +}; diff --git a/src/dashboard/src/layout/Drawer/index.tsx b/src/dashboard/src/layout/Drawer/index.tsx new file mode 100644 index 000000000..e6040e25b --- /dev/null +++ b/src/dashboard/src/layout/Drawer/index.tsx @@ -0,0 +1,97 @@ +import React from "react"; +import { Link, LinkProps, matchPath, RouteComponentProps, withRouter } from "react-router-dom"; +import { + Drawer, + Theme, + List, + ListItem, + ListItemText, + Divider, + useMediaQuery +} from "@material-ui/core"; +import { + useTheme, +} from "@material-ui/core/styles"; +import { makeStyles, createStyles } from "@material-ui/core/styles"; +import Context from "./Context"; + + +const useStyles = makeStyles((theme: Theme) => createStyles({ + title: { + padding: theme.spacing(1) + }, + titleLink: { + textDecoration: "none" + }, + drawerHeader: { + marginTop:64, + display: 'flex', + flexDirection:'column', + alignItems: 'center', + padding: '0 8px', + ...theme.mixins.toolbar, + justifyContent: 'flex-end', + }, +})); + +export const ListLink = React.forwardRef<Link, LinkProps>( + ({ to, ...props }, ref) => <Link ref={ref} to={to} {...props}/> +); + +const LinkListItem = withRouter<LinkProps & RouteComponentProps>(({ location, to, children }) => { + const locationPathname = location.pathname; + const toPathname = typeof to === "string" ? to : to.pathname; + const selected = typeof toPathname === "string" + ? matchPath(locationPathname, toPathname) !== null + : true; + return ( + <ListItem button selected={selected} component={ListLink} to={to}> + {children} + </ListItem> + ); +}); + +const NavigationList: React.FC = () => { + const styles = useStyles(); + return ( + <List component="nav" className={styles.drawerHeader}> + <LinkListItem to="/submission/training"> + <ListItemText>Submit Training Job</ListItemText> + </LinkListItem> + <LinkListItem to="/submission/data"> + <ListItemText>Submit Data Job</ListItemText> + </LinkListItem> + <LinkListItem to="/jobs"> + <ListItemText>View and Manage Jobs</ListItemText> + </LinkListItem> + <LinkListItem to="/cluster-status"> + <ListItemText>Cluster Status</ListItemText> + </LinkListItem> + </List> + ); +}; + +const DashboardDrawer: React.FC = () => { + const { open, setOpen } = React.useContext(Context); + const onClose = React.useCallback(() => setOpen(false), [setOpen]); + const theme = useTheme(); + const styles = useStyles(); + const isDesktop = useMediaQuery(theme.breakpoints.up("sm")); + const variant = isDesktop ? "persistent" : "temporary"; + + React.useEffect(() => { + if (isDesktop) { setOpen(true); } + }, [isDesktop, setOpen]); + return ( + <Drawer + variant={variant} + open={open} + onClose={onClose} + > + <Divider/> + <NavigationList /> + </Drawer> + ); +}; + +export default DashboardDrawer; diff --git a/src/dashboard/src/pages/ClusterStatus/components/ServicesChips.tsx b/src/dashboard/src/pages/ClusterStatus/components/ServicesChips.tsx new file mode 100644 index 000000000..cb6480ce3 --- /dev/null +++ b/src/dashboard/src/pages/ClusterStatus/components/ServicesChips.tsx @@ -0,0 +1,48 @@ +import React, {useState} from "react"; + +import { + Chip, + Theme, + createStyles, + makeStyles } from "@material-ui/core"; +import AddIcon from "@material-ui/icons/Add" +import RemoveIcon from "@material-ui/icons/Remove"; +interface ServicesProps { + services: string[]; +} +const useStyles = makeStyles((theme: Theme) => { + return createStyles({ + ChipsColor:{ + color:"secondary" + } + }); +}); +const ServicesChips: React.FC<ServicesProps> = ({services}) => { + const styles = useStyles(); + const [showMoreDetails, setShowMoreDetails] = useState(false); + const [details, setDetails] = useState('...'); + const handleShowMoreDetails = () => { + setShowMoreDetails(!showMoreDetails); + if (details === '...') { + setDetails(''); + } else { + setDetails('...'); + } + } + return ( + <> + { services.map(( service,idx ) => { + if (idx > 3) { + return showMoreDetails ? ( + <Chip label={service} key={idx} /> + ):null + } else if (idx === 3) { + return ( <Chip icon={showMoreDetails ? <RemoveIcon /> : <AddIcon /> } key={idx}label={`${service}${details}`} color="primary" onClick={handleShowMoreDetails}/>) + } + return (<Chip key={idx} label={service} />) + }) } + </> + ) +} + +export default ServicesChips; diff --git a/src/dashboard/src/pages/ClusterStatus/index.tsx b/src/dashboard/src/pages/ClusterStatus/index.tsx new file mode 100644 index 000000000..6d806f508 --- /dev/null +++ b/src/dashboard/src/pages/ClusterStatus/index.tsx @@ -0,0 +1,458 @@ +import React, {FC, Fragment, useEffect, useState} from "react"; +import SwipeableViews from 'react-swipeable-views'; +import { + Paper, + Table, + TableBody, + TableCell, + TableHead, + TableRow, + Toolbar, + Typography, + Tabs, + Tab, + AppBar, + Box, + Radio, + Switch, + Tooltip, + Theme, + useTheme, + createStyles, + makeStyles, + CircularProgress, + Container, + useMediaQuery, + createMuiTheme, + MuiThemeProvider +} from "@material-ui/core"; +import { TabPanel } from '../CommonComponents/TabPanel' +import TeamContext from "../../contexts/Teams"; +import ClusterContext from '../../contexts/Clusters'; +import {a11yProps} from '../CommonComponents/a11yProps'; +import ServicesChips from "./components/ServicesChips"; +import useFetch from "use-http/dist"; +import Iframe from 'react-iframe' +import MaterialTable, {MTableToolbar} from 'material-table'; +import _ from "lodash"; +import theme from "../../contexts/MonospacedTheme"; +const useStyles = makeStyles((theme: Theme) => { + return createStyles({ + root: { + backgroundColor: theme.palette.background.paper, + width: 500, + }, + paperMargin: { + marginTop: '10px', + }, + }); +}); +const tableTheme = createMuiTheme({ + overrides: { + MuiTableCell: { + root: { + paddingTop: 4, + paddingBottom: 4, + paddingLeft:2, + paddingRight:4, + } + } + } +}); + + +const ClusterStatus: FC = () => { + const styles = useStyles(); + const theme = useTheme(); + const [value, setValue] = React.useState(0); + const {clusters} = React.useContext(ClusterContext); + const { selectedTeam,teams } = React.useContext(TeamContext); + const [selectedValue, setSelectedValue] = useState(""); + const [vcStatus, setVcStatus] = useState([]); + const [userStatus, setUserStatus] = useState([]); + const [nodeStatus, setNodeStatus] = useState([]); + const[showIframe, setShowIframe] = useState(false); + const[iframeUrl,setIframeUrl] = React.useState(''); + const[iframeUrlForPerVC, setIframeUrlForPerVC] = React.useState(''); + const [showCurrentUser, setShowCurrentUser] = useState(true); + const handleSwitch = () => { + setShowCurrentUser(!showCurrentUser); + } + const options = { + onMount: true + } + const fetchVcStatusUrl = `/api`; + const fetchiGrafanaUrl = `/api/clusters`; + + const request = useFetch(fetchVcStatusUrl,options); + const requestGrafana = useFetch(fetchiGrafanaUrl, options); + const fetchVC = async (cluster: string) => { + const response = await request.get(`/teams/${selectedTeam}/clusters/${cluster}`); + const {grafana, prometheus} = await requestGrafana.get(`/${cluster}`); + const idleGPUUrl = prometheus.replace("9091","9092"); + const getIdleGPUPerUser = `${prometheus}/prometheus/api/v1/query?`; + + response['getIdleGPUPerUserUrl'] = getIdleGPUPerUser; + response['idleGPUUrl'] = `${idleGPUUrl}/gpu_idle?`; + response['ClusterName'] = cluster; + response['GranaUrl'] = `${grafana}/dashboard/db/gpu-usage?refresh=30s&orgId=1&_=${Date.now()}`; + response['GPUStatisticPerVC'] = `${grafana}/dashboard/db/per-vc-gpu-statistic?var-vc_name=${selectedTeam}&_=${Date.now()}`; + response['prometheus'] = prometheus; + return response; + } + const fetchClusterStatus = () => { + setVcStatus([]); + if (clusters) { + const params = new URLSearchParams({ + query:`count+(task_gpu_percent{vc_name="${selectedTeam}"}+==+0)+by+(username)`, + }); + const paramsVc = new URLSearchParams({ + vc:`${selectedTeam}`, + }); + const filterclusters = _.map(clusters,'id'); + setSelectedValue(filterclusters[0]); + let fetchs: any = []; + filterclusters.forEach((cluster) => { + fetchs.push(fetchVC(cluster)); + }) + Promise.all(fetchs).then((res: any) => { + //init user status & node status when loading page + let fetchUsrs: any = [] + for (let fetchedUser of res[0]['user_status']) { + let tmpUser: any ={}; + tmpUser['userName'] = fetchedUser['userName']; + tmpUser['usedGPU'] = (String)(Object.values(fetchedUser['userGPU'])[0]); + fetchUsrs.push(tmpUser) + } + + let fetchUsrsStatus = []; + fetchUsrsStatus.push(fetch(res[0]['idleGPUUrl']+paramsVc)); + fetchUsrsStatus.push(fetch(decodeURIComponent(res[0]['getIdleGPUPerUserUrl']+params))); + Promise.all(fetchUsrsStatus).then((responses: any) => { + responses.forEach(async (response: any)=>{ + const res = await response.json(); + let prometheusResp: any = []; + let fetchIdes: any = []; + if (res['data']) { + for (let item of res['data']["result"]) { + let idleUser: any = {}; + idleUser['userName'] = item['metric']['username']; + idleUser['idleGPU'] = item['value'][1]; + prometheusResp.push(idleUser) + } + } else { + for (let [key, value] of Object.entries(res)) { + let idleTmp: any = {} + idleTmp['userName'] = key; + let arr: any = value; + idleTmp['booked'] = Math.floor(arr['booked'] / 3600); + idleTmp['idle'] = Math.floor(arr['idle'] / 3600); + fetchIdes.push(idleTmp); + } + } + const merged = _.merge(_.keyBy(fetchUsrs, 'userName'), _.keyBy(prometheusResp, 'userName')); + let mergedUsers: any = _.values(merged); + mergedUsers.forEach((us: any)=>{ + if (!us.hasOwnProperty('usedGPU')) { + us['usedGPU'] = "0"; + } + }) + const mergedTmp = _.merge(_.keyBy(mergedUsers, 'userName'), _.keyBy(fetchIdes, 'userName')); + let mergedTmpUpdate: any = _.values(mergedTmp); + mergedTmpUpdate.forEach((mu: any)=>{ + if (!mu.hasOwnProperty('usedGPU')) { + mu['usedGPU'] = "0"; + } + if (!mu.hasOwnProperty('idleGPU')) { + mu['idleGPU'] = "0"; + } + }) + if (mergedTmpUpdate.length > 0 && fetchIdes.length === mergedTmpUpdate.length) { + setUserStatus(mergedTmpUpdate) + } + }) + }) + + setIframeUrl(res[0]['GranaUrl'] ); + setNodeStatus(res[0]['node_status']); + setIframeUrlForPerVC(res[0]['GPUStatisticPerVC']); + setVcStatus(res); + }) + } + } + + useEffect(()=>{ + let mount = true; + let timeout: any; + let timeout1: any; + if (mount) { + fetchClusterStatus() + timeout = setTimeout(() => {fetchClusterStatus()},30000) + } + timeout1 = setTimeout(()=>{ + setShowIframe(true); + },2000); + return () => { + mount = false; + clearTimeout(timeout) + clearTimeout(timeout1) + } + },[clusters, selectedTeam]) + function handleChange(event: React.ChangeEvent<HTMLInputElement>) { + setSelectedValue(event.target.value); + const filteredVCStatus: any = vcStatus.filter((vc)=>vc['ClusterName'] === event.target.value); + setUserStatus((filteredVCStatus['user_status'])); + setNodeStatus((filteredVCStatus['node_status'])); + setIframeUrl((filteredVCStatus['GranaUrl'])); + + } + const isEmpty = (obj: object) => { + for(let key in obj) { + if(obj.hasOwnProperty(key)) + return false; + } + return true; + } + const handleChangeTab = (event: React.ChangeEvent<{}>, newValue: number) => { + setShowIframe(false) + setTimeout(()=>{ + setShowIframe(true); + },2000); + setValue(newValue); + } + const handleChangeIndex = (index: number) => { + setValue(index); + } + const isDesktop = useMediaQuery(theme.breakpoints.up("sm")); + if (vcStatus){ + return ( + <Fragment> + <Container maxWidth={isDesktop ? 'lg' : 'xs'} > + <AppBar position="static" color="default"> + <Tabs + value={value} + onChange={handleChangeTab} + indicatorColor="primary" + textColor="primary" + variant="fullWidth" + aria-label="full width tabs example" + > + <Tab label="Team Virtual Cluster Status" {...a11yProps(0)} /> + <Tab label="Team VC User Status" {...a11yProps(1)} /> + <Tab label="Cluster Usage" {...a11yProps(2)} /> + <Tab label=" Physical Cluster Node Status" {...a11yProps(3)} /> + </Tabs> + </AppBar> + </Container> + <SwipeableViews + axis={theme.direction === 'rtl' ? 'x-reverse' : 'x'} + index={value} + onChangeIndex={handleChangeIndex} + > + <TabPanel value={value} index={0} dir={theme.direction}> + <Container maxWidth={isDesktop ? 'lg' : 'xs'}> + <Paper className={styles.paperMargin} style={{ display: isDesktop ? 'block' : 'inline-block' }}> + <Toolbar> + <Typography component="h2" variant="h6"> + Team Virtual Cluster Status: + </Typography> + </Toolbar> + <Table size={"small"}> + <TableHead> + <TableRow> + <TableCell>Name</TableCell> + <TableCell>Total GPU</TableCell> + <TableCell>Reserved GPU</TableCell> + <TableCell>Used GPU</TableCell> + <TableCell>Available GPU</TableCell> + <TableCell>Active Jobs</TableCell> + </TableRow> + </TableHead> + <TableBody> + { + vcStatus ? vcStatus.map(( vcs, idx) => { + const gpuCapacity = isEmpty(Object.values(vcs['gpu_capacity'])) ? 0 : (String)(Object.values(vcs['gpu_capacity'])[0]); + const gpuAvailable = isEmpty (Object.values(vcs['gpu_avaliable'])) ? 0 : (String)(Object.values(vcs['gpu_avaliable'])[0]); + const gpuUnschedulable = isEmpty(Object.values(vcs['gpu_unschedulable'])) ? 0 : (String)(Object.values(vcs['gpu_unschedulable'])[0]); + const gpuUsed = isEmpty(Object.values(vcs['gpu_used'])) ? 0 : (String)(Object.values(vcs['gpu_used'])[0]); + return ( + <> + <TableRow key="idx"> + <TableCell key={vcs['ClusterName']}> + <Radio + checked={selectedValue === vcs['ClusterName']} + onChange={handleChange} + value={vcs['ClusterName']} + name={vcs['ClusterName']} + inputProps={{'aria-label': vcs['ClusterName']}} /> + {vcs['ClusterName']} + </TableCell> + <TableCell key="gpuCapacity"> + {gpuCapacity} + </TableCell> + <TableCell key="gpuUnschedulable"> + {gpuUnschedulable} + </TableCell> + <TableCell key="gpuUsed"> + {gpuUsed} + </TableCell> + <TableCell key="gpuAvailable"> + {gpuAvailable} + </TableCell> + <TableCell key="vcs['AvaliableJobNum']"> + {vcs['AvaliableJobNum']} + </TableCell> + </TableRow> + </> + ) + }) : null + } + + </TableBody> + </Table> + </Paper> + </Container> + </TabPanel> + <TabPanel value={value} index={1} dir={theme.direction}> + <Container maxWidth={isDesktop ? 'lg' : 'xs'}> + { + userStatus ? <MaterialTable + title="Team VC User Status" + columns={[{title: 'Username', field: 'userName'}, + {title: 'Currently Allocated GPU', field: 'usedGPU',type:'numeric'}, + {title: 'Currently Idle GPU', field: 'idleGPU',type:'numeric'}, + {title: 'Past Month Booked GPU Hour', field: 'booked',type:'numeric'}, + {title: 'Past Month Idle GPU Hour', field: 'idle',type:'numeric'}, + {title: 'Past Month Idle GPU Hour %', field: 'idle',type:'numeric', render: (rowData: any) => <span style={{ color: Math.floor((rowData['idle'] / rowData['booked']) * 100) > 50 ? "red" : "black" }}>{Math.floor((rowData['idle'] / rowData['booked']) * 100)}</span>, customSort: (a: any, b: any) => {return Math.floor((a['idle'] / a['booked']) * 100) - Math.floor((b['idle'] / b['booked']) * 100)}},]} data={showCurrentUser ? userStatus.filter((uc: any)=>uc['usedGPU'] > 0) : userStatus} + options={{filtering: true, sorting: true, exportButton: true,exportFileName: 'Team_VC_User_Report'}} + components={{ + Toolbar: props => ( + <div> + <MTableToolbar {...props} /> + <Tooltip title="Show Current User" aria-label="add"> + <Switch + checked={showCurrentUser} + onChange={handleSwitch} + inputProps={{ 'aria-label': 'secondary checkbox' }} + /> + </Tooltip> + </div> + ) + }} + /> : + <CircularProgress/> + } + </Container> + </TabPanel> + <TabPanel value={value} index={2} dir={theme.direction}> + <Container maxWidth="lg" > + <Paper className={styles.paperMargin}> + <Toolbar> + <Typography component="h2" variant="h6"> + VC GPU Usage + </Typography> + </Toolbar> + { + showIframe ? + <Iframe url={iframeUrlForPerVC} width="100%" height="400"/> : + <CircularProgress/> + } + + </Paper> + </Container> + <Container maxWidth={isDesktop ? 'lg' : 'xs'} > + <Paper className={styles.paperMargin}> + <Toolbar> + <Typography component="h2" variant="h6"> + Cluster Usage + </Typography> + </Toolbar> + { + showIframe ? + <Iframe url={iframeUrl} width="100%" height="400"/> : <CircularProgress/> + } + </Paper> + </Container> + </TabPanel> + <TabPanel value={value} index={3} dir={theme.direction}> + <Container maxWidth={isDesktop ? 'lg' : 'xs'}> + <Paper className={styles.paperMargin} style={{ display: isDesktop ? 'block' : 'inline-block' }}> + <Toolbar> + <Typography component="h2" variant="h6"> + Physical Cluster Node Status: + </Typography> + </Toolbar> + <MuiThemeProvider theme={isDesktop ? theme : tableTheme}> + <Table size={ 'small'} > + <TableHead> + <TableRow> + <TableCell>Node Name</TableCell> + <TableCell>Node IP</TableCell> + <TableCell>GPU Capacity</TableCell> + <TableCell>Used GPU</TableCell> + <TableCell>Available GPU</TableCell> + <TableCell>Status</TableCell> + <TableCell>Services</TableCell> + <TableCell>Pods</TableCell> + </TableRow> + </TableHead> + <TableBody> + { + nodeStatus.map((ns,idx) => { + const gpuCap = isEmpty(ns['gpu_capacity']) ? 0 : (Number)(Object.values(ns['gpu_capacity'])[0]); + const gpuUsed = isEmpty(ns['gpu_used']) ? 0 : (Number)(Object.values(ns['gpu_used'])[0]); + const availableGPU = gpuCap - gpuUsed; + const status = ns['unschedulable'] ? "unschedulable" : "ok"; + let services: string[] = []; + for (let service of ns['scheduled_service']) { + services.push(`${service}`); + } + let podStr = ''; + for (let pod of ns['pods']) { + if (!pod.includes("!!!!!!")) { + podStr += `<b>[${pod}]</b>`; + } else { + pod = pod.replace("!!!!!!",""); + podStr += `<b variant='h6' style="color:red">[${pod}]</b>`; + } + podStr += "<br/>"; + } + return ( + <TableRow key={idx}> + <TableCell key="ns['name']">{ns['name']}</TableCell> + <TableCell key="ns['InternalIP']">{ns['InternalIP']}</TableCell> + <TableCell key="gpuCap">{gpuCap}</TableCell> + <TableCell key="gpuUsed">{gpuUsed}</TableCell> + <TableCell key="availableGPU">{availableGPU}</TableCell> + <TableCell key="status">{status}</TableCell> + <TableCell key="services"> + { + <ServicesChips services={services}/> + } + </TableCell> + <TableCell key="podStr" dangerouslySetInnerHTML={{ __html: podStr }}></TableCell> + </TableRow> + ) + }) + } + </TableBody> + </Table> + </MuiThemeProvider> + </Paper> + </Container> + </TabPanel> + </SwipeableViews> + </Fragment> + ) + } else { + return ( + <Box display="flex" justifyContent="center"> + <CircularProgress/> + </Box> + ) + } + +} + +export default ClusterStatus; + diff --git a/src/dashboard/src/pages/CommonComponents/TabPanel.tsx b/src/dashboard/src/pages/CommonComponents/TabPanel.tsx new file mode 100644 index 000000000..25f704bd0 --- /dev/null +++ b/src/dashboard/src/pages/CommonComponents/TabPanel.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import Typography from '@material-ui/core/Typography'; +import Box from '@material-ui/core/Box'; +interface TabPanelProps { + children?: React.ReactNode; + dir?: string; + index: any; + value: any; +} + +export const TabPanel = (props: TabPanelProps) => { + const { children, value, index, ...other } = props; + return ( + <Typography + component="div" + role="tabpanel" + hidden={value !== index} + id={`full-width-tabpanel-${index}`} + aria-labelledby={`full-width-tab-${index}`} + {...other} + > + <Box p={3}>{children}</Box> + </Typography> + ) +} diff --git a/src/dashboard/src/pages/CommonComponents/a11yProps.ts b/src/dashboard/src/pages/CommonComponents/a11yProps.ts new file mode 100644 index 000000000..2aec97d0e --- /dev/null +++ b/src/dashboard/src/pages/CommonComponents/a11yProps.ts @@ -0,0 +1,6 @@ +export const a11yProps = (index: any) => { + return { + id: `full-width-tab-${index}`, + 'aria-controls': `full-width-tabpanel-${index}`, + }; +} diff --git a/src/dashboard/src/pages/Home/GPUCard.tsx b/src/dashboard/src/pages/Home/GPUCard.tsx new file mode 100644 index 000000000..9b1e33db1 --- /dev/null +++ b/src/dashboard/src/pages/Home/GPUCard.tsx @@ -0,0 +1,282 @@ +import React, {useEffect, useState} from "react"; +import { Link } from "react-router-dom"; +import useFetch, { useGet } from "use-http/dist"; +import { + Avatar, + Button, + Card, + CardActions, + CardContent, + CardHeader, Dialog, + Divider, + IconButton, + InputAdornment, + Menu, + MenuItem, Snackbar, SnackbarContent, + TextField, + Tooltip +} from "@material-ui/core"; +import { makeStyles, createStyles, useTheme, Theme } from "@material-ui/core/styles"; +import { MoreVert, FileCopyRounded} from "@material-ui/icons"; + +import { Cell, PieChart, Pie, ResponsiveContainer } from "recharts"; +import UserContext from "../../contexts/User"; +import TeamsContext from '../../contexts/Teams'; +import {green, lightGreen,deepOrange} from "@material-ui/core/colors"; +import _ from 'lodash'; +import copy from 'clipboard-copy' +const useStyles = makeStyles((theme: Theme) => createStyles({ + avatar: { + backgroundColor: theme.palette.secondary.main, + }, + cardHeaderContent: { + width: 0 + }, + textField: { + marginLeft: theme.spacing(1), + marginRight: theme.spacing(1), + }, + chart: { + padding: 3, + backgroundColor: theme.palette.background.default, + }, + dialogText: { + color:green[400] + }, + success: { + backgroundColor: green[600], + }, +})); + +const ActionIconButton: React.FC<{cluster?: string}> = ({cluster}) => { + const [open, setOpen] = React.useState(false); + const iconButton = React.useRef<any>(); + const onIconButtonClick = React.useCallback(() => setOpen(true), [setOpen]); + const onMenuClose = React.useCallback(() => setOpen(false), [setOpen]); + const onMenuItemClick = React.useCallback(() => setOpen(false), [setOpen]); + + return ( + <> + <IconButton ref={iconButton} onClick={onIconButtonClick}> + <MoreVert/> + </IconButton> + <Menu + anchorEl={iconButton.current} + anchorOrigin={{ horizontal: "right", vertical: "top" }} + transformOrigin={{ horizontal: "right", vertical: "top" }} + open={open} + onClose={onMenuClose} + > + <MenuItem component={Link} to={"/cluster-status"}>Cluster Status</MenuItem> + <MenuItem component={Link} to={`/jobs/${cluster}`}>View Jobs</MenuItem> + {/*<MenuItem onClick={onMenuItemClick}>Manage Users</MenuItem>*/} + </Menu> + </> + ) +}; + +const Chart: React.FC<{ + available: number; + used: number; + reserved: number; + isActive: boolean; + +}> = ({ available, used, reserved ,isActive}) => { + const theme = useTheme(); + let data = [ + { name: "Available", value: available, color: lightGreen[400] }, + { name: "Used", value: used, color: theme.palette.grey[500] }, + { name: "Reserved", value: reserved, color: deepOrange[400]}, + ]; + if (reserved === 0) { + data = data.filter((item)=>item.name !== 'Reserved') + } + return ( + <ResponsiveContainer aspect={16 / 9}> + <PieChart> + <Pie + // hide={!isActive} + isAnimationActive={isActive} + data={data} + dataKey="value" + label={({ name, value }) => `${name} ${value}`} + labelLine={false} + > + { data.map(({ name, color }) => <Cell key={name} fill={color}/>) } + </Pie> + </PieChart> + </ResponsiveContainer> + ) +} + +export const DirectoryPathTextField: React.FC<{ + label: string; + value: string; +}> = ({ label, value }) => { + const styles = useStyles(); + const input = React.useRef<HTMLInputElement>(null); + const [openCopyWarn, setOpenCopyWarn] = React.useState(false); + const handleWarnClose = () => { + setOpenCopyWarn(false); + } + const onMouseOver = React.useCallback(() => { + if (input.current) { + input.current.select(); + } + }, [input]) + const onFocus = React.useCallback(() => { + if (input.current) { + input.current.select(); + } + }, + [input]); + const handleCopy = React.useCallback(() => { + if (input.current) { + copy(input.current.innerHTML).then(()=>{ + setOpenCopyWarn(true) + }) + + } + },[input]) + return ( + <> + <TextField + inputRef={input} + label={label} + value={value} + multiline + rows={2} + fullWidth + variant="outlined" + margin="dense" + InputProps={{ + readOnly: true, + endAdornment: ( + <InputAdornment position="end"> + <Tooltip title="Copy" placement="right"> + <IconButton> + <FileCopyRounded/> + </IconButton> + </Tooltip> + </InputAdornment> + ) + }} + onMouseOver={onMouseOver} + onFocus={onFocus} + onClick={handleCopy} + /> + <Snackbar + anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }} + open={openCopyWarn} + autoHideDuration={500} + onClose={handleWarnClose} + ContentProps={{ + 'aria-describedby': 'message-id', + }} + > + <SnackbarContent + className={styles.success} + aria-describedby="client-snackbar" + message={<span id="message-id" >Successfully copied</span>} + /> + </Snackbar> + </> + ); +} + +const GPUCard: React.FC<{ cluster: string }> = ({ cluster }) => { + const styles = useStyles(); + const [activeJobs, setActiveJobs] = useState(0); + const [available, setAvailable] = useState(0); + const [used, setUsed] = useState(0); + const [reversed, setReserved] = useState(0); + const [workStorage, setWorkStorage ] = useState(''); + const [dataStorage, setDataStorage] = useState(''); + const [activate,setActivate] = useState(false); + const { email } = React.useContext(UserContext); + const {selectedTeam} = React.useContext(TeamsContext); + const options = { + onMount: true + } + const fetchDiretoryUrl = `api/clusters/${cluster}`; + const request = useFetch(fetchDiretoryUrl,options); + const fetchDirectories = async () => { + const data = await request.get(''); + const name = typeof email === 'string' ? email.split('@', 1)[0] : email; + setDataStorage(data.dataStorage); + setWorkStorage(`${data.workStorage}/${name}`); + } + const fetchClusterStatusUrl = `/api`; + const requestClusterStatus = useFetch(fetchClusterStatusUrl, options); + const fetchClusterStatus = async () => { + setActivate(false); + const data = await requestClusterStatus.get(`/teams/${selectedTeam}/clusters/${cluster}`); + return data; + // const availableGpu = !_.isEmpty(data['gpu_avaliable']) ? (Number)(Object.values(data['gpu_avaliable'])[0]) : 0; + // setAvailable(availableGpu); + // const usedGpu = !_.isEmpty(data['gpu_used']) ? (Number)(Object.values(data['gpu_used'])[0]) : 0; + // setUsed(usedGpu); + // const reversedGpu = !_.isEmpty(data['gpu_unschedulable']) ? (Number)(Object.values(data['gpu_unschedulable'])[0]) : 0; + // setReserved(reversedGpu); + // setActiveJobs((Number)(data['AvaliableJobNum'])); + // setActivate(true); + } + useEffect(()=>{ + fetchDirectories(); + fetchClusterStatus().then((res)=>{ + const availableGpu = !_.isEmpty(res['gpu_avaliable']) ? (Number)(Object.values(res['gpu_avaliable'])[0]) : 0; + setAvailable(availableGpu); + const usedGpu = !_.isEmpty(res['gpu_used']) ? (Number)(Object.values(res['gpu_used'])[0]) : 0; + setUsed(usedGpu); + const reversedGpu = !_.isEmpty(res['gpu_unschedulable']) ? (Number)(Object.values(res['gpu_unschedulable'])[0]) : 0; + setReserved(reversedGpu); + setActiveJobs((Number)(res['AvaliableJobNum'])); + setActivate(true); + }) + },[selectedTeam]); + return ( + <Card> + <CardHeader + title={cluster} + titleTypographyProps={{ + component: "h3", + variant: "body2", + noWrap: true + }} + subheader={` ${activeJobs} Active Jobs`} + action={<ActionIconButton cluster={cluster}/>} + classes={{ content: styles.cardHeaderContent }} + /> + <CardContent className={styles.chart}> + <Chart available={available} used={used} reserved={reversed} isActive={activate} /> + </CardContent> + <CardActions> + <Button component={Link} + to={{pathname: "/submission/training-cluster", state: { cluster } }} + size="small" color="secondary" + > + Submit Training Job + </Button> + <Button component={Link} + to={{pathname: "/submission/data", state: { cluster } }} + size="small" color="secondary" + > + Submit Data Job + </Button> + </CardActions> + <Divider/> + <CardContent> + <DirectoryPathTextField + label="Work Directory" + value={workStorage} + /> + <DirectoryPathTextField + label="Data Directory" + value={dataStorage} + /> + </CardContent> + </Card> + ); +}; + +export default GPUCard; diff --git a/src/dashboard/src/pages/Home/index.tsx b/src/dashboard/src/pages/Home/index.tsx new file mode 100644 index 000000000..ce6da0ddf --- /dev/null +++ b/src/dashboard/src/pages/Home/index.tsx @@ -0,0 +1,27 @@ +import React from "react"; + +import { Box } from "@material-ui/core"; +import ClustersContext from "../../contexts/Clusters"; +import GPUCard from "./GPUCard"; +import Content from "../../layout/Content"; +import Drawer from "../../layout/Drawer/index"; +import _ from 'lodash'; +const Home: React.FC = () => { + + const { clusters } = React.useContext(ClustersContext); + console.log(clusters) + return ( + <> + <Box display="flex" flexWrap="wrap" paddingTop={5}> + {//const filterclusters = clusters.filter((cluster)=>(boolean)cluster["admin"]); + clusters && _.map(clusters,'id').map((cluster) => ( + <Box key={cluster} maxWidth={360} padding={1}> + <GPUCard cluster={cluster}/> + </Box> + ))} + </Box> + </> + ) +}; + +export default Home; diff --git a/src/dashboard/src/pages/Job/Details/Brief.tsx b/src/dashboard/src/pages/Job/Details/Brief.tsx new file mode 100644 index 000000000..49f55d435 --- /dev/null +++ b/src/dashboard/src/pages/Job/Details/Brief.tsx @@ -0,0 +1,65 @@ +import React, { useContext } from 'react'; +import { + Card, + List, + ListItem, + ListItemText, +} from '@material-ui/core'; + +import Context from './Context'; +import DoneIcon from "@material-ui/icons/Done"; +import ClearIcon from "@material-ui/icons/Clear"; +import {green, red} from "@material-ui/core/colors"; + +const Brief: React.FC = () => { + const { cluster, job } = useContext(Context); + return ( + <Card> + <List> + <ListItem><ListItemText primary="Job Id" secondary={job['jobId']}/></ListItem> + <ListItem><ListItemText primary="Job Name" secondary={job['jobName']}/></ListItem> + <ListItem><ListItemText primary="Docker Image" secondary={job['jobParams']['image']}/></ListItem> + <ListItem> + <ListItemText + primary="Command" + secondary={job['jobParams']['cmd']} + secondaryTypographyProps={{ component: 'pre' }} + /> + </ListItem> + <ListItem> + <ListItemText + primary="Data Path" + secondary={`${cluster ? cluster['dataStorage'] : ''}/${job['jobParams']['dataPath']}`} + /> + </ListItem> + <ListItem> + <ListItemText + primary="Work Path" + secondary={`${cluster ? cluster['dataStorage'] : ''}/${job['jobParams']['workPath']}`} + /> + </ListItem> + <ListItem> + <ListItemText + primary="Job Path" + secondary={`${cluster ? cluster['workStorage'] : ''}/${job['jobParams']['jobPath']}`} + /> + </ListItem> + <ListItem><ListItemText primary="PreemptionAllowed" secondary={job['jobParams']['preemptionAllowed'] ? <DoneIcon style={{color:green[500]}}></DoneIcon> : <ClearIcon style={{color:red[500]}}></ClearIcon>}/></ListItem> + <ListItem><ListItemText primary="Job Type" secondary={job['jobParams']['jobType']}/></ListItem> + { + job['jobParams']['jobtrainingtype'] === 'PSDistJob' && <ListItem><ListItemText primary="Number of Nodes" secondary={job['jobParams']['numpsworker']}/></ListItem> + } + { + job['jobParams']['jobtrainingtype'] === 'PSDistJob' && <ListItem><ListItemText primary="Total of GPUS" secondary={job['jobParams']['numpsworker'] * job['jobParams']['resourcegpu']}/></ListItem> + } + { + job['jobParams']['jobtrainingtype'] === 'RegularJob' && <ListItem><ListItemText primary="Number of GPUS" secondary={job['jobParams']['resourcegpu']}/></ListItem> + } + <ListItem><ListItemText primary="Job Status" secondary={job['jobStatus']}/></ListItem> + <ListItem><ListItemText primary="Job Submission Time" secondary={job['jobTime']}/></ListItem> + </List> + </Card> + ); +}; + +export default Brief; diff --git a/src/dashboard/src/pages/Job/Details/Context.ts b/src/dashboard/src/pages/Job/Details/Context.ts new file mode 100644 index 000000000..96c79d4e1 --- /dev/null +++ b/src/dashboard/src/pages/Job/Details/Context.ts @@ -0,0 +1,14 @@ +import { createContext } from 'react'; + +interface DetailsContext { + clusterId: string; + jobId: string; + cluster?: any; + job: any; +} + +export default createContext<DetailsContext>({ + clusterId: '', + jobId: '', + job: {} +}) diff --git a/src/dashboard/src/pages/Job/Details/Endpoints.tsx b/src/dashboard/src/pages/Job/Details/Endpoints.tsx new file mode 100644 index 000000000..118205671 --- /dev/null +++ b/src/dashboard/src/pages/Job/Details/Endpoints.tsx @@ -0,0 +1,251 @@ +import React, { useContext, useEffect, useState, useCallback, useRef } from 'react'; + +import { + Card, + CardHeader, + CardContent, + Typography, + FormGroup, + Switch, + FormControlLabel, + InputAdornment, + IconButton, + TextField, Snackbar, SnackbarContent, Tooltip, SvgIcon, Link, +} from '@material-ui/core'; +import { Add, FileCopy } from '@material-ui/icons'; + +import useFetch from 'use-http'; + +import Context from './Context'; +import copy from "clipboard-copy"; +import {green} from "@material-ui/core/colors"; + +interface ListProps { + endpoints: any[]; + setOpen: any; +} + +const List: React.FC<ListProps> = ({ endpoints, setOpen }) => { + const { cluster, job } = useContext(Context); + const handleCopy = (e: any, copyText: string) => { + if (copyText) { + copy(copyText).then(()=>{ + setOpen(true) + }) + } + } + const sortSSH =(a: any, b: any) => { + var worker1 = a.podName.split("-").pop().replace("worker", ""); + var worker2 = b.podName.split("-").pop().replace("worker", ""); + return parseInt(worker1) - parseInt(worker2); + } + const finalEnpoints = endpoints.filter((endpoint: any)=>endpoint['status'] === 'running').sort( + (endpointA, endpointB) => endpointA['port'] - endpointB['port']); + const renderIpython = (sortedEnpoints: any) => { + const filteredIptyhon = sortedEnpoints.filter((endPoint: any) => endPoint['name'] === 'ipython') + return filteredIptyhon.map((endpoint: any)=>{ + const url = `http://${endpoint['nodeName']}.${endpoint['domain']}:${endpoint['port']}` + return ( + <Typography key={endpoint['id']}> + {'iPython: '} + <Link href={url} target="_blank">{url}</Link> + </Typography> + ); + }) + } + const renderTensorboard = (sortedEnpoints: any) => { + const filteredTensorboard = sortedEnpoints.filter((endPoint: any) => endPoint['name'] === 'tensorboard'); + return filteredTensorboard.map((endpoint: any)=>{ + const url = `http://${endpoint['nodeName']}.${endpoint['domain']}:${endpoint['port']}` + return ( + <Typography key={endpoint['id']}> + {'TensorBoard: '} + <Link href={url} target="_blank">{url}</Link> + </Typography> + ); + }) + } + const renderSSH = (sortedEnpoints: any) => { + const filteredSSH = sortedEnpoints.filter((endPoint: any) => endPoint['name'] === 'ssh' && endPoint['id'].indexOf('ps')=== -1); + const sortedFilteredSSH = filteredSSH.sort((a: any, b: any) => sortSSH(a, b)); + return sortedFilteredSSH.map((endpoint: any) => { + const identify = `${cluster['workStorage'].replace(/^file:\/\//i, '//')}/${job['jobParams']['workPath']}/.ssh/id_rsa` + const host = `${endpoint['nodeName']}.${endpoint['domain']}`; + const task = job['jobParams']['jobtrainingtype'] === 'PSDistJob' ? endpoint['podName'].split('-').pop() : ''; + const command = `ssh -i ${identify} -p ${endpoint['port']} ${endpoint['username']}@${host}` + return ( + <Typography key={endpoint['id']}> + {task} SSH : + <Tooltip title="Copy"> + <IconButton color="secondary" size="medium" onClick={(event) => handleCopy(event, command)} aria-label="delete"> + <FileCopy/> + </IconButton> + </Tooltip> + {command} + </Typography> + ) + }) + } + const renderRemain = (sortedEnpoints: any) => { + const filterRemain = sortedEnpoints.filter((endPoint: any)=> { + return (endPoint['name'] !== 'ssh' && + endPoint['name'] !== 'tensorboard' && + endPoint['name'] !== 'ipython' && + endPoint['id'].indexOf('ps')=== -1) + }) + return filterRemain.map((endpoint: any) => { + return ( + <Typography key={endpoint['id']}> + {`Port ${endpoint['podPort']}:`} + <Link href={`http://${endpoint['nodeName']}.${endpoint['domain']}:${endpoint['port']}`} target="_blank">{`http://${endpoint['nodeName']}.${endpoint['domain']}:${endpoint['port']}`}</Link> + </Typography> + ) + }) + } + + return ( + <> + <CardContent> + { + renderSSH(finalEnpoints) + } + { + renderIpython(finalEnpoints) + } + { + renderTensorboard(finalEnpoints) + } + { + renderRemain(finalEnpoints) + } + </CardContent> + </> + ) +}; + +interface ControllerProps { + endpoints: any[]; + post(data: any): Promise<any>; +} + +const Controller: React.FC<ControllerProps> = ({ endpoints, post }) => { + const [sshEnabled, setSshEnabled] = useState(false); + const [ipythonEnabled, setIpythonEnabled] = useState(false); + const [tensorboardEnabled, setTensorboardEnabled] = useState(false); + const [interactivePort, setInteractivePort] = useState(); + + const onSshChange = useCallback((event: unknown, checked: boolean) => { + if (checked) { + post({ endpoints: ['ssh'] }); + setSshEnabled(true); + } + }, [post]); + const onIpythonChange = useCallback((event: unknown, checked: boolean) => { + if (checked) { + post({ endpoints: ['ipython'] }); + setIpythonEnabled(true); + } + }, [post]); + const onTensorboardChange = useCallback((event: unknown, checked: boolean) => { + if (checked) { + post({ endpoints: ['tensorboard'] }); + setTensorboardEnabled(true); + } + }, [post]); + const onInteractivePortChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => { + setInteractivePort(event.target.valueAsNumber); + }, []); + const submitInteractivePort = useCallback(() => { + post({ endpoints: [{ + name: `port-${interactivePort}`, + podPort: interactivePort + }] }); + }, [post, interactivePort]); + + useEffect(() => { + if (Array.isArray(endpoints)) { + endpoints.forEach(({ name }) => { + if (name === 'ssh') setSshEnabled(true); + if (name === 'ipython') setIpythonEnabled(true); + if (name === 'tensorboard') setTensorboardEnabled(true); + }) + } + }, [endpoints]) + + return ( + <CardContent> + <FormGroup row> + <FormControlLabel + control={<Switch checked={sshEnabled} disabled={sshEnabled} onChange={onSshChange}/>} + label="SSH" + /> + <FormControlLabel + control={<Switch checked={ipythonEnabled} disabled={ipythonEnabled} onChange={onIpythonChange}/>} + label="iPython" + /> + <FormControlLabel + control={<Switch checked={tensorboardEnabled} disabled={tensorboardEnabled} onChange={onTensorboardChange}/>} + label="Tensorboard (will listen on directory ~/tensorboard/<DLWS_JOB_ID>/logs inside docker container.)" + /> + </FormGroup> + <TextField + type="number" + required + fullWidth + variant="outlined" + error={interactivePort < 40000 || interactivePort > 49999 } + label="New Interactive Port (40000-49999)" + value={interactivePort} + onSubmit={submitInteractivePort} + onChange={onInteractivePortChange} + InputProps={{ + endAdornment: ( + <InputAdornment position="end"> + <IconButton edge="end" onClick={submitInteractivePort}> + <Add/> + </IconButton> + </InputAdornment> + ), + }} + /> + </CardContent> + ); +}; +interface EndpointsProps { + setOpen: any; +} +const Endpoints: React.FC<EndpointsProps> = ({setOpen}) => { + const { clusterId, jobId } = useContext(Context); + const { data, get, post } = useFetch(`/api/clusters/${clusterId}/jobs/${jobId}/endpoints`, { onMount: true }); + + const refreshTimeout = useRef<number | null>(null); + const refreshFunction: TimerHandler = useCallback(async () => { + refreshTimeout.current = null; + await get(); + refreshTimeout.current = setTimeout(refreshFunction, 1000); + }, [get]); + + useEffect(() => { + refreshFunction(); + return () => { + if (refreshTimeout.current != null) { + clearTimeout(refreshTimeout.current); + } + } + }, [refreshFunction]); + + return ( + <> + <Card> + <CardHeader + title="Mapped Endpoints" + subheader="Links to access interactive/visualization interface" + /> + { Array.isArray(data) && data.length > 0 && <List endpoints={data} setOpen={setOpen}/> } + <Controller endpoints={data} post={post} /> + </Card> + </> + ); +} + +export default Endpoints; diff --git a/src/dashboard/src/pages/Job/Details/Log.tsx b/src/dashboard/src/pages/Job/Details/Log.tsx new file mode 100644 index 000000000..5d072d245 --- /dev/null +++ b/src/dashboard/src/pages/Job/Details/Log.tsx @@ -0,0 +1,24 @@ +import React, { useContext } from 'react'; + +import { + Card, + CardHeader, + CardContent, + Typography, +} from '@material-ui/core'; + +import Context from './Context'; + +const Log: React.FC = () => { + const { job } = useContext(Context); + return ( + <Card> + <CardHeader title="Console Output"/> + <CardContent> + <Typography component='pre' style={{ width:"1632px" }}>{job['log']}</Typography> + </CardContent> + </Card> + ); +}; + +export default Log; diff --git a/src/dashboard/src/pages/Job/Details/Monitor.tsx b/src/dashboard/src/pages/Job/Details/Monitor.tsx new file mode 100644 index 000000000..6c1bce64f --- /dev/null +++ b/src/dashboard/src/pages/Job/Details/Monitor.tsx @@ -0,0 +1,27 @@ +import React, {useContext, useEffect, useState} from 'react'; + +import { + Card, + CardHeader, + CardMedia, CircularProgress, +} from '@material-ui/core'; + +import Context from './Context'; + +const Monitor: React.FC = () => { + const { cluster, job } = useContext(Context); + const jobStatusGrafanaUrl = `${cluster['grafana']}/dashboard/db/job-status?var-job_name=${encodeURIComponent(job['jobId'])}`; + + return ( + <Card> + <CardHeader title="Job analytics and monitoring"/> + <CardMedia + component="iframe" + src={jobStatusGrafanaUrl} + height={1080} + /> + </Card> + ); +}; + +export default Monitor; diff --git a/src/dashboard/src/pages/Job/Details/RunCommand.tsx b/src/dashboard/src/pages/Job/Details/RunCommand.tsx new file mode 100644 index 000000000..2d5a66896 --- /dev/null +++ b/src/dashboard/src/pages/Job/Details/RunCommand.tsx @@ -0,0 +1,48 @@ +import React, { useState, useCallback, useContext } from 'react'; + +import { + IconButton, + InputAdornment, + TextField, +} from '@material-ui/core'; +import {DirectionsRun} from '@material-ui/icons'; + +import { useFetch } from 'use-http'; + +import Context from './Context'; + +const RunCommand: React.FC = () => { + const { clusterId, jobId } = useContext(Context); + const { post } = useFetch(`/api/clusters/${clusterId}/jobs/${jobId}/commands`); + // const [commands] = get(); + + const [command, setCommand] = useState(''); + const onCommandChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => { + setCommand(event.target.value) + }, [setCommand]); + + const runCommand = useCallback(() => { + post({ command }) + }, [post, command]); + + return ( + <TextField + fullWidth + variant="outlined" + label="Run Command" + value={command} + onChange={onCommandChange} + InputProps={{ + endAdornment: ( + <InputAdornment position="end"> + <IconButton edge="end" onClick={runCommand}> + <DirectionsRun/> + </IconButton> + </InputAdornment> + ), + }} + /> + ); +} + +export default RunCommand diff --git a/src/dashboard/src/pages/Job/Details/index.tsx b/src/dashboard/src/pages/Job/Details/index.tsx new file mode 100644 index 000000000..a7209f72d --- /dev/null +++ b/src/dashboard/src/pages/Job/Details/index.tsx @@ -0,0 +1,164 @@ +import React, {Fragment, useEffect, useState} from 'react'; +import { + Theme, + useTheme, + Box, + Tab, + Tabs, + makeStyles, + createStyles, + AppBar, + Container, + CircularProgress, + useMediaQuery, Snackbar, SnackbarContent, +} from '@material-ui/core'; +import { useGet } from 'use-http'; + +import UserContext from '../../../contexts/User'; +import Context from './Context'; +import Brief from './Brief'; +import RunCommand from './RunCommand'; +import Log from './Log'; +import Monitor from './Monitor'; +import Endpoints from './Endpoints'; +import { TabPanel } from '../../CommonComponents/TabPanel' +import {a11yProps} from "../../CommonComponents/a11yProps"; +import SwipeableViews from "react-swipeable-views"; +import theme from "../../../contexts/MonospacedTheme"; +import {green} from "@material-ui/core/colors"; +interface Props { + clusterId: string; + jobId: string; + job: any; +} + +const JobDetails: React.FC<Props> = ({ clusterId, jobId, job }) => { + const { email } = React.useContext(UserContext); + const [cluster] = useGet(`/api/clusters/${clusterId}`, { onMount: true }); + const [value, setValue] = React.useState(0); + const theme = useTheme(); + const[showIframe, setShowIframe] = useState(false); + const handleChangeTab = (event: React.ChangeEvent<{}>, newValue: number) => { + setShowIframe(false) + setTimeout(()=>{ + setShowIframe(true); + },2000); + setValue(newValue); + } + const handleChangeIndex = (index: number) => { + setValue(index); + } + const isReadOnly = (email !== job['userName']); + useEffect(()=>{ + let mount = true; + let timeout: any; + timeout = setTimeout(()=>{ + setShowIframe(true); + },2000); + return () => { + mount = false; + clearTimeout(timeout) + } + },[]) + const isDesktop = useMediaQuery(theme.breakpoints.up("sm")); + const [showOpen, setshowOpen] = useState(false) + const handleWarnClose = () => { + setshowOpen(false) + } + if (isReadOnly) { + return ( + <Context.Provider value={{ jobId, clusterId, job, cluster }}> + <Container maxWidth={isDesktop ? 'lg' : 'xs'} > + <AppBar position="static" color="default"> + <Tabs + value={value} + onChange={handleChangeTab} + indicatorColor="primary" + textColor="primary" + variant="fullWidth" + aria-label="full width tabs example" + > + <Tab label="Brief" {...a11yProps(0)} /> + <Tab label=" Job analytics and monitoring" {...a11yProps(2)} /> + <Tab label=" Console Output" {...a11yProps(3)} /> + </Tabs> + </AppBar> + </Container> + <SwipeableViews + axis={theme.direction === 'rtl' ? 'x-reverse' : 'x'} + index={value} + onChangeIndex={handleChangeIndex} + > + <TabPanel value={value} index={0} dir={theme.direction}> + <Container maxWidth={isDesktop ? 'lg' : 'xs'} ><Brief/></Container> + </TabPanel> + <TabPanel value={value} index={1} dir={theme.direction}> + { showIframe ? cluster && <Container maxWidth={isDesktop ? 'lg' : 'xs'} ><Monitor/></Container> : <CircularProgress/>} + </TabPanel> + <TabPanel value={value} index={2} dir={theme.direction}> + { job['log'] && <Container maxWidth={isDesktop ? 'lg' : 'xs'} ><Log/></Container> } + </TabPanel> + </SwipeableViews> + + </Context.Provider> + ); + } else { + return ( + <Context.Provider value={{ jobId, clusterId, job, cluster }}> + <Container maxWidth={isDesktop ? 'lg' : 'xs'} > + <AppBar position="static" color="default"> + <Tabs + value={value} + onChange={handleChangeTab} + indicatorColor="primary" + textColor="primary" + variant="fullWidth" + aria-label="full width tabs example" + > + <Tab label="Brief" {...a11yProps(0)} /> + <Tab label="Mapped Endpoints" {...a11yProps(1)} /> + {/*<Tab label="Run Command" {...a11yProps(2)} />*/} + <Tab label=" Job analytics and monitoring" {...a11yProps(2)} /> + <Tab label=" Console Output" {...a11yProps(3)} /> + </Tabs> + </AppBar> + <SwipeableViews + axis={theme.direction === 'rtl' ? 'x-reverse' : 'x'} + index={value} + onChangeIndex={handleChangeIndex} + > + <TabPanel value={value} index={0} dir={theme.direction}> + <Container maxWidth={isDesktop ? 'lg' : 'xs'} ><Brief/></Container> + </TabPanel> + <TabPanel value={value} index={1} dir={theme.direction}> + {(job['jobStatus'] !== 'pausing' && job['jobStatus'] !== 'paused') && <Container maxWidth={isDesktop ? 'lg' : 'xs'} ><Endpoints setOpen={setshowOpen}/></Container>} + </TabPanel> + <TabPanel value={value} index={2} dir={theme.direction}> + { showIframe ? cluster && <Container maxWidth={isDesktop ? 'lg' : 'xs'} ><Monitor/></Container> : <CircularProgress/>} + </TabPanel> + <TabPanel value={value} index={3} dir={theme.direction}> + { job['log'] && <Box marginTop={2}><Log/></Box> } + </TabPanel> + </SwipeableViews> + <Snackbar + anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }} + open={showOpen} + autoHideDuration={1000} + onClose={handleWarnClose} + ContentProps={{ + 'aria-describedby': 'message-id', + }} + > + <SnackbarContent + style={{backgroundColor:green[400]}} + aria-describedby="client-snackbar" + message={<span id="message-id" >{"Copied"}</span>} + /> + </Snackbar> + </Container> + </Context.Provider> + ); + } +}; + +export default JobDetails; diff --git a/src/dashboard/src/pages/Job/index.tsx b/src/dashboard/src/pages/Job/index.tsx new file mode 100644 index 000000000..eb5d2e472 --- /dev/null +++ b/src/dashboard/src/pages/Job/index.tsx @@ -0,0 +1,59 @@ +import React, { useCallback } from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; +import { + Box, + Button, + CircularProgress, + Dialog, + DialogActions, + DialogTitle, + DialogContent, + DialogContentText +} from '@material-ui/core'; +import Details from './Details'; +import useJob from './useJob'; + +type Job = any; + +interface Params { + clusterId: string; + jobId: string; +} + +const ErrorDialog = withRouter(({ match, history }) => { + const { clusterId, jobId } = match.params; + const onClick = useCallback(() => history.push('/jobs'), [history]) + return ( + <Dialog open> + <DialogTitle> + Error + </DialogTitle> + <DialogContent> + <DialogContentText> + Failed to fetch the Job {jobId} in Cluster {clusterId}. + </DialogContentText> + </DialogContent> + <DialogActions> + <Button onClick={onClick} color="primary"> + Back + </Button> + </DialogActions> + </Dialog> + ); +}); + +const Job: React.FC<RouteComponentProps<Params>> = ({ match }) => { + const { clusterId, jobId } = match.params; + const [job, error] = useJob(clusterId, jobId); + + if (error) return <ErrorDialog/>; + if (job) return <Details clusterId={clusterId} jobId={jobId} job={job}/>; + + return ( + <Box display="flex" justifyContent="center"> + <CircularProgress/> + </Box> + ); +}; + +export default Job diff --git a/src/dashboard/src/pages/Job/useJob.ts b/src/dashboard/src/pages/Job/useJob.ts new file mode 100644 index 000000000..3b29d957e --- /dev/null +++ b/src/dashboard/src/pages/Job/useJob.ts @@ -0,0 +1,32 @@ +import { useEffect, useState } from "react"; +import { useGet } from "use-http"; + +type Job = object; +type UseJob = [Job | undefined, Error | undefined]; + +const useJob = (clusterId: string, jobId: string): UseJob => { + const [job, setJob] = useState<Job>(); + const { data, error, get } = useGet<Job>({ + url: `/api/clusters/${clusterId}/jobs/${jobId}`, + onMount: true + }); + + useEffect(() => { + if (data === undefined) return; + + setJob(data); + + const timeout = setTimeout(get, 1000) + return () => { + clearTimeout(timeout); + } + }, [data, get]); + + if (job !== undefined) { + return [job, undefined]; + } + + return [undefined, error]; +} + +export default useJob; diff --git a/src/dashboard/src/pages/Jobs/index.tsx b/src/dashboard/src/pages/Jobs/index.tsx new file mode 100644 index 000000000..12282f814 --- /dev/null +++ b/src/dashboard/src/pages/Jobs/index.tsx @@ -0,0 +1,998 @@ +import React, {Fragment, useEffect, useRef, useState} from "react"; +import { + SnackbarContent, + Tabs, + Tab, + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Snackbar, + Box, + CircularProgress, + TextField, + Grid, + SvgIcon, + Tooltip, + AppBar, + useTheme, + Chip, + Menu, + ListItem, + List, + MenuItem, + ListItemText, + Select, + withStyles, InputBase, Container, FormControl, InputLabel, useMediaQuery +} from "@material-ui/core"; +import CheckCircleIcon from '@material-ui/icons/CheckCircle'; +import { makeStyles, Theme, createStyles } from "@material-ui/core/styles"; +import Slide from '@material-ui/core/Slide'; +import {red, grey, green, blue} from "@material-ui/core/colors"; +import { TabPanel } from '../CommonComponents/TabPanel' +import {Link} from "react-router-dom"; +import { TransitionProps } from '@material-ui/core/transitions'; +import useFetch,{usePut} from "use-http/dist"; +import MaterialTable from 'material-table'; +import queryString from 'query-string'; +import useJobs from './useJobs'; +import _ from 'lodash'; +import ClusterContext from "../../contexts/Clusters"; +import useJobsAll from "./useJobsAll"; +import IconButton from "@material-ui/core/IconButton"; +import DeleteIcon from '@material-ui/icons/Delete'; +import CheckIcon from '@material-ui/icons/CheckSharp'; +import theme from "../../contexts/MonospacedTheme"; + +const variantIcon = { + success: CheckCircleIcon, +}; +const Transition = React.forwardRef<unknown, TransitionProps>(function Transition(props, ref) { + return <Slide direction="down" ref={ref} {...props} />; +}); +interface Props { + className?: string; + message?: string; + onClose?: () => void; + variant: keyof typeof variantIcon; +} + + +function a11yProps(index: any) { + return { + id: `simple-tab-${index}` + }; +} +const { DateTime } = require('luxon'); +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1, + backgroundColor: theme.palette.background.paper + }, + searchContainer: { + display: "flex", + alignItems: "center", + marginLeft:20, + marginTop:40 + }, + input: { + marginLeft: 20, + flex: 1 + }, + iconButton: { + padding: 10 + }, + formControl: { + margin: theme.spacing(1), + minWidth: 160, + + }, + selectContainer: { + backgroundColor: theme.palette.background.paper, + minWidth: 160, + }, + allow: { + color: green[500] + }, + notAllowed:{ + color: red[500] + }, + linkStyle:{ + textDecoration: 'none', + color: blue[500], + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + display: 'inline-block', + width: '100px', + overflow: 'hidden', + '&:hover': { + height: 'auto', + wordBbreak:'break-all', + whiteSpace: 'pre-wrap', + textDecoration: 'none' + } + }, + success: { + backgroundColor: green[600], + }, + inputField: { + fontSize:'12px' + } + }) +); +const Jobs: React.FC = (props: any) => { + const classes = useStyles(); + const [value, setValue] = React.useState(0); + const [refresh, setRefresh] = React.useState(false); + const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => { + setRefresh(false); + setTimeout(()=>{ + setRefresh(true); + },1000); + setValue(newValue); + } + useEffect(()=>{ + let mount = true; + let timeout: any; + timeout = setTimeout(()=>{ + setRefresh(true); + },1000); + return () => { + mount = false; + clearTimeout(timeout) + } + },[]) + const[open, setOpen] = React.useState(false); + const[openApprove, setOpenApprove] = React.useState(false); + const[openPause, setOpenPause] = React.useState(false); + const[openResume, setOpenResume] = React.useState(false); + const[openKillWarn, setOpenKillWarn] = React.useState(false); + const[openApproveWarn, setOpenApproveWarn] = React.useState(false); + const[openPauseWarn, setOpenPauseWarn] = React.useState(false); + const[openResumeWarn, setOpenResumeWarn] = React.useState(false); + const[openUpdatePriority,setOpenUpdatePriority] = React.useState(false); + const [openUpatePriorityWarn, setUpdatePriorityWarn] = React.useState(false); + const { clusters } = React.useContext(ClusterContext); + const [currentJob, setCurrentJob] = React.useState({jobId:'',cluster:'',priority: 100}); + const deleteUrl = `/api/clusters/`; + const requestDelete = useFetch(deleteUrl); + const killJob = async () => { + const body = {"status":"killing"}; + const data = await requestDelete.put(`${currentJob.cluster}/jobs/${currentJob.jobId}/status/`,body); + return data; + } + const approveJob = async () => { + const body = {"status":"approved"}; + const data = await requestDelete.put(`${currentJob.cluster}/jobs/${currentJob.jobId}/status/`,body); + return data; + } + const pauseJob = async () => { + const body = {"status":"pausing"}; + const data = await requestDelete.put(`${currentJob.cluster}/jobs/${currentJob.jobId}/status/`,body); + return data; + } + const resumeJob = async () => { + const body = {"status":"queued"}; + const data = await requestDelete.put(`${currentJob.cluster}/jobs/${currentJob.jobId}/status/`,body); + return data; + } + const { put: setPriority } = usePut('/api'); + const [currentCluster, setCurrentCluster] = useState(props.match.params.cluster ? props.match.params.cluster : Array.isArray(_.map(clusters,'id') )?_.map(clusters,'id')[0] : ''); + const [jobs, error] = useJobs(); + const [allJobs, err] = useJobsAll(); + const filterJobsByCluster = (jobs: any, clusterName: string) => { + if (clusterName === 'None' || clusterName === '') { + return jobs; + } else { + return jobs.filter((job: any)=>job['cluster'] === clusterName) + } + } + + const filterFinishedJobs = (jobs: any) => { + const filteredJobs = filterJobsByCluster(jobs, currentCluster); + return filteredJobs.filter((job: any) => job['jobStatus'] !== 'running' && + job['jobStatus'] !== 'queued' && job['jobStatus'] !== 'unapproved' && job['jobStatus'] !== 'scheduling' &&job['jobStatus'] !== 'pausing' && job['jobStatus'] !== 'paused' ) + } + const filterRunningJobs = (jobs: any) => { + const filteredJobs = filterJobsByCluster(jobs, currentCluster); + return filteredJobs.filter((job: any) => job['jobStatus'] === 'running') + } + const filterQueuedJobs = (jobs: any) => { + const filteredJobs = filterJobsByCluster(jobs, currentCluster); + return filteredJobs.filter((job: any) => job['jobStatus'] === 'queued' || job['jobStatus'] === 'scheduling' ) + } + const filterPauseJobs = (jobs: any) => { + const filteredJobs = filterJobsByCluster(jobs, currentCluster); + return filteredJobs.filter((job: any) => job['jobStatus'] === 'paused' || job['jobStatus'] === 'pausing' ) + } + const filterUnApprovedJobs = (jobs: any) => { + const filteredJobs = filterJobsByCluster(jobs, currentCluster); + console.log(filteredJobs.filter((job: any)=>job['jobStatus'] === 'unapproved')) + return filteredJobs.filter((job: any)=>job['jobStatus'] === 'unapproved'); + } + const handleClose = () => { + setOpen(false); + setOpenApprove(false); + setOpenPause(false); + setOpenResume(false); + setOpenUpdatePriority(false); + } + const handleWarnClose = () => { + setOpenKillWarn(false); + setOpenApproveWarn(false); + setOpenPauseWarn(false) + setOpenResumeWarn(false) + setUpdatePriorityWarn(false) + } + + const [message,setMessage] = useState(''); + + + const handlePriorityKeyPress = (rowData: any,event: React.KeyboardEvent) => { + //return async () => { + if (event.key === 'Enter') { + setCurrentJob({ + jobId: rowData['jobId'], + cluster:rowData['cluster'], + priority:(event.target as HTMLInputElement).valueAsNumber + }); + setOpenUpdatePriority(true); + } + //}; + }; + + const handleConfirm = (openApprove: boolean) => { + if (openApprove) { + approveJob().then((res)=>{ + if (res) { + setOpenApproveWarn(true); + setOpenApprove(false); + setMessage('Successfully approved'); + } + }) + } else if (openPause) { + pauseJob().then((res)=>{ + if (res) { + setOpenPauseWarn(true); + setOpenPause(false); + setMessage('Successfully paused') + } + }) + } else if (openResume) { + resumeJob().then((res)=>{ + if (res) { + setOpenResumeWarn(true) + setOpenResume(false); + setMessage('Successfully resumed') + } + }) + } else if (openUpdatePriority) { + setOpenUpdatePriority(false) + const body = { "priority": currentJob.priority}; + const response = setPriority(`/clusters/${currentJob.cluster}/jobs/${currentJob.jobId}/priority`, body); + if (response) { + setUpdatePriorityWarn(true); + setMessage('Successfully updated priority') + } else { + alert('Priority set failed'); + } + } else { + killJob().then((res)=> { + if (res) { + setOpenKillWarn(true); + setOpen(false) + setMessage('Successfully killed') + } + }) + } + } + const renderDialog = (job: any) => { + let message = ''; + if (openApprove) { + message = `${job.jobId} will be approved soon`; + } else if (openPause) { + message = `${job.jobId} will be paused soon`; + } else if (openResume) { + message = `${job.jobId} will be resumed soon`; + } else if (open) { + message = `${job.jobId} will be killed soon`; + } else if (openUpdatePriority) { + message = `${job.jobId}'s priority will be updated soon`; + } + if (message === '') { + return null; + } + return ( + <Dialog + open={open || openApprove || openPause || openResume || openUpdatePriority } + TransitionComponent={Transition} + onClose={handleClose} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + <DialogTitle id="alert-dialog-title" style={{ color:red[600] }}>{"Info"}</DialogTitle> + <DialogContent> + <DialogContentText id="alert-dialog-description" style={{color:grey[400]}}> + {message} + </DialogContentText> + </DialogContent> + <DialogActions> + <Button onClick={handleClose} color="primary"> + No + </Button> + <Button onClick={() => handleConfirm(openApprove)} color="secondary" autoFocus> + Yes + </Button> + </DialogActions> + </Dialog> + ) + }; + + const handlePause = (data: any) => { + setCurrentJob({ + cluster:data['cluster'], + jobId: data['jobId'], + priority:currentJob.priority + }) + setOpenPause(true); + } + const handleResume = (data: any) => { + setCurrentJob({ + cluster:data['cluster'], + jobId: data['jobId'], + priority:currentJob.priority + }) + setOpenResume(true); + } + const renderUserName = (rowData: any)=><span>{rowData['userName'].split("@").shift()}</span> + const renderPrioritySet = (rowData: any) => <TextField + key={rowData.jobId} + type="number" + label="Priority" + variant="filled" + defaultValue={rowData.priority} + onKeyPress={(event) => handlePriorityKeyPress(rowData, event)} + fullWidth={true} + InputProps={{ + classes: { + input: classes.inputField, + }, + }} + /> + const renderDateTime = (rowData: any,time?: string)=> { + if (time === 'jobTime') { + return (<span>{ DateTime.fromJSDate(new Date(Date.parse(rowData['jobTime']))).toFormat("yyyy/LL/dd HH:mm:ss")}</span>) + } else if (time === 'startedAt') { + return (<span>{ DateTime.fromJSDate(new Date(Date.parse(rowData['jobStatusDetail'][0]['startedAt']))).toFormat("yyyy/LL/dd HH:mm:ss")}</span>) + } else if (time === 'finishedAt') { + return (<span>{ DateTime.fromJSDate(new Date(Date.parse(rowData['jobStatusDetail'][0]['finishedAt']))).toFormat("yyyy/LL/dd HH:mm:ss")}</span>) + } + } + const renderActions = (props: any) => { + if (props.action.icon === 'Pause' && (props.data.jobStatus === 'paused'||props.data.jobStatus === 'pausing')) { + return ( + <Tooltip title="Resume Job"> + <IconButton style={{ color:green[400] }} size="small" onClick={(event)=>handleResume(props.data)} aria-label="delete"> + <SvgIcon> + <path d="M8 5v14l11-7z"/><path d="M0 0h24v24H0z" fill="none"/> + </SvgIcon> + </IconButton> + </Tooltip> + ) + } + if (props.action.icon === 'Pause') { + return ( + <Tooltip title="Pause Job"> + <IconButton color="secondary" size="small" onClick={(event)=>handlePause(props.data)} aria-label="delete"> + <SvgIcon> + <path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/><path d="M0 0h24v24H0z" fill="none"/> + </SvgIcon> + </IconButton> + </Tooltip> + ) + } + if (props.action.icon === 'Approve' && props.data.jobStatus === 'unapproved') { + return( + <Tooltip title="Approve Job"> + <IconButton color="primary" size={"small"} aria-label="delete" onClick={(event) => props.action.onClick(event, props.data)}> + <CheckIcon /> + </IconButton> + </Tooltip> + ) + } else if (props.action.icon === 'kill') { + return( + <Tooltip title="Kill Job"> + <IconButton color="secondary" size={"small"} aria-label="delete" onClick={(event) => props.action.onClick(event, props.data)}> + <DeleteIcon /> + </IconButton> + </Tooltip> + ) + } else { + return null; + } + } + + + + const onClusterChange = (event: React.ChangeEvent<{ value: unknown }>) => { + setCurrentCluster(event.target.value as string) + const selectedCluster = event.target.value as string; + + } + const isDesktop = useMediaQuery(theme.breakpoints.up("sm")); + if (jobs && allJobs) { + console.log(allJobs) + return ( + <Container maxWidth={isDesktop ? 'lg' : 'xs'}> + {renderDialog(currentJob)} + <Container maxWidth={isDesktop ? 'lg' : 'xs'} > + <AppBar position="static" color="default"> + <Tabs + variant="fullWidth" + value={value} + indicatorColor="primary" + onChange={handleChange} + aria-label="Jobs list tabs" + > + <Tab label="My Jobs" {...a11yProps(0)} /> + <Tab label="All Jobs" {...a11yProps(1)} /> + </Tabs> + </AppBar> + </Container> + <TabPanel value={value} index={0}> + <Container maxWidth={isDesktop ? 'lg' : 'xs'} > + <TextField + select + label="Choose Cluster" + fullWidth + variant="filled" + value={currentCluster===' ' ? Array.isArray(_.map(clusters,'id') )?_.map(clusters,'id')[0] : ' ' : currentCluster } + onChange={onClusterChange} + > + <MenuItem value={-1} divider>{'None'}</MenuItem> + {Array.isArray(_.map(clusters,'id')) && _.map(clusters,'id').map((cluster: any, index: number) => ( + <MenuItem key={index} value={cluster}>{cluster}</MenuItem> + ))} + </TextField> + <MaterialTable + title="Running Jobs" + columns={[ + {title: 'JobId', field: 'jobId', render: rowData => <Link className={classes.linkStyle} to={`/job/${rowData.cluster}/${rowData.jobId}`}>{rowData.jobId}</Link> }, + {title: 'Job Name', field: 'jobName'}, + {title: 'Status', field: 'jobStatus'}, + {title:'GPU', field:'jobParams.resourcegpu', render: (rowData: any) => <span>{ rowData['jobParams']['jobtrainingtype'] === 'RegularJob' || !rowData['jobParams'].hasOwnProperty('jobtrainingtype') ? (Number)(rowData.jobParams.resourcegpu) : (Number)(rowData.jobParams.resourcegpu * rowData.jobParams.numpsworker) }</span>, type: 'numeric', customSort: (a: any, b: any) => { + return a.jobParams.resourcegpu - b.jobParams.resourcegpu || a.jobParams.resourcegpu * a.jobParams.numpsworker - b.jobParams.resourcegpu * b.jobParams.numpsworker + } }, + {title: 'Priority', field: 'priority'}, + {title: 'Submitted Time', field: 'jobTime', type: 'date',render:(rowData: any)=>renderDateTime(rowData,"jobTime")}, + { + title: 'Preemptible', + field: 'jobParams.preemptionAllowed', + type: 'boolean' + }, + { + title: 'Started Time', + field: 'jobStatusDetail[0].startedAt', + type: 'date', + emptyValue: 'unknown', + render: (rowData: any)=>renderDateTime(rowData, 'startedAt') + } + ]} + data={filterRunningJobs(jobs)} + options={{ + filtering: false, + paging: false, + actionsColumnIndex: -1, + headerStyle: { + backgroundColor: '#7583d1', + color: '#fff', + whiteSpace: 'nowrap' + }, + + }} + actions={[ + { + icon: 'kill', + onClick: (event, rowData: any) => { + setOpen(true); + setCurrentJob({ + cluster:rowData['cluster'], + jobId: rowData['jobId'], + priority:currentJob.priority + }) + } + }, + { + icon: 'Pause', + onClick: (event, rowData: any) => { + console.log(rowData); + } + } + ]} + components={{ + Action: (props: any) => + renderActions(props) + , + }} + /> + <MaterialTable + title="Queued Jobs" + columns={[ + {title: 'JobId', field: 'jobId', render: rowData => <Link className={classes.linkStyle} to={`/job/${rowData.cluster}/${rowData.jobId}`}>{rowData.jobId}</Link>}, + {title: 'Job Name', field: 'jobName'}, + {title: 'Status', field: 'jobStatus'}, + {title:'GPU', field:'jobParams.resourcegpu', render: (rowData: any) => <span>{ rowData['jobParams']['jobtrainingtype'] === 'RegularJob' || !rowData['jobParams'].hasOwnProperty('jobtrainingtype') ? (Number)(rowData.jobParams.resourcegpu) : (Number)(rowData.jobParams.resourcegpu * rowData.jobParams.numpsworker) }</span>, type: 'numeric', customSort: (a: any, b: any) => { + return a.jobParams.resourcegpu - b.jobParams.resourcegpu || a.jobParams.resourcegpu * a.jobParams.numpsworker - b.jobParams.resourcegpu * b.jobParams.numpsworker + } }, + {title: 'Priority', field: 'priority'}, + {title: 'Submitted Time', field: 'jobTime', type: 'date', render: (rowData: any)=>renderDateTime(rowData,'jobTime')}, + { + title: 'Preemptible', + field: 'jobParams.preemptionAllowed', + type: 'boolean' + } + ]} + data={filterQueuedJobs(jobs)} + options={{ + filtering: false, + paging: false, + actionsColumnIndex: -1, + headerStyle: { + backgroundColor: '#7583d1', + color: '#fff', + whiteSpace: 'nowrap' + }, + }} + actions={[ + { + icon: 'kill', + onClick: (event, rowData: any) => { + setOpen(true); + setCurrentJob({ + cluster:rowData['cluster'], + jobId: rowData['jobId'], + priority:currentJob.priority + }) + } + }, + { + icon: 'Pause', + onClick: (event, rowData: any) => { + console.log(rowData); + } + } + ]} + components={{ + Action:(props: any) => renderActions(props), + + }} + /> + <MaterialTable + title="Unapproved Jobs" + columns={[ + {title: 'JobId', field: 'jobId', render: rowData => <Link className={classes.linkStyle} to={`/job/${rowData.cluster}/${rowData.jobId}`}>{rowData.jobId}</Link>}, + {title: 'Job Name', field: 'jobName'}, + {title: 'Status', field: 'jobStatus'}, + {title:'GPU', field:'jobParams.resourcegpu', render: (rowData: any) => <span>{ rowData['jobParams']['jobtrainingtype'] === 'RegularJob' || !rowData['jobParams'].hasOwnProperty('jobtrainingtype') ? (Number)(rowData.jobParams.resourcegpu) : (Number)(rowData.jobParams.resourcegpu * rowData.jobParams.numpsworker) }</span>, type: 'numeric', customSort: (a: any, b: any) => { + return a.jobParams.resourcegpu - b.jobParams.resourcegpu || a.jobParams.resourcegpu * a.jobParams.numpsworker - b.jobParams.resourcegpu * b.jobParams.numpsworker + }}, + {title: 'Priority', field: 'priority'}, + {title: 'Submitted Time', field: 'jobTime', type: 'date',render: (rowData: any)=>renderDateTime(rowData, 'jobTime')}, + { + title: 'Preemptible', + field: 'jobParams.preemptionAllowed', + type: 'boolean' + } + ]} + data={filterUnApprovedJobs(jobs)} + options={{ + filtering: false, + paging: false, + actionsColumnIndex: -1, + headerStyle: { + backgroundColor: '#7583d1', + color: '#fff', + whiteSpace: 'nowrap' + }, + rowStyle: { + width:'200', + } + }} + actions={[ + { + icon: 'kill', + onClick: (event, rowData: any) => { + setOpen(true); + setCurrentJob({ + cluster:rowData['cluster'], + jobId: rowData['jobId'], + priority:currentJob.priority + }) + } + }, + { + icon: 'Pause', + onClick: (event, rowData: any) => { + console.log(rowData); + } + } + ]} + components={{ + Action: (props: any) => renderActions(props), + + }} + /> + <MaterialTable + title="Paused Jobs" + columns={[ + { title: 'JobId', field: 'jobId', render: rowData => <Link className={classes.linkStyle} to={`/job/${rowData.cluster}/${rowData.jobId}`}>{rowData.jobId}</Link> }, + { title: 'Job Name', field: 'jobName'}, + {title:'Status', field:'jobStatus'}, + {title:'GPU', field:'jobParams.resourcegpu', render: (rowData: any) => <span>{ rowData['jobParams']['jobtrainingtype'] === 'RegularJob' || !rowData['jobParams'].hasOwnProperty('jobtrainingtype') ? (Number)(rowData.jobParams.resourcegpu) : (Number)(rowData.jobParams.resourcegpu * rowData.jobParams.numpsworker) }</span>, type: 'numeric', customSort: (a: any, b: any) => { + return a.jobParams.resourcegpu - b.jobParams.resourcegpu || a.jobParams.resourcegpu * a.jobParams.numpsworker - b.jobParams.resourcegpu * b.jobParams.numpsworker + } }, + {title: 'Priority', field: 'priority'}, + {title: 'Submitted Time', field: 'jobTime', type: 'date',render: (rowData: any)=>renderDateTime(rowData,'jobTime')}, + {title:'Preemptible', field:'jobParams.preemptionAllowed',type:'boolean'}, + {title:'Finished Time', field:'jobStatusDetail[0].finishedAt',type:'date',emptyValue:'unknown', + render: (rowData: any)=>renderDateTime(rowData, 'finishedAt')} + ]} + data={filterPauseJobs(jobs)} + options={{ + filtering: false, + paging: false, + actionsColumnIndex: -1, + headerStyle: { + backgroundColor: '#7583d1', + color: '#fff', + whiteSpace: 'nowrap' + } + }} + actions={[ + { + icon: 'kill', + onClick: (event, rowData: any) => { + setOpen(true); + setCurrentJob({ + cluster:rowData['cluster'], + jobId: rowData['jobId'], + priority:currentJob.priority + }) + } + }, + { + icon: 'Pause', + onClick: (event, rowData: any) => { + console.log(rowData); + }, + } + ]} + components={{ + Action: (props: any) => renderActions(props) , + + }} + /> + <MaterialTable + title="Finished Jobs" + columns={[ + { title: 'JobId', field: 'jobId', render: rowData => <Link className={classes.linkStyle} to={`/job/${rowData.cluster}/${rowData.jobId}`}>{rowData.jobId}</Link> }, + { title: 'Job Name', field: 'jobName'}, + {title:'Status', field:'jobStatus'}, + {title:'GPU', field:'jobParams.resourcegpu', render: (rowData: any) => <span>{ rowData['jobParams']['jobtrainingtype'] === 'RegularJob' || !rowData['jobParams'].hasOwnProperty('jobtrainingtype') ? (Number)(rowData.jobParams.resourcegpu) : (Number)(rowData.jobParams.resourcegpu * rowData.jobParams.numpsworker) }</span>, type: 'numeric', customSort: (a: any, b: any) => { + return a.jobParams.resourcegpu - b.jobParams.resourcegpu || a.jobParams.resourcegpu * a.jobParams.numpsworker - b.jobParams.resourcegpu * b.jobParams.numpsworker + } }, + {title:'Submitted Time', field:'jobTime',type:'date',render: (rowData: any)=>renderDateTime(rowData,'jobTime')}, + {title:'Preemptible', field:'jobParams.preemptionAllowed',type:'boolean'}, + {title:'Finished Time', field:'jobStatusDetail[0].finishedAt',type:'date',emptyValue:'unknown', + render: (rowData: any)=>renderDateTime(rowData,'finishedAt'), + } + ]} + data={filterFinishedJobs(jobs)} + options={{ + filtering: false, + paging: false, + actionsColumnIndex: -1, + headerStyle: { + backgroundColor: '#7583d1', + color: '#fff', + whiteSpace: 'nowrap' + } + }} + + /> + </Container> + </TabPanel> + { + refresh ? allJobs && (Boolean)(_.map(clusters,"admin")[0]) && + <TabPanel value={value} index={1}> + <Container maxWidth="lg" > + <TextField + select + label="Choose Cluster" + fullWidth + variant="filled" + value={currentCluster} + onChange={onClusterChange} + > + <MenuItem value={-1} divider>None</MenuItem> + {Array.isArray(_.map(clusters,'id')) && _.map(clusters,'id').map((cluster: any, index: number) => ( + <MenuItem key={index} value={cluster}>{cluster}</MenuItem> + ))} + </TextField> + <MaterialTable + title="Running Jobs" + columns={[ + {title: 'JobId', field: 'jobId', render: rowData => <Link className={classes.linkStyle} to={`/job/${rowData.cluster}/${rowData.jobId}`}>{rowData.jobId}</Link>}, + {title: 'Job Name', field: 'jobName'}, + {title: 'Status', field: 'jobStatus'}, + {title:'GPU', field:'jobParams.resourcegpu', render: (rowData: any) => <span>{ rowData['jobParams']['jobtrainingtype'] === 'RegularJob' || !rowData['jobParams'].hasOwnProperty('jobtrainingtype') ? (Number)(rowData.jobParams.resourcegpu) : (Number)(rowData.jobParams.resourcegpu * rowData.jobParams.numpsworker) }</span>, type: 'numeric', + customSort: (a: any, b: any) => { + return a.jobParams.resourcegpu - b.jobParams.resourcegpu || a.jobParams.resourcegpu * a.jobParams.numpsworker - b.jobParams.resourcegpu * b.jobParams.numpsworker + } + }, + {title: 'Username', field: 'userName', render:renderUserName}, + {title: 'Priority', field: 'priority', render:renderPrioritySet}, + {title: 'Submitted Time', field: 'jobTime', type: 'date',render: (rowData: any)=>renderDateTime(rowData,'jobTime')}, + { + title: 'Preemptible', + field: 'jobParams.preemptionAllowed', + type: 'boolean' + }, + { + title: 'Started Time', + field: 'jobStatusDetail[0].startedAt', + type: 'date', + emptyValue: 'unknown', + render: (rowData: any)=>renderDateTime(rowData,'startedAt') + } + ]} + data={filterRunningJobs(allJobs)} + options={{ + sorting: true, + filtering: true, + paging: false, + actionsColumnIndex: -1, + headerStyle: { + backgroundColor: '#7583d1', + color: '#fff', + whiteSpace: 'nowrap' + } + }} + actions={[ + { + icon: 'kill', + onClick: (event, rowData: any) => { + setOpen(true); + setCurrentJob({ + cluster:rowData['cluster'], + jobId: rowData['jobId'], + priority:currentJob.priority + }) + } + }, + { + icon: 'Pause', + onClick: (event, rowData: any) => { + console.log(rowData); + } + } + ]} + components={{ + Action: (props: any)=>renderActions(props), + }} + /> + <MaterialTable + title="Queued Jobs" + columns={[ + {title: 'JobId', field: 'jobId', render: rowData => <Link className={classes.linkStyle} to={`/job/${rowData.cluster}/${rowData.jobId}`}>{rowData.jobId}</Link>}, + {title: 'Job Name', field: 'jobName'}, + {title: 'Status', field: 'jobStatus'}, + {title:'GPU', field:'jobParams.resourcegpu', render: (rowData: any) => <span>{ rowData['jobParams']['jobtrainingtype'] === 'RegularJob' || !rowData['jobParams'].hasOwnProperty('jobtrainingtype') ? (Number)(rowData.jobParams.resourcegpu) : (Number)(rowData.jobParams.resourcegpu * rowData.jobParams.numpsworker) }</span>, type: 'numeric', + customSort: (a: any, b: any) => { + return a.jobParams.resourcegpu - b.jobParams.resourcegpu || a.jobParams.resourcegpu * a.jobParams.numpsworker - b.jobParams.resourcegpu * b.jobParams.numpsworker + } + }, + {title: 'Username', field: 'userName',render:renderUserName}, + {title: 'Priority', field: 'priority', render:renderPrioritySet}, + {title: 'Submitted Time', field: 'jobTime', type: 'date',render: (rowData: any)=>renderDateTime(rowData,'jobTime')}, + { + title: 'Preemptible', + field: 'jobParams.preemptionAllowed', + type: 'boolean' + } + ]} + data={filterQueuedJobs(allJobs)} + options={{ + filtering: true, + paging: false, + actionsColumnIndex: -1, + headerStyle: { + backgroundColor: '#7583d1', + color: '#fff', + whiteSpace: 'nowrap' + } + }} + actions={[ + { + icon: 'kill', + onClick: (event, rowData: any) => { + setOpen(true); + setCurrentJob({ + cluster:rowData['cluster'], + jobId: rowData['jobId'], + priority:currentJob.priority + }) + } + }, + { + icon: 'Approve', + onClick: (event, rowData: any) => { + setOpenApprove(true); + setCurrentJob({ + cluster:rowData['cluster'], + jobId: rowData['jobId'], + priority:currentJob.priority + }) + } + }, + { + icon: 'Pause', + onClick: (event, rowData: any) => { + console.log(rowData); + } + } + ]} + components={{ + Action: (props: any)=>renderActions(props), + + }} + + /> + <MaterialTable + title="Unapproved Jobs" + columns={[ + {title: 'JobId', field: 'jobId', render: rowData => <Link className={classes.linkStyle} to={`/job/${rowData.cluster}/${rowData.jobId}`}>{rowData.jobId}</Link>}, + {title: 'Job Name', field: 'jobName'}, + {title: 'Status', field: 'jobStatus'}, + {title:'GPU', field:'jobParams.resourcegpu', render: (rowData: any) => <span>{ rowData['jobParams']['jobtrainingtype'] === 'RegularJob' || !rowData['jobParams'].hasOwnProperty('jobtrainingtype') ? (Number)(rowData.jobParams.resourcegpu) : (Number)(rowData.jobParams.resourcegpu * rowData.jobParams.numpsworker) }</span>, type: 'numeric', customSort: (a: any, b: any) => { + return a.jobParams.resourcegpu - b.jobParams.resourcegpu || a.jobParams.resourcegpu * a.jobParams.numpsworker - b.jobParams.resourcegpu * b.jobParams.numpsworker + }}, + {title: 'Username', field: 'userName', render:renderUserName}, + {title: 'Priority', field: 'priority', render:renderPrioritySet}, + {title: 'Submitted Time', field: 'jobTime', type: 'date',render: (rowData: any)=>renderDateTime(rowData,'jobTime')}, + { + title: 'Preemptible', + field: 'jobParams.preemptionAllowed', + type: 'boolean' + } + ]} + data={filterUnApprovedJobs(allJobs)} + options={{ + filtering: true, + paging: false, + actionsColumnIndex: -1, + headerStyle: { + backgroundColor: '#7583d1', + color: '#fff', + whiteSpace: 'nowrap' + } + }} + actions={[ + { + icon: 'kill', + onClick: (event, rowData: any) => { + setOpen(true); + setCurrentJob({ + cluster:rowData['cluster'], + jobId: rowData['jobId'], + priority:currentJob.priority + }) + } + }, + { + icon: 'Approve', + onClick: (event, rowData: any) => { + setOpenApprove(true); + setCurrentJob({ + cluster:rowData['cluster'], + jobId: rowData['jobId'], + priority:currentJob.priority + }) + } + }, + { + icon: 'Pause', + onClick: (event, rowData: any) => { + console.log(rowData); + } + } + ]} + components={{ + Action: (props: any)=>renderActions(props), + + }} + /> + <MaterialTable + title="Paused Jobs" + columns={[ + { title: 'JobId', field: 'jobId', render: rowData => <Link className={classes.linkStyle} to={`/job/${rowData.cluster}/${rowData.jobId}`}>{rowData.jobId}</Link> }, + { title: 'Job Name', field: 'jobName'}, + {title:'Status', field:'jobStatus'}, + {title:'GPU', field:'jobParams.resourcegpu', render: (rowData: any) => <span>{ rowData['jobParams']['jobtrainingtype'] === 'RegularJob' || !rowData['jobParams'].hasOwnProperty('jobtrainingtype') ? (Number)(rowData.jobParams.resourcegpu) : (Number)(rowData.jobParams.resourcegpu * rowData.jobParams.numpsworker) }</span>, type: 'numeric', customSort: (a: any, b: any) => { + return a.jobParams.resourcegpu - b.jobParams.resourcegpu || a.jobParams.resourcegpu * a.jobParams.numpsworker - b.jobParams.resourcegpu * b.jobParams.numpsworker + } }, + {title:'Username', field:'userName', render:renderUserName}, + {title: 'Priority', field: 'priority'}, + {title: 'Submitted Time', field: 'jobTime', type: 'date',render: (rowData: any)=>renderDateTime(rowData,'jobTime')}, + {title:'Preemptible', field:'jobParams.preemptionAllowed',type:'boolean'}, + {title:'Finished Time', field:'jobStatusDetail[0].finishedAt',type:'date',emptyValue:'unknown', + render: (rowData: any)=>renderDateTime(rowData, 'finishedAt')}, + ]} + data={filterPauseJobs(allJobs)} + options={{ + filtering: true, + paging: false, + actionsColumnIndex: -1, + headerStyle: { + backgroundColor: '#7583d1', + color: '#fff', + whiteSpace: 'nowrap' + } + }} + actions={[ + { + icon: 'kill', + onClick: (event, rowData: any) => { + setOpen(true); + setCurrentJob({ + cluster:rowData['cluster'], + jobId: rowData['jobId'], + priority:currentJob.priority + }) + } + }, + { + icon: 'Pause', + onClick: (event, rowData: any) => { + }, + } + ]} + components={{ + Action: (props: any)=>renderActions(props), + + }} + /> + </Container> + </TabPanel> : <CircularProgress/> + } + { + message !== '' && <Snackbar + anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }} + open={openKillWarn || openApproveWarn || openPauseWarn || openResumeWarn || openUpatePriorityWarn} + autoHideDuration={1000} + onClose={handleWarnClose} + ContentProps={{ + 'aria-describedby': 'message-id', + }} + > + <SnackbarContent + className={classes.success} + aria-describedby="client-snackbar" + message={<span id="message-id" >{message}</span>} + /> + </Snackbar> + } + </Container> + ) + } + return ( + <Box display="flex" justifyContent="center"> + <CircularProgress/> + </Box> + ) +}; +export default Jobs; + diff --git a/src/dashboard/src/pages/Jobs/useJobs.ts b/src/dashboard/src/pages/Jobs/useJobs.ts new file mode 100644 index 000000000..c1181298a --- /dev/null +++ b/src/dashboard/src/pages/Jobs/useJobs.ts @@ -0,0 +1,37 @@ +import React, { useEffect, useState } from "react"; +import { useGet } from "use-http"; +import TeamContext from "../../contexts/Teams"; + +type Jobs = object; +type UseJob = [Jobs | undefined, Error | undefined]; +const useJobs = (): UseJob => { + const { selectedTeam } = React.useContext(TeamContext); + const [jobs, setJobs] = useState<Jobs>(); + const { data, error, get } = useGet<Jobs>('/api'); + const params = new URLSearchParams({ + limit:'20' + }); + useEffect(() => { + if (data == null) return; + setJobs(data); + const timeout = setTimeout(() => { + get(`/teams/${selectedTeam}/jobs?${params}`); + }, 3000); + return () => { + clearTimeout(timeout); + } + }, [data, selectedTeam]); + + useEffect(() => { + setJobs(undefined); + get(`/teams/${selectedTeam}/jobs?${params}`); + }, [selectedTeam]); + + if (jobs !== undefined) { + return [jobs, undefined]; + } + + return [undefined, error]; +} + +export default useJobs; diff --git a/src/dashboard/src/pages/Jobs/useJobsAll.ts b/src/dashboard/src/pages/Jobs/useJobsAll.ts new file mode 100644 index 000000000..dc52207b2 --- /dev/null +++ b/src/dashboard/src/pages/Jobs/useJobsAll.ts @@ -0,0 +1,41 @@ +import React, { useEffect, useState } from "react"; +import { useGet } from "use-http"; +import TeamContext from "../../contexts/Teams"; +type Jobs = object; +type useJobsAll = [Jobs | undefined, Error | undefined]; + +const useJobsAll = (openKillWarn?: boolean,openApproveWan?: boolean): useJobsAll => { + const [jobsAll, setJobsAll] = useState<Jobs>(); + const { selectedTeam } = React.useContext(TeamContext); + const params = new URLSearchParams({ + user:'all', + limit:'100' + }); + const { data, error, get } = useGet<Jobs>('/api'); + + useEffect(() => { + if (data == null) return; + setJobsAll(data); + + const timeout = setTimeout(() => { + get(`/teams/${selectedTeam}/jobs?${params}`); + }, 3000); + return () => { + clearTimeout(timeout); + } + }, [data]); + + useEffect(() => { + setJobsAll(undefined); + get(`/teams/${selectedTeam}/jobs?${params}`); + }, [selectedTeam, get]); + + + if (jobsAll !== undefined) { + return [jobsAll, undefined]; + } + + return [undefined, error]; +} + +export default useJobsAll; diff --git a/src/dashboard/src/pages/SignIn/image1.jpeg b/src/dashboard/src/pages/SignIn/image1.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..f2b7b8ba89da0a0363c1925794738c024e4616b5 GIT binary patch literal 327019 zcmbTeeOyy_zBRg&kO+Y&)h$941zTu#YZy|tO2M`PCK!@{WXHG3N#|hfFxlKk4-7@7 zcI*uzXt!t)B4Cs@hP;4NJ@z!6qP@;RYxHSPGXqKLZLM|?XPAQ4PDeUDbvn=7^^0@P z{e13U_ulEqTXuH#%lBoi^<BUH_fP&lNu?Vqt12mxNJQ!JANBXos0vD!B$Y{%WHPC2 z-aJ{dJY6nNNs-g@7o?>xp))g=&>0!Z?7S7qrH?Ml$jDih^JxC#%o9x33U#4cRhXw@ zRAeWjdGqGUljV!#@<l3ThEnza`G0>OqaI0?QuCY=(Mn4Eh)D8?=<k=Q6%-|s;%LbM z|Koomu>{9EFF8e?iVfaLr^F(OM4Tj%N|TbX^&Y%VB|Rctn5!w1Ei!MLx3Y<Ts_n?h z<VVZjy11B|_|s!W_Mh!dk!LK)%u?p%Kdxe)U{|kMTl{oM#Se9r>-ALz%f?Ncw`~29 zb^8lHssE{iZ)kq8WmoIZU)tTiucLGSfmel|Uf0pS{()m|?~u<Q2o8^&`nR`3@BHfB z(R1f7y?;4;<?8?Z;KOJv{_(Z{^OH}n-<rDp`#((o@r#++um601?wh}S`=8%EAm=5b zBo9yPzt8OdabAz$yu?XKlB9X$yhP$w{F6MAB+b>x7M7XkZEISz@~O6Dy8Ot=w=SkU zTEzWnvHfQg@{GsWTX|oTQ%juL|GN|0`~U0A{`<uK@AJA&rAb7%d6Gw{=cw<(I=$CD z^rsQp{_E~Zx_N`R+tRTl-R^4AsdGEjbZ8$Pa)rihT-2<WdIgn8N1JOV)#^N&n*H(I z^%gPB#d!We3+?g_q#CP?+P!@}eQgJgRXVCfC^KsJON=J^D?e4zcigC*AE74<ic;sS z)xg}S2-AD}2Il+e>(-=V%g8x@z_~|VXgRg77<-Ig#v5t^;%HQEeL+z9{2r;_>#7yu z6HHS2->nAoHD`vvI7AMwkVJoVI#2R-KrC2IQ9-NnldWlQPoI-EYrWm6pZ9e)8MPhW zHk~BHW!6!22&eL;v6{Z6rTjRCwp8PsZ-{kTtp@9MxwY(CUw5ibEF@*~4sZAS7u0#` z969q!$VG+Z44<aL^>C^la1+}1{dh^AVqAaApvrV{6P!q=rky5^n-FAsdj@s6hlc1; zZ*S&+FZ^s5Cvw>b1&w}tldhEB*kxcG9=p9yFl$o;D^5d0-m3EUIpxJ(VS7eTpP;49 zx*WO6d(dgt(P=7g7e{%!%zC>WS3^`~)KPh~U6AF`p<1z7SD?{K^VBru3YoQ1+?!;a ziebAPUkb$Bgf3^HU{#H#;4>%}H`P_EVV(`mYP6jb!Y)-<lJ(kdYfGj_ww<*PR;om! zL-cIoYlstRs)QDry1{KH8x|^eTQ<Lrf6jDahknn@p-6HP-Ff0UWra3HCw7f%Tp?~e zH_%H5`r2Ntrf*jXl%|@UH6-O_`hCwhL@IwcuW%u5TIL|H@_Uk2_AZMZ%2$aPCO+U3 zbN0TT?i3wu5iyz?pQl`D*u`i}vZ&=yKI4q(an(B8(gH^|u=_0H{KbppXD<ykZ9evR zTId^}dBfuO(nGUlhG$M`t+?}u42@AMV;tUry*P2oORlC|71ng9X)ZnFMPP|)0)NA0 z=Xu64nh0B5HMJ&Hm#3!G1$>d#GmksXa^sQ_9`_`Jg!FuWpnlLaPi{lF-<ZcuyBYq# z9#vr4Wf4vFxtU9+rn0Z$dpx<(ABc5mQVoh6e;}9VRe{_%GKjjM@(NU|!Mc;*?WS6H zTX(1el8)?|5^f6nKW<Ph?x4MRdm6(_)#7Yjp2{=NtWzUc<yNg2_goyELtKmddT=h@ z9zj;ho(n{y>9Y(rlo+dU#Aak)svCEnvX)F}$)<hXl=pBvlqN+UI}KKqukcI60QqSo zlZ$fWB%SnP0a0x-8+Ey_8nu3p{Y7uTA7T6ojqvPVL=OZxk@oTs9pW-^F_|<(t`9{Z z^f1;CeuX~2w>!mXL~tizGlb)u-`n=8QLvc>>fwH?YJ6Yn?Pfu$MkLy5i#@WxB!Vnt z?_VC;<3RFEbu}Xsm?qtmPM2$zak{u@ZH^~8p}jJmh+4aV2&Y4>o}@57LL`(-AW})v zj2Rf7q4xG8D=8O_m@;T^F;Xg#UrD&{)W7V0*7fflG!;WYp5Wx$C8lQ@Doo*AYMI44 zSluAYnQo7|<O*u-1vNr1&`;x5#dORNc5G<6qnC;oB1%V8nG?E_?)!R~z<lBhNF08z zbD^MmFfKM51%oy(_uHVo$GNnUX~I#d!Ud|Zc&SdkxLm1Qd*Rt`f!gK^7e_6kP;5hB zT%wO?I`n*|#k9G&Yy*9xe(?FEgF56!hKRvs-C8~&sDgC}xi)Po`CZ&Kq010CJlsUx z!ZxE(=S1o2GP|{jDn@g1>nd?nFj(u<IPzpGgX{_+Fk<PR=rls5SZQ?xYsJ$i7-w`` zJT>?_U!*I{cK_IQ1Do89wFS6|#^ubXf%^W{<tu9Fj}W=@>DjuV#8lB1lvMjqY1eTv zCVp8pW2|!9#&gU3_?R)Pxf(ki$A^h7A?6+FAy1S$O{vvu2DPbL-<=wo;=V=KMMXB4 zwW&1^EV>d@*(w7={a4s&Q`{s9QQN4{eB5oye}{xG=^zik;M}PYCvQOw^m{0?*6$M; z7)B?NBaBgKGf^=nKJPo2wxdLl8Z%LPNW~=4Y^~65L;f2Pa0p6jXA$niqqsLHxCn9* zfc0G*sn_}>zlWP@dzA#(-af&q%~$z{=rn^31)MT4O-8*e8l4ifd34B*t>{sYj1)|t zCbXRng`7K8;gX*2%f29LconIS)Y^A46M<plYRM6LwmbY`Iyq75@yXPh!*j@vR??ot zJ%d#wo=NSdFXIDHk`oczKSK&TiZ~s>F{r!)`@(c+#cplkoR6NK(LeJ=X6G@X=a^Pn z_{|6taHez0M%p!2R_6DNOEe_qw4LAjy?p~|EEgS@R6ia?MbEs!*-!AOv!#i08zQI2 zdj|yr^)C%fbsi<_>FdIQkhJ;67cPzV`o#--2K78Lanw7Y2)JAF#7aJJf)6+sy7_6h zL(;j7LtnmAMB`ga2Iar%r$>X^Y$mSFD5%cbRN{Wxeo%UeQ~*9%Q_1MmrP9l^yd~3L zH#*?OA^joskQdt=-QPHbeVmojxT0H8h;&c>cYx?gsjDMEqqBgIPmt1v((4d?hbsAs zE4ihBm(tWNbaC&%eAx-c6}p!@iFlUO$ObR3v$PFg49b0BI}(GvwJ~=tkrK1G#_4yX z!VYy|wt<nV3xQp9Dj(|e^;Sb&Nr%U5lKQ_SrBse?N|Am=C%35caLf5J6m>BQbqzgc zwVK8m>K5r0B;u0%-ow=dDsiM(GgJuG9@~@|A4Gvp(9uZ_ppm!frD3u!(Oi(@#wnC7 zq-v`VVUC=LQsI0Q*21rhm8ucSk8Yo7Mzz&+s!&{|^d3@kvuAkM8WLzZO9dM@hoY-P zrOAC&Fj<Dfxre?=+<sE|!Z{0Zar@~sE#vS%;Lv#ep6z^-v67(%<`9x0>N@ku5Z#%d zC{+~N^MeV!Y>MnU_fR-9Xa_>TZThl`#6I9a+D?)fPEyMYNkgy;+Qe~CYyPXommxLg zNDNl1X*pFwqMbGiq>SHon~)5&-3c&4T75NvAQPn4JHi@i9wHn)zgDu;tqC*Uwgdd& z%yjQ^*Vd$&>XmO)hpEiKZT2>bs#j>q3`iKhsgiNtsO3Kyx**O%E%rK>3Wk=)vCn;$ z5r7tiFO5Fn`ga2wm5G}=j8iqV0Aqx>DUwg$JLqIZ9^>;!=*~W&j*VJIN@Jo>C(<MR z!-^U<rkA<#t+VX}0&nprFQ|(-!~_cA9U$2R(6yq=nxu%@@f<JmdCaQv2XefE6@4e7 zrneU0nwAKgvoD8LG&g~iFyp|7Tm^|MRg-5kST#n&#U_MS0rRtt%ebuE*?TmOTm2+e z%kYk~He46%MorhMM7#r)55XZMsf(8Zb~wL}xnfe4cT%q?U7g8JHr#u1fo#x3->QD< zf1phB4c+@mu$juI(fSE$5vUebK&W-3Klo>Vq2+B8fe5B@6lE*L#9iiX==?~zWO7d0 z`5w-!y~-gu#7GVm@V3wIuS)}vsMP>uZw6M9;vn$>z!$*SB!GD;WWEDbJy`9hZ3J@n z-v>^K%B9E@fR9Vqzm33<He?3?NUiEC6zIM-U2aE#%Ii!c5uU&knnaY#(Bm)ry{O|j z=S;*<?yKu}BWOsJ`ZTyM1q|i#9!7&USK^~ZdR>ViThrHnp#a<X!<pasu%96oFrc4x zQD#APg-HW|wU*S1cX`N1k&4~BIA<-rodx>6qSS1-dODB7uh#|-JHphOE(E3`OwS@s zd@BhQcDe3f>ph}Xu&uaJ6lWRsBmwLoVo}_IbuOUhuMx-*qL=|MSka4dXiXa>uejv9 z80NqRKT3HD@)NNc=sCfM(kgU6Fc`GiyvF!>k8}Q<S;vaaWivWtf;x}NCHx97v5t1( zZNGC@7_0P2VHv^2B+7To5+BAeFGrQs4m!}&Cc}-P45gggK-J8-fPgxCTv25Ks&$Yv zH)aV4<?Ga~VZZmikIDsdOX*{z9_)@Hrs@iq^#+|kTa_L%>og!qy%!d55yj=E=y-0N z<6`LmQ_s4_R9=_44mVwOmY|W#ncluG?(CJRJ^-#am!xx3y%*FOq9$BrtL<dds4qCx zExgX1$(-~1aJ$$g8A8q6sCNv-aiUIS)FHCz*&XVfj1x>~mIS;M@8&Oco3#s2!}q!@ z<uB|-K;Qk?RxzSuaS*e%7(j3_X*VYcE&%xh_)S`eh%FcCe!#Z7e@u$4;m2TSp?1M) zU&4Ymk%07R;#|E@o^7m_fn|D+8{HMl7(O&4P^jM%1Z23aT6NYG;#68};dMFLs6*rs zQE~&(Z+sGR00PzM?dDL&X*qC=K;d%RfXWfSXeB7qulaMpLU|Aiki;~?v=*`Ty>tjD zUbXtsj2=Oi8G6wdR)Jiaxaq#`%c$~kS7&BeoFI6DRrWXv0^xa)Y@XWV@S^fMrn}6a zch6(%fs{pVQ*^3Ldr2^_PsBCBAc;DSV70te$)xR|@0x%05P3dPxdv4qoU2N(;1w-s z)ZZA5Hl-{p$l1G-`Da~s0myd%Ye<@^Hn0N`L_R2~tyU8n2u}6V&K;=yf;~&nL`u4W zY*8k5QDNF_O8N@vrlN-MBZx>Kmtl6JfPdp-LG{K++&zM-$>@C^`yQT&U)RzDevgIn z`F#PAem6JnNO!p|eBr&l5HQ2s_+MXfs0WcBY-|LFSap3+FaeV#T83-ZK$$E}sKU;r zAP%e(Fvtl&Qth3T9T!^imWUVysq!f7Yn<sZeb8O5l5~LXUy$JbSSei3hXnx{3yuQ{ z2BwA+IGNT0eCj@_A)HJf@(R*IxmhP3KwQg{=vh+Mm=Z2V`F&m|+O^Lvn5$~&9J$SG ztht1?7k;*vc4xXqQE32Uz~*&N&Y!|9r_?mcBYHmpGQ~=09*C#i2paxUk5I9kDI;k7 z>qxOhs^;uxzeYR`nkt5U-;eN40$)Ox)g{MKm800x4{%B+6xiY54DSSckx~UXSf6Cr z7s<j`SvM!h5<*G`inEUdYZ?(P%83R8GONq+`+&-eBI?|OM1Uc0{z{KRj*dS%bO&*J z(5NS9-P_~hGB2ox*jiL;U=Ol4aWn>yE~vf&C6xa5G+=%w@Vu*5ykd8ev5dAQp)yy< zRYULOc0#8B;rkp&dto0Ue8BJb`jY%IatqH5ohCSlo9a9096&=v)yMtNbm00D9F2QS z6beFx6XXps>UAz6OjDarv<A|tEpJr`G7_3)^qZQ&|MUZ}I1M$SSrdntn(k#gQA-dq z&<H^AdV$w*ZxLe9EJChZy26FsQ8(%Li7yigStN$`-~;AH$In|cSoxg1RTLT|2<VK2 z2cPpdF0B&wAg)HegH^gK`KqgI$Ym>!nso>@hzPgA<?<+85X<Pk2b|?lE=XveI;mF7 zSw;YH2EGY$wPIxj4oG&SdgY1TsLnfaV&Oc=B3ZZ4Y9M{&K{-QxShith?~a_MRU5)< z>9HNTLGBUlKK6QxY+zY7?Rw{n%z4*P7E#X3RR)DPLr{6gpda-O?#n^<K@laFX;<n2 zMwjVzE@4CFjr-I;*lmz%Qfm-lXwTk3QyD^H3?wj)LQOJ^+72cp5Tp@S11l~&*$6Jv zcMyqT8Tmv^(A#F@^(}*R*~z8>kGz-W{*c;*!W|EtE5EHVZU|@krb@eT3o=KVvfrm` znNLOlS3<7P&pO5|!`^;XDsXghZMU3s=O=m=hY?AOs~~~+=-FQ;le*fhFOPMs=}TyX zdg-!>m>;mYSZRwfW!}k>{yT&dhRnzr6%Fj{9bDgFC6bk}i?NlP0ftA%YE|Ll`SCMi zxfSxj7(pGt6kXiZ(JOgsT$V{XG~f>}fs*I)9=)RS%ev)|_xvECL;*qkPJH9-^NA5D zsLDE;!-az86At5k4oFe9334|#;%Z+}flXO|{Lb3U>r4~EI)P5`aW9XHa8QWt`Mf>? z-l<WAK^O;yPOH`VV!%p`VT^EbFkUZoFEm2HT-<%Sa}A0WVF)PSD(@6gD@RiV02Knb zQ7p;XL13!^vfil*+I#6v1pOOGqO%HgiT?zYh46+y7*=V3UQwS_ejNS7^jOnF=vxhR z6M|}oX0!@a3kn5XX)-q<H&zM-67YV<sNyR9q8}Qfs}sPW72$jZV4@1e;YzL#G-Nc{ zfij)oelmpmf5DrAXLyZPn(M!Vbn5jZaKuXm^sTWS^lS|ZFXITHh2ZQE%TaP5I}vdN zcF&eW2sP`deEeRE+;Xtgb&wOEBsnnUTiG3Z<c}WIo9PRw0|#|<{orL>L7-YsJv4-7 zZ3nr7gO>~V_3SZ^(y=*PM@?*WLeC3bMuD~Rgh+=`X3g5LS0KR;R^-w0I(q1IVTOw! zmQh6CijG1W8m{s4HrdnUn&6hp@dc>!21MuhzGdUUnqx=?`ri$xLG;~PF)sasHStjC zktvt_=k%!m!%~t0pZG`4+J3ql0uSXI4~tJobAs}Plz32J8hRFwp)1rZ-kd*G%V>d; zM+ZlAnSM_z+6GcVmj|GV*7FyiMu(!)J%5>{E%0JcqMc9QwUh;gB;+rSh>&L>5P}>C zsIGvp!S16&dtC{<U6BwS3FOgULL<moavHt80{S(oGm16h8dp*{?!<vW1?kclr1(^u z1hr8&MpB-)+2vNLdP$}Wc{&Li7s>QDLIYK;FccJiwLaP>Y!NBwdI2mKb+9nO#;>e6 zs?PKt1?B^51!-)8h)06fs8c`LWw1g6x2?hl7$!5r^bin#5<~>x=&1b-Fw2j)DS(Ne zp3cdt#I?JkLKekCHko-tSmw;qJ%=1yN{|Htvk4T_44|F(dX;rO(CDr_m8ktb?vV|w z%eEb@;1x!ZlLgV&)rmyqL;)mx7YQlkMNZbWei`nAO*RF@E})nztzZT&A7=qHDCv$u z>85uD8y9w&A+i8iFA*v7ilgSHSF4OD>E!Gd(Pmw#)5S&Y_>sF!o0}9Y=Vops;W}7G z!J(`3G|7yjI9e;V@Ry^ngeX^MU(dj0^htjsIt>19zSh?Z7JxUn0R52*X`HzXzvGIe zeBRU7<CFu0*FO52ng6}SV4X|TMQGQB5$8EF?r2TFJMj$}QlJbd?O<CMX84oPm!q-K z{bs<@Ummcwtmzv8FRO-zhWi{#M>vfGe0iOu)kDEER{K2w5s?Dk8Jc`4Fb$x32fdv? zK$?e2w3`3|3TdMf#cLHR7E0QLR?a~D%UPjAAcHP&Q$5|`J4RtgNw*AP;qA5=5bMlV zyU-ZG|My*O4cfa~f^u&Kn7nnDh&1iv)QkS1<smzm-H+$yyWT8BOX{cJ6mx$AM`KvA z9SE+LNox$s>k#H8pkdzb4w{n#S|P%<XdDnTu%8N23<00X5BNLD0gArBK!+w&RBy*A zw;3jX5bZ}QZ%Gfyr7H|uGDD%gAhl4!K}Rv6xduXGpLjT=;wZnzOnBp*_h45Er-b~C zb1Gn(47V52BB+2{L>fdTse^XX77@%gbM)w?OJ*YkMbvZs7<k)mbK|0Av0=X$(1q~x zCOrU#3xCW)`<?E>%L^f<his&BBi`&#%`_Ta0aA@b0bUiX8r>Md-S`N65?YQ;+>3%j z@-Jj}E(Kquc>0EA2Fbx^1*e3HtiybA5E2O?YbMLrRffqN`VsH}s2Z;@8o+8p3joee zQe6jpkQ=d&(mU1RC7GR=b=8Vnd{Aya@9sqB=@}#h)UYLcFJkYYlS7Bc+#~VRgsHri zcc99HN#*6CnrooG@Cryd+!0;jQY3|V;c=iBdrRPNeEbG*jUDPFx`Ym!6Xb)?$N4A` zMxVPvwNKNe(i<p)$#Up%D^&q0m~Z=Ixgd(DTQgCG908FQ99Er$5<!KAPUoph4_Gno zVDIH#8Z<@lT^xJ6nto@?;{Emg?1znkfj80FZbXIF^vRcy1aqfG_jA+7jWv^{Z0Mut z^x)-E7S$bnI~1a3RsKk!oKevffb<tkvthEt#nty=8^0<L{Udw1q(?AEC~-e~y@bok zqsRGAy;DnAPt4(y%ru)K#mj0XU-%-Zi?xv3#-wWc!Z76*w-J6TKxC%x=Ke<w;{Q(d z!UEMwp#>bt(t+97Z8kJIl;z)c!-R_SM5T(T)p{w^o2Upvu~DdiIIS`2=gn6^${|z| zbEXhLHu-`h2ZkTP;oG(k`!J9si3+-kM&<zpwuxO_i+^N$M*nHCt4VL40JP2Jb!%Ko zSxFoD6gsq^!K}MNV3!kup05y?!Z0cB6I7%dM!PTdvKS&Q9K>8GaC;%3(YOZkcN7>M zM94b{%H9X81m0H|#?ME`ph;P+$OtVxYvV|<YclGttm#8Ii+~o1LXKhKWe|OHqnu1M zMpLwq1W~_<z-X9cAwEt-2QG*6O8V(Dxkwc<R<!poo6ufHO^5J+IYk%uH%B^fl&-W3 zSa^dk{G<~|;IA=<$OlXlO@N(!mP2_IkPKkZ1vF(y=|sJA0!+-g(g>v3S1SQO>O4y7 z>l#F$D?nh4Kb#MgF#x3<pd<lV!uWjTFBz03LuCwz?F8A{7q$Zoqo^<k7=>CkIJP4X zycR7aKHy1WW0BIYecnkns%&}OD$9Euux*rpC~aO|NiPj@xo3@YVeiQxdWyyku0pNp z#u#XH-#UQ)(AfPWD8Og_dJMXP+xB}4B$UquJLtZ37v+yY(vzaUIDb5cJ4T&Dw-)#V zjX?|3yy<7Uphe`5uw#2v?6_lbmGFL8&2k9w62b7}ceEHihUyO<Xi>AvqJIfCE@O0S z>36CQK<mbRR2MWs)xao6u}08Vxy|NpgY|=#E<2;IHN3`7@;5eTSE_yk8pvEaPO5x9 zesp-`8vQ(ZV%GRJ^!+>fc>)?@A$AOHBk5Vl+1bpEd6;W{RTEkA*e+hjT82kcF&?^3 zq;TKRIs~t#@+AzhLtJfjMdmCM;<EQ+=1DS7ufPZsvmV<#F1nyuJ0iM72L6C7x@sbe z0)BVDY_96ECT2HA{WCLhGI@lS4%LTJtL`8R(lDSGj-VQmgqTGu0-p$Z+W<EACh|s# zx#xtAwxJ{Jfl}!NQ_ycSUK4)ovL*c%X`rWoe~^77>Z7SLqQecmk6PPLJGJ0R-~}L@ zmJtByC`J<nno&Pq3{2*sxFfDF307OPE|CwE56ST`%JE0@VTVYHcdK)j9#$bJN5KIF z+sg^GjhLn&@|Kbse}ixY8yQI==s_?NWuMee02h1PA@HUoAPXpphE^bbjM4#F5bum_ zI|JUi5+QxPDR4x;3o|K_HmKZ3$AD5DqW~HtyisAnh@lQcdT{Q@rSF0<UO7UBO+JZ6 z&jSk;W(g0)5L7b_&J;`-nK3|$w}y-j0?^)piqJsr<*H^dYy&(mBp-CpsYJT~g>Z%5 zI7;QMIl(J%tz5I{Dt34mGzwfwg0~2c4?Wm{PEc~(B=x~<k;Ex6;F@<l%1u0fr3|~* z{$Mx5G@Qxh0WUNF84M5fNQY2dkyUmmfbkGiG6+YG2o2bZ(r%c_cDJmD2V~xK-+-c6 z*}53wdb6%nc%M|sdl}1^_;1k45A0F1V2aZyrN@TYW@sHd_b?4RA&1bR@0^Y7EmKhb zi!s0qRQZ5UA3`y`fntgfo<Hvy$8UI#<t^P<2}-n4H?|AyW0qnZ<OoQZ&Zp0X*FdEB zK8i`cV|0IeL|w#AIz+$2hwzTk4#2h9f<ovQP;w}rx6j`N+;XG?Gb-Emw>K=#q`%f( zzKV8v(Y*@_Dug6xcz$1LLQ2U1s%gnN8wy>AqQ0O(MbBb1iHQW-9Q2TuJZMRTAZwxK z$<5rDre+xiLWM!O!3OCBLKLU~@<IO3r=i<$-}r#T#<+375y=JgRB`uM3nY<S+7u%q z6&4L35OfU!#72D@z?`N8+`-O+_M;FpNTfO!_PJ0AWFR=ZRO0R{Q+-7DlQbD?#4x6y z5!^MC+Kqt6{D;0<k72xm03fm_AV-jdAv%OO&nBV?Vi+?G55V^>9LE_A32Z=Y!ioWe zpr9b3YEWA9!7T1P%r3N$sv0lyg<DRlFJuz-dlJfzREMh|_B_?QIG-6sNCWn(R(3yf zB!Wn{x9ztA@5)g!QWdxcD9ZnUK#5_iB#US^VnPuHJlplYAX-bv?h+8csL*866n9~; ziZX_nZB2(M2Z#ohB+1)eRWnJu0zC&i_ClW>O$JR3lav6H2FF2{nXUOA@LrecDaX5r z17&2v1!_&Zfmh&b4Y)bB3DrIt83t;;uqZtYN>m3d1+59~bs)uvEF(@KXpYE-RD#qW z&RxFo_H@NWalqm7g?&B!bf@I=LH7=tx=T_*H`WM4$2fCi^9r}s!GauDxtU}B$dc3r z5f#SstZmggrP(x(KIfe}i=Z7?z|K;Q3EY%a;!^HVvtzIDpAKL2e^qnKZh8=HH@DZw z4hrwzHz?M-cR5D4zQP0k6gWob2PKgr)-%y~_D_8LLmR$7FasBv8_&)%MW7D6?s0=^ zbU~!#n9*I3ds_epq3>m68=i$_V4`saI>d;LLgQW83Gt)HB|mZy;(x8`EJ!*c&|fF2 zp`BbCVe|j+muw>!gd0zcHH8Wq?K9+Tgk0Mlju80;o*R{Cdj~9DBcn(QzaMQ6!`2ZB z>8x9liHgsO3{Xu#ZQG#7IwADP8==6dRlapmQAu<Hd|_OQfXN&Pz6hZ1qohcVk<pxR z!P5lA+fHhLTezw2WGN!rO)`f3-lkQ2W`xSr6v^%S%=oK8(ENI)sfLt%K?B{(vwhbD zqEXzPhQ2U`e)1X}`riDjgI)EiupSEB<a)uhaRAgAlhQX{^WzUiM7;r(efZrl`BazX zYd{+WZ594J_8M*XVYkU&S1O(bm_qpWbWZ^GYod^NK#CuNt9%u-Ez2m#@%J;NMO~Rn zMBELTz2|jZzG^gWh(_k<W%lmau;+!J`&RlM#eg;G=j6}!?zBhUH7@+ncF5}zpwzfJ z2j8&Pgn(wiCbT4rz#z~XmLRL6wNQvSWfBhaEyzJ@X>_-`H4y!iXcxx*DSDnSiH#=M zM3;dm@ZHgIfC~laH02cxb+mcC)sE~;WCQ5Q5R7t~WXIne53rdA@R(2wh@cdNCM53| z@B6BTbtT_#K+;IkQC?9GLgp$0GagP*>qrWv%t4N@h#WxHg!9HIOnV9>ZyT$<2Rjl2 zSPF<5uVYGB;2;z_qF&SIJbQP-O{(@*DEuzVDB%^?Rij%Q**`TD%-x-6VyoP>4Vohc z#gfIZ78%`jtS9;~A!PmRy^O~qs^R;H`8S`be!9Anxr9%e?q|pNPlJuwHOQ57-pOYZ zzk8>$jrg2q!c6YQihz7pJ?m1|`6HN^Dll508ZnbCK}und>_^dd9f5ft#opG9{T#Us zmNM1?!T`Tnwbz+Vj9ObneEhn|JGt&fKbQ*a03nyW8{RMV5)mY4q6-njA@b@UxIe_j z&x%0OQSYbPQ0c)9&{xPMxuxGm+%dUGO!OGDQIKK=g86Y6vcx!cqR2r7Ph*<2=pU&9 zBvsY)(J?B5;u*9DZbKlWcE{ebG%h>=*yC?<B9X&52&r1FhQ9!_y+qak7&W~rXuhmL ztALtgv%F;#v_z=~+-^FAJlW2oiZ=<GnlL_nbO=d#`#KVcB8fm;D98_O+GkZlFFk;2 zU&;I@yamXo=V%w2N|)vQ1nD<u+Dk;RACO!^R8A1ZC{gku&_lDdA04X+xzHa7iw9k~ zj05O{2&6&8l{)vI&h-b}1%(edZkmu*SIBin{78ZatzAAh5Hv>*Ixi99&cGD}<^AfF z-HIz9>?_e-;C2BARwnEMqrR(tLIZAaNDl2*OxciBTfs>_!5laSg0>QL0>ZP$jt#)a z)4}$82GB})gq;DhBIQyts!?EVVo+-$h_FC)syuz-K{UEMHo6Z)gQPsR!+0~FIq*;b z#-^VD(k<+cy;UXHmsYcXW|BP%SdzByS)NkDjyfzMRCu(*A6bJP41ZOHQ^pX<F9T_y zNnRkAa*-T~T*9qH+7g)-V;efnHi1H&gqaQ$Nt9oV-@$6=kgFDW3Nq7#8ypz8X=FV; zVLb7oPtq2wKlZtcOaBx9ac1!ygQD>4_rxzYp8K62Ndc%d8I3W`rV30q$9cGY2Z6@y z%fSicjVv+dplwO+R}#|xz!sC1vS%b0{gJ1LkurkN2E@AviocF)ubvl0X1v1l1oD69 z4@|d(3$yXT=4;*a>H41j3%Li~Eo<3(b7#bYwG2roIgN2LvIwTR#xxG(!3I}CNi+&k zAHnNyqyW+B0XZ@Nq1=gI)Os1xf^-J8co0y@MZlQ3CNMP!EtMu88~1i|haIDr2CIaU zc$d|xW#SY`G_9{t3=yi4K0`?<JLLAGSAdc-6G=pf$MG&=Nl}er|9((h`@{6<WV9ZO z6GCA>?SYU54GLI{5NL>FV`g&ai9`x+0{(yw1YFh+1ctvO6_x|PH$vs1cX%h)B0Gud ziA?!D;;)NErfFudc`r8CZ%6qiU}u<cYYmK7q-zAtLR}F(FpW&pZzmZR0f9)gARf_- zBQ|WuC>0@Xs0Yio$tKVYa2PedD~{1lcy05RWD^4!0Y}~A4)3jPgg@U{l}mR5*j;*` z9}OxETT&H6r<0nhzVY6c!=jLoJG<a+#0U<=1r`fB*~C2pL1`6EAY2R<1M|aRm3}iK zdWvD37~JNO5kYkxMF3h7k^%0S8ebq6Je$7vP<|r5HwddRbAIloeE#s_@<q#l#FzsB zX%!}MFUI3ZdAXjbvhJmK^3;nzZYBo0Q>Jn|B;lG#j9%z_q`yrP*@9e~y-?#uPmM~E zZQ9^R7?g(9rB3;ek+C<x6j5;ra`%Yafu^AgLs&+WgQ*9y03q{))qXDQB9l`<xUmTc zylrqIxY8yt35LyR?#<GUr(X%-!_S^6!PLoImvPu7XVP|oAbe?HY!w&%GB<SCnCQDX zzw8)1@CSO)D+>9tVAI0-emIdP2{N+)`Do&y8_{Vvc_0BhiO!2L*$HOobg`23LHAFJ zm1r}ealRRbBv7!X!!j~GF%YrrcR||ofb^HwVHX{RkMk$s?{Go%VO)p+7qlhVbSx^; zm4NbO&JtwjG}lD(h`CrOLrz#5!K|F9_Mjz%rWe;1VkmP9SwiM?X*N3%WVNbq(N=;R zyHtKwUO{8GAS(#)Zg~qb0OO#>#jENt&O_Vkq__^hxX+ga9LC4fV8|j6v2hU+sBh4K z_?`?QQGAl}*so$vI5ytnp)~aaq$7|;HR7XV_185V{%|JgWykdfy#1haEVLsy)pvBP z&NjaJ4cynX&nmC?Kxo~*X5v?GfSe`&CAwn-{h}c0=e{SQ?;q^yYIpC9aZ`I$qmS5o zcQ)8a=KvTZa4Pd7GX@PIc=j>Pht!9o2iZV=BX()b2R*Amd%MRqf#bqU^bPbl7U&B1 zK~I#bL|$GtYtzE{8V>aR(J%`idw5A!Hn7-YPx1`&drS3`hcAIdAafjk@8B3u$7*~m z^?o&bA8v*m(5LqHn%iR7qI65rQ4Bz<wl|?uBpk+V3ZnQ9kYQlF)0w5m3<ci=o_<iF z?R*5#3LK0GU8aO5X)htzP|*nWuhIgxVb)EDj_S**uJ!c{mqAA);0?11M&<KRQ6-~T zBKeI*2S3zj+m{~lh1Urt^R`1nFm_HIOy)7sA0W#Gl1nCZY6%2ED@8`j#9ypqMLLY+ zvW8eYx-LK>H|>;5F*Vt5b|<jNb#rxLMqlrYv1Td_m{LWBp+XL%;7E<ylwWmOh68Yd zJq=~5W-6oO_GwyPAaNp~pwUvE<*K0z#yYhGqOl%i!0VF+>yNutqYJtCi+5wATM3Q= zy?^sL_Lh0tr7TF=+rN4_KQ__$RzBZso*s0!rY`Wy1_CF5^!C@oQPk&L4t=4^(gqAS zXohz7WXFD?)!L<O3`oN2we0McR5^2#4?z{a=!2*EwTkryjbXv*9BEL#L*>hO%uj#x z0E}lI_f4Q38W~Qw$f(WbFS!|AJ}w{Rzo;mFs~T<R>@5h#D9djlXW=Xu;hBa?GT8u@ zD~5%BBojR`h=f5AgW1T40T3n`L+@ny8?zw@f2>B^K`WEevt>1l*Jcv`So{oZdINqG zQ85zF%!y+i`OGIC395fhxcW*yqeGS*L=~GO-6qZA^pH-4zJ)r!C3_#<1Q@r31T>62 zTQUdWxz~`a8^uJpD-rX6RKSH8aiG(fO{B4W&v681B`l!6)%l<e1StI(-``wMh*7>O zyVH)Yr4b4;V5*Lrn?3=8`$skqZa)PIK^e*sv)zg?JPsfu4|#_U(=<)Ubz6_AG)sgg zqAp?B$D{PK)JL5CbzYI%#k&5?_$=ukGM@xuBA<ny8$i-uQU#Zj0SbbiXun|PNl;mp z6cWb`X$@fyu?!k<8(0e&Q?s!d=D@Z<YzD4QxU)><FgI_^!37WG_=aY5g_zR<7-`A? z&Qx$IxeW@mEEr)h`hy89HzksL$G|UOscSy=bd~9+gXo0s?cBaibw@|vBWrJJ_hQte zRrtOAG&BaRM<7UU0O=gMn~SC!H`kG6E@uJyG(Zq=(CPL8$kvRB;e{L!kt<#Y#YkPc zkbcn%3p*r^AUt+b<d+nSM#Px&wWR_o6RQ9`Goutt$z8RKZQJ_Y%**k&Jm^$VQFVEU zGK>Hr4XYr9`Lm&7MPp^?7lfTW>jG_Ar?e&VW}(FB?y|hCyR1ht98m7!Z>)m&>cqU{ zOp+7c5il620hb08&<8#!s)?+3w-BaeP*gIX0t3w5!q~>&_!f>$-UuO@9(v8skU;yq z`{gJgdl?L}4<>Y1w|$3o19a$2#&U+=JNUY4lo+%34jOKsy>$(pC+>u>t!!kSkS(xQ zfml-;aUSs-(Rf!2)?q;qD1k8v>0;sIov^a*DBjqGqT%hWQw8N({^PZvOYQC!xR?aP z9{m{Hd4AuR8RCGykz^G{-m#9Hpvcmg(dT5w$aSO9MGiVeSkxwOL{g!?GncyWit%#X zS%bOq1fek;pd-O83515<?BEUC&oB-zU<3laW)e-H=U~?k$aS#AxpBRzi7K5$jF)_H zOhEU-v?4iZ1}pEf%!ID6I69y%7e38IK%b!{7xrt!da?nAOfVnJ{E%sBKgoNJzELYt zB+MSvn%=~Usf2Tg?ku7S#TmE~B@I=(rU$uEn-<Pn7jvW|Q!sCD%Yxic1?wN+6)1}0 zs-Lup0maHL%LsB71tPCdUK2`KRD0g2GF1?#(;c0Y2Zcg<8K67YTw1C}Qf|sksSdsv z7$^DR*;Y1mfrj%Yu_}R6*3q4=zgqYklu%)O9Uj1d8{kXJt0MUhiaj<|9O&~%*}*R& zx&Ly>x$e(^UeEVGwFrv=yzX=t7u}TLA}6I_CIDV}#4i60FqU9|#Rt&ERF2MA3yGhY z&!X}oE#>WJ$;t_^rx3;y0Du-UI}3=v^<t3i!04<4KM+SHnVm=0bWJpE1rR)DG~C*D zbC`D3cW(s?!q9D(&p)G=CNw6mK&<*Xevf2%=pL`Y&4a><AkHg((RT;z@o7M%IsEY! z=I#ZOHZq<s(9Dss4#cjS8I!E<=w)CZ7x38v`WE?h>_~O-#=2N|iD>F|MP&(TE!f~x za}9kj2clQoKZM!L>%dW%4qgI`WG?k!Vhw`MV>T`_$p+n6RwKZmBiVleSS&sOxiB<a z0avW|#1mTFyPRcFNM-WIOq9;<OD7mAsMe=;VcKBBDy$PKYvXh`1?vhG6zR9sc(}L| zsvFm)r$`If^+cuk1cNx2lYfmcYG3L(A<cCL%?4=>jUh9dRC?$Z5Wz{FFE#7QGP5V0 zW-aWg^LC>Yp}u166}iw{3w(MwXriWKeheU^C4-5B&Q8F29U;K3dxahda1Uw30Lr23 z9t;z6F2rx=8$vmuBzeVA`+&kDSO~y!-PYw~>9K)LX79bjp|gzDGo!&HdL4y`C(2b9 z;Ld;g!uWMyL{lBkNbOqL4e+(kl6qvS#|Puw<%D2`bv>#pSO!4euW=J~>&jwyjKYsM z*faSj%9&A*EV>5+5|)ZWJItf<(2113B}=Q}kg}9DF@eGAK(2y~y>?+un}Qt(T6m%r zbawAUFa|V5l@8d`qxJ1o+IIGHw0sbb6H!dmAN{<SuJ2`YrFlS7^O=%bSPwZOoaOph z(3ShMjN{BuE+z#hld<vw9Tl$5(gWyEBbc_E;qpYpBMa~k(_g8Ywx629*np^+kowWr z43n_rs7AvG-NEoS5cyU@=`l%z=NUFNvLfuE_(flEcre$A<ECRI;Xvll8{d-t;F|Dk zXzMm31Rgl8^GdV4AS4|_*ykz@-&BQlWS@LHMDIas+Ut@V%oQ*hVRn2LOkqdKK`In- zh28}02V)16!PIQF<*5_T%us*g$Ko%+Gt#YWT%FGT$&tDci)@BuuG%C|tPLW#XO&5z zTCH?9vS*%LST8tO*o2;AEoI*`WmrZT<IDU_hvC0t!7MNqqCBw?FCzLDSq_%=bN^S> z^yD5Uxt70ltOh!8u|<bvVGpeJJc!ENWW%~xo9`*C;vwH9;gVx0K$ys{y`UPYLo?g> z;$$=T6adq5{!;e>8VlFtTC63$z?Y3|UjZD3u{(Av^KHlMA1{@>9)R0Xzdiii>J0u< z($Uy+kp2|L?dY>(XgV<00?;LNL}5$ne4n=~N_kOY4t8N_aXqLXDSf>2oR4)K^X$s# z>1B_3Ckq@XhY?8C7*b(muEOjCyjBNnF~r)o8QnYbAmaNTz)qS;xWJg9!T?U4#9nXR zy(7iRVIU_53=xwBSjo8Mb|4cBvt2hV2Yen`H>T$(<(YiZ1|$M?XhiWG9W%)|lE-#f zt*#&w(1^llC%M7pF4tWN@Du|6@oU5)XkQ5ZWKG)vT^_@}F{qJ}F&{?m{j{=f4VH2p z*LEI>u)<4#TKkk08v<?$JBixc(tJTUSqI>W>gIuSfYt!%426jr5UtGxGQ9zV_ix!3 z6X3~;p`*F_E0gp=w1p@Khq0hcxcd}X1I{vx-sw)?Ma<)62wt%2hqBl`R2atjU+JI* zAh4lelXxdJhsR<CZ26Fr0}jDGsR|O4CP5!ASf<4gurdUx<TO>l?2hSvu$wrHTEudd zw->pERSq!GMIJ6*x3?WULF`i^qr1#d^3V^SW|ig@%j+<)gDfPDjiC5~$d(!PQV*tZ z<B|?F2I1&Ep?6koABkVY*kKnxHkDYx4mLqTQkxjI$eeky6Q|9{hl2<DTY#-z#%oE( zN5<m+8`eO64@)&nGn1H3a$xiVg1Xr>PiS1d{3U-RCy(~T>tb)~UvNgJhh+WhqGA)K zHsCNwG&7mrbT5<ufhkJa&UKcz3Eip4o5$XL5U-22SGDEymxkuKcQsV%nVY+@pAKW@ z|KgkBE$7Bs)+rC4Q0S4R!}n(p{4X5y2PE!VN9Lb+{+M6pS*2INHWqEyzi=!lF}Dk~ zy!2H-!1d?*S3jC3b1A!&wy*UsxR4Od6=E%4HvD$YP40@(Z6lw|J!P73*E=F2JNb^4 z(uof27Wp!=-9>jyk*q+*3TWi2$dc?1qzV?A@88y}W8FpIz{5nfES*2ZqQ~aq;(|Lc zMLJ{$bW#XU9X^2J1F+`}Txze<Eum0~S*WrBBCKNUP)bq-N-L`8KV&1~c}l>0O#Cz$ z?-?-21KeuztsDR;hu>?U6SHj0V2EC<#C(UjY^g3x2hd-QsRSwXqz|x(RWoudy1^%b zKoB?iAkSmL?9(XfooEm6Ked5Y>k~bKII-AS-c#Q6IZRUl&FGx(Dtni!l{E5-l3u8s z^Z9eZccApZvKYlsA3~gCbYCP3tIls{^t4y|8;hX`>sm9hM`MD!gNmp?ObF$6!@UA} zjrL0nUFahP3HyT>UN_`}fwGZBy%?!$aCv4hg>q(eO`v>Wlt3}f#PRR&z;fdZnRTK% z&}Z19W-2k?1g64bc7Uu}Y3}^Lpt&6EvQ6Wi6hxy5<F%vVZBU^xKatDGiX1BYi^QNF zE6%7{^EE6^z;gHFm3-POd|3dHW-jz$BTI(yFn5#n@XcgxTvVdVJq)s5$%_aTZ?Eyp zt3NTHIp^)ZD!}ZGVvg0Bq}EafaFB!0yTm_%0+x>4SP2bE=Wcv9MBa%RC7-+}D=^*v zG+EdFh5>V(%<oBJoKWPfa6?H}v7K<9px@Y{;!W5^9?UP&SD8<T?;~X}EXCxeSql~; z^U6*rj9CTzhYdM=GdDJ%ti=yyBUx+iVtHtVrG4XDy`c13<q74<j#v5CjXw(R9LavQ zmAg0G=D*2ZweN^~;yF_%6uCXq{VhKvd02%M$z;!sh@X$2Jhcug-3P6fcQ-zbWPWQ6 zP8wyvmr`UrkKVX?Nmjw^^PgZ(k=&k6zsws^HB{t9vWCAkpYPt<&(;s!6p%Z6=o=I6 z0!`e~tYx7w)ORIT9>dB3_JOE}*^5L2L0*tZVRo`3o&kl4+6?<DrqB`xVvo!V>DdY$ z^d&5VEUE#;f20P*9JZ;v!m}7?8_1w8u}Ff{ajT-X(~QuSfD)kc!y6kTk$t-g@C49L z6N*9_POfL($;;Cmi7H>BqofDKYGB|Nb+(~*IXMhBC?Bgd)MfeD<xEN#_-e*D7jP%# zVzqD~tPu#OA_O9*O!0X(i}_ICxb|)qCh-NL>iqs<pZ9j!{c09}e3OnD_>fKV4vgT6 zC888xKAweetB&nN1sJQSGGLyvY;_*J#nasJ-%_y(VlWClOeWp|DL|1Fo><Tg3=glN z<v3k{R_!#=2)yKh8?wLn$rz-MsJU=7m}C<*2{nprFIZIxY*k0F6&MOx?F-cviz`0d z1@{!}wGJ$M%PT-06nz)1hk)u*0^T@xIU;$~IONzlGMT@ZJLe^mt`n%`CI7tBR4L8X z_hUIcY+FS2G0hm=)Gq#IaPvs+VfsQTS*#PEt-Ax8mYebR0k{B#U>lNO^)4mV%Sy<o z9{r$`WK3>PyFuxVK=>U{?wWNfYvc0Ew?;l4E~-)JuG$;pUw%xn?~TYWV=hy!bgdTI z>(HAjEbnq+Tl8;FAoleyoSPdWX`xRkA{_<0))GudViSGFOZW&`Qo6WUX?np)%6c>R z2{eQALy>}ptskuAa2OF)@$mLMMUBjZ13A*$ZhY_la5IVbckwFuFlBz?#b>O3m$4>N zt|(relP7y!Q9;u57ZdJ%t4m*Ls5H6(AscG>v;im+xg($Yp0c5@9fZNHFn=Ch?^V=9 zDpD89{E<opG&7vgTK4XEp&!5kwnu#M4p;>)!U3xMIZI(p86J@UNYE{PoFy7emC-~5 z7Q_VzB@|sG3UPvsLQ}0|COKmD-a`g#ShERxApEO|NkKPOpkZLp>xcKy2D8UoRYGef z5uG6hJ>=G%697~ijP=M!8;eeR20G!(%?<1$%ZCFaXi<RvNGn5Ko~*BX4w3-4z-2bN z3mrymsV<ILo#}s#Sp}Y@_TRz!&vHkmYrnvOR*hng2=4w2+FLHhJu}t$C-_nC)KWAd zU})saafm8aJ{0nBU4D<S1M*{0mAgng_BZs+T=q<ce5ZqT*;XY*Yxxp5IFOeqUS!X8 zJohDIVUJ+gLb0yU^@4c|bo3VQ;W{{SH&JHv)^rg3d(geCHVfvJC?_z><7(WhOQ?Zo ziIhc<Y=pj-+XS8IAv8-5k=f(C-6Zv_h9WF87EBX(A_{}%gfTpNRg^?Fc)Gg|en#d1 z%zgpM2yRg7daFT&u}M`W0zdQ-f$|WpvOwZ}9=G<{t{8^$+R{6i8D<g2(2UK#!<fWv z`&zqz3>)hY0`D}N|ANJvy+G_U6=*!uU1&UR**c}fLf(zbVj5yaDpb~osnc$Lm`X^k zN|H*`Ly=MzFb*wDsCZ1)zd9SMGP}_eP{*B*DQa%I53-)|r1#DN6JdgxM&>iCqW3&0 zDB(XuTIpYXA8kWox#&qL3)Y`h7*6Y7_%vAWQH0kiwMO_0&|47BvG$GHSR1H?iv9BA zWY!bE@xs`+;)jZYS*oAC`?VR6PQS3-=sti?-ueN~-~ckA%cX2w7uNz^jW3)&q4d(X z4DaXR5Yy4xw!U!Ae-p6K@c!uE;$MC;JfnM3diCCKtv2@#a1mj2GXAHACq%=Jl=qdp z8nSQ1-nAd?B!`39(um^)&%+W@kyVJ(NsCVVR@ETUlN)yNg^89W3rcijE@g>D6qL(K zTo(RP+hID??ry~D!YY_Ks~~LRwvQ7SLI~n)Qsxjnslv$dxC5#$k+(D(pa?|4L0GtU zVe*|x4oH%HWT>C^6KE}-Ob`+uC@%CVSk8!RtJDcno_JF-SUJ{jU~&M9R*MG$tTW2h z_pa7!NuV1}rn-0Jvd6Fng8WfiiXZ=o`z-ZE$Y1yUpkb#Yzn4u`ux<rdnd_tG4Zmft z%l5II{TJ91f(%+veQzdL-+s-_r&+3>aj;Q7*<xbf<e`mb`2bH-JYo#=nzeHHZpn%b zV5Uie49QYcBSeY3e8<WjSYKizfwoQ%V;5E5JyE5V@kNzPk=7Zj#)?ws6mZ&HK{M$e z_cG)J(58ouc?FNWwP6SCY@r9g@JMM4^<t!>KwLu_VATd^#+uKB5<uqm-mX}Bs1u9` ziHWsC^?i795qcA-Jsy#g1sx6P62N3GYIb9pY7vwKNRWl_8N%h!42PK%3q-x$c)r$k zthKPk9NsZxV39WU1g!SnqX_$EIA}mfrjNm-Uf&h1WvFvO4sO7+6dMv%YQEBSG@?y` zcy8VHI#V_TcdPWO1iBd(H-)fDD9~-gq}wR`;$%~i5k7*a%ctOxnb76$y?q};Np(gd z!|+MHtQWk*7Crj`ljtyo@WOy>gOBH-nLHCe=Sw;VI0WNOCs7U38=j9lU8}LlEPgnS zAXUWQ8_s%mcGqlsi>jfobL&l`+o=i4M$RtU8Qh6Z)8^mSGZK{4qH(n?LN@(Sv2L~G zvEW86n3Gn|jBPA?Yr{>$SXCDne|co=j~A*t@kfg&T&!G``w33@=Xe@JDm1g`A5sN| z*I}vK&ko;$qt?8Ec4uGytoqZ40XzBg;olov?=^A5!Kt9@7vW!U&W0VK_nxq=|EN-0 zx}$VQW_UfbC7tnKe>v7stla-dU_E9`!AbM4PF`+RD%L5hL!OOWTbBo3e`0m8UAJ$Y zrO&M>85C9+n4!a=+07J7R;dfXwxYNTw*$iV1OR}QEWN?gM<6!r^ySWlKAJe+eKl$! z_0SPO!NbNkqKm52ELBxi?m9IK&xS`{frv4?c`TX(fP^3s5Mz-nU?s)zMb-K44SrG6 z<tV0TCi1|a=J49*4CPpD^nnk(_1G_wQ)G`3Rx%Rm7&&(ek4LF@S0mt>b$I!dDDlpn zD97I<wvW`li8oAOU!tE6vHM8oRO2XiLJi(GQ5`X6hHi{kv+={Xa6F5uJ<|N+x_rl3 z5!=}ntsm@Y#M82{?b&-IR^8exAL#)SR#J_JHiTT<rmb55Q%$k6Db-jPt_Q_GuD2@_ z4udMUMb95FRD@+%M0k0^sN0o^$M)oDQYsS*TPg`_%2SJp%a{OW)GiRtee^jW_KX!( zHsT}e`YXA9uGUlOv!<vS&!S6;IU<_*$SdOZvJnRv((+tjLOcJ#1X8Me!c86&<1VW9 z!w5!<EVc?P-x$QEq<Nw|x?9uDl^`2Z2y9yD63`sNP`NSdRz<|iAapq*IV;u^({i&m z^%|rlEDFE`mET&RLMZ<)R|whSAu#O8j#owBGoO9~rVYdA!g?p~oOt7OX$LW@y~h4j z?0k$chg#*}WrJersb}K#hwJigRyM4{fuBh_HaFB##7Yde^CDEd4tP=W7&2`Y*vUe( z8-q@^^SQ^k>0_YilKX}Qesoi!2w=uzi)Q7|KL7D@2UHC5jtTeXEJ9=I55=7Wt5pqv zWe)KUFr93q<k>~{{5NZN24%jaTO+r=-qa5M6ng46vyBa>&kz2(`#(e@7f=2A``Eit zF#)79lFeuTwdUrgcP;Il_<ypKAOFbu`tpNm|8{zVVpE0X{qdF2cl63r^Md<5ssA+6 z`trxm&wV*}Ts+>e>%s8y6U&c|{c8D%f88XpwBOhNWNdEo8`CqzRovM5<%8!(etCbx z{ZcOb;6Kf5I(2q;<L*B8M+)Pn_9HdF<E~U!MTdT8P@eQ2$!XZtuqxIz(xh@JTL>E6 zpjsGv+q~1i!01Xr%C@%@Kl7yYgEs$fFI4>c(A?I7YIn-;yXNyp_x}te+k3Nec5^Ct z-|G3eLTj==c)PLzS7Bu93l~Q|J4H$qu7~fYdFQvjn}+xEZyL{^#5-0ceKs7aIDaU< z6{xu0i@!QoxQomralad4KbmmYkq$<FZLY$D9qI>Ckw{g({DJxT$KB<;Z#?%?-%Zoa zDmT^Cu#@*i7is6Cy?qqjNi-ExEY;Eq#iW<rhr+gYQ+~$s>U_Rq<h_v_n;%0i-;Cdk z#}GTm-SxAk2b*l0SDf!fTwsaF`)@-8IOe-qb88z8;}Q>ugSknoKc7E7veu5*?^oeH zJ7)`4mdB3`|A+a!b6IsR8;PT!EULx<9K&*?6dd-rrYbPq?XCp(-*Bx9N4FRH=e}{# zH=gLCDz_}#;q&Epr&hU3usU)Y0}ssW(ZqpNFI~bw@0ceE3I>+fFkq&NJR~xDy;1@7 zXW2hrz*B}6-)KGr7OOGA0in*tTs0F;&@ntsaSji`^7dz80l$Dps*o0Fl14C6A{cf| zb@eo!_qT|4Jupx^)N1;!7|hm>@vHTT9?)cOn+(=BtO3{Kkrk;#3P}&Wcn8C~gl<CG zCs}b>3GWS5-c9)sr663P8$0n-Kope3pqLbu95Qk(TsC2>bfm+p>8ceAEm*hHM&4E{ zhPNcL_)(LLZ`P@+_cua<aGC3}QFV&2POC)N=|IU*JZ><oA_krvc;d;YL+pom<jFFU zmh-H@v}8Ta`-ykd&~HlGhFBMO_G-QJrND%{I;|H7bqG7%x@AS>?ilQ+7n?qXU(^wK za{lcwg+J&>6DDMh-@4^7+Do>5v$ThUXfMG_Gh;DPu)be)uNYpthzjlRIk+gMT^s+r zzwl4-$&Z)wWkask#}peB5HKPIwdZn>t?9)<2{%>Rb1&FAEDE+;w)H+~d4JO{Vi)~& zk8cj%J9TSh?5`7bt0dra|GbO2HM2G4Vz9kd=KqZ<3-7Fzv6JyPlg6O<kcM4%uH>D9 z+qIp+PCPxp1ii(hm@z2(nJtf_?Bv8xjudx%@V4;$$Laq={`?sFaxdxhD1EP+Z=6_N zF#E5g(XZHZ<$pL7-#ILq__hCX>#DDY_H0>T{B^(TKb8(FHhml2@Bi?rE3<3kOTYM? z(UrP%_-3sPVop=m8f5SM*|pps#CziJk-Qx#ZhLmN`jbSacUGT2^f7S9$B&*Xm|eT^ zpN4Ph_Wu685ePy4h~!+p<0fikk=6$J{}^)m(ruU-j}PC}+FoLB#G0!kngz((owIxc z)r3D7Ek+Bv<sy^Xq}JZ8a`O$_@ZXlKeBM8TBpt4JOkH)}t1fG(t9p+<4)LgdU|~k! zUSLtx`GKvk%vQ%QA%Dl;)LVOzdB;!fLxDh=7u~6uO#C#gHvMp4tktsC(rOufwH04e zxt-pr$0GR+>n7Z-qVIf>qFp@5pWPH)WWMJk>jrLCortA!i*%Qt11b6~CKY&bs{3_* z-&8j=C%j`V8;#rM5wwt~MMa=rcz#IrTDBe%2nvZ7eV=Sqt7+B)k83-yj~*wMxtYcb zeQ4fdI$B%Jx+#m^k>Bm!EICXBm*@Xj{Gm1R=uXh)%s{RSH0A1F6x(dOkdH^1;y zfR1C7fNACv`>;L*i(<)x3qaP1X5$wNW)tRNc)}7HSr0ANVhFGL49_Y}7@j6R$44aG zv|-i>yX+9XwcLzX!Wmd^js?EbE79nLuxiOdya~g@L*(fpj}c$gK`D9IRw&IBje^_1 zgUJGU)&PcU!Nw&lus9w!uoh_{8763e`ahyLM@3Gy*0-YL2Mrj#p;zM(JbPWyOb^1K z1TNTkC!Wl(1W$xpZ`dV@TV8SfGzx%yAHdA2EfIEP1ted{poV@@G1Fv5A9-{Iq^EvX zdbPgq*n8}`Z?KdUOOnus6V>1by7hd#{84ne?KL2hdM7CBL>T3}8}`7}%d4|IDa}SV zI`ZuCu6}@qANhZK3mqS{l>p(Rmo6V4so40Vp<?f34aqOr3FXnT<3Rnjo&KBJyNR4b zgW8gPFn8wSsk04@4b>g5e%p**{4M%;*T3OCzx*mV&nG>Ec2CVs#yhba#TG@4LYpDk zexdl8EjMcv`c%=(#aZ%Z>YelbEt?fW<8JNNn>7nIy&LWHZ|iH1wQmAkm`Qv4eE*)! zzcVcF&S;OmT72rgnO07G?eCQJpPxF_>Hl3-*E_%N|LD|vU)<!Ly7u7tZ~keIX!d^q zNvz$3937dDG=J~H?5}6nn(J2m%g`R}^8S`5)6Syt|56s5H*)qrmd><(yk`y#I75>7 zyf42gcyyI)sAbFVDj(Q92Sr~j*!1oF$_K}3D^B?IuLpna_pJIC|IJMhUJr`4;{u)` z_`>-{%hHE^eBr&J79H~YZqj=r?*UsKA3l4v&!;x`!P}6MYB6s739|Pra{c^3b>Tau z3S@0YVEQ<K`JzgGU#u#)?X`T;XpebPNOGq5Zt9%9E%{$r>u1a3NWEi$`fj&l9?*g* zl0R!-aegm)aYVf0yxR&)K0UC1C*WJLFH(qh-ef|%#ljy{6<>sUt|fmq0qgGpBA_FW z?cs(hXb;;OE1JLpDJ?rEE!;KIGy1xQpET6TR`OcjB!Uw;h6i=bP?5`iDc@wUUD zyyv`a@@in-Ub4RwpO2*gG^*a%4OQ1%r4^y|yy%;!hKK*++|aytT9@4hC;Y49vzeNy z#1eI^lX(rJ`8ClUf!u_zxSYY_6taLzWBTFGhZ5iMJN<pahzzf0m*tV=OcPmABip0G zuV$=y!E<C1>&W{0`WIIn_zC=MhZ|yx$iR?z7{lt?l&Y?1bUYW`U7dLnF8nFj8Br>V z&FE)DcK|!RLJETSVp9kUvoY)@R-Goi%J5M5GGGaV<rE|EJgji=UTnlHk=S?*@5`b1 zmfIi7#P-CxHnJiSOE+y|OyKZf!)JvZc=F@Z{=kGjG4*Jw*39=FI}!b(?9|lR2Z<F~ z$adr+8918Y{lm`@N}XT;pf!UqH^ITK>nvr>ZfUg>Sh5Gt&TBSSPm@RXhHgx7S8#87 zUysWND~rvb7Q@6BqBE8?VH-bvZhg~Drnx#y|E%NcL^YmK@^ty_w`+duUvE(458Q)r zfQnHwG>Rv;VyPG8+xim<ox5lTjq5W2aBA-4@YypP&XWq@QucAHzVa(-;r*h{BIbYD z8Qke1DV)fE?N%iBruXe9l-(I?E$y3r(y(i`VpXRfe<RBASVQd<yg@eYTs4p2evCLC zR~Qs!7$ctj;9U$oaz~mCV@8w#9U=P*??EJEnvJej@RHes>`pvHr5Axs66<FjAH0j1 zN-NinmmgPD&h7`X-<;aJKk-@Zc>dv5h4H)xFKkx6@YgSH)*@B#aMm}Fs{e`xy(X*f znSuxZdfxRc^MA2)-GNa5|NkO1ln9wo_Wam;%U;<d8bm02Co36U_TJ>~tjw}k_KM<+ zY(iv~Lbl)Y`u_g9^LEbrc)y>|*YmMnLeS$aMjya8jHn+9OL`c=3OioTWZKT_U+FNp z2={o-j2hhMGHVLBzOVZz4q74ZBut35@{$JlU@YCh?}fGtm%j-C+-z_I!!^`_>*s`T zt}ejgR5Toz_&4oBq^ZAM9W**F@aWmZ#ihYNNf{2X7IjPm1t1bJTwclnOEvxS((_oo zcr~S$I8}|^I9ztTfQPl!rh+U3cd24=1{g?I!u3a;1Y^)KqTnA4hgbRGlC{f&)F0i4 zwK&ZqwtR~Pu2V>e1R7kOeN#id9ju8U7K5>oUyeW|u>cLQEeI|_qz>$2IY4tb#|eZ_ z-OlWAdvUdr4xzScZvOYW_C(@D_-Z`1vc!X$vF7(4nV-(>YQu%U0(;M9qr8_a3tF<O zNT%Lir<&?F+N`@kSaJWvlfuw?TKth=+_Lvq(thnN2{$CY$DnlTsN!wSbd*CoMy<Mz z%8A!fucD!V=QF|mF#RUI5`ohV`eIX;=n{jc-36H<j;gB+huwx>3Ofbt7DBw#ZkJKh z?UwrLv3K{sZg)KTl<~)OD&?29f8)rfYs2Fdl9_1U56WfKeSAGaCso0DGdi^LjtwGY zS0!U-E4QksHkZbAzlgEcMBW+gNucrM-at7Cbu|P%GBs1W)Xu~fuT(XC<9zsI;+Drz z3zb^Wlu^}s4u#vQN_R^YYlIvMy@}|Eck+HJOevQ72@dep-JG<jB7EyhANMv^gHr08 z;oGszB@ZLD>(cwT-;N+XpJAwl-EAGvQ(85RHMKElfp>hHMlJ@$y9(oB4y>FGAU%I% z9_+x&X;Il=7t-T7RE?`L$R(<r60rCggt*e_Eug2=stXCaa20V<^SR*#M`>KW0yz?h zW+yEw5q%)x3dwi57y)`$$^_6mq@b!~9l+Taipx~2EjkC-`@fJJu!x|VfbQ0>2Y&D3 zEWq<%(Pf?V6dVJ~Zfz%+!F7@HE>Qc{_*K1VA=C^P3`?z{P6@fMxKu#oGeul2>=p`T z%(udT@CLWml3c|lO7e2po*Dp+4JY<5VZj|1W9qg82o`D#x?5ci0W~X`I>*v&GFiw9 z8x0Zi%MqaLcoTjXP{d7j=>nWSMj#W$Nt{;5oEyh;;Y6o4X#`Z=ZlJ|G0EWf|hANX7 zP(UTn7|Y4xK|ZF|f9lUK>yGH#&L{^JY|9<5)}e$lgLMybQ)nWvgO5{}6KJAg<;jfs zN91<v0k?)kz-=aUgry1PKPq&+qNL8L&O%;ACA3B;6EBl+30!G{I#;y1>^NyS$U=cM zV*m17iEJyMu)qy05CC1w0|F0#<58h}fqedQAS{<~GY$wVpt8Xc{8c5u9>s+306kp} zjPZ;vNXAX>yoE|iJRFqs_yC~GtAOuZw*gH43Y5_VVH~g$ns*x%*PszAhw=cb^JJl7 z{*fFU8)0-g3I(!A7*-ZP$O%nyh(#@>;=mc3x4H4mP#EF!VRB<xVc38XLknPfc#<Q` zbrp*DR8ccvo<{V&!B8s|0fGs0b%2qDe#8;;kaZ651yB$G&;T<8ybm|5RW9Z^q6Yx# zVYP!_!EX+|JP&FeUhM)Dr~wwr%Wu-uVfyC2L5zc&$9c&Ut}9VebmFKX?i-iX_0@3Y z3E=32jT<*)GpW&F*5i7UCa4CKS9vKf4ld4ox%fdz1mfr5p9i05;}$I2FT&%E8=$m| z7~~3t5{DB!!*wJ}o&xJx(1A<9(i^8p-NzMCj2*^a^Dd+$E=k&njKGyzrvT6R^-0yL zVH#CJg0&+;&(@2nla*6b&k}9Lk23})5I-WvUQt!LMIH!xjU7FzK8_K~#1?A^7NH@$ z1*vXADY^4Lej|6=1zA4yO7RT!-&0%K>n-6nO}TwfYzq~`e$}*{Q2H|;zd==|g0#-v zxK)SE&6=WUUW&=UKtj2{UG`5!BmUbmcD%9N`&nCJj8xmmNm1L;V6)Ij;qmNcWjQJ9 z=ck6`d_~o(51gC{?wt;#Jx;q9z#n(3Uhmv3@h+WwW<xaxb4jk+s}iA~mF&@FRw9|D zETcU>H$GVupb$^uck|Tzq+&OGs7m?d#Ruft&qu4Pdp({Yw0-M?l1T`?KU|tL^WIAN z(O`|$y1u>ZyZcTa9>jG-;$Ld_iY~fvD*Sp^{<_CT^Ox%C@qo<M;li&DN9O;&E8o_P zLP}h!5`B6(NphEGTEmH@m_5C*gKlee-_W-7ydZahIFYd7YpZxQWSQ+xcV7Mc;axX3 zkqJt%?!sq=bDNhR2tLWUNR;6g{h-9f{qfkF>s%%SSQVQ=;nNTijDyr|4O8c#gYc^} zbu%x${1q7@U90~}@{XBalv+9(HRGZX|KlcN_vvPMjh`>iQV_a}Xy&ej0Y7~ymB^)x z-GU!)duu#};*6BSm${s+jV>HaUsxfm+Su*(No2b-F%w4hxA>B|p+fQ7atr~_KyOvu zt2iGuYB^PUoyB`b0#%)8*Y}&;g2=^^6<|kOds>MFE|k2J_CQ>ex<^iomnqA+ih6J` z5wW_uh4Ll0tYGN4KvAtQaZ08`k?mF3G!P!8@M7kG*`?EgTa#>9UszqoEyqt`OfvX- z4@GM?6H+PpMa$di+RymL^key<Hpp9*F;l@L_FZB%e!8ffdcn#rC05P|vy#f}j)|SA zw1#C?tXBld^G*FX6B%1Pj#Rk`*9@+?k_cH=*1r0Gg(~C*lONyZ2)E(z9NxnQX<lf< zhQOT=@gv(W(LW#NQUc7B=8;w{-Iohzh#;iX)TV@A9>dXwu6HTw0}GcL(%rS2{bV|P z@g#{bQ)e)aTelkBN!}&sz&dRJH_N%J@V?u-DDn#tAp8Z#EXE}^O+5zb$N|Z2xcHJ~ zidRCqNOkd-ts2p0;^^GP@=8ArSOd4ULG}(g8fWT&x2eUrfd2zTA`2<RAR583;oud7 z01-U6`LM$Zz~4axC)m(+paS5WhT|x5g06)?7^cC$@!xY`h|6&X5-VIV;YD~cxT79T zJF-vVo;<7_1Vf_+5^n+C4C1V==`Dle0-p*+hsCKY_|*yRT;whwaE{4N#!HY<N8ZA( z%zksH67lMvh+Q%Q?(4;|1RiY^Ee!J*Jdm^B#l=n_a8dGDz`tH0bZfw^9jp{6SNRN3 zEFlwKl^XJ2(jY2@(*S!OZU&Fgl|v5>6XUFqib||3VDU3B7=QN$>4BG^`^X%Te~F_7 ze0s#gg^)H(-$_32owtynXG??+-2fa2AmwWrQOM#z9Gcl;$PtGg0?9&w|BTx~Xnydx z3M%`p27$m+h(=HeqNsqB!LiLag4IoX5rBD|1SIhP8$qBSu2iNBRs!jGcSu!x$0jab z_zF4n#`HAKXOdB$Io$SPJfj3f)7EYt5I^O;iZI__wG&itb$v0E7A)kL+s+0RJ`83( zy>fGyQk#Gga})xsD&7MJEw`jpv95)=pga&da74i@-UE+qW?d~U%!7IWp_QgJN(rO} z87#e3(ln2T6>uE`H9%c-?PTN$pbIuBVE{i1cH#!nG-oMjLo)`q4m#m8#6y8Uz|FM) z^B$l8gR+6ik~$CGhz~Gq%>mki|6$nmfgqaQ7?CV&1v?1<OE_rE95-~j0R${7lrTN) zrB{m+IN|yfFW}Y}agwL_m<?H2Ym4gE=cjm*0}GiZs5ibLlMXbE`eHedDDZ%<Y6tIO zOa<{W%Q0z+I8E$0c+gn5BCbF%ww(E|=r$f&{o!Xnh5hcI_6(3rQu(D`de*x{*0Q%# zaC&>6MEjS+=vHFhQw^V^Sa1KK$N0C2BQBm>(|LyQE|aWm992}lva!ucjK};aWM{Fa z>RD$&&nIUmmLtvh$)*OO;{NHKR@o4r;ZRnArmeE6UlREe`um18XNW-i$tgRUCog%Q z)LEzN)@HPm$4(a*BJf&DuWrqBI{#rz`kslli49|JRxA8Tv*64B?A-4-qeRyOaYp>( zq9S=$maDq12nOF$uF^~i<nN@7E;Nj(<#DDHtjWw@T%7CU`tj}w-HMH;t9bFtZHgks zASwqr)%bE@L2p4=K8tW;)M(g|wtW5Mn^~v!BuZRtPElHBouMrfZ7iSbSEHWf`Es)B zTi)xpx7U~x*d5nsQO`+xikzqBOfAs87rC312j@>z)E(5^2|kUcT1HkiUHQ~4Tsds! z=#4qwwSBtzReVyeb#EQpkbVBsyiL=83rA1U|J8;sWPf_&@+eT^8e%c-DaV-mV*kRx zBPFc~)!$XGCtem+FwYZvph@c<7Dbq3A?VT$XFh&2P@G@CaWS{55gRw;S1MaoAoZ|% zVVq*QzPOqA<^>K1!kE9YGd)Ebly&?o-&ukfBN*%#PO%M7W2dLqV!zHxDcbQ=;%B9F z3%xQf(qVh~Un4c0(WKY-?hpEoc%O!joU0^{dN-rH8Y`yUo*?`PxvCSLY$vOV-A2)c z4fhvqG5Bof7ZMZL*W>S{Ucuzw7R*jcA6$S4nTxrJE#@{MdJ`@A%F7~sY4o2Y`4CUa z$*ZnhEOwY&Fft^T(;EmHyox?D5S#2^z{t$5-Sg|Z@t7N<rXLYJ;&(+UUMaKNW%WA$ zw|n&31m>Zdd86D2F>#PYHKj_ZuBj-LR0bRV)-li88MajpX1{(OX{yThl7tZLw@vwn z#^aJ_W_y#hdkNcE>6}`$mW*7rd{DS?z%0+-imcNnvA2C@uCG}}s;mmRnNbZrYAs(7 zMpY$CURSC}J`>%pJy~*zkJcQSwYjDmC-EunQcwg*sehxfl9a)>$9sZ_?4R!E+e(c3 zej0Zm`boRIfhuv;e%}?olp#9v?9hPaHFE`X7JBRp&1SCP;YZJB)9YBWJETR?MoYt% zUzdrc#$&^(m8=%x-XC^q%r0m3Nt&$6e!KO?=`d9=DwPU-hIkdq9Z>M%4AIDyfqr*5 zs=ctsC*pC%)wKDZdB?}uFKowLw^B3TNK&#DvI$LcQI90dEPaYF@B{<2?%lilOSV() z&-!L_=Es_|OEoTS)Dc>LnPpq1c%y!mOVuz@)cd_P1<&WL$z|GvE5yjzmfxJuj1f^i zQz#q#wX0X0#@CnLV<w56Ma!Jr?uSS{wnp!W=gXH^IxQeoS)<(Nh>`1SSM4>84G&XH zqT<j~;Ke7!LOXtlt^GHH!GhKpPYPVVkShcSs0u}fkmps9T2&B(h|dJx;dlt@A`QVz z0YC*2E)KjL``|pD|KH+Nzcv(Z7l>qe6Lgaum*S$nh+C{$)R9x<&MPU&I@|)wkf4V4 zr0tYWiV<y^phiua$TrMH=%F;vNfGSw<tv0q$quwyypP5;v1uY{7L{%Uz-2iJHMptu zaEl}*@PPcl?P@z{cAbQvQ;*wl1YGiG?<Oym{QuaQQZC|7T6;}hq}SztM<5%<<PpRP zEChnhjv5;IBu>Ev$u&B-k8a|K3)8TK*dIF=96GjGK1rhPKdEUjO{lBgNo!lX0WjI{ zWCT=bb@QQMHKh2<wq*#40Y16>XIBQGTZHiJP;gy~e9Lwj1l%}_9;W}|RnUVZK}-mV zy)*eGb1W5DOhJZ=$a$_?oh<ZC?h0bK1*}`d=64CG0VpDePJe}63NkApme6hjeJ&cz zk`THJyf`>jI>89W2%0gFLPCGR4&niHBoZ%#0Pfhpy{?cNI<274-ScoNpqUjq2cw84 zVZ<OVNg@?91L=q$qqB)uWcbI$08Ro(4XYghJvcx{5OAkQ(|+fNMfGezz5=RU9Q0Fs zgL&qss+LN9JdPE!i5T%Mn9&>Dg4UypYrw$oI07A%#tmc_FuE{#neb`=?sOG6853Te zf6oAmt`k{<-nu5LUm=R4-hp&^cL|0&a38xF#FR<_Ju3)fmN7$`s6L0F=ADK@9Pc{_ zRseWJBK&<&9+286Ddd=ky(>vFKwAWFfY%P45jr7sz>^V2SPkH8%(&SQ<#FTkb?Cf` z0Iz{Gz)g!awLYebE2{mk9=t4GC=92)j}}TVH0K6w1ALy(cIKWi3A$+I82q^kuYxTI zYBBwk#Bdox=y!YhH1=Oq5hb;no@Hd^79~^b00er&_AV;QoN#RcqU%t|AFPCMek8Xc z%V|!c?g%*Bu+`<LmL2kkPcWh$v>}p+V-k6dpdTFRg<iT3nePe?>TgG7_;HF&Ef)$Z z1&V)}mCa<+{o}Sv4sh5NTg!kZVwJVT%XWLwV+0Ia8HO3yCu!ydUe-ido(uD;AC-&Z zSH}k1O8=;1>WT5wCZ#zeXBxhcYuyxO|6y-UFoYK`_^>?Gxl6JH<8y16&fkM$$njES zoWf#_?1??nJ!$6m8orpceP>3Trsfqo`M{&`6^6yTxz(H-L6vXzPd9wd5FR1=zCH&0 z)~!Ubv-P4#>u_#4Mtmxx^+S)M%U9=`Vix0c%;>IhP<=F0v|xPAOmDtex~L_xcW^FF z>L@5JO8yVsCdDA_Wt-D;`*ix+iH^^2FhtT7i^m3Q+*47}`IZ<qVpH^B+%Jyr)Mg{Y ziV9;B=6*LnHiW#-(+{#YfJx3n*vX5vpi$d73UO{~+F#0q9CmI8(sq(#R5#Uv<=V+D zBWT1S?0|_}KT3<m5S+vtz7R+Z1{9n+)r|iqI2VR`hRPpXqFpbPG&qr}*WMuIirQ$T zY0Iq)G1cEU6?mDn^5|-VHQVeH#Z&AVB1Ge)!m38bdo|<XR|>UzevUT&ZEOxs#p7yK z5e>>46pmLkCnJ-Y%{z$9orow}vBQVc4MY|9=*vfoT~#KpJzQ)u4>id%@4PLxtDp8~ z@~)~B%~PtwTnmDstk=~sB`(#^2fmk`JMP?x&oC<5PZfzA8_3$XpA}1y-noU169qu5 zg0`c4qpy{9V~1Jq4AG|H6unEfvc}oeB(t#Ry5V3MSoP)O>o8ge|GI$FD*i_GGeqk% z)Y=4QH_fkoLZf166Fp_#yG`FJrFdigRA_mdd3@o*pIxEm4?4BJ)by;A{HBxlE3QoP zWyVq8_#krbW>=Llfg;EGoAUJQssm~kWDAQZrcU<!P0W<`LU}*b{Yi6O=Zc$?BH*K+ zQtGsXRsa?MO-T4Qlzs<Z_Sy}l>Ualrqj;)(cp3YYo&(C^2I;>i@cOn6UpTZ7Sy?Jc z`V8fug%nu^s!h^%N}mE{m%CVT(M+pKWKzUn+VeIf<clo4tb>D;&?;)ik{!5kzl2k( zvO!BSB)kG@|4HS1q9}cMHFmR5M7J6W?Leg^`m#pNWYoh7%1LvBboR?SDdMWz8pz}) zRkZvXDdy;haJmk(c7KT2;j_SrC-a#=bOi?bmiWsK_Y`5?NoWA=6@=_~`4bu+(%>M~ z9-L=&WM$pU4l$I2Ms);8MqXiVsb|~5@wQIC$_9IfglDJ}Po`Y-ga({N1wD_f1MjqM zKP8of-U-K)o##+LC9Q_`zu(bz3E;|2Ouys>jy9O^;G7{A_TQr(jDmRZwca2B5AUNu z5Z2++tn47N4bmDDAW$Db>q_%SN?`a)fq<8PEG{UoalwO-Gl2ub)mua$F52^dd-vqF zfXW`Wp9-}TlqZY>`WdjHyLj<xph#pt2T&W(M+nen!+2^8G0G8tl)_wcX!tdtc=j06 zBDE?vhSIphaQn~D-BqHLcd3LdC!l{{LzV_?de9GeJX{BTbnReQ7r`z>>HZ<O1r-vN zVBT(Li)Mvhp$7vRD9`>)M>zHIzrrrp4aIg~#1VZSSLkhL<a*uIG7NlSAC6vt)*S6P z2*Th6U^reA)B=$+2&F@Ly}`r;fwhoKh0C(4ti|99l}cU#h|Ga^SifJAbRpS6l`^v$ z)9-ovqV|N|`TUT=W$J6{&gv;V;zscg<=<z%&!154Go~h-6d8?7Nar*Uv%FGinLnYb zmn>!y2Gm>iAHdJVHk2TLZpsd0yK<Ry0YXgpRm5B)Zi7=c1vN>;v0_lS3)+#L&-@dZ zyeDabKjp}5A`bOI{m;i5{qs_~uo=@IWVR>!L&+u<Q7@r%i0%5I?2|`{@)9EUk)lzJ z-b!B8;%P;{{^(ixO7Lz7YseeF@8=`*?8ZJ3Tk0%4l)dElDah%T)Qh|~;)W%tgCItP zPwr&&TPNXEfAy6odgCR;V}7d#VMn6^=jAaKwsYd^b7Ofa$Ghx{d7O<)p`S^<DNH|m zLj8lYv)WiHKFdc~e_1;Go%0TZdLbS+o|4|!wNK~mYd@c^P&mG`C!<<aAY5#~ShM7e zeJ8;3dcA0M*=UQcY={wH`XZB?A9rGH`S=xCqjQI0x}QPci-K<}RCLSbUP1!s@+{#Q z&X5Os_T`$@=O_yO1P0E@1$ky<7->8E*(ax5<uBjVVvK?qzi;)BjPK~CHI?%+xrD{E zinMS}$lub6iik>x_|=oKjEg^!?zW-Uv+CUqafGTA!W*8eUIL~A+|_*>w|%ZL39P&{ zxE>reA@p+6yd=txooTy-URL|azPUF+pmejgcOiGAIIHbWlrph}7qMrKlJwMbJu(vA zgS*o-p0kqr=W_4|v3$2P#KiKgn9_L9yC*DDiRBF=P*PYdMDW6nM0gM`k8@A{bFttO zX!9S!A{PNWqJW$X;;7*^0f8>$-J8@j*48lfX5GOrca-HUQ?|<+YpbXs3Evuw7PgA< zjiA9+ZgUe~sC$i)Mjp4-NvMw+r>!aGQ$82Q%ruRkAyiW77MbyTjyb!y`bSeuD40nH zkzXsm{^SXzf3?2K#`dQK6|S>z3%lVNWZUWH;*E|!n$rCmS)$jw5Z+il;l!32B}y7G zdSv^TZQ@hKpaY7czk4D3VAWnC;PM$__nooM*hXpma*W)`m;%v~T~UQEzk~w~)G7%P zHDh<}sOBP9jt*bL>2%Ts9EW0&aPYFj8$G4BiE^N8`&Y@+p+w<ox<wT)Fe>Jrx1m%e z@X$imPRBjl3B6e5Nn`r(JwVf@X8%>;YOIfk2)t2Um?a5sG;i_dE@om?BgK=`$GzF& z-h?_CRMo=o*zmQnK0Li;dgQ4EyLv@+vNc_+<;P_CXo@EeynR{D6~>=X)@BpRb7tZ% zJN*)TJf1kv!p%qVqF(2}v?{2$ZXw8K3wC5N^d1#4lalZ7Tc0}63Tk9<8~tAOG|a3_ z$Gv<H>+p`iPGsma<*g~LDI+{L<vwv-Sx&eA+^XDX{>`#_lHce>y;edU?x@2%Ne<6T z;(;aAk%><qqO5rza&C0;_I;>?KSzqSLnXmGS|wT~US~%56J$HkD#0n!SH1<NDn}P; z4YVxSl~si=ylt`M-w+`l7>%27P~-=Jw&b|<K3;q~xM;wYrNqm@F-?H;1m1u+k4x6n zsFXVykPAwk-%rV)1R03%2w_w}Ld?q~;hYraL_=C2Sb;3}5YWv)MiqEL(qMC}C=OT$ zLx2+tF%HuKssL8N>)`#26afivVhhwwb=aaLY6gT(0BQD9l2_dl0=a;>C-I;eaANQZ zWn0MOd=m&bT7#YED4D1bkZmpouS9}iM9X^xAx)5Nrb%c8u?Z0H%ECh)=7P&LBBpS) z^Bla&0rSTV2i8fgk@U!0NMrK<gl4+pg@~2cJ4G^o0{4Z1@HwssFt+H`b`nAet57K} zW(B4etuELx&Kw)8?|j{Xmg4|`z`@G5b|c?Z37=sm^e%yADS}1vQ$7;pv?||+U%4L- zrG<tv$|Lcx!P)~F=NdR#ZNWwr8w!0U?OfqA0wCE$^_KI#Gj>ufM8IqN@G8^oH1sG7 zYdfY?jb4Jxq_!o3MnT%{vc4L`mmn1gZrLl0m|LbPs+y%~-M|l_2{;itLv-6IXRq_h z&I`X)eOfm4r7rsY>%o1+ZymEN!+G_=dPsb_toz;J8QWQP@BT6nd2~2U&y1_{e|7us z^piz?Ykc(8`@Yi*C+Cg*`=zyfMn7EoRj`ni719?W$sle1f@@pIw6m!)d+RgNpH3Td zZ<3CrGlZ{t>KS4^)%aAoInL2%X5WC=8(#>OHg*qf^P}S+wNbECVXkqgiskPS*W!Vp z-Nt{6X9$a@E(}N86sMNW=`p^SFk)i!+4rm759E)rYa?ZBS2MP?Sesv`U6|2HC95GS z7*L+HN#{KMtNkJVWZz)yjflmg{nvLt>aWCuINj{;ft27|^^WFvTiKb2|9(^4*TLI= zZhnU7Xe{+UuzrNZqz$iA3Wp^td&@(kn)=RQw)ZAF^zgFZm#Xf~<tGQDYBJPzOs^Un zw2wDsv6ZHoH>Pjie1f$9AWC|MAkqJ6u&E$WgI`8xTxoI4R-W!clh~!>ibA4gu1)0} zc)XVX8zwszU8Wn~)K$>S)W`q&E}mTL4?7!bo#yVWkat*Ke~K@3sc(!TD_UkQGK_sF zm7%rollAhjhrdGmp&_>x+enrgUB?{)0*z}bZfTzKrLSKQmAqwgXbArDiB<eqNx1Pw z8A*e6!^s3WNTMc;^G>mBaTZYlFYP`BnMHpqab#_`^UD53^(KH5!<t;!$;+VAbTg;r z4DrpNNPeTU?}&7{aGkCNbyD9TbX_3?{nD_zh}2+LKHc5sNAbnHNm~uquo(3l<I*HT z;lx^rx^bMoK1>2q8be?4vrdX?QlqGdkH$2I$Yv#(P5R0m#fpfi-+e{CMBM$YQ)(YX zh;vG+BkmvnF|PdGufRH;Oed;oNK=(3^XZ2UgSUrRUJaqU+LiZap|w%?j0_^vMt5|D zhbdIQk$K>YQI(t_Zr@s_d&#g<l+52w_bQQ@q8nc<-$sSsY*;Hxd}v87kWUijud;sE zJIW?)fiMPfYD9%UxHME?aj()bmWyyIBC(5$Dn9G^NJZ&V>Mmv>7euY(5{cfBnU@zx z*E0V&O$$|#kLRnmx1?W78niBl(GOj+$=W_ccxJXZPXB#-H)|n1!r2>S-FS3Eq%%5$ z!y@Gyb`xLx?F=iv3L*Lk#Os%IYozU_t)4@0{spEHJ$xj>Wk6I6CFGHVFTSp>%u$(h z_gsW7QjbGCM=@{(FCp5@Rtmec97z6lT`M0?N!r;v=R!fD8LvRbVbm^#Pz!7u2lNkK z0?h>>5A-=@K`iqANC)Em4zu4Clgj40O6Nh%_29p5t%t}mq@seuGr|pj_C_Na%eKW~ zM}9Lt?;<jyY{zd635>`k4mYEsUJsU5`jh5{{C?T7gBOc`gDFtwE3_Nl#xk2Pvb%D9 zYVFqMR9%T<ogt438#M_w<NR5~GHgL|7k@IDqIGIN`ffO9vMJ*9a4Ea1tb1Wq{-$*i zgE>EieX-io6AL82`LPW5hlf%<c{=xB85T5Z<n?x^tbZo_-f3JJ?c)030lDSd(8IAu zDW$g^UH*odeP~_sPuQZQMT!h>Yg8xm^#IFNh=+ykS(I-GeOHfxoHCK~kEB;y0XVb7 zR76ZDPsUs-lio#NsrF4_a{RW07@58ZgJGdeRbJV>DYu<x!#uk)5(_TmUTTfM1o%i6 zY(G%HE!W#1UsUG2N0ly1BQhLG1yGn)!ZQpo&!v>K?;{fqwe8#pLZP3DL5qU%1lsKe zq&At@Oo|Nu05v;=!073N-~i`8`;ogq4KZ`jEO4>NYZr19`PFnl+Z9L)+qSr9Lab1w z?ju~d9ZntwVU@5YB{-x(GMWS;u`M13GZ1Ru0%e0u=F5d4(65gZ*~F!%2?EL^15$1< zR)Ojr<lSIw8K6XT!<L#Tp-^(&G!6*rjlfa?83cYVH|+snkw9AulFq>^H~wco<h7et z`~!v#4}yYPGVrtAT+`YijscyoQlTi$t80UrQ)>n*26aKr(EmRoMX}}A;iXj1MF-C( z)E3lWw{qK<@zXd!h^!Ro$0F(>>Y*nGdTv0lnv2?4P~fA|+S$1MY$$`~RTFJt5bv6x zt|Q8iAC~_kN(D2T7oj_+gP`%;K^_cOcJ|!-6KIezgC(^NSRgp0SX&2L!J%yRB-nOY z5aMJ~=?hK%@jJ?XoX_|taNkTbf`FC?n7!KT0G|6FWTusYT?P-tPjw?aXRsdw(}Po6 zvfGz6C;M@od*UJKVK}GJoET~bI<Hb@49QRR7E^c{>~XG$S}AhpZBg}_#V)sX=7?j_ z(W=)7f0F1>{X39(D=LC|a}E@yKMQQpCUdH0An<C=Yv*?SsWo#q8Rx!Wn^VK7zTmyJ z8qz@ab*Qyt^0*VUoMby8KfpDH@{q?FJc#kyNAw)27iwks&JaFDd*R=!#QZINep=9l z+8cYyB<>5<{Ti5{K0`zYpCNLPKVQ@Tx$pL?H}wTku|Ot96HPkezhO)>*_D&+rQ7rX z(;g7%o$~6tnHr5@A(i@<_tI@Py4ZJ=1j;V=sr?or?Fk~f-#wV`$yxT4y1x4^;8)zb z+>(7V&*}D+<26HSvt>uLgWEVW(^%oaUu9{o?kW=Mr%AD0Mmx>s|MlnKejw$k#u?&2 zQaD+AQsWKlr}3Lib*s;wA1+{&re*Y0=p%jZoJx*9<fxZ46|L2&RTkVIL(51tKG;n% zitM&=Ik9!ON$ON~S^Iqa)~zPaZC$_n*;)>FskHOc=wD}uD66Kg#gzx{gLyuD^fvSU z=Z&I1EWYEBG^qa&ua()Ddg*7BHTCg@;tZS2V#hi%JtsGmE!yz&@rnJ#%+{kdSdjE) zhu8boMoc{5V9a`~IQV3EG8edlKkA;JrtB|w|ApqZF{=COFvm4o*`r~pThfQVlSwe@ zt;?qADgD=-O;^dURlb2qubbtW<+Y`otyWE~y3EAr%*~^<ele^X#5WbQ<!wx-hN^tJ zn^b(U(F)aosNsq2y?HMngne5%zDD+$c2g?@wno=4ysoApi^ElLPG&TgAzUbb>A}?e z&ZmW9m7?)F8H-I6!{NrqMdL@Pw^a$H2(JhJiH|%8$T+F#qJPpY6xl6lMW)0@6)2MQ z8!cyJs@i3KSX;b{F}-m9ka$clK_c^_dPi)rwO1<7gv;l7l0SLEC7E3Yt(NUaC1e31 zO7xgEwXo_$c{&>bPPJ=RPfJN^3$aIub?Gv1azDiKCg(K-d}R?%bo=&zL|gmhvR3cb zEyP#lu~=tv_AT$CuOdm`<j=|PmWFHOK311_=3?A@`PVnvfcK0KJo?TMoZZ>q{)U+| z?3n7$usW?<RQ0q!lnj_N)D4wP+u@i&Mfqk(WfQxL%Z`5XV>qeQKkj7QSHZwyr09NE zz@(DXuhLbwtSbMJq%Vwgix~6L#P0K1E6HlV9@8B>A7}VEmHop$V~$9ee3{;8;I!z| z{po^@+FL5k9C1uV*S?d~QlzDC*{*i-L=(q<>khZnd+lr465+b}<y6uzto`Tp);KNn ze1wau;Ne13sUZ_}mp#GfMT@~Of$8Nq)n8m?c-~R0ES0|xWLt07>=rqFW4Oln$+l)g z+K6nfulWtz!EkK}CACa$-5;Se!ZL<gY!$)bl<dBt9XIc{GsL~8&e=bemHbqZV)Oon z0(8WW4-NhF`*~kv^nCi85M9tI()4i=9eIYhFoXPQ`SIy$mdv7YxG|5at6InP+PeI$ z#o1IP8-DO|2GYIgpm{BxPq>~na!x9cp^=mF4PBJoi!DB7l_ufGD`~>#YBBoV!o14< z%?Zyudc`|$;L$B4ix&za1uo{!Gdn&PRgcnXYB?@)92t(P+&XW9G2AT_bF89uer}l( z#z(cOg+H5cE1=;^Ib><f<IhH1a^z4M3xw?&iLyo_d~)bKbI#in`q`UYABcCH&su&x z#N<%adeYJ<Y3KY=K3Mv7K7xdo<bG}nI&`6|l_a}LcckGO-ljP74n4;?)l%s-lSg_2 z8?rKqLe&wq9kFi5;#~m}rCwbf&u_Z)<X10fPJO^WvmSYjw78>WLs!tiSFxBuADrca zeUGl13`zVDl;&$Mn8@^frJ4PP@1yZY$~~>jX+(pVS!A5@w71d&j3_xG6B4s#Y?;W- z2?y=$_%#*Wl<ic75)f-w@G-p7wg^MQP;+;H_p!-dC5+c&jToEiYB(R&#`1s0cR4Wh zuk;5~<m7Opim&a4wP)>>&&-xCJ3A#QQjbWxGoP)m8GbOg@w4Mhn52kZrS!i&yhWcp z@cWm*<?Xck`pnlBZNKN7PTVQJhpT4<>kF;lj$HG)x=?7arNgmWQnb20wm0=Fh6W{y z6#5ZGsm9E7^!ikICD?_|V9ayVgD+B@gPFLZW!(s!weVe1P0Gp^we-?cW}jiK+-6%O z$LXf+WA<0a71u5WndmvHaC$S&V;#lvFr?!njbDXnb7!qbf>c`#B#nPlq{rFah)pw_ z5FV7f3f+I>v92?Z*_v_EE5EE|1N_`jvEzDUrTqOqgSI(2W`?=w-~(f3J5F<h8vIfB zf%{+d9_-jDpZ=H@{cc8GMyn@3ALC~~KU#H;O4rwwoEX7;`-6MK)s^Bwu3qhT1*Fk6 z7FMVAHT$De0c2}2iziIp_*Aiq2KC%t(y~H{ZHkK;7}=)kkZwP8;b|4>=^~fuRbIxV z|D3%p4GhQ84~^2;747}qtKlp;zuiNkReVLO@%e~ppq@=M-=#;+I_jPjy`p+s6c+>3 zVKKlJzZ@@o_0ZYglL9C)z%jvca`3N>!+|GbAO`^@W2r<;n$}2aUW~a1c^gc!Auc=m zZt7Pk&yCo`Wr9>NK^J!V3}i<!VrjS$ws|LDH3GjH=t6Eiqu~Z}4xHT$h2ywT22q=M z4=x0Z#}DlVHE{<7HL}eR{<aX>AqXY~oYcev+~SZj0=}&BZg>G)ZU0snf^Y^VR{vH= zh@po#ODf+;9p>Rb`3bKbEW>ajlPp_s&w-@!)gXS29woe)%ub7oz0KMck)SvA@h-4; z^9?Vd$5?-NNQegqo?9m%7Lp*B5s4Q=pARa4B!y%TJ{BX;w6Bmi6yPa<;swQ{1@5Yb zf?0c>k<^8v1=cyx-vCn=nCya?1L^w=@}VY8<SyZwpr#RMb51$wVEYxapSzg3v@oH% zoQqz1|1u}exhb)*+Cj{Inz{tG_MDKe+E`5IXNc+0l_P|_7^*@laAI_EaES1?fU}so zu(sO<Y(kJ54CwIybtiuL#y?$*`n)U%+@nFa3Yu5&qIE*Vjwa+!;G$h_#c!efIj@b_ zU?Oijj5vYJwI1!sGN-Xj1Lq5A52x0&Yga~W^4cpzQ`MP^jtXFT#O;%c>c>3=W1Qs4 zk2ymx>{j7-YMDc=U05oOxJINjfuIPiCgKVpLlqslS9n>s?*_|^y#1>x+Jf&CUwGTj z5HI|dD}@+kE5Efr`BmIEI2f?RcExc|Flq%Wd*xb9`{q-}bF6n|e~y=l*A9CqtxKo- z6U$<rN4M3s?z_AzKk67bL*#b)?6%jqK3!SU)*zMiAt4{JDXnKpYc3yQwne!)^uLKj zM`Y}*>ezf-`O6nOweqg!>YQ!0w2J`4SZdmj2=rgo&Z@r5hSN~if*RLi4Q|~osTQ~8 zOId2A^2;Nq#2mtlN3{X#1@D@4*eIG#Msw253%cs#OtToK`S6sOgR^#h%)Zr|TKJR3 zYmP4Y&`kAvzEBUO6mJ=LdT(05y)YxqjIq8NJ&PaewY?nmxVkp-P8h3dUsNJnbn4g6 z!}8xYOkZUA6qM}tZ?2j8Gup28cU~4TxOtUJvCCMQf!w~R@tmYF^P0wF=M?JrGcuzn zK_=ksvV$|h2-^x3n>f{my}Z|rzh?-EwM||cjno=47bkv1#_ww}_PJ~N4y|5HwQR!m zML!)Mbo8l96;?kzW=o+Zp_E`a@LZFfv0s!KB7Ly?=HP4I3t_wegk&iiUd);kO9Yg6 z)%sAfM&J`a_}lX3cWy?Rk+DT2Kc#uiMugaRo$89|+KI0+yWH>d+QaT=RUX`Wv&kz} z*){0@V>hqY=~_fKk3@jGv6)?pOo#(dV0kpN*Qt9l+9{ao3}N!|b5fVs@`uPQ;m`an z-On`&ETdG_yToHv^w*6seF3C?CjTkn|0@0ajr&7&^5S*6(ziSpy8_0Y`s!$D=>fSa zdp)(vM6!NA`d8Z6z>X8OA97|<sp__7&jYWX(<aHUl(*47%yBk5lyP5CQoSBrJt!H& z(f)v~zV^eh;Qe@G)sn_cqXWTfGu7WhwRPCCDnAWKYF=B$_Y9kL4%jnGJ8IucikrIg zHJMqs(KWcG>stduiGC*Q=+}95S6v5<q7&_p-x$J$E%ZA-nA%H3j*ga1n0C1JS-4(U zN&C*a?b+zRSLVGxPZU%0r}V@8=S&BZC^nH{9j-UZJu$>q&O2q&P45k+^;SP?JKuFV z91cdWRXM)NFZ95$chT&em|9Ee7xxvL9lRmr$}I9Fv41?vGj=<T;z)tdbJC4$OtmUb zOtHA{PSjlcFjY|vY46I^R;*hC)Ausvi>@<tTYAeMo|rZNH)_?(I&qT59l&I~^ONCz zxb9@%Kn-zrv+SPzg1J`Z_jm?Hqn&rur976uzt>!jUgVa=NMsb=zjvNf=k5A^((O0v z8pRj6t-{C6gj+b6==Z<S?W~&oeLyy%6pOB*55T&eA>JE2xU!QUDoe3dTb(+k=~B(= zlpOq((U>6Mk!N_%9p>1e>bp-bNH{vMI_Y_kMW>yZ&T1@1r!2P|gZAgsvz0Rhec{sf zW0%#W<gPIVUJXQ0moZhDL~WYw<0OuUw?>iWGlNl8g`OK(WJ78SYIe^VT<*W3kE`fk zntZk<`%Ur#8D|rwhi8~db}Y|{-tKjOtOX+*KD%sJ<G^>;Wy;^v1idF5|6v5~<S4Ov zmHj#zLl(#6PTAcoZWhMMeWFb~>@>36PKxLw$z~+VvJzezHpiz$#JQh-%a?b4<3v;b zPyykuziL+0Z~n7l`1sKXPt|{y#?4LDSzgULOcObWHa>4SLy*!P3l0ZtFtG}ru9hk) z*HwKO;aT}uaqkD2+)sQ-##j}@o+X<)ev&z-;ol;!JZ#@05o{P8ezIxxU!C7k{I~gs zRPw(W=ZYB1+L*W>7@{t&#LC>M{k-AwdnLl7OLFW6kMj2m`mPm3iPQ)K@6T$&<KC11 zk(5Mb;#R1MqYdoVwQ)X*2VoU-#FrL7=wJNvE+<3$@xpmMz0giJvr2gmO=YU3Cm5N* znhb;(v9lZYEw}po4uxeGc3~Cm>+7?udVL_JBc!qNJYF|F5mWrS_8#8TDRG_zernA{ zMmmzN!lk`VGrNp#M!Th{DDsMxNlax!h~b?z>et<u#K>*i`oB!OY1A5he(QCiJi6E9 zxO*uwaNf;is5kT=km)0J?iiV6kg{9nEhaYV?k1s|w$!(``{-zFrmwnk_$PAK$ocFE zncp$FS^4k}G8RR|;=Nb!Au6He0Z%leo`68a{K%|j_})^Swhle#(hZT&h3jtD)f|g4 z3yPKRgL|%g7W+AXuyJj=$e`}}(l@#*^TT%jd8XlCLM`DD`U^>_>F$o85L+mjv8(ZH zBdo1X;+L{Cso|g<Tp^ZLAuQTpDL*%}IvYsmxWZ0~c17i|rm;Sw*yxT&x_0!7F{E~? z@}!16nk%)%!{gH?!Nv>ohXjW>bX@;TXjk@qC9|g`P+3-H2hpnCDShX@-MN%J?Td7= zQ^+T7tD9<pn;AB#o%iZZ7GC1L=W2Xi_cBJ|w=&5`3e*0F(cBgDPn^4j`5e|nidDYY zOBlWS&-x@?qUVEOc`bk9qw@d6&s#C?{G2Sh9TuxA&Mim1m{!?o$$7=zTt4rD6{&@V zA6oDtrK+49A^%INxaWJt2Ug=t{wh=h%zX*wUQDPGs@v&lXt`Q)(I!@qIi1H40<?rc zlLC<(=%p?;AetsGM@D$?o>TeR{7Z^3m%D|X^mfxk{4B2>)x=pNw&f5}J1F|!9QhA# zh=Zdu`DU2V^X1A&1+(iyj|WMFQcJ`KA+9AAO~jg9qL+4=fIWv8HpvjUd{K!PLdT~O zbb6s^G)j*3r5$68lz@<<gg^$(Y{j<llGyc-OvKN2e~-_)YMp%2cnqYbszGVWia$Qx znPsYYP0`r)y@9cp!E9dHEdpCRxq%LC?J`)RiMm56$rVk}LViFh89VGUl~!`&%>%an zYW**?_oyiPwzZv6v=<!(T=|1nRX8uxmt?=rNmC~4+GeLFDwTFK-t<T}t*?tN6S$JI zINP{c(KWrZB2yVOO-%SuT2O_(_<6A#s&vW2dNszCqr~eHom55P4mW|1OJYS(%Odh} z($t^>RS&&?WgUUlS4JI~p{?5o3;L030w1&0yB$0`xGEANqd!pf$}El;i#}P{G#hSz z|HIilao9Tbi8u1)<uZkUcFd!TXcc1G<(s3I1e#IbRr2o5bh^+q3J?+t@Yz4$^AcWu zS2$bja(hOTlI5{{!&c!^kjfsQ-bq$R4qaX1T+4JcRiv(2F{RY<`Z3D#yM-dXA$>Ld z0%Gg;pWH0;57+&VmxAQi>kAal-sz^TWcJ2xw;pz~#`K_r0-YzQyQ$yi586wA-ZWDY z^l>&<Ezj|xzinf1{x9Q0J!Qv;;QmVQZ`QMMf@S?;n;B8LwW8ZMi}n~SMICuGocIea zUHsefw=`aUKeE6si>hge?R~|^P}>>CtYrb~D((v`R16(Yq>z30C!PVz6J*b~?&cOy zA|~zZdCJrjg4f(Dcgjll)hg$K>D<&fZA~0U=~i=JQAVJ@b^xI3*sAALweRQSDx~Cn zJsvqeiQ2uv6Y?6hbogge>vXPylO)B}*9v3po%EEgBFdZTwi?6hb4>a7rT^#}9oHx) z=q>+6&i#tFm(GvgmRCDNT$DkbA*A1)A({y+4}uw9oVw5P1^9iGSXoX6uqykp`{ezq z!nGSi_lmc9eb_$Hog1s)WicRh=%!+$fBHpAn%L`krLMXL>t)icT6(UJ7DaCj5IGOe zP1bXWj861gD5($O>{e08k&bxrym&2;2qHEHwfI2daqg(ltrM5)@NX1Bf|4H+aIU~M zfu0QYki)bJWGE!rYGPr(CYP%-auB!=D#}fe=Rsb_j9LS%*wOekck@zbGb;b>>IODp zNOQBMMvwOjaG+rmQ~oG0WKUxqA&m(a+;lB3J&kJEO_ks$u&1nRiVU;y7$JCnfQ0o0 zmDdQxvcR}?9V91Hg>Jel<aK$s4$!QrkOmXD0P#%_EdBqp8f^Xp0R=h3;Jss^hRyH- zaof86A$SQpHiL?@RasD@lZW>qF4x-(2<o;=9201e?ciW;0L~X8o)3pm!`HA)02Ky; zOXy#9vZ<SKfEpDo%`uW$=th#DsSEyN=W<<nF|?4mCq4k*peHpWxFrN)QFE*tE+hpW zmqD^`Odkd)cmFcdn3~u2Gp7`GITmM0PlYHyT++k<Z6V@h1OiTRiLw90oHzcBaMZbN zwII{6rUu){F&6CU3OEBDs4OAiCb!j9a|o<$;N2zYwPl5$UE?N7Gl~Kz_Lk~9iAo_} zVfK$SHRY(fU7fZI#2>HhKD#Qa^>3)Ce#ej)vG}LVWS@+9?yAQf>hIt2PxmPnWu&|+ z>nE}@j0d#-Sef6@U5q!h{8;~yiS?j6m5}7P>cNW6jQQfMjY;nsy_SSVl5y99wtzfU z>rR=GnCHW<^|4+Svh`u7^(7Bj>)x&YRlH6uaQd+A$amxn@ziR1thsfv?dhHSBVPub zfcY~%7)ww=F7xtg2tAIEy&WS<GZ?yBot<*(aXokQ;ZwyynHzF!`>DActADbwWyc!T z*Y>}d8q!_-m{arsF+Kaj5F51e*!8A=V%2sm&3`oOngT>=vOA>rJ*<1pL^lu9zhWa< z)(<jx-dHX>9sW%}Lnx)?p^#-y-IY2zvJYTmlGE0LfuVcWFaCDA7dfRKN96zNK3sdA z-CFzC`$5-c#Af&z;zv!Hgsf+i%DlwEDgO3!cYe~`A;-HOnvL)f@`optrw?NDxE7G7 z8v(aU1BU4W-pgiQXP6Pi76^|yrko)@M2{uiE1RjoHYAEX>~$~O|MlkhFS_|?FyO1< zsmBAY44w?D<K5gX=Px9c$N;l^LVf@3y2}s0mcAeg{Pb;(K2Gen$QfcuwCu*c81XXi zlk`FJeU)yaqfgPtuSUCH2k$)_3ym!}L)4!kguFNV5`)tQzkdJ2Ta3&;a1Hd=(A^J_ z8NhqVKcLrKOFY(%o#j6?azFV?{huIv!|>Aq|9q7Hm+hqeAdjqoL8vd?AwJEyA<Qv1 z`8te?{0Af*Uj+E8{91*@tRltAeXfXmv(p@#TqI)|?xpc-k^Cgnm>*{d5i3zj$}_~z zk2}Td?QhZ_{9T*Nkm&^TmctnW^JGr-N@v;8?wVe7-JgKegzS$ep1d{F!d9x5+_~L! zhRQJxQllkQ!IsVK!o@>z&u_;5@@GwLd=k`f%A&RJJlZn7B(XALT<HJB#jmjOX_U&I z^MihApS<c^+RUcb)2epLrpm=74+Bs4rW?zBW<8$^1K_$^IztRbKFC;Uli-zI&HqD@ zTGVqD^<3w8Fa8XnAndO~1mEBvd$7!fMKO3hr`tSwzpH#_Zu>70T&0|j`JMFv&AG2l z{N9svALx6C-c+zUT0dUbu<g+?Fir9Qewy%u+%Jn)_Ip}3HeHZ}<zQ30=Apx*)zcD{ zRkM3~PH&4@NcRa_Q{a!H9e+U67iJjBA?ri)OE4m0bi&B^7hx7-nppbgZ`b+QLwwUy zi-D^8UYA0<q79wCZM8u%flFl->qbAY=_ku+|9u+4x~9c{7td7xynL?^gA{hRu_S&X zMW!QsGc&*?FKmMNiF<f=zh-vA3WMToee%n1uUyX%wyVB2=SwM{UAxFIy-WBeQ()6m zxN|F_XOWXWo8D!{?_H9&$^G6RPcIEeHEDji{&a3?%=7ilSi47iwnnTXvF~52GXH#$ z-RNdM7(MdU<7T`k4S{K;d5_@DsoaK8m+qT6!;Vs-Qj-27?yu&0th~>){?wOZ`w%(* z^S8CHowiob!f_%~=`_jb6wAdWf{L%mRo-Jk*Dyi{afWj*;p~(KBZ6PrYsg++bNT$E z%xXE}tzlz$%nYCM*43w}ZMhSa8TVIYee>>{bldT4Rt~Co+{nHxF|T%0b-EeFcxp%- z(|bPsF#+wlu)&>@v0v36m`G__|4Z&7SkkI#mibHnqvqFV41aw8EA>_Zk!dEYxtHGW z_6&Lajkr+Ramw+u;~DO`!V35LU2`~?E@x!5)pI={ST{B$od{XF@<o92^zlNn-Y=DK z_o;BKmiU%vM6hj}$06CP`6IpWiJJ~8%Wgi4`HNb@AK5~V74DL3@EEV|shlA$MW2#t zy<qnm@?h4CQLFOjkJ$T*O0u{caeriTI=k&~@(GPH+Q7r<y;{(rp-YZ^XSvX)1>XRa ztSknht5ACoC3`8gF)}bz@<X(G1?!stI#xBFd_7vn_UnW{d{&2%7;dsQSzp?`fJXG) zK-2QnOGjZPiVSSz*=9a%Juj-oh`x)p=H0p~zEVRGpjI9AeOyB{Iq3H>dTGqV0$WOR zImU2CW!qtXaWcNCl2fD^(`44kb6Z&@alGQHsk2N~RyTgs?#G48qqQFlt!`H<y7d9- zA{%0e-NDlOea{%~WWLk=J%et~G3<8kCf7JRAv0be>`Mz3X7W>nzZJ6KB<oiXnL=N8 zhJa=9nuyWhLBQi$pTRCne{A?(#Zil^D^Xd1yxDCU0sKv=#|-9UU&3~3#<m731v=uw zC*@cYZkOxE_cA`2{E%03DP88S|DM|yY!3U7$naR0uPI)IU&%sbluM^!YVLPEscduo z3$y;uRz~zMOwDHYQnkEuFX6HyGdNj^r`}~G9&=o5C?QW7cu+w*deZ(bE6_olZP4&( zq`=|Wk16ws=~h?TM@ZFFbJ4sDpY7|_d)s~y+)AC*>hFIb-YYeHUi1-W+;b(WHYZD5 zXl6WzfH6)`J4rL7FHo*aZc~#@tflZ8m-Ek>VJ4x#n_h`E;?v_@I^XZ|y^EWA79k<X zCtYp%u7;<ZyyXpfZm|z-(4-twsSf$*>nQ%0ItZJTaN7Y5md1jHi3|F(Z*wdG*>X~M zUWaT{+n@3f5NxiS<qKN_a#j##X7{D4Xz#T6?2!-c-<5C)LfSnWWMm^m^2c|cw_2AY z(7?|z`g2YurC>r8vE(XkCpoOiZ5Cys@NsLrH@19h{^|kMN)<s(cB;Zrkl|?}OJhnp zuc|rSH@8FG{<ii^KG!;~b!1ik>hNsM*2t@Aij1fmG9LrkehC|Hv3z=FiJURip<u|o zMKwFV3J9|2AlX;_oQ7XhDuoti_wJj{Q1NQjq{SDZQ`oLz!uv8jR<R$1wM6_xR&(kN zOvw`^9~;xr-86_bFbv<aw+K1l2_DqbkDG~{$|Y01&(mxvutl5f-1TjZqqx!Lwdi(j z91%VnM%pW4p6*G8*E>V1VnGUSJ|Qf(OConSJ~!g{3x!~4$~(VAl+s<2B=ib?{FfEI zRle~7zZ+94Nwfm{iHlnOZ`KHvb*hzZ_aYf}8!F@zP!wW4Vq)TGM0`tcNy@xL$!2E! z17^N06I9%9twJicIGtMt9>U2&*39BD;zW*$PlQJAy+S#J2dJAHW=fe!?*vThX(!bk zOi~c<?@%)u+>MEQLe2S{FR$oQyNHNj@PVG2(Q<>Gv7Le8j$x;vrH;D)pwaa-bKlYI zjicCh&S=YpcPZnleF0%Vtupjo&*8<lbYsk$K4^%(zAt~vd$i}SGS=7ac&!M(A>#R= z-hrT6WBAfTl-o6QptMdEEKBztI{t8Sp?#xgW6Oy~hFL;Tmc`=tYsJY&jaCP!#1Ddw z?FwO;n!q7VV5VV;mfLRUBJ;jX&4`Rt?V@tlneB!X;#P|4_}Y9dy6=$x^#0YMvMt9l zruhE2O-=3_PaO^EqmTbke0zUqfOD!VFp<rQ{DP?tV|H6}!M-2S#0!SE%CGW-_Ln$I zU21>pEXJGHruMQ}uC}M9kiEG4>zCD=8P%(E5&_%7$^k(V96c5~x-HUs@a5|JUUsY3 z*7ULwBaXkfoMwB!9(BFfOP@(oru(8eU_37pN+gkm4j^YZ;^EjQed{?QEPJ#nyPvk> zByeBWm+fTlY5yrVjCw;=_o#N76ug<@@IL3fyg2Q%MB`~)r1zXjQEQ(oclEEOx5pLv z3_QM0{x8RbmDc-D9*%eJE}X!MDdefQ<L~A5_B4zBzt`1-s<(T#240#JuvXH5#bxE) zsfSuG4>fPFZw4i1{+_S+QsjS6oGW~SpGR-ib)QPeO3Zt3z44X&RQqCn_D&f!^~!fk zLFDcO3aLnmq}bm>0@>-+w8vU64i*he8ZF-CMzGh%MERMd&1R%ece!u44?dXEZnaD_ zZ4%TuG9c8YO1+#oP3UW!^_EETH4&Zu=D1Iw>LyKm=tIGvxu{)-)ZdxMqNG|4y|D~- zt&?uZc;VKzJ9mo<`2?I6_^v8Ti01FU+g>vc6gl|p64b3Z@+YCNCRbw8Y?=4Jyw~O` z-j8P~62m7A7GtBlK`J~-8)^Cbse5>Kt?T3a7kBb7&Y&#Ge2-nI{o$bz?igo>c8#dD zERuV)CTMK6P5$1TcO>!=gRiHn->OLGC#yWXOk|mp@0Gsgi7}lNjuonR=J+pt3S7Ao z<SJ`dRZH{Z3?6^eem+u*`Ammavg6lF{6Ct`I;xGn`}U!<KyfJUEw05OBzW-x#a)9- zaCayaC<K?HZE<&p;%>#=-Q5HI<$3SAe?@0jGAon$o^$s8?ELBp#9dEBgw`ULat2UZ z1tF(Td<Y%lL}ZHOC#dJLzG0biK~U&D5ZRJZoBtIs2pv!KvN}RoBa4^<K?|=GM>*+N z6vxZ{t27%U5H_?QX`j<LkiV-Upt=lJ#{bd4c9U~W5koJzpA$~s5oI0%<n^D=dN<Pe zKa==>i5!!DRtS_A03pL!e1nXroDeBSAZ$b+88bpol&Hp|gb3uD{*4HeMQEhloe(&p zJj4?VKgpno2qFaYPazG4$Ugn?Uz`h~H8aQ~?erP3H~qIR|9RG$lll-e!T+q<S|soZ zgt`||@FB=X=@}|%lEl1W2>8*@H~)k3zCp|f5oU301O^E_X@Q`aex5=odj9_{B4y)$ zK^B4kG0I7JPo@#HD+HWS)go5k9kK8K$1MAa5IT_;A_jdKQ#|rO-oOA}Rm|xiYkd@d zSL5j*9?AEJR9Dw-Rc=#6NRp{VY=W%y+XBbRcXEhhf|wg3d=-;c?6NqZl0^Zsub#!f zNpa2vcC>iIR~&NKN>Lk2=%IctPe21HM1xf`4>Rgl>mZIux|C;3uTF^V9g^};sTlll zHi-Rs?wfS_@AQ}x;zrXV4BRnNr-m5al2T7gZMnLjO`uftOd^UC<<-XCHlQf0AS<}l zsAP%gNbZiWS8681B01t?C+#=`RQxPXd60sNxq(F%3F40fR*iMJ)4QxNWq9p@72YqQ zfbA0*D>`>oSfFW6qIHzrqO5=NRF<unUub|b(!VX-fc`f;Gpr_8%f3<i5TwyKlHCOh zSZBzW-+;{g16b%&e<v~-px(dSItnsy=pXnbw42A_e~lP06P_b~Zco^>`0hoRyNk9h z0(mW{a_Co5ali8ONI+=&F|>C`GH=z!-j?`BEbSK*Ge&9;;BIS%Ifqx5-0@h36i_}> zROSKUu>#ZYUG9Q!G>t~o(+6v4>uu`uFr>Wb<iVX3p&kV+^K@oZ>od>jATNiU@9F|h zI$Y0NOWnJIkIXdGV{KFlhcH_KGoOOvqq;wY_h)gfY~QGFdrLaZoFP;*XVl29wkHBz ze?V>gcdxqqgePAe1fNt@^;ghdRZz{=hP$q?bnOaD&tB@q>MB^taf=_nBt>&*Xezh0 ztNDI5;&!N2a&RI>eO`2hxo78bh4}r<jE)L$ovfG7puI-4>!-51?dBga6de1x5d9+2 zO#TG}Q$Wt%^ORB&ooNfi<qG*uk+VMU?{<amqu#BOR7#gWyX0M(6zV>N@9GoYNIwQ< z^tGCi!po?T-%ZR{Un9rl$q0sUFQmlaF}zCDO)2M@23Et*2YIN+hoT>6s1z>P!Yj(R zlv^m4P>=Mzmq}He7GAw*tlT$r>qezSQ?<A`m;}&G@pL<)+oJ^~?&Xujqc&Cm>)sW1 zEe#GH)Xq_1Q`D;Z<e*JC#jsuq&)v#A!0k(k4%wtd&#Lqpd*5x_Y8AJ8jN4K7%>D8o zdQ?27VsD?Oug_xDtTJ025@U2ktiiY8F6%3bP)#FTBPj@Y*ZQ>wx|U5b)KG?6V1;Tk z$!KdT&D-{oTq}=paJyY?Xm%sYB%oPQt10ovd&U~|bh<Y+uS2p=(}JzGEuzA6JM6L( z=Son2jm<W&lP=jCPFOlrue%ebgaLaH-3#BeY{jb$qhw|eNdBaYl;*N4e(1GG;BykC zd3GOHy#QaXuVuryp~_aoXQ7_KDO>72!?GvAeW6@|wA~WIuT_%#sit3J9WQSw?igB! zH^aWW<sA_Z`f#N^Fj_5gz5N{h^Ng}8US`A4*?!Y2c~+o8u}J*ZsA$m8ASKKF<*Euk zs`K)fQEUp)4$Lwi-<2}wZbEcaodebwa`nHHn=kf;9R~slkGh;AtAWcJp*{(>;W2X$ zs)ozS5q_y;L2)C6pPY>?MVWIOVKQPw%x?!?-YI8E>_|O82cy5$;Tqz-!np-Jv*fTQ z(a113Jx_g--K2j*fy;HLC~C{)%t|Jpn@)|oxG3GL1KoCskzi%G-1swUo%Puk7O)0+ zww|Po1`<SZCPkpaW(+kxVj5SXqK4e?@v#gk8_|A>E2AnD>9=mgABuXl_nh<Pka%)g z97;NX9>pKhzsgXEmeEa2V<Ox6nC1d~OQqzm1|MZsPi%NY5bad#rR!JB@OqePc}ujX z|8d%TwvKJojP^mkD^lByp=N%AcHE$SdRk!g$NTF=rX=e+6DH|9{`eERY0LzZ_K7WO z0dOetqee-lolJ?$2dt5)Gm!<~IgsOhT)<HpqW-rQJ;;{Bx%#D*=qf&t-ondPr;s#* z0iGl<T`*X7MmxBlo=~PQO^IFpbkQQFg`Fq&x(M|^2hlyDMt<$=qyA{e?&1F4Yb$2N z;E&ySs7k-uJbH{RUF43`kpR#9=xf=LGZtLpct?tW<7ovR_Pn1_)q|MTA})V3+c%W> zu?j|Q=7{n7?Hz{>ged<3T9&)rC&o=39cA++Js{?mhzUG-lR;oZns^WiCI)`%9xd`k z;qBzU5q(xcvFAtmmZm8_VT&g@?t^|Q&)(@<{LewNoF24Dp(Zerb5;vQnJ%Iirn>s6 z89n8u)>xx~d(+OuS45v*5x|#mjJz)fz{*8B2Pr~st%dbkaWVYDAxHfM8`{Cr0{Btc zTW7~MHcc973Hzt)S>GmJIH${QMKaG{NKHbHLG}F<);k$7T;0^E@vZOXvRFrJV<->F zpjD8fmE}Q(Uj#clUO>7tRXNHrqze9bO_36BQ3IWpT@)?pX+?EYnajvu${B5~n~t*t z=L%Xc2!x`962<AO1fpVPyR8#MnLD03(+f;iTU%6TpwL=5`_t2c%s&(4KjS6ly9Thi z{d&Jx^SG|I#!UW>{v5~ex!AFH&Q~UQ$)O#e6sKkl4SXT6XZ*=mV%H+Rw3Jrcy%QDu z%^wH#^oguR+qaL-{q#^C_7#(=btYXFF$W)h2O~&p;5FH1A`{`3bQj6abbBNNjTXq$ zqkppfx|fyxY{Nf;z6a!MP2rCrtxp%$XpNVB7A)nWl`udz6?4jn)grt>`DxZ%875kn z9&nwSN-#bm=_qtf7x~c8TM!UgmofmVumn>-TFvxJv=lL<p$-31V$EOsfgyM>xSN-D zle)o;0?L@7%0SnjQyk`CE1y3Mz?{@?j91#DaP5_{p||FG#T&g}l06xQv&#?vHcm?d z{58NPKr761lV3%PT!)g>gzzG{{+zmqQ`w5NUVe+by$5iDF=PMrT={G$1tR4WQ*fra zN<TiKm4efEVEX@j_hmCNN)=lX@cy<0$9ZzG7YRmxIc(AuIGO;LX|EF#M~M{E)yc;_ zww8-=G!Cs9xP>bUZ3Y0b=X0b`$*Tt7Hb2K!nZ77_{lswT=5=@cyo2e2qOnB7_9|<g z0@i9-raoAJKh)+qF7F&w?&Ce-$Wi~(yjt4~E!`P41wU$U`i3t}S(q82!{Q7G_vi&I zL61#DrX8h39YktR3lOU_A4CA~buRBIGM7AacLgs(A&5+xgWS~1@rL*2rvw>SBuGW9 zmryVSe5wxtq6!wnV|3#rc>t)4h*!Cd<CHFyCdK!;U3!IxlNq?hX>&ea(BjH$vT!P` z77$=sy-w4VXao+3B<6)5$nElT;t-%oy@8=Jl<W9qdiYfX!)b$)WZz{>5A(oydI6a2 zz{}31C1KkFgnGC7AK*#YUU7C=qOyD+H?UEI7UVJ^!#7+TC6gy4qe%;uMUIM8WZ+$# zKXw4?(Jzj=6m=HZ5Ni?j&G{@<oX%1E0#GO@aMui5HQ9G7Fc+ojv+6aAlaEIl@OtpA zxxGOyu^{w;Pzc+naur7<iM_0@-)dQi<cE2V)$Knnn8$4Gk}BmqV@_)9Rl){HeP?Zp zIjmtcW{8VjZilNU9SV)hr-{^sAQoBS=uh5=B+Y<i-?9gi>v7Q}M%py1W2ZhFO$&!6 zV(2fIG!7aKpC7^fq}G&`h2P`lj^7y8O!B?`2bey%Q{5pF(<Bnq6vXN`^+cU5UK%BE z9I5Uho%fB2M<;72SX)~#=;hCLq2uY2o(q#GMcWGoP1;|RdT7Hg{{hq>C{gw#NTl^) z#5CN6<5&q9&N6ML`LrqAg_vc*QKFnu`mKLjn!TXqbaAJof!tifRG_T7tayPRhk6y1 zR$BExb%Pg~w^=N-FS1jw`Fw`;)B`xSd6Kq_NksM2hRu>7s}OVjbqcrqU-ZFuU~e@n zwKslcQCeCKwzyO5=9h#E9n11i#%sPIQBg1VwEfz4JQ>^9j{16oY$}0bFbI__<vnn3 zF(0b=*Gc{{vm#Wdf=`j5&mnCZF;k37)Q^?zCmG++^tE>==qz5gjy-Un-JCsigXzh} zgM%G^#wFW9rZ1*45=J~Bef-Uad6r(nt>d@O&XEgP*+qL7We#MXS>38LXQ!o5+}x2# zv_xYjMv%13H6?2#dc`cC;Ys^DkHO>G<@`C6$wg1yHPexB$<L9aFf<nLs7Kh$Sr%#P zc+LTAKm4SiM^*|QRVmQ5BJHe#oeIrxRUks&>?{T;)s5y7M<nTt3hNKG<rD-vhNMc2 zR7t#paD~P9aDOOiy3{Q`RxYOyK~(5{-$4Tt=abPeFoD-j<kT3W7?jhq-UHrH)U#y7 zx0qkewnDbzPz|OU{{d3e#(1(*=W_fKdiO**;{E{wvf-?87X^WEwM~Tq4!RVDs9x%1 zKvEOwcpiRa{qA`g!Ps!;I`!NA7Img43Vo!Qb;42^!Vzn4t?#A0Eef2x1w<EY?^}0Y zhmugX6;IS&I35utU0*H1S>g=_Z>wl81dX^bs3PEZT7ma}Q+**Na_o(+UfWBpoTzKT zLRw|_^?o*EmxFw=CrK8sap;GUV_aTt9RDzJHAI~LZe%*x!z~`dUf$x<mD2BWZael` zQ6Q%s#7{(K4+ECxle40<p=%1~y{xNDP0i*^^L{rerGEb#MyH-&Ls2v^u1l#=m08=g za_=vSc7WS@o4lAbQr(v;?yQkRS0T!YF8y|WeUFm0aq5Ph6k1lt<f<sVjT^p5p8EBo za4(R5FM8SK-lGEK^Y%j+9$Rc7IueUBwzCY_{Y7I+&rsYpA1!JQ1FTZ3SQ$)m3vgG1 z1tfWCl(_hkXS&iIfXYN56>#>AGXmw?2sWv&OJrJMbLP!qTePR!S4(A{g#Cly@RoYS z`Mkt;;!oBZFSp?T4{#d3_b`{beD60oQ2{;<VOS<a{v)(kv0*@>8!cBP(wBNzRmR7} zYGq=Q%4G3g2Jon6$`;T1Ay|BiH8ucQ&uGD8&ZXD^pSuu!QtmHOl*@tc<J{LXxWI?} z0x;gdh`SelNxW7g&Ep;6>`|AH{+9ZVZLZTnlAx;iZHeZbv&4e2fowPHyDJ>;L$Ij% zMhgN9ae+GxJxS)|vSs<SX$Y@(7A&*ZXypoUbWoFMtFNio-gKxLBc%S@g6p{L?bd4v z{^VXa6veyyr-0I-u<13M84wgChaTvk<WA{-`uh1C+uONK@W|jc<foO;S@LZ1)zU-B zHbJD-cS2-p57Kdp2E)3)2KPK2&F+!^0Oo$uSK3;dvIPAC1w+G8k0IL6Hkl80YWYjL z8B{sN3%b_Lc5Buq;M+cHd_DIsdQM1RU}#%Krz}P|%N#9UmgdHvQW6?c5VPUqE#Pt6 z!#*pkx(>B!W2E1Q-Oa9*T-bv7P%YjmNE+mJaHKr7diJ?B>`bz%xu#k2au5UTkgzX4 zhBKS5VOQE9Wb!RPhb%tcZw4V~V)bW8+8RduE!6cuMna<;YPX@Nls2UePcnE{r?yJ= z*vAKkw6hM-)2v-4+7_4`N!69%7Ycms?JJneZ0kk&si7pTtWM+!-J|9;OILC8zRRZL zW$xL;vFoIbxq1fE<7{@=j~j*WsaxoMAkcRha8vNd_+>c;$2MjQP*~V@;ssX<t8BMG z7*337Ru@0<N^WOeK2$qi9rpE6GpT^2w<gJOmuniAQ%h6ksP^2Nu06=ijD6_ll+|I= zKPYEmesOJoVnvGKAArL}xIay&s*{?D_Sd0O0Qyyu96dA?#+5Nup!zO`oBZ?9pE<Pc zM&t_K5)ZtmYF3}3Q5Sssbkp8<-%fQdMUz~K{NK&wH^4q|y>}#`6%Z5_LRT$QLcemS zupUyH&|+sRo+KC}aYK!)dDB`mP=wCY@=Bs`Q1D33cKul=DL1K1BABQC%ZNx$VTHlH z+pph;%4MD@$NY=_F_yQVB1y!o$4C|Gu@zkAP=Tf9(Rr@FPt|^@vYb!>8@(gNI-7BA z9Eh#@6-=B8z$rw|M8jTYc6~yRZqKavW31l?@iDX+({`ni2vJ<_-Goa#HNI|*{J_r0 z$&v0VmzEORWN3(`AK((1u`lOzloo%)6^Gjx2-mVI*HXGGDp^a^I*HrYbH`))U9JFt zB<QwNH$TAdFVI;rhFj93L8=O}WwX4VnbIUMX(YzKPSfhjcqEU+=p3p$V3>t#t!+}6 zWuR!X8Jc_89<s;h^8l7yH@rp0gR~n5+{4G$0XXAdOLZ6%Mg;g9xaAt0NFTxhp^Q&W zXS1yqr>Ma@zd>d+=PDtVmnAudd_;K~zkfju6a^hV-!W6D;8O;dD}&QkoBs0YIiUY( z6K+qkYY2dF4WKt7JhsNLOqmT0IN^r9vM!ci!2<7WeF~mesglSQ0C3uWFzR#H&-#Q0 z1{UpecG9lt8{}58S)qqC&{d5Oab?+<8@Ln}6u^OaxoeWU9@AeSXfA7Y(r)-Xm&n(A zQue1i5$4)-Tm!u-_4uT1s71xWnv#vUBvh=w_hFh)EJ*-yXooOPnd0p}fIR(m)Ouup z^ht`cZ;EZbF#3jy=QibvvT(EzZ~YU;0wCV~%}la=;}`&S=OkDeyzN@#EX72If2b03 zV>9NYzNVf~F(4Achv`7}Kuss{=^=tmZv?nSThKtTP_C+SrPo(p7E23YqS|WTQ~2SE ztvb1?Z~rj1mf$&bL)HH54Vyi*26xa&h7PW7IKe!}1E4pU-kB58-9|=UVIOHKQ)5m0 zz}2EH9b1AO2MU;z0}zCXDuA9CJ*b+%EEe#X@Q_B&AM2bWt@&X<j>RD2J9Y+3IZr}A z=|G>Y>5-h#y)>-GywK~#C_cUeuNt<~I4&98)@Do8OBe5{<ty8gVJiL9&eZR8U4GSd zmt6k`zrAT@<z?{smpwv3;{Lf<Aq3WjN;@~(NFSbIR<^vKr^FNg9eix0^i&_xAUGzZ zwrKlf%edOk*r^|-_76=A&Yz~1E>->K;7=>*Es1OG8GEQfQx$CvzI=#q7`|vww>K=) z-kYNi!-CjCYy{;T)9awJh$d)yvw6=zPkbB;P2jAe9}biG5{zi<G&aOrn5Ik1b#7bE ztrm~EcaF!86*_hEEAJUTUmSI>Qcu=lX<VPYS3PjO&r2{3jRX%7Y8yXqU17`Q`Cjp` z^s>mNn}6&|>YF(AYvWqYBtwvDC%*pN{LWga09Wke>L|iLw2(=ZBUuQl%pe;7d8QM} z{WfOAyr24`cjkJAP7kJ<aSb6oXF*V0hFRLoDDH2amx;zsljxsYg6pc8N~WpgE=Q50 zUk<W2gb~Y0*^}A*@3u_|=<bbC!J{w~o({&Sz4P4NSm$dFnWD@_V~jmmhYD3bxWJnK zRFyLaj2PFmP^w8ZR$AjMvX|p?wq!91(%m8jGE$zi`iP%@{Lb;dDN7W4MaJP)hcdsp z$}f+9k88)v*KZPdU5HS3*(UxzgD~^S-uWJi(bA&w0jKS!DXO;WS&0s5KRCEhe2$bg zNXNi@yaP{Q<!v8UOWK@~f+S@~Su-q2Z#q%3dF`DV1#8rv3;`qP?UXoWKt4}Wx$Y=x zc_RW(&3^!~$RUM6cFQMq3A-hEy2~0k4lMdaXZDtun7?>7R=?f|Vn?cK?b0>Z*hKF` zoURjy)4aVkS*&-Y`nxg?+?^4UVOsQnLMsGD8hGHAd%*TScUq#j_<TufsyURo9Ee8e zvNl;Fp=&EGVz0Ebdqub)OhEPK)7>2h0crNmL5R4)h6J<VI&6EYbSrj5mt<y$*)&fQ z6T6kFPD{*c@yOh>DLUd<D+)c~$$^_wHmrjigC9{i9L?IPEY;v}RW$}>IgyOhA(rOq z5~ONgT&L*n2?z!33QkJVq~6-Y?B!VU90N&{7ZLYRj^H3|&?wK7?%K}UE)7(@`iAsK ze>wT;lZJ&%jB^-LCn;*IOjW*q&+OIUyC@+pO$4j#ROoL!a=Fiwxw#P#+b1NJxuj6H zfyx`xJ41`%ppMq~a4Afdbm%IIEQ`rd`r*Bo49lyi_1j;$WSK&1OH>Kc0c0>rX=Z~S zp9K{U9+r5(jY!C9CDipOytm2|-VXLZqlKQ06`d{5OZ7=}JzGUa)nlnl$dxVS6n+^2 z#VzKL9eX|e_HQ-5Eq8^r!dRvY>nD>NQpTb^@$DDi_Wvs02`?X=C|gQGwF@~!CHSWM zZ5O@NX$ovnWm4Ikwl?<ZdF-TR5CH9S%Fffe%rsk=X+!08?%^0qw5h5d7$ErirH2?@ zS+E1OXs=FUap+`-W`LBY4zif5WOlWEInDgO3;Iy$$Q3tzp>#RH9wHa!MZC?jwcWYr zxP3KVP^S1h^>mbQHq~+@%96q~mu%V0cS2C`*p`sk@xf6$<4vDjZi4?wsK5CZ$^Skn zPw5E7%ZWr>2XFh*$Lt#jWeI2->2+R%8Remei67S?Q?gpqZ3}lopkA;{<L`-Mvb3WT z4(v@-z{U6jFP16ojJBUGshMV+B*#PtNLmqed_|sv7VN-FK``&(uij*;#otV}rxk(` zyF5v0i1Zf-XC-!I#As){KoA9dD_`I6PJduzuoJ1wu#3tr`YmkAlg)+x2ubUCBR94x zT-=`m-9N4t;(Wmu!2VM!*o%HV^D-MA7mBcK=tkl5W7vz_gmA(*#Zp0G{X^{>!U#G4 zY+geXe+sU$Eo;BaySK<O8^+FPxtnD_Z-k^8BVa^Qj0GrJB;GM&P1aDV&7Hl*r0rxG zj?;d&o{V_qGWnm-70hKW!8es+RBgouoJ~^*c!|rX2Z7`6+j7i`_%9h~4_n!r1LG$5 z6I8i}6r>dK+_|@W;OHcmU%3oTb?q6?2g-&?Y>QFZt)4%+h#xe<<YCL_JohRCUAq^# zV}7jJ@rOJ1@yZ~Uo+#{`qc5!btcg?>o7`e|`9%+*V_C^f;n2LO=xSV<qOAipIWMQ( z#H(=Nv1jf&x{L&i6jf5tZA;%5$v#f`^~%@lZoB#4RGL|nE;E>hEhE37fHLXxs<NcS z#OSsdnpwxu4Hj9bxk=-~6#84NZnv>*vb)zvB=d~qGO4iS<?I)<l}SFJ9JP50qxx!9 z9T}08+L_8a)RI6Y?9ok=3@{)W=3Fs(&Wgj?DG*t;#DsWV+7VyS2JogOgaW^pXd>PW zE2aQ2^P?5dlkdHiJS!0Zh)GP<#uL-k(8_+1qBv2fkX}1E2s>HA4MU&6#&#xtjjxQH z&+4Iizmz}hRba!v=vSHU6+Nqi%w6H^*hH9!xCH(KywW_OGT#)%FgOq>d!0uIFasOM zu69T6SQr!#9vjjj4t)L`F_92fXdnBCXN%bz8BLn30!qN%(PXQZ?gfi7k6(;JeO)wc z9o!cexEHlY`3yoo$WPa`(c6*r?^4fVK3`Pi4SFBF<BVZ1-ni~|pl0pQ62^|QJ7iC_ z>Yl9O;uf>|36%tE{+&U6^p+9Ib0|vH?;Jz+ss`=S1S@R8E0Sx|g_Eo#p2V$cmM92N zSE)anB!lC*z~S?>bVIB7W(>CeLDkayz6X(3;oeB<@<VzbR}?PSm~EO+UeI@7hFnZb zB?Ndgeq9ohn6yZ#EzL0)pJvS`dl>N#08ZU2;KR9%gSyAvYqpkl)+*eR##|J_584;* zsGKX~_+dF|`}JuCxt4t6C)=Y*XgQJ{{xr?!^%ZZ9F(_)J*NYp6Uic7NJzU;skG|&M z)z#I5s_Oh+?JaY1m;Q>Y3g)l=&pgi?MxR2S>K?VI7;;lsrE6y2ihPS2Ja1QFL{+JQ zr^8uz&TCXeuW+m3hz7vIVrqZdAjH;#^VSJ$1!tRX-+yoZAbPK+x8a_b{eY(9ulz!I z+|<hjQ`DL(@gS~_*vdXHb;J7y;KKL_4}i1C`ja23)CbhEjZb;pYgO#=&IuNIE{+#6 z(N}#tf4<2^0asY_XM+Y>WE1&Ow|P`OmozUJBY7_%ZY~8fHWbZhTlQ-aWkiC-<D+Ir zO1c8SYIAq%!1c;VYdn@G_3d}V0ihym9e0(DqR>D;!jehtE0v})|2j3>ABDN6Af>rh z@%(#3d!otB5%CX++`M-BW_YmyELYZW!hnyD!}mHZ4av)Mzi)Hd<@xgSM*ZZo&nUOO z3gE0y`QSCg_|!iw==^rC?J;02`5%Dn8qw5@oI6Tti97!7g*ZA(S$7AB_vpc`kIrA# zsqbHupDqLX+Bi4UXG@OUjK18efM1q6iSal^p%mT{h*w0r3~6WJK*+6v*On(Y&iGqK zk*0BFn=weFglA!r&frRotX!vaS=1Q?!>`RYsS1wr92r_DK5|TV+K$P&-Qm-prGJ1} z<@-6eEEk3A;t(gzPbt1w<wp0qtJ=ni@}(m76I9A}F1>2^K|X$4s1fD)u#e2{fw2KM z2SC(c@FL*2llrfbnW0P>cAZYSb@$lp>n+3=5Pf0^hl?`47%jcP{{e0__DNO7tgOET zki9Upp*vfGBEir8)D#He=+i&ITK3COsrWiAIr8n@)=O&43rmZd6B;$Rs$wRv03#LW zl@&rHTBWy?F0Hggmi2D7E&%Jm-rU4J|AR})cc<|R-5t0|&dPndYW(fvL)7?PWEU6w zzD%PzWO$YF+whox0}1>fd(j=eQ}<<rYajC<;c>bjOX=VxZ>+T7f<nPpn9_fR`5&NO z@*g0wkoMm6H{|_v0V&<dWf%}4+}HZ4Z=;Eev$m$&JGPc(Zs-T(J#fQdN-b?j9_X;d zVhI-2J&O8Z>bSL0`{^n`;Q7huq97(LHv1tU`{lXfe$KG@G=4<GjaAzGsY4!{d3ckO zMxItEx-j;VIDn!4OSle2;JrCwy_15N8=r+DeiDxNCa8#GHjWq+e!QPEVGMT-uYX8O zsy+p2eVm@ky`YMDMVzA2pg}2TGow~8%?5k_@@@&lWiPQ2g6p10LS4puFK0Z2)S9^U z9Q4AsGFo;}fBbqbj+uo7^X9b~Sq08_S2;CTkNqL+Nr?udp^|yk!@zCSE(Hp}NAv$S z;cY)JJPrq}D)c|m!fk85_X>gQ+pS^8ydoyw9i^Y<$Ar`o=m?rdZU4z#>DPfDpdshQ z>P)v-*a|O%SRoL%?PW~A2_=V+N0$?JFJMQ<PzB-4=$0SR+jxOw`|&9}GC8nIPCu^- zb!~DYe&hG4i=6q5$6R4-Z8+tWL6B`rNFXZWG(k^zA-GL7a%yf|$=KUDdeG9a+*0H| zQV;IqZGQJzR$?Ns)n~V4*;jnhKFa4;n+W79NwjzNJt0*$c3m}PT(s?Zf^xmzU&ECg z6iib2U^L)s%2tgc5n`QD56Y2-1_rCHVnXd{CVfa?>ej1zJGeu^@?ml^vx7T*lYQh! zBtQf{qfin4=+_M8*NPGdU1vI4D`Ql^X_2k7C3W$on}T9PKYlca0=)>bMAvygcAgMh zsj&|tKj-}eY}y)bZDN(;ysFQ3dpdXxVap+f+vvdkY~M`dl9LXm2!cBLSiBrd6h2fC z7a`iB=mDPuM^eC;emjV5Z8Yvdzo6`Y_89-{oc#kJl|_s8e_1s!>`LbTxKO6Moj~(Y ze|w-a1^*I>5ySe#ytKWR4aRv+PE5ic@ZU)=vpu&J>m)CC3|KUJVaPQKF{JDxSw@^= zwg{rv^rJ^l#K&3}KZl$aJa_geTV_af=<s=4V7cbeR%zvBGrHd{iO+AeemHWOiGVoa zQ|<BCPhYb03Hu&{cL;Sf>Oa6cCF(bD4rz3Qpo|NC(b!T}qiF6{d<Bj;{5Sf6HAM9F z+t^u+Wk3;TBmSa-g{6b!re+OLn&~{9PXI?9&Vno1Hgw?c_x15mwOlPr+O}@>gL8+j z%#(7Q=T_1CS(I-V&bLn*de+xRyx;eRX}Q3btMy>Ht!FKHJ*mjWfnlcA^)Ta-rN-T+ zJ>B^D7-z4d#a-f_iZCkb2{3U$K;EJ}#|FAa)Yv?EAov~ZM+MFK=&6}3Mes#-?B2bL zVMvLu=AT*91nD+~S2e>sHB#j8p908qH(P`wE4mmyjXSC>j@=}kgU)NuHhpS$rS+Vd z>CK=Yb+QYT8OS^EEwD2X^{N7Ix?b52yhD69$s2M3EU(eB4L>oj4Z4$aKHa`vEU38Z zb>>_gxUC+`xpT@8;7ekWCyPzW>=m*L?sd>z6t|fqNw)uH;>fKf%mbStTg?629Fz2o zCP<6Ep$*nI%RRom%)h^h>8STp@P=l>4f<$swX{sQS^{Sb(X+19pSd?`p6eN-TYUSy zaX?gP7Bx~ko4{r}WB1Z7`<}GS)<h)Rb+YRsIYM$r?|n^3t@pEV31bfz^@-fx7x`ss z!HiR+^mv|Qxl(hb$vt-YUAy@%$brgkRzKI)8Xx|Y9%h`P05~nlB!%>H?d73>y*4+X zIN)h}<~?o_rHfpYdKp2uh}ANOcbqJjh9hZA*@vh-!uMygB_ZST9LQ3r*(Pd&QYJO- zLRX5;IO37%ful|PR)#q8sCAf%pul+KmJGG^#Ga>=zrn)dBz*LBwKXLKUnHWoGyIWP zQ_q{X6ODzPdO>dnIv$*bLL!}<)k8uO5a4Jd1~F-fqq36FRFb*$fF@=^g#;GXD{@BE z*T#<!g4uqR)m*NDH7-rN49jvwps6|Sh-%Zzd6743RJqKS5@R5z;B67T7zy5k8rPMY z(mor@C3DkW6uLiGBoz3I>ur=rfov$+iWp6>Q<`K#FDP&(j|E3C&v*z$J<uCpXkw4Q zo5TXG2ft}OE`xgd#Z~_`@+^Wtc|Vn$zhP1=-$0?>6dv<KR%E)*Xq<Ou8xa;wu_Oy{ zw9b=a#k}pvWxbzptPJwAS~qh_kQP43^<73hu29s!QoV@lrTgmeAU;Y@Q<-J!WoYRC z`Lw@H+WLvaT{MX~I((bVn9pn7Ef<*~Jy>WmWE8-3$lL>ILlNLQ)1dc>6;juWCT?8T zUG|B0QkH^{<2s@sk@f}j<wWZ&VPx63f44ocU!A9z!od5YBy4>wy2y}oHp&_vE8oRK zC5u}3q4*(?2Nx8}BO7iO`=+0f(x!K%s3F0&lCc804z&MTF}7rsPf12Mp2X0Lh)E-J zew`&M-bT(2YWyZIVnRk$itaB)K__^Qq`tv68C@qwdpNTV!8ZK8%Us6r*6nrd0Kho@ zml`6cbg@PBn+WlYn;yIK?c_q%iXCGh_1S>B3E<SL#Q*&{PQ)n`@(&Q9p1Wv$&T2=x zQU~aIY$&7(OGq=2{=Fs{Soc+b8LgYn9EN1KRZ(Yk*i7-LFUNbOR}CUG;`pMCcbM%J zfXgc0inD2u;TfQYNlu~HE}Hk@p14<aC}2~vm}^Y=5tINg($ibE6x#e!)TWKIf$#`c zoL}`g9t>j&<b73uqF3&{4(fGIYY5?c;4rhrBG&aGG`RS&4hqIOGlqcOOJ@I&&;8b3 zsI~uuL$BmN@+``-|NF#!FlqBzI7fNz7~1t+uQ7s2k35jZa<Xo-9Uo=>P_{!5cw&gQ zh~1c8lWQYZPVf(qBZ@ker(Zi+S0|bIQI+DZA>h1h>HG3{n&$kSle1R*W^4PVVMRml zDus%tswmf|;%Z~O-H)r(pKnTS{m2bk5@o0|eM@}?$v?#OtZN2Dg0VIQ%a3!(Tvk3b zjM$I=fXSGQnhTIysY3+HU@7YeLqm+ZPfJ6r{}~orXeDrr+ji3aMo+K)19_vFIXKkO z$&h+O^woXRdXu%C?u`72MkXS=FlhIe5!!vqIA?j1JGT27>(|HA1GTrkQu@$$fBJv< zerYqhPd;(AkZ}q0+GicQ$tISy->Xhua_d^H{Rglf!+7=6e%%t8sy)<Q=v#=}K6~6~ zVQS$+G9z~Pc{$Np*Pr+HI_2-g+GFW{b;(&g(RJ>7ZhKEpJNXb23Z6#hKXxn^*~&>Y zX*%yQRxw_UDtqy;Z+#cgtf#RvZd3(4wffmiXA#b7p1yKDO7vRD{7qEaB6dhXl{2qa z-bz`Ki~k@b@{3L!uxh{LM0d^=mEBdnV^_pGR@2SzuDtP7ChaG4%x;Y#M_3DU;&?z? z{4wUfbu{YSu-KzI=;vmK=<8|s^*X!1dm;4rf?#)rGjewPA#V)bim;#A3<}$PYd_w( z>;*m-^48SM{%Pa9Y(sQqt)>WeszlT8GmUk1@c*`RmbUxY(7MF$rp2NHA~%ZAtoIgL z)bVuqtTjI|=<$I#bsIvnZM>ItGn986Kc5QmBmkN($^k6@pn;zr=Y%1Y#}=+IK6VUh zE;Edze$<LQ_9d`p<&er(ZtCRE&&S^A@?TAIF=u3^PvPA5y0hBpLp~!LV!l~C@tnY% z-V{W}93)tZrmHlNW78YvS+3mOJU+i0yR@`8&ilQVeOf|Fe9m!PvR`*igu!*1eey|u z;#8IU(q3Ckbp#3NHY2tz@m$EY@b=yAt~9LF%L^(_Hi)6`(4muUa)taZD&u5a@|0D< zpt_-<u97Il{i<zQG1Vj~a}(5vkBcg}ACTnoSP=osuaw~{&$MA5rdV2}V?%hWJS$e= z-Ws66U!lJu@<!S(Cvb)<VVug|b>b^P9+XGgzu@<Uk_A#|$<E{NQkeP_3o7pZsNF{% zy2%>bYkK{TCpCV@Dm%=H?%csY@wbSPmI8uMkcN3JqV2LAmwt^K^LbRiHKZTyu{cWo z5g1E@?zEPXHNA?DZ{J|*wOt0OX^F6i3)kR!C<}{z`{08KKqU-Nh)1m>dNEVrYKaOw zCf-2`0t%xz>6fRM?>k{^C*|XDI#d@YK9E$>v{Rg;IKiZh=IW?%1|inm`Zm;ye{^$d zCw2uScrGb00sc+nhQKaQTM(^-1N*Ui7TbhrL#p=55x)gRi6R9tXx3_>s)4qMf=Z^% ztJu?fA^(pk)QaXCCY`Ns1zlk7USv_>5$@-UR8p4~`UsjpR)L<B-OcekrXcJd)3*aG z*jx#JZ-i^~)P5guXv_E2lpoO*v%{K|-xxep<-@8El_pAR7C-gd-hK9t*;Ck{#@E?* zT<tH?ty_+%rJ2tyqV$4ltq~4eiO~F4F8W|6xI$o$)F0efGl~$Z&%Iz^X$mX$3^MjS zBV$}v?TP@sH7I&0g}uq`Mi{j(i=lGP?n8ZDy{Ecp>WGHc9CFN_foSLrsEoE7&-(BD zb3PGU5p%i?k;TBFpfIq48pa*_5OrFL%Ayqod6;usHzEpm{eB{<+4j|%rgYH3F-&YS zB<lBiprhAqX|bw)MoX?GbD@Y;PEq6B@v*B{w{-*8auPa!@I)|RI}XN5HMVJ9=M}$s zxHxoZ#ASv~=rSFs{BU4kn0Fzx;!97Jf|5eQN)S#b#e$B@)#`MaaeZ3>{OeDruk(q$ zE|X(&bD96$P&9-o7v3E-zNNzk+G+j5a(U?e@#{6HWXVAMSSAycQF|q?GWO-F->_Bf z`pWsELv72C=vKAb>cV7)6yssccQCH0*=k>-3tNc*^kwT8!s_YTr}K&Dv=bvsG31sv z4oTqyB%@wNk-plCbMw07Or;#ZRXOPEEvT*R_MAx9=fIA2ETFcJ_mrYKk<rarC+2iy zr8S-qiCMOpG-kt#&)M(QfoOg&YT0F;$JXY4PUzMt_}~`F#`$-U09=Y|s`fnRX8CdU zYYEcXw>phUJC|MAsw*Xil*Bqyrg{ULEe$X!lfCz5SfpQ+M?kpUSLO36(Il74dnMs+ z^gh<}fB(qtPOmEHhc8@)c_AfKXdR4is7cz=@e^b1LS1Q5K9r{J0fFO_wQ%cAO0pxl zzcSz0%Iz!IO^xb@Swr>)xCW@c1}iJojjZUeDZ9Bjvd4?o68Un9whWGm3$r|!9n%XM z?iG1r_?@<q9pVkmHWabf41Bw*Hb(=8#J@E;Uct86I3LRvsiJ9^oz%<hg0L)Bvhain zqSAycT~;txwKfVQ7=kNoy<+$o>pz1;^}~!>Vsx|C^xMAo{9QeYKV+qdsvc3*<QXLG z9I!aVMy)C}AYECQZyXY67M3w=s{LF0Bw4UZJiTe5Lxj6O9LO)g`2J5)ub%B<X!PAu zYTNShy)~l;qd$t0OUBiae&S+P*5l#nt!2E9oWl==WGE0QGCj<0x;eYaKKXg}XBhKb zjFRUs72o=VCbhyHvvI@oKS@qo29|O)_%h~bkUI7pmUlatlT2XCD2pnQ5A!SgSuE4F z5qV**UsF)NR6XP%AhTAa;EOXIM2!az!1?N{+nhK3Zrlzp=Tbc=dWF*o#)1>RDw5LE zoLnTd*iGS)P#QVUAe*EG9eJdB`>0x=oNxwzGs@cLD%6J2l||P4S8BJa)cV7r9)zhj zF#h)bb>_kt8f81nCfI)CgjlLswpQ4b(wfqvMX$&N|MpF@Wx2E<+!kfD4kP^T12j5J zP->5`z_##6mvTKt*^Avg+EGqC+xKGNarPpcA>!!JlFWDPB=K4=+>WCml^!44Bw_eL z0XZ&qrSW7GdNKtyo2~$>u;%pJ<@YU4mr6DGcHhg=AY6v<5=hS~Q}Z00*8H7;!3Yg{ zc?#aAUuei6_j31l87>Dpo2y4Mt##kDM&g$By1^oP1kX#DK|?qF8@<Z4^7>~k74tyV zS~o3VCY2bzQH_|0VqUVG4A^B1h1<XJt{f&E#`@MWjR}$-7)kCVk`ppO)vm0&P!pmv zbCO$KGkIM&RKCC6Xf8JZMQmW_woaC_VNWhM3paPjy@x#-2(A2Fgql47-0ti!seT|@ zEG+W)zQgJ!gG_3>-n^3Rvzb%{w)ngl)T#I$Qdh5B!dN;kh2kb_{TZ~SHb$l3%;sS4 zu;|os`sVtWyAdxM5LWlj>uXB15`Q{G1JZh8#o*xfQsf(EnCz}K-onNrS_1Plt`K&T zXf$2pxnhfT7h~9(dW~MA!2+34rla$cGq%j<b1oHZg+-yIbNs!d>C3@i6Eqg84jf8X z!SN5CMxS~8{Qd#tkKs5Q_uCi#5tLs&Y`yu&eM>#wI#uJYpFJ<$A}3^lHk-oDYFV*H zd#4#(qX%^CHF_!ryYamc*V*gximn84jv~sNzbPtVcvXwL7au_UP}5(JTa+%rj)Dwz z4nHo}oGnp)ukMw>q31o#DdBYsyiZe3?vFw|iywkF@Xdvr08g+L+W~f8phJEqIIS!! z&(>$R?}N11L^Xs%yK<u?3VF7rO}WPqx4Eib2%KJCVHc$}!rtTm$*RerWjfydZoJHR zwB+7M<-BcXIz`~Dh<rS;wxcAFGdF5GTU=YHddA}eYuZZBpYyYDH;`Vm(we-8dXAH3 zkm-ZY_vogb>d`=PAO$2(W8zwr@Q6W<4bEF0#}s~A$6VXPzaB3$I!@a}$qcP~4>2C5 zYH}hfz}$UXmbLpSnO-ff6KovP|3%8u)1xQtPx;)Ueb_IMULv>b<X9)|!;9r0NZKbs zr+}W4==&uINZZ<AQ8t@Uw!c_6md&1=>2uq{ucO!+Cr1WFz5gMrNpa5Uhvj@LL{l3t z7y4D`>z7ZHN<-DMu>jfSULn`BUT^-G4L^l1Rnv??yG=C{!{vq=NrYkE06$>IUY?CW zu-gfjz3MC@OmD`7zE+haO>DLEk1;A^CGYI}GQyFz1yLUbDlP|%McsapG6%={yZ5V$ zGMb+fgZgH{5W(^5JR@B8x(*`iotNYM`E5u7{HWj}xuw9`{bRo2a(<q0=E!WEb}Ol# zXXS94o)h{)|Bw)sSFhLVcMqF6wU%5i{OH=@vA^mt6rroRX-qBM)-A91KE;}0Sp^0R z>Gaq2LD#8}uPk(Nxf3}93wu~Fo{lp$YY~E(vX27d1;Og;g)LGvR5_^aN`A#!)1$=) zWNkC!-+JtZ)O8gI8dNZmnsUHJ$h)NdqXr+tTP=S{Qe0Vke@*eQ5S!A+Fpr=}0{C{i z^kEAgCFnbs8R)(>d}-fR%|nkElnXGTl~FkV7zvl$?JUs^KXJz|q(IJg=9VSQgPfoB z3c0sA`hp1fn*-8xsjtI4|GEzxdf$<j9y!FMRMrfd7xefRE)!|71!fp$y<v|zA$PJb z=tZ=}c^-abra5~Q9W}sk=6JW{YbNzX>0d1AvLy*ci0&86lhJ-&+Ltf;<qPg1$&Ozz ziE@(Om9dMibJPgQPb%BKwqSpO#7i{%>KN5;xZfx|Y=AWze3zolU}mFRis$P1d~tA7 zp*;=uDzZwRz4d6OJ18{3+m@m-x72q7U?#*(5vr7_sPANyT{7HxXR{p{qPI<5-&@qX zwR(|hWHF)a1@;auqrP_<QxZgJvS?`3)FIw6MtK!ou-K;6XUN$yNBcy(Ur&~~*-!gp zv%#%VC8o6(ZNRV*jH&hGTU#V~`mj~``})MxsHz;661+PB?E7)j1B2Sb@sw5mMCSa` zK7+53v&?K|be|w0yycYN-;f7(05CJIuXjtWy(xQ76MXz`_uog((lSTqtI%cy0#wC; zg9iyG>enUW{?$J-@wMY}mp-l-qtZ|{-4>r4<hoDMMZDkU_0a=xp88yuXb620&Wv|3 z-A(`a!^x(hMvB5o@ck+t2iITj>{gKRZtk>UpPnT#mJ4=nZc!+Ej%)%2BpBOsP_R_z zluEo20jjU}v(m)OO+7CNlp)ld-FB_=-Z0btsTadWGKKdbCEmIHE@<4aAZ;J4us;6I z`YqvYyWIih_JW+QQ~=*R^w?Q$@YyyoVm%`7>9guci+c`Tx}(e7uYudmO#7W-D1YmT zGj({9zd`CCs3tZ5r!U>Pxn}C`hn&`EPzi_>61i^pI@wg`q(?~;TZxzvxX(3*XkHBs zggFkhVN36<#~C<;wL-8m@|E#)431*K48#qM?QYS*fwb$$T>vqvEs|bmXM>(Ol@yp( z1LoT(5$xsXzblPrQb0KaSydIA{p4L)GQtmh#GlW0*G;@=NdQ|gA9M~KB34g$;lzZq z0gwZS)`QOJUZd<iln)J--2BzOKITVwW44)DA)RHtchITz#BZG%?o~an60~@RPjtz& zAa66ygRBg-+hliQ_FM9Tt~Z%|B12d5udS*thu54l)~UG0%QT7Xz<*EhN#>k}ank3# z)8Z+0a%!@W-ZWmRsKg|{B>@f$tUpCgpWoK_<?W~S;8_!SaISR_mUGxQ)Uswkqg$xF zpU{<@qZd@7%*{qPM=Ddh3Nbrtf?C>1uXp1&y*TJJ6Ra1Dzt@SaV`3-md;DRXa4Tb& zoqJ+coGhn_&{gQEL>`S3AAwkkd3Msodv7*fH!75DI0cY(Fu18X-abf;Uu3x52o}QL zAB~c84W((y>!rpigFQ6I>Mv62yI*@EVG``^>6?5jcZpN}uySgKPQv;w7~wGh1hSS) z8A-J!-?B?zYM=km)U$9)K)pW~$5<1V;$M?Ee_mNx)}&&H_^MLV1+|a*U`#AHX!lAP z;2e;o(9;JhA*SHXtsru?qy4JiQyR|<(ip;w#d+;*Nm;C*M`EZQJ=(=4gE^90PYi}3 zb;4QywtAdj?n&3wv?vU-OMrrlMLLG=O$A?L+oC9AukGtj<Ju%kyrng>Hf@Qjbt)1> z5##B_>*;f9)yXJqtRY$)R!m;cC62ES|6HoiM#uR=I0zaOa;3X1{MDF6X=v?+vDC;x z%Td9Cf0;sA)941a6wUOYBEPpTJQ5P#tup(oXbmmpcMvg)Lh;u?X1p#J?I98n@?AB! z{?aetD%-V1HD7>Um0oS;P^G4B%eEj_02$$|$jHc$70MDj+UL0`ZXjmRbA`#~7<|p# z@Q1_-K4NO53p1RaE-Iv(tq?#Fg{ZqWzKCJLX<EStxn{mhy$LZA!7}yu$tUzXnk0>k z)CMmxcwQK`a7P(C|Dso|u<EBQw_rkvi~ibg4~@$h$U$>76x8xz<~|njlLwd|$#MPl z8~#rDuW{D;NG0#^-lmM<I)F&4qT>F>g8uGq*D{N{y@xI6pq?d~W8fs-yqwLJ5B?si zT51_3u;n%Vbmc;%1#-p!kY_wEUyCFavekOtJh5W<IRmecLZi*v;)$c}hqs&-y&inB zZuEM-%v;S(cIO`wHJ8edL^!@TEpO~c--<H3W=u1jX)wcPhFbLJ4LZ5;<yG3I7p??8 z31XL*WK_~z3XXZ3{%n{kNMlUq3dc(_l)Te&>L_weUA#OM{J_Z;FBq+>L&S^e`MSmt z05A6WRn(RlU)Pbg;M<AmG(tTLfUhx$GBHkL&bSQS64thb)=v5!aBMxvI|RRDLHpa7 zK#~0!z%3_|m?N!)Yxk#Sjh1k6(tasuISW<aXF9<K`iGp%<$(XUxYHi_=GV{CTs6SH z)n}oZHLN2^c^%cQ?nU>)yI8xJllK_~qQ3S}oY(G!i3QE>9M~Yi<$=nn+~%1#nN2)g z7GEH=yM5kfTMcDm>LeN(!~8B$t|fS_7p?gVu})1FK@IbkQ^Liz<GhP{%jBKl<%$%z z3epsU_xd8GRcn37NZy7y7<2{fc!VHdmf+2=JcpkBij=e`XH%6&9V%bwdUnLpy&(K< zt))l;ie)hy7*HV6k5AVQQ))DJnrege({N+5BxE6hH8bGD5@hBl5(+KpIqXj(R|CjD z7*Jiz%qX&yy!YDlvz1#Lt@QJnPa<RnUhPr+DfOA*$JZZ7Acb&`GIWzMEW38DBaJuY zd6P`3ffQts02$QsyCp^o3$(IRyDj@L<S${9<m*)iZ8P;+uHW&Z{sC%VRh?+`tXVd5 z>$HXw#w0IvBP|v9YWZ#YV0hL3$hA_Jdu^7()W@}SVCX(O1sxN*>MXO?(4ciDJ5<0M z4qmFta+*6Y39xeg$VRN%^AuSO1=Yv8B$H4Iit3itZ&Ju=qhKY8Y_-|i5H+MFg{8YE z^J`AnKe5zu5azpXw*)W0Yjo+@EUI&2{V~WfA3;YnzNOoYQs<B_Q^@w{s}--m1V+0A z^bwhK2a0cdM^StC6Wzsr#N}i|LbYBNNirq<Xq?4)yQ9BeJW3@FN9!y-bRN`6Ow6e; zWsUe6e@YV+qn|NB7p$N?O`}H2=t%7&csssp`+K$(RP+<}cse!oB#Vv5{+0EV029-7 zA2e5VKh+)Mat@JvqWZC|3^lcel5ze@>~je_^b%%ejEGD$O**?LCUZ_`aOpJb=D4M6 z2bhSF`~y%WnUj&%*xFU~S|EMU3tR2je%|e11h~J-eHmzp{}eg9>hmLlE>|UG-D8Wl zeL?G;j&RQxrK&DM3%c!7U}ql^;QFO?7Cw<Yq9`xK&01nsUmp#>-?jEa5$+y7>V~R} zPnP_^6(ec2JMNW7I~QBu6NEEEsv9!`+|&bxrIC>@N-!YySh?-CSD#M_dIp1Y=0uaZ z&Xlu2uf8^*L23AR5n?}UWMUQ!01790Iq+aA=<}bfX+zFn)(|@sLWV@3${ZjN^+0_D z|0||T%r^6?Yn)!|2SI(=p{Efqr>JxCLW=8Kf|IhrKi_83JVL>QZ=yVAYYc6+xV_>e z$w0+OcOz!_jW>L{A75KNCZ{1{Qg&+N6?Xh0mD$blkS=R5Eg9z?=J;%X9bO#WHERXC z@wIdJsN}O;pioY)!?O0$+RyhCrmyXt57&&6Mwjf>QdWX_rXT%Sx2gow;(qQCBTT&0 zk22C96%dns+0Ki(Z`aSSW;jk-cSWqCO1QUz<^qLTD&TBKYt8kVImi@Yh{AOBzC9$T zTo3ug!26bOb57L@Q{Ncgx_w>dSW~2%w&!7qCFGO_H5eB|fGSc-`A$2nO+U;lxmN)Z zPVHjI1+luY{fX!qVo#eTBd#eUmqez^+2fHj?QuUVGgFXmaj}I0ZjSPE{}1Ru7r!kf z6G-9d^2$%4*0;o+Z|#9Rn=db6Z}n}Q{{Y`qjqxX0{{WtLgZ|l{@vk$tx?^QLl1VE{ zs&K)&4l0~i?(!<M(`1}=HPW!H{Ek%VUd<lQ;jb5K+TNiw52)z6d`SyPB)eUTNd6r2 z$@Hr_H;XmBVtDOs+f9Z=V$x4`!YFo*a9EHR(!9G%kz;7yU}QkwFej2SGAq?JUk&KK zD!emzCG72O7THbeUCrgk3pWRsJb~@$T=H7j>xBw#H?YqS_>)<@y}gS1OHu+Ci_1~9 zM{awMeAP{RTGzCD_O+8#(=QId!b?4pASeBxI{Q^U64@+owF^X&DVhso5tb<fqvIng zM^Wiq(sS=vxjTxD3Uix_TN;bv-F)4$HS9%p<YL`vZ#-45CA+fTARvFw1^)mF$BlES z5Thig0DWn71t!*91Ja*Ayp4XXOOj68T<M#{8l!k-(&OzoxOp(r2-lI1Ojb*HgI8?k z{z2{(3VL(k>l6O~O;7SQks;4Pin+<Dw2Y|IgreI;%_m;fWni~j#mYuxD$-{=SLs!+ zwe4TYJ2h+NZQx~QVe47e%EBCwKrTQglJT<!cBsRs<G)^OYrb+EA?%i#_ad9b+VabR zt7;@DEzIt@8T$UUQXdlQj3nKx>PMzO(w~lgwUsc;>_6Hg^`}CdP4pkmy;T)s$L3~~ z+U~Yy^4nX~2hFs%+T4N9T!MJ|S6g*|s5gM^oL^nM=6o3zW-LGlzIs;xyFh<23vq#l z#t*G}uZMgosCa|nl+##68>Nap%-h=-RbJkw+uDi3=~)k(+-I6escLN-G1M;34lrkI zpVqpWu5NabCB&?8jF}0jA@FVW=YsW3dMF5j%HlZWb~`~0k$^jparhdOQ1anhdvs*` zVRlJGlB!0WfUF$wLFDH>sGNFO&C8*0!*aX$XI0$AztRwV$35zXq(rbtNLAqdDt`~o zSHSw!#@v;e7%pp{)b$ISX{58hvXa_hxMX~WQb!pF82oCKb+}IMT6r4UdH(=Fgb*-P zcIKgmA1hV4Rs%Z$1MA5B4QI=$zMJ+mMUG%%NeE!T4F3QL&tJoiYLZ>Z?KG-<wj^h1 z`qrw#v(((y7t3gofXI+31M6FN6WN8G{=^X&4TF0fydPe@LHsL+Hxo-F3hvClVC(f2 zVWhj7H;7Fpz;b?m0mrR2(2{))-@|JQ_)^_&+{bR$<Vfq)R|h!vtUJ(CShb85b&f(Z z2s!P=Ua^X4ejG+E#EmN679j>sFh}zuu&%8x?C&k)x!eg)H8(bRIUmds_*KeDG>X;9 zzw2XPUYC3+@@>Nb$3I`zx?d3~{?>8{AG?o__yDgwn(k>fEV42Avx8l4#0!HSzTsqG zzC>pL<F#!U?zTf(BvjJB<RN+Ze=|}t{{Y1H5BFR7S25=>b|IT2jGX;y#l71{aEyh6 zDx4aXHL+Z*=(WZD^@;2!Pv$CZd}B<P>0IpBmlK#>+Cr-;az_B?igu-Qa`w2D<=D%S zyP@K*+Q(7*Idwe;T|xG^!5{MN5B`8Fneg)}*-N;n{psp`g=l!<c^6oZ$#zv1krDyN z@YgFE$^D%3lp-dTkTLFkD{A#rYBlD`j9imz`qb;RRz&cdyE273i}Ft&a;Y1E{j~uP z9@UAyZQkOlMKX_vDm(uGXu1CY$aRLdNV9Gy?xFr-om!l>UQ6;FuP6O<I%~N^n!T|s zvaZ(P9y(N47h82JQ?TMN#0>jZO^w)l*hm?|vHdDb%djsMj~G&aF<f;kzH26H8c&y+ z$6Mh`ueNyGOa(S=4tnSKO=Mr)d97rdakhB*&t63*fG?N+6J0%@fwB6Z_Dy5MezNLT zi`0FhBmV%83f^@ev5S6xpmN=)o`a(xJ}}WRdP>Lt039_$S%`mU$Fwi{#N>TNbT_KD z9y-xbs-SP1raGtp0A9NviT)mKBg0N)VYOt8XCAV+AD|WI&oaIq6(b$nUy)80Nnfew zo+7b$j-2jSA8S?P)Ecj-A^zB~+nzV)A4=#xBt`w3rrA#>02-8|pY~00aca@t>QTBR zvY6wMfQff>T;%&>IW?XW6YZ(<$@MaO*&P>#a~`X&vYaw$k`PC4m{%>TuuV&oz=Zz* zI_a(ChfTb@nl^bxp`&F6-VCQ{Xu<A5I3ly6u!VIft~^o`OtF&4%K>Q>8dmv0C#mCb zVmk0^&~>597N7NYHyK(wY`<1T;meb2;3u9Y+W!FB*jDR|!;YQ$n)6AZjqPI|cGnw- z47?whartvzmwBpNYdU4+mW?pBw*J;K+q0_zw&FY~APvmjGr=_#w6}??SgyMIe3mx1 zmasF$w8bK+An%PvU5?Y8+~+y!Yo4`eX&dWfWlD7)G*+5^2;^dfogQKtepW$@@_t|b zwRav2v_s=R54??%FXK_hRsL>A)}+z(O(R9|E}?C!D_rRA_ZA5XL<DMz(gZvAdiUi0 zaa)?Uv873Uqj-YK=E~gJUcu&R@y`=H%G-Wm5cb0pkTIIh7@v7Y+IK1xnrfpwOI)|P zu+*mg>TZ3q4=PaE3;`U2*PcgF-m878X<=o!oS{*H!OI^?QEh#w>Jv|AYj<+R_Y(|E z{e5v-HvSOt74B9I2UU^2@Y`(pKcy?{V+hAYQnh&6F&j(}?k;3G<oZ<Usn2(N3(S60 zZP^6i4aD>L)hD{2PIG8&))}E=9Lpkn+k+g22lB_YRf0xXrOObmHj#|yoc{nytFhS~ zt;N5cYQib^d6Jn!^}sEh4{DNlkhXKRb~2C$9QUq&LyCKgHiVL}?%WsxNXKf>)eMoW zauqni3_UYPGTDof+UUQgTqoM*a>2J1DyR5xF~_Ao5Vr8c6s(fS2G)_6cT?B(u3pXE zXOWN|NWk<Jp`hKY8oUDLGc-}MvyAe}4<eJYS4Bb!QnPkD&jkE5J|KIxw($wKcvL>d zvn1f>9B%1Z+V_X_+jWxK*Tll+Xxj?QWFs;kOy_~0TJE*)9@%P|w5{R&Kl?H}n4UIa z9t!-*z~MmYw2#KSJMV{9&!zZ!{?#G55ZkQxFg>|txo#V7PaqI+-=%pn#MX=Ep3Bgw zQl_2xwDde0^T8TzsI@ox?5iZmT(8-F?<5X)bJDl;uY|fDr=;pP`e%t}xE5COPUvk_ zNZPxABYr?Q>x#MKy(()z6lvO3s$0g=d1a6$(F&Jiv}E+>9lKXm@Yg}R@hz^YB$KU? zSY)*ki&ZD)7|&2a#}z+acD42S6C|wc&zRT)GiRnvJ{XmYZpR+=d&C-g>0Tt%^yQGq zwwF@dha&}oIb7qmaaFB$2YutQ1A$)T8j?(l=q->Y-sf&U>i+=fLYUDq$Gnk@;+<yZ zHIRT00CV-Hv3UkIoDXW5G=r%C!*w+&jRNO>Po*@jRE#L+uX>rRrdWK!hZ*UPaqCme zhh|n|o|N6?M{iC=8O1fh>N-;pgyhnOJ*kxS9<;_>O+r+RDlAGm@R+ScxYO>fVN(>5 zOfV)T@~_WOIL95itR?wmQb0ZNT31)l&k0!VV}*_mR1ff^-N9%+X`?r4k%Q%A4ae(K z$!!$2#iMfh+{A_NpOj{?M3Q^e{U%Z_h-KZCBN^*My@g^}V^xJzOi{yR-~*r5i^rSH z!@-EN@}BiiQI!LEX>72vo_=CYEw-<B1TjS%t+p9F=(0)J{Bk>If%#OoCbTIfeMzsQ zj(HYaJ9!c`LA<kParO49(Xj~Ki0OmgmR(ZrYss#e$D1+v$N&TQRcR(CJYziZQxwv( z207$+k)7DHvB@Ollj~C3JVrn#UzGP9syjPti`j%$kV`zLkdYx~KY*?Hybt1u8w10- z&#(M=OaB1G)i-TfEU7J$JLc9lG3RJKPJ0TDYa;_s9mG!@(JKa48%nt92<^{m*@MD9 zDJKzVdb>!xf-L3A{+v=x;cpl;vP*HHYjVisv$VDnv0{3XO7dxYMD`LzFAeCeu8Xu= znBw~-v)tl#Dy_Gwsm3=R+3SkwH4g$sr1)1)*M^~Ebq(NMkRukI9EL2ramZW@pK)A% zsbQ+=ke|0$-P$Xk`E72|=i87gEhm$K<}ks}UX?!kmDAJE(($#LG$pW{%30!(7@k#- zu0J|-8e_{kA`{8v?al{t^s29UaVr*@c_d<TOR*%?>m5O@nV)X?0OKbEzH&R$a%pOW zlE0B2tENk*Ts%{WQdE6~WQ?KCGvC`En5k@ZhPsX2WeXyMlwpjK+pR6t?Z&xx0>}ZB zw$MgNVsJUh_7(1p`#kuPZ4@+@mNx9FWHFG;<epAQ?^5Q{)d|@iUE{3>Letj8yf>;% zsI;&KxZN0EE>GfKJ691TQh9r@c7u%9tKWPf*6a@TxVMoM^UDC=rDEzo4|VYzge-ee zcep}u7uTn~MLO{1vti7n?v5#}SVqodkL4N1b5$8g#zrdC@LiRg<dNgIJ6(}URqP2q z^-wb(THfjG0NaBp12x<HJMd)s<?Ws9*cxigpJ^sQq@Z!VM||zhD~57TF`g@`(7aC- zhMT9^U)j8|THU?aD%fT%<$^KvjEv{MwI<qWuKM~8*G&4_KLY$eym;&_{5<AHJCv(( zJ;~kBR=4~rUkEfw=XiU3fk0xi0h5lrHF#V<AGF(fgs^yKRAp`9h1mx`m3j|?em`is z_2icNk$o8zH!Qa@?+$W4QGwRF>sO?$?31^j{5g~;wed8~4O8Jif-P?n;^V`YQQEpN zDjXcXMi1UN<2Cc2#{DD18c&IBQ^E@@cCQgL#_p}P^V<ie4}VJcPa1r6(X?x;n@ut= z?VmmZ8sQ)0LxKRqj(cYub6+T5&a>Q>*r~gmDD~u@&Xlonoj+&F@9U@BQl%v;-$Ko= zh;Qt)3;hz-OjusXl1zmGEK@sq>N?gg)(04?aDcOd=W6sd+IUaG?c&XL$49hyA$zCW z8?oVd>T&e0S1wj`Ml!0@Zgbub(*~`pt&!l!Fr=JwkUy<--ZJo8cy`&^;G;2&x)fr2 z;+bKhzLD_?eHAX!E0`y1Wu8e^6%MVATO=<d*1DgGx+b$>qsMctUc+(crpR%19GL_L z&KI^A9Mn3IYWm+#LMKAHsI1p2z1LgxvEx}ZhdHaCVtu(LCI0}nfPY$Y*~y$1gD2Ky z{{R}(ofFE+@Y|UboPm$loUpM6B;<BJ)u9Yh+aW)=ll!W&2Iugt3oBbmqPZG{%$8S3 zrIr<gCPGG6Ibncv*PL<Ip~_qIDMtDjR(CSl-A6nJ5+TUrrF2#ouxbMJCxppyH<pm@ z1c=V;vt$v2fsR4WPfE&;z!o@~B_6UL@D)CamsZ+>+}OM!;9!37$MUJEq+5>Sj;_r5 z1K<=_5qM`-j_o|TQu{2WaDL(+>zef`qHi(s7a#^acCRP+A$F;v>d8APn(FnXBXH%4 zrbqt(TD?)O<9Q@xbzo2cK?gO++mmgYMe^uiYUjjqGcJ{3W)ri|ae{vUKdmo=+r)NK z-G9PG1(%Y;F`H>JLGB1Wz4Ki(WDqJTj(<Apg;#40#S~k__M`6F-!MH0w_*?JQMZaM zi5RoLIP3n7YSB?l+C3`$r;u2PYSHQu?Mr##!;kf;w?9ghs6DB&j4J;CyeeE)ZQoLo zBJ!R?h}B8=?L*s38O!Zq+~bi~EWq;{=~CVy@{bjyU%FD0*vea3oEA`ux$oYX(8xYd zEqCXqYS@Hk6$7p+ox`LzLF8boj!ELG_pozFrzNE6hG2{bTz064ttPmRNLZmbDo$%O znpGB}=5pNT{6Ts4FA->NA(mo9*hs+1;eB|mCGi}AdD1Vj{{R9102<Bl20!5=(24&5 zrvCu?3f7;TnZ*iCD<U~lcO#R;l3<n5@5uwF$}{@c8?I?utLwLVrIfJSEc<>&a?Q|n z;}zWCX0x?^A^!k@3TmUcr3GW<-9qEY)U_cn&BIF~?^YuOw>*A-on`LHz&WmuQqd&5 z*6y~=8kr|-DbE=^RSiPLG{`)=h}c{=-6dFe>-4A17VUjZyv*i}l{Xb(^%V7VMMjC- zN#BZ+EkV;wp4FkZb(E>z>6cBp`>oqK<aguJsUvpv?}}>R(kxB6n=84yl0`$+jyqGs z1vqSud8Ci$^Frq>(MNu0)C&o40zc0qGXe^ppIUHmQHkCexo&tSt0JmKvX&ijx4kw- zJ9iJ_??`3lWQxnnUn!WknZ0wnk@rtO(w#n~b$124cULpqED9rtOCezz4vM+x2cW1e zVp&uUH*>~%(mO<v?HJ^OamoDYb0+LpD@x_rbvB<*OEGI=m|%SP;0r0n?hAL$?tY8u zR!O@Mtdgro8YnxJ6>>UqM_QM|(OTQzL2&m<6<j%0eT}zp=-4<o92^`|HQi;k9aiOo z%<)SrF=?dp9m#Cvn*(w6I2k<BNy*s`=`BFpNYI(xCXnP~<jRrjpUSJtZE%E{-~m{0 zSpeN$z3|K8UxYk2;aAgqQ{lGNu3BQ^R*pFUObjp0(-=H^)`pq!bHQ+UUit~VBWWJ2 zHw#H(wS=h$C3okh4gtqe=}yz^P1jS%w7tK+xx1250yBv-l3W5nKU3bBrTKc)%V?2B z0M6!>!yIEd$xu(}Rdp{D>Q^6YEjU%VnWSY^z#F=N2LN-@v}}A#x_*+d>GqICc!zVm zn86-{)SQpXv6N~-=#AI2oKkMq@*|G^YiOn<Jb=a(gn*r^*yr1|BC<8onV#NZ9AK6U zm2c`#H3hDrJbFyGQAr4tA1Xzam6^jFj49-RRP)&5*0FxmAdhL_=cuhOCS%JOn{wPm zCC#cEdtkdZ<s&KyJ=@gP?MmNH({9~lp3dfGRCMw~G+>p^)4=F>KK0C7-845*PB6_I z1_Aj89N>>iwc;IK<6YIRH7l_a+)mrrM!QrWVc2_AxXSkEP0q){cQJTxR!iCRn{7xe zXZLp~UNtA9V0RVkS|7!2GW)^@!y4|5X|XI>E!Bp=(uEsIC+?HU+y`3lD7;sxE}tcw zh8bd&)!CjPWF>R_eMWQ7tzGe#iEOn?+i5hLX)WP`*)77j3d}&|n1jhDHG-v4%U`_O zUeA%Z@7R(L?Do7tdkoNRRcQjSDpa>Se*^kgzh7t{47^RH=?QJ(*<wp8VFKphdCI?e zQGs5A=RGUsc`hBd{OqCnfYl1h@nox&$iczIbbB{@+{Lu*q3$=|4s|HJGIhx{1cyVJ ziH>%1?%<LXl~Irnr)=|7JQv|d@kfZP;F9RX#y!%gEYb)g19GUr{t<vWSDt9TFV?iX zD~mfVM&cVwtO?^+;y^lQ^Q!P_Qrph5LoBH>wgdTuarLaDMs|GCHujOSvOcfB_#@$M zdMMw+Pc?~(B_*WE^BAaMki$I~bm{3@ei!&@;I9t&ejR^UjA};K67#zcJ`wBtunu<j z`d82QpBwyNai>LP7OOM}vH3-zRYF$)4nWTCyyw4a<R<Z+wAyU?clMpzSk7}SCPTHk z&m%s()w(fiGV9nJqT_3x#qoPh@OoXxu6TAN`wa2#x`l0BVnP{kg9-#}!#F1$53P8W z{Nf2BxLrPIV4S<eB+k}c;B6#)*y)<C`u*{?mf7!a+SWn~MdvoxBc?|~il=j`Npg`# zJd?_hsYsSd2@#ICUqPP2pRrfCdvA3`9-Z*##rB>Ty3+M6Q%%#gJ6W1lHoj`Kv7Qtc zRtIS6LGSoite=cu4>f=74KKtV9EQr=ZiOd;D5lRi%9R{?f-)=RTRWh)2xN@3K;@MH z&ONhFp7hDQz=c$t4l~E4D9}pwM0-Zv+31bpe-3GWH`Jv|FYQkcsyf=+vh4F11F*g^ z#(LEaBjOgL{i$I!-NF{PLjrl0$uU^=l?pd@<lr8a!N|o5mQ^|Ssn-0Kd1r}bcYVzv z;Gd;e)#23|tX){Yb)Ly*@m*hBitofva}4$*wXMyJE9S9O*nyRfz`VI2cRrQEc!$Q5 zYdYQTr0UU0D<p$zfLW9f9Fftv$;W!+KG7kQ9%1W&lSkSNss!@6An*?&tx<`Lt4>bm zRjYWi^I2CKo#V_=S8Hs+Z>|q&t~J@73*WR)_U4))3bo8acSQsdu(2fw$5Gogk|d4` zDI@YEHxD2_V!l)^KJMIg&wSFfVkZ9pNpc@Nn8rUk6&S6B?PSJk7b5cVdx)Wq5j2H_ zP=XON{39gs_|t9gCAITc%#}Bxje@Zohg=^}YFo*)`6hoT$YWO98If{G^);Tc9i^E{ z;PoSlr4-ROJqy51;LTDSeLutrePyULjK@%pWC@SqO|C+Y{{S&H$7>G=xoIJGjdx}+ z$1S(9BypaVn}%_~1Jr?1Mv=xHZU-b~Xp9tH!<p<v%+ix179_S=k2xl$x4W7sWQsW? zSfs&q2LZ93znT2%8+Y;<$qk%*zh8P5cU34bRGi}#ZqGp-9ge@PXm*oW$8mLQ1;VSz zZds9J1aMh+-H>uKT?2UcU%l})oZ3F8elFD#(%M^_fYKl!DFyfd6V87#UU-K*F(W?J zEKo?$nYSvaILT}heXA)`k5Q>Lp+2KD^50I^jLmRdnHaY1&7Mws^WLAQK^*t6!6cEu zlCV!UazZA1oC0z56;^v`RZDJH!)-fTCp6g_Qiz*e70xS5U05#ec78kXrMHLgEbVMH zjW<<U;1?c!teM-N;BIk|jN`90hkc-5*l8BpmB5eew=BsS5~*TWu2dX>^{k&d;wc8% z4pd+e-332ToZVeB$8L}Yz}XuX9X&}Vr<JQ&jY)Mxws&^%+Fivw4<pE+MC&fm8)vB) z`MBd16fCSl7Av0gT5XEq4a7|G?i<$_IL|ouu7dja!zz|vVzRwEiThA_Q#j8XK3)%A zxuam+6Ig3neU^bOjCwRv>QgXre=Nr_0CVyvz#jQ0HKDI~n_kspv%0<2Zl#_S3k<$_ zXG{!WoaAQ&{&mL5cN3Ae06S!y;<fcZ5}To9me!Wm@LI%?h}Jd4g-&-#fH$$p<nx1$ zm5iw?-5_0=CEdmTfvQfrcB~{UrXzD_D*-Bz$j=8IxupAky=kOH9Itkob;~nF<|_n| zhXs_L2^q#kW@{RgYI=;fl0|O|%H_;<@rdI>PeoC{9>TBK_=e^St2dH+nbOhx*b(I1 zsNm!srz08aG0izgOW3%rbUh>BSBEt36<%Cvw{{+F`I0$fyfY9WL7m?(@e{`tr{jNx z+T%6FzLnwG7TfI)EKdcvc6Jy9ZqDUQWQFIS&bEFH_?yDoYPO@QNYd-~_giL9DL{=$ z&h#S~IXvUHYWB|<j}b=$+2|K-4AVcJvpirT$K7`mh1>M!HOCsynaK2cB^Y}bW}d45 z0F9aOZT+>)mE6|1F}zbk?HrNFxMW~(K?k7*ph+d#PX#m1N6TKt<4=b$SX$j`df$s| zBi3bwuU^9G8D@51TW?>yew}N{t)eqs!)_#0j%gK0c{V3pWRt)HJx@yQja4Odanz*Z z^ejnmkz0>G$~eS|3}kNS9mY>{L@Vb>9Ff{YRY=TZBsbTb^X*Ydq29@<-Pu}4_Sdy` zM{7^tp#ibES#k=K(DXT~pJ-)|$p`>m4o4u>-E=VZHT8SH?K{Y4HrCd5=!pnRc=mu4 zV<***bKa%ZwMq4hxTn-)d72nf-5G!wL>N)Wpstg_9t^hE{3)txvg+D⋙*l(r4Qw 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%0DHKD8Q2^P$fl zWpDkSoqRyElR4?dXYOO=I7i#|cFP4g2M_OC!qzjD&qDB#{{V?peD37n*H064t~0?7 z-`MHq1Gu|qKVPkN(6^;%kd%&N#B-qWOi0W@`#1_W0i5scimTxXV{45hEZ8CB+IJ34 zV)+2=KoY-yO4iqc$Ersx&e0=Bi2nd}kt>hk2irO7KOEOLqsc7#hJ;QJ`dMSspfUdd zv;Gxo)*mz4$Fj3yIjsFHh>Co5`$XURF{<2zlq05l)jcfY-faH>&v}3JHm75LVV)ki zhT_$l-bRrXV^g@Fl!O@^)~0J4!#YjIjb!j$Op>&ON(z=Z1a$d{t#WDDm`g#~^sX6o zTTMU2mowg$@}szI+eS&}itVyTrEsg@&-h8TjZl528+DTDV`gZ9$0r`S=z9JY&x)Nm zQk1H@N2#?gcQx$Q*@xn-X3tjEw8him^41B0IV-nw?!l`*D3vXasUY585xa5#`^k>K zPW)DN){_>Sdu!#%=De4T%nk<MoOZ8j@E?aB`&^!T-6UMtyni8<*c2=m`^+$T1HWNh zj}aU_6zRt3-lZy)YDv^-HL1?GF!S9%rF`9Or_Cj(xM<i!4&B-1j~M`WBO~*#rLO)~ zn+vpH;e9=;%QT-GczVOYr^DKYikh3IXScjkB9b{645MXES8xXds1>d%a!Ndla|IQ9 zGtAhho0aM6MJ$GBcqT*)py1?WcKj)BU(dODil$wQ6^%l#Jm3Mq`U;Bb@_VL3b2O5e z&_<?5Ewx7hMtT9*P?gAfY(|ZqI)TMcH2IOv-@Hyp;;E^@$f*P8u&Qw<ncBln4<m|^ zTWA@_<x!8ibBYEsGfa{A2c=xGw~h1XzRZvm54=hJF;x{uQC3WuhiMDAHsasJeX1pX zhSYgt?;(7*MhvTmjPbYDl21LI*v8*6$pmLS)u@i-1&&GPrN|XY6DhZ`PTI~}$;zx` zags>D6*#p>*rPu~jEbHc&#|$6is-{@0!ZWVsD=D@SBL<UV=z$CHsU`VRr@sUsh>Mn zE3J#NZ-3pO$p!e_J-uovHYNf-;f!>~Dr@L&vSpMrBXhWN2iB;cYEZCB{YUOTv~DJu zNw|vE<k)_3(wBRGc>Z-B<3{QLm&|d-Pv=aV48@df_RTkSF77#~k{TML=jV_ONh#i` zj4siRJJSPVlrSd-r<bV4gPdolLyFP2n$9bHNejs-Jm-Kafglbkn}se#EysHr1nKNa z#lrL37^7~UddoQf0Dyy8`4rOB{g;~(myxQLu@=h6%%`RYU&gE?YZnp5p!$)x{<TsI zxZ;H}a=>%~qZ3=Ra~{t_nl%j#7Rwy5EPYAME;!r4U+%ETsG?=sHV{YFn(}#NeLeZ4 z0#9t4a~LSznaHZ|Ya%etHj)PLa--|%QnkkJoYm9_V?JgFL&?Xz4x>TJL3wPJcP|*Y zje?zl!N@+~;=5fvw)lQB8<7DZgN|`ru(Mm6V$$4ZINTsyag)%B=+?!ClO4D#@}N@H z5t6@|w<NT0W0=zKWEXmJZe`s*(!OJ0ax>rg*Pu5&YmJLXfXaqBkg#m8&EC2R<PmN2 zcn6MOy=bI}<!f`0k5{?b8+o@p+++P|_V(%RUE^R3cC4Q&(WDUUjDU0H@xk@Sty*Ww z<~_x6*{HbGS&wt7PZZZmqC7`%kuF!U>r?6al(z=RNU{F-xNI-?e;TnOMVv^BxRA#n z5>Kr<IH6sJIKU~LxIyygt}0!JXL5$K_Aj<Ly0~ZzcI_(SGQr3^D91v5`1h=P$AZnG zTcL9UNgB$5;`_+MD#M<HS3!Gi4a|!75G;fY0?X2>jV{HBA7mNz1B#_t$3#r1^E;fD zskBZ!vR*O)Qdb8CssYAvU97r1Wq^$%01OT5k|_p<1KSUhGdOde2wYI=-&P!rXmLT0 zVnu6sesZ%&t~aRw^V+wUK|_({!=I}PSabmtz~L2HhU`B<NzF7Itjn7EGb1R4c-_y? z*K;Q}h(FmM%DJ1b51^gAvAH?}<_?FgbQ3UG6mA?S9co>=0*hC;nxuMFg{sYUYY0X= zav}$sv2PqvD)}g?g?IpC0;za{(CT(Ez+1}02FV%iQ>?`mRH-;zepLx0ZEjR;Zrpj9 zigU6}yhWV*k80H=NKyd7=cRD-cz$+ShV=7fe=$ZrTyxa=^Ie>8wg}b7%oGpOoVj`q z4p-Q(b$@Mdav_-QV!OWKAbvFaDdD_?O$Hcyt_U^9c$#lFTr8OiA}jdwTOJRT{{Un< zf=s*4GtDU^kmEPc%2(6%Td6!&VK4iWF^^|q+xk~msD9=drel>wEJktCvMlr{E$>as zIrIF(Hu7<fdSlYGb#3!n$qqmgjfJt(JXS7Azay#<lDlOs9_l?-*KtJyBoL=6e@gmG zz!&jd_*+liFnHpPbgP06e6=8XZT|oPJXg-1AJadwtvrR3%naMIR|dX`_+{a%j}PgO zYpY2dw))ENRzr}g>Nfr$*A6z$-LL&zS8<Bw-}?UmhdHf!$8E*TFO;}OZJ>5_Jg?LI zYm)H@d29v3U@H$UR3QAvt{8eBO7;H$8R;#lY0%zqmvFJSxWfJE6Tkcje<56-*rS=6 z5_d-`Va7U+{{V=u3S6{hXVdaNmjzj3DDum5$2@(nSYBLO>X6#nCaW+e^$f3q-L|P1 zAD^{xH&9D9s|;YoqE}FWHWhMz`t|FVx^<3;Hmhr`ThAI@MiM*dEd=h1oSmTXGr%XP z6~|cV_P1~aQqszjm4-);VIw~&uWC4mMhc?zJgH&k<mkPYsL;@LyMG3~mEl~hHyM8| zK`!gLPxbui`t|RGd_`jw)`4kr7OKEC^34+YMYi%&9ORtVSNtNE5v{$}uXPj_l3i~^ za9GLnV2@AMwe9pKhVxdxw24;AX+lPzjoI6i^7R9ry(^K*j9aLr`M(YJHdJQjtgRpT zbIi4EH|&cNg>RkC8@JN4qLHGNN)oK6AdbepTK7#$t2k6(Fl>X2;Mbn&T0O<3yz|b= zHp~zfJjvH259jr-V+)IoSn|tr%f;ZON%KKz`4V_$Q68l7F~C@ul4`x?qb1$rOvH%C z3cl5vj8i!{&jzH<M@sIBv$^NWSB;CmYf;c2#8$4SXpv~KUJM^FE<oe0bGWVTLi!ne zH*EsQrez=%&m$G9l$%<a!ADaK%E*Txf;w}WoLnc_RT!Rn?W(MHiezA!G8b{BG~T4A zV_FuuyLh3~U^2+vBMM3Ug;&2xfq1VY^5=}<m!>J!6W&-{EzPX21fcasBl%L_8Sf^N zd0f1*w&!D#NvxD=b8V;MYNrleUcZ5kzkGdZ4^vh?%QCP#3Gc;1(Yl|R7=1-+X16jK zQ=~zy%kCKm7|lUbG-V)1&)z*LGNMUqZnFo=frD0KungZMpq{)Mr)zT17TTe=b~SPv z&9fO+KG-9$tmVyfNOD_KS>c)sRA$VioDM4*Se4~r_gg3Ed9QQut*67?U&8m3>K-7t zj^@(SdfPA&xqZBa1bo9jmE;pgX)Mgq$P<%|#11oDSbFo4Zb_rkt4f^Uoj0LC>8?7k zs?xwC;iX*qRsBZlHf3m;Sf^^oVq2~~Yq~t$Pd29{bVzp5{qf1gO&z4HGZ|L*2CMBO z0E&KDR|~jft_@SaLp0Y^UtKEotlsEA#t7SMIP7Ye;<T)1mJKE6O-%%gI)Px}o6Q(G zZkupAa&yg5+%6;_?TSL(_#aPdh`HVKY+RHqoOd?-C*s{t!uL-uoaoWWF(eZ%=;R*b z{3`7JFNR=qqgo+9+<AYi{cD=JvX<Qu<YXcK?$eyrY#h{kDsp`G)SgEddmUf)?wn&* z`#h;21Qi2v9QzYhn^e=LNc_uuNU`lM6Yn1R=DGc(u4#uNrBH%*Mf1q9B%x#`A~EZ? z6sd4W-Z|$rS9i5Ykvp~#a%-aL8MN6vG9r@P<n|QJk;)viwsXZmz^`7^(D?PIaagk= zw%JL+I2=_h;-?a__B{Tz8)4X-W4{z#gF|ykMmVCGHa)-M1=>U6T{>0{!9n4An)LgB zA8JzAL2GT{y+TvR%p^gcJuAt6AcETNpY=7~_+L^*)GcCJMo9qUkUq8XnEG+{Fm5;B zzvz0HMeQKs-%_`Td~b0C%@zKOdv7j2>ycco#<?~8Z1(zP<F7(hcVe`@C)}*M%WQRF zo(MUv9WEXw%yJZORXDBk{5&N^R8g~jrV^;`(>dGwTXS&oUS2E?#E&RO%;S^Su>9(? zHk%Ot0GG-C0PmQ={&gm!AD^knByu_H*S}iXkHNxAb(T$5)FBxgPUHFt_T^d>l2c|; zgru(&W2M!!G5L_#z%kxZ(jVpr=~8Q&wcWm}Iwb6+Ncp2sgz?;tYfXGGvnfkUnXY}J zfVcI}=~`0w3sBW>Wx04|g&2eT%qBSZ=k%gAAmp3dUH)Cc##XX9WxiQeUT57Q9F{ph znX8e_6p*;yr0waN>SOR7jiQ5nsM(+$b0+@)<&Wn>N8wF3?@uMmkNNkFSM}unwVc!> z^fXROMsrqHbIWxDgpk^l+aW`YjmILoIP_f#c)~|*ERWnPw6FOsO#UU(?Ug>sqD19} z<!h7XAJ(efc-vK(c0yyfPJv@wVxn}VQQbDZ9f4Jh7b{mR!{DeLgpgR>N3`TL<zWE+ zJkpN}X_j#Ve{-g*XC=9mpX4i&)pbkjh@GaonT|icYMr4iZiMnk>T77xcGZ}=(R(Aa zVc|<Q$Vo0-f6pk<2j&KUT1$@^SXrtqo{0k-;56rXAD(NK0;*rB{oh}`TB>UHWU8+E z9i{J#b!L)Cl2wHjih&T@(AGW0_4U`yJ=D*h+s#$EAL*0-0M|zTbe}gtpU#|Okq&Z} z?50$WN+M2pIjt~x<kdB^ne`~RVptpn878_bTN08-Bo;4l$W)Y*ED0S!0~{Yp+uBA8 zR#a$Cdm|#_afpGCr@5%?8)lmZ)+LKJMmlmk*I};f7uPyVY4TfHL1!eU=gPOXNWz`C z9D$sDYmT)AEaBrA+$yQhosO+9WUN{ey8Y_Tzm)y1*MY;2<|^EeoO4;1@+i1~mK-)u zHPF(r%A2{W(lfEi@0?Wj?Szm4)edT;X>%$Eiq>|F{{YKEyHo!9JXF43&yst~NXa}1 zn9AQyK3~?VZal3D3G*go>c{;$B?{Tu5Z|7kD68o(hz<EC)~;PcVkCk`TZxE?3WDJm zpvM3c`qWpNh=SakdrMT{oS1}-`L=7#m7B5YQ*wlFV81VX)~Ra@v*r$br7ez6r9hex zjx4n41OEUJk^W)-00A8Sm0wf6o_lLUB=-_2K;<4nMsj)%gQZ*3T4^LAIAxdx;6|k4 zrYn{{;G5=`y`ePm+9VS!(@iJJxe=z{;vLEUm7cd&mZNpOf-??5o<jcspuqhrqPFnX zudF1$WU_yqLV}>lgOGU&4nG>-u=rDP3_-Q6O486CNR~xb{D$LQbg9pkXmZYR=6arg zCbfOBytncJ;4Qcj56N+##-h?hv&_-$c_kw}0!gn}gTuZJ(&vgRi>r(I--QuEF~_@P zewDdlsp)o#V$(DmP^0*+zkEl~9tYqnrZnVp&b(rI#)aYSQvOFvYdPJvvfv>sGNccb z0)I;Dw7-S=l0+X<meR>voZPri(DV9MhNI%mUVU=T%k7UHt0OZ-56HX&js;jO9k(`1 zdXq{~Pex?%HnUwwBk-4nG`S;={`y<Vuvufbiwbeq<~RqhrCNty(6oHVqj+}I<Z|~| zfBbX+D18rA)b$$^V`FhT`BR*#^~TKe_>a!Hom)-5)7h5p)>vfj9dTYJD<0`q_EBjb zt{R+YO-fsxCY$4pXH>knw>JxGZyJKJTCbD_bLmTX&npL!+27TPCb;hiG5*rOFUA!I z^RBU<p$9eX;biAYNuG8swL2Le69BfRqqXDz07}sD5XOsof9aX5-wHSqYQ&%Ud~y7% zL&Q=6qTX^p)tcYzm^Y&N7xca?Z2ti3ihm(g_2vHniI$Cz`S?Hj4Oh0-?P9gJjx#01 zvBbOMw?iRcly%2X#<4Ze6Toh4rZU??E#xw6c?6Dl#x|yL_>n}Wk~ZX&r=se2iVa}+ zcIqp5H3pt!jRoAFGq6xO^)<*|c+KwevCld7ix7|fnZHq6{sxBH`%9AU?%Ei{QyfTh zkVpib>`Bk#S$kC8gZ54_ZeP}=pA%~kUTD)>*>9iB00~rLCFFH;h5c(tU27L?L{{)y zDeBS^=knlwMAUlWO;bm>w$UJx`e{gyHnHyj<bCEG55(75YvErLU8k8O7FtXle9Or< z9DN1{>S=Rbkf};b-H+aQ&x<unjTT9OX1KU}k|bu8l}QWz)Z53gCb@_;ol5bx#^w_L z056wpzEE$$dj2`Dpc}v*5o?(ayQxjA-7LOq$8Cj0W7~{z`PRM0qow#)OOjs>czaH^ zWgu>nFvR5JCjboAGK+e)biz@mXvwRdA>jW21o+EUlNMT4$cwvNBP#uQ8LvY>2ZP6K zd_}HWTD)t9g5US9dUpQ+5nbM)sOlGtR(rWjT|BjItN9Kq2gB+%-TwgB%l`m66*bh& z;?}&YVwJCjd=cU6n^@OKy47wcVyh+N1yDfFcJp0L_PKLsrCbdvD{V0rCMqoA2u24V zH&a-;+K=rm9~_f2kNj}b_=EmI*ByX`{{WRZrO@ZAiK%XDm!b17cA2JWC1K0E7C)VH z+H6l1)~+9*yEym21Nv64gD>ryP?a?@6r`nMK-@3~f@_WOt>xv9i!~qY*`j+{q~2}c z0A%`c^shhrN}I3jV6Jt`EU4k_Chc;c#5=PEoW>c9ZHU1t*gtu=t_MPuMz?71D8Ov7 zEz^qC*N59Qo3k=4jvhVR&UzEj4u49?@c7cj=G&Q=pi){%6;*#Of0(YWcP5yjhji)k zu<)A*zqi|V*83^j&IvwZ^sLjdj$48{lU)~t?;^OLR)oU|{{TdiIF=~>V#I(kj!DL9 zlUu^b01J%p`NeQzs&ewD{c3AU9`wFK>z6Xcs6x<4$(u;BRO2~8RTo**W8EU%&Kq`5 z&UvevqzO9PN?d|1V(sX^{c6dQ7Te1L2Q|%3dL(pIPnoscON~nM;zKOAGPIb+bI^KH zX?nc&H_dQJl(K~(kEK>*JBb4W)6%PjCp=@GmC~abCYhR)lewn9z0#&VaKMV|z8w5X z)qDvZ%vy976Q#x^Z$3Tb5yu1(isfu#&Wmw?d5{75RXE~O$2@UPGUb+qM|&Sj_-o=n zi9B_zMWhQo8Df$7mP=zIvW}z%CxSE1E57lUgQ9I3;_i5Kv-W66jx@AYSk!de=tr%2 zhrrQoW#Y@ouDqp{Z%9c?Wb$|b=NLJ!ZupU@Lu25bT6Sf9u%DGqNv=85SiC()RlBXV zw@ah4QdDX~*{gRxe6aX$H;ioISSCmzw~>p>CRuXIpC|*Vz#Me^>sn8TS6&9vu5TiE zH7J%f1c;eDvNQ8CjOBUf9+lpF8q}>dF9X}Xp~EDy2~?k$F=BcA2(8~6Y8KjWg*8d< z9ReG9BUNLMF_}mRJ;~y^9UtBMDX~i%QaG17Yp%!7-VoCyy43D2HLINt_R>gAys(`; zqEGfl@T;}5FdLF9TjBI}8t$;;RMYIN;I|vMtYK8+I7Y|HSdMZ&@COyd*yuWw!)-PF z&Z9M}oJHr(<~Q4dan24nuVT9J^4a(!PScePciM^bUBLO74mP3nQgO$&demYo#(v|~ zecqcMylHzS%(QP!@9IsjXm&cjhkK~YV6ee%6)lxvP)RHf=4^T%e=78U2g15fg|xR= z)n~bmHgMd$vM|Xc^(2x>C$)KJk2T4(?HqVhP?-|y#@SsQHy0Rfx%%J}&t7ZRbV*I_ zu^4O=vbRXaI&LZpALJ|0!pqpMNWE^7^?SdO<5u_EZ)N>{CH->kqK$20Z}gc#%k=Wc zu6tLI_}gCzAJ{bf{g`~r`Q}#j`g#iLd|9g-eKzj!F=;O5FsB@<4o~w@USWT%-aC*k z7_L8vR|Y>2>eSb7)BX<UUIz@<yi>M2)#sizXw#Ab`{&dAYNo%fTwmRJGAS0wQZfJ> z?f(E1lUMa?mI|@T>U@<z!=_t;->Lm88u%+LDz6!4oPq;#Nyrt+NnLX^eI*ET+DVvN zJW^^u5-q3s13Q_1W!-`SIQ=WrJa=y`jf`xv7LZt4sQG)gLm&I=`PZN5jTMK)iz(J7 zREukr<nRYM&vS$M*QaWhX7fS0v6@pQ<+96c2yC+zVI*zt2?TyMrY^0ePqm4{N_5|} zU(Ejiuj{$xel5R0+uD*Y@UhPE#n0VUJ5+uE3Zdbf4N<h|Ww^c{+4k{V7}3W&+9q7* z8OJ`f{ZiTFzL}VpXL7}lImzo?{q~eCbQiaXZrKJ77q4o!iS|+CkJ>WCVbtaC{gWTz z=8GF!qoIi<yR#?@MkP#ns(D_G^zU0*hMf+R3tLFg`Ep!jeU<?T1)!Bc!0LD(%Co#J zZ13R9Q#@pOi!3BJOl~|M%Ca<_ax425o(mZ5Je_9U3M&#D<$g{@3CdEbuFT@A7{`*w zpZJbAn^3rTHv}>es@+2JMRNAx8vJ>nY*JW*(Bqs}N#cu-H&nWmfVq(H2f40pD`_UX z@?y(K?SKIFuX6~WC7)x(#ih=yeG}kYZAZg?3A5EE^9W}%NdhroV_*spJ05wjKJkBu z=kW%sdp*fv5>Ms8jKl>1oD6jO*S=_RSloCz+Sb-%acSfT12XMlg$sf^8u_<fv}=7{ z_TLO7jz&aMFgSc?^UZlMN!OLmd`;r)wlRY==_bxja&g$!YNp2DF*#nNHKU^Hmo^u& z{hoQCc?Xt!w<-wFLyn@ZCB%kjdGS8+mNPE=4!ut!^RH6wn`q`Sm5kCAP@+DWs@GB+ zlFN=bz~dFas6}Z8mt{N_<_RYAgqd>Njz&2>uxlAlnxR-_a0w>1i&wf@0^Fy(mB{JR ztm#-cuqv_nq*Xlnkx*QsB49Bd-n5!oCs4NlXZMTK>MNmYu8WT5zlaZ)sZ9_d;POXG z#F1H~!j8R#X=_Isjln;AapTgcTay`)zGBCrr5zQ}=eaBv_?aV=4o2=9`PI!xvOA*? zPBB%MGv`J@UBvQ9#b|hu_un@P3F(T4?$yd)!+#?d5r${T>P7`hzv{s!kGqbzrbfXg zViY#+gB3LECm1X4zLjrtG>DsF04OcTe$}xRswIqWyl+xij!8JqK9!ED6dd(EaauZC zqeST=k=SnRD>}MH=%k5lt_*RsLnM=x9*4b3(5uQkzzcoj#&9XuR?*r^w6JHuJRX^- zZ>Is2zzD$VdCg(*Jq<Z+xdXz}MnG&L@xZFCq)5Ur2g{F6m1}ePDv>z|+3Cj~^+)DO zMgd?CdfEiEha$<jm*pPVsAkLK<-+E#jl~N$$iRZXTB&g%b0&BsgO5tmGP@<B6Y`7> zN2gk;C*C;XtOJaWhM6bM$_VGywvL30A;*@abWvMg3A$?^5W{q&hlN?e9dZR>PxDH| zdg8VGI~kWy^3>s;CjkBxH4^1%np6HFvFNiprN-xrdS*!Z=org@e*?`?w}gLb5BDUk zA(zwNBj)>Q$J5TOEi*%sZ6@<rQcO@v*5qwGZc;(d;a+NXJ$jZ_Z07FNJ*>*-3Q&T5 zv0VL_A8E8JoCP@l0KScH>L+cL$Nh6}<y`%X13jo6kdB!CBVBP?924Bpm=<6G<eXQq zX*wWCG+Un{#_ea8WGGmTrAqL?srBI3k4V^=KBB!lMAR82)8$pT8&%K9x$ZqH%gk<) zx0&hX(dNCMVz0$(5o_R&4BRfAYi;HQ%&_BhgvJle@{g}mTr3~zBhU9mb$=4IS+4va zpjtb*ju06`j9~d-S0w{x2*~cB*IrwHV7a@$%=7aF%UPmIM(IXrjMx?EBhM?^NE|U3 z#{+@wQd^(D^{SeCW;gsll~}hwf9p=#i7{`F`Xk`<=ZcjThK`I$+EsuU{{UsP%~rpB zTEFE;w@NhlW5NC+dF`~(C91b`N^M$K>~&rgd}*ov@$2=js@?NkKZa%h06|gH41b+< zFiqGR>yF1I^)Wn0u=uXjy5xuf+!&6V>XwG8w^{}lZt2*JJ8}N6)YQHpnNNt}$vF_? z+xV&-BIOcI6$Zv{Jg{zZ013xjfBLD|()u2@AGT_)m~#Y!{+mnx0HL)pCk@`C(&Yj< zdFedA`Wsf3E1en^bF9!t>V-E(oE9gy{{UX6-C4RyG*FdkH(^&CgO8MP#a&}USJebt zO3aX&&+ybMCZ~S;Vj>&FxRswDENUFF{Xdmj2NlOj4YmE=r)@r~V_}(#G?#K=Fc011 zJo8*Rl^<({oEKDf;A*H>i<8jK*Vzo3bcy#~@<YJ?0H&4ho&fP>qAO^3(*}5fnGrc< zRz?^gbJwTVya&V=HmPx@TUy=0cOqOCl1M{gcRqIHfz#T%JC7eOfv4QZZ=&11%o|y* zO6_R@`9LkVJZ|;v$G04)RGkGFM(@u<qOEFv-SfQ}rFOu{E_fX0ius}7ET~;dBNMfX z<0JfQ=-FrA8@b_%6Z%)rvpJX_G=?OWB<HE**P&9BoveB8s=H=sOynkZ`kwU@!?$*E z!NJ98M&a133a&?1JqP1YC<2Y)S-Sn)4Aya{QuYTp+fx!a*pY=KXBow9-CfOQO}tUW zW6J<GcBrMdmSPUzG3!#@MAJxQW>m*Ndb&9!74DgHCiOCBjI>5v0hb_vI2==Ag__<; ze}zZ}p3cTL9QkGQfzqr(1a`Yf+$6y3(y>bAw_&#XZSgFMl8(wxPpx0Jo(qiT#W*-5 zF%<}j{!k=E2au$Unjwvm_O5gEp(!qcZY<DNwlavtxF0Da3~^E{Hm@QlmU71f=QW)b zy~|7pKu$4^aaXO@bes2)@(&wMYh?V0X?G*kwC%G=6l|hD_Kr_VqPklQVpZVeb6N{_ z3r=pl=5PpH0fon@s8(>~pT?G#wTG>*a@DiX0-QQ*11xcxF6`|t7&hgNx{`^E)k_<K z*Do7jCezld%`=$^3z5^MLybCbnMYETr!~sng_Y86q}}D+=LZLlhMOjigCFTh&jUT` zkC@T3an$EDuI$Sbo}0aWDf?Le059Sm&Hn%~Z~G9RyE>o3pqELKGxx)Bo&`Vc7{LUQ zjPTW=HS|%)Rz!1v0KumjMoF}U;^OUdDKx3yJ9hEw#Up7qD!B>|ur;}6i+3`#O8)?G z@!zdjXvABI$lIO?rOi(KuW_hNu4S>yeS&X6(>>{rX(|Fp1P)FObauiEzx7+a^1NoN zYSyM0qv#BhDrXd&>FhNLwQ~*aWgS5L>7Qqp`-Im0>Kjs_m|=%{fXv`>7xJn0l>3$J zs&+Esw3bFAZrpm(n=%dyrhPM7e`xhp#V4MG{nRF{PMx<W+0^W28*)F5KkuqhYDwl; zm+IB44#`-85j*7ZRt=6i#E0IRbl%Kdvt0}aMz_IPqdETLW}o(*!}pl{JJzwW!{^If z52YVt^o4&RNyaazE@jZof>_IdGD+ZZR_2JwYaw2$GtEx6;vBQ0s}OK8%`#7x)^b0E zc;dOIWh)~{->I)1w2|nR(zS?UW>*r01AxGP^{b_fF|@%#j#T`tITf4ZON7-e^n;~X z+*;dxhSuHEAi*V@1ABTBSk?PqF>E6<nGA}VZRahYe|pM{;Vomi(=K~1ZLDEf>H36M zHpW$eqBdNscJ{0K@x{D&IQ{B>I&HqUZ#2+Gt0w7N3&8zq-Q$D<DZeBPU?0bt>CtM; zd2gaHke~QS#x~=7=YgJ{wV|MrG^lrDJm&xb)`YpWI+2gaK5!@J*fH<TShjec*ccPb zPRqA%=|#{ew9FlLU^V*z@{y6akPbd?T9d-_Z`7~B3O-|u3}=%@sc#G4#wFZjY;7TO z2jkkGqQwMva+o)696nbW=7vq3%YG!^{hUN<S+@=X=k%udcX^KM%u>I;J94<lt9Q3a zZwC1T5u9O0O%@kPI=Mi=D{VW5DO*C`=*8CNmOVmkg@6Guf=BhPq8=3eYc}Ihw}NI# zH*N%H<tMFLlI9qdmFA5PTr&Ylpj+I;OUVweswVk1#}l%R=KzmE+O#|~aV5@|=1N_g z%p>P)p4F6=P|ZB@$8wvT5a2L49qU^_wH9{I9B;h~1CVn{O>7A}llY4AOE~S!AxV+; zoQ~M6-6sA^{r+;G#A^Hw=DHimVx3iHMQ}M88O3M4wY{)o3~Gdoe4`YW#Z5Bh^up5B zTt**|F;sQAKFJB4aJT?tBo0rtY%eG)Bg9tkb!cKjp@|2Rfx)D=HETkgnwv+fC7=pH zWnAw$=i3#nVL#g=oSY9XQy&eUm4`N!aVLl(x=_AYl^d@(6}h3>%WVbFm|(rbxcVB- zcIY}>*VLtX3=v#yc!QStvw&*mmjrB^;!=T|1Y(x*%+GKie%uUntGaEufe{OC`Lmo< z%GNbW^K5ZiZ1UWA&DKd=t4z_6o;K$guIE<7T6N0~Hb%@p0adMaT`=mOWsTI_`Gs&0 z_03$@tzPENNMktP@{{aqIke=ibkd8DHJ!}w2wgq)m*&dM&E^%$V?0;aIz5YB_$y6{ zP3d)K5-TYjkC1Wn>0c*!S4@3286a{9-MA1q=Dxr1Y(7SZr^_sjDY=3)lP7>e06$8_ z#uKT@(Wm+xc!xN~4qJ?tIPc?i+!yWe;B)t8UiHQQ0A@Sr_Y+2A{mhslh8{EPp5nWk z%R;L>QDjRSeWow>*Qoq>HHCdNHhP2IC??BLhkT3Z3Hkp35P7dF8%<jE7Tf;-Ub_6w zybWg;%N>8<bosBy=ln&Xw6VswF`*A1%FD>z>JM@LHRg8)=SZ0SoW{PNE<yhQ>sPmE z`MQShM7ed6Yi9E;<Od;#Zhb4od~l-9MUGHI1uA=YuQS+cy4?4$wH%b8J>zehdoZ{0 z%#lPa^({KiXh#?c^5ef1f5o2^v<Q3(+Fgc}kleU2#z?~P?!1n*<u}$^ksyd}BSu~7 z(q(rqVl&iKeml?$cy~_JELUiT)gJL$PIr<<;168j^z^So4~M3zMK`{OJ}E*mbBuLb zqR)giB-Awbc}J9CxJW{l&UY&x>0M`tJRKIZ;QQ&LRa=-COB-XJoR7x5e@?lQ4-HOY zCwkvN!v`G_O8PUwx`Y~^!@WXy*l+UA@t#!Tr!!N+%~hqRz5YfxTFSLQXH#4M0G*GP z^%#;X=@mnOrvx~`%|P;P99E9Iez%&8l99V4@HicK#bm!cjy<dEDYoLSeBF4c&8X~4 z9GUA*C%#2M54A}d8^-a$tuJF63fzH?ltx(`GW8YLSVFPe#s)T#oM$4b+(jM-$iH}Y zsrKGRp2JJr&Wb@O%8~9VT>PMnVSQ?O&<mB0GN@R3a7`rE^IPrn$Rv){(F&?sbvflt z#f$};vgfvG%)Xz_tBeT=B#ise^8CO8!`vF$=@Di@semIH#_Z>(HJh5Xe=nIf0XPGt zRjek{WvvkS-)YL^Fyt^jE2GeSRjX)PR;{OL*9je_s~C#uOlrl{XQ3mGr=@df*vR$w zt6>^mRA+#A?@`L7xk^qtp&C|W$t0R%FwT3^wlEnge=3Q?6UR!sS!gS_xUO^D3Wpgq zq;uAlcsw3zcL6ya>35OVkeYtw=vO~ONCzp-4?{x?oSwPsR`jcAFK)b}BSoHs^NP2q zX)9?eubf#}XK)_)srIl>mF`-nDMiU_jOBdbF+6n1>0XoYm*Mrtjqh$CvXaYkL`gG} zLg%;xocdP^no(zUSvg+6Eo*2;RMBse=Sb9Lvz#6pHYi)$gUGH-O6P^C%FNo0PFL?M zmV9gBc|0kt&!yf3l4ztcmW+I*hBzOXt$zc2Jh}0Pjji2T-Pl>jr^V;Z_MRN51pKEQ zbUnp!x}4M8Y7Zsc%{ALL=aI=JkEtiEKHF2hvyOP6o#R<oY=KTU4?|TNGqUA(Dz#c} zUh1+hcyGbFpNKW3(<~MQozb05%z`8;2PbjkrDI$)c6V~dt-PTtxlTdmr<YN>l^t4B z^Qcqnf!eb?k+}Wuu4`I!YX1Ps&%m0MY1`Ztnl?uy{N$Vpt`{P$ExzW$<%#s>k>S~e zVn=R8ZS2}H=S+q<9zVKkQav^G3leMuw->O7mf|Ik0U{efU~}_wdI9;>Xn%K}0s7Ro zS94hEG2YuY+2fE(g!BX)kHa8T%9Tf~P&unurFmg@V%smRZY`XQg_<&ik6aq9R<#@3 ztJ{Z??s+D5IXjr2%Bs;GYkS*?+oA3rHj)I?ZNAe3Ic%!n^f{^Qyf=GwYPUA_dn03j zO~Ro+`1NSt>FcR@>%+E;6@<4cq%g=lj<xR|C>l<g;ozE6*a_q+inhvt5PI@Bua(4C zt4f-5mHXW9rxh3{-PrlBQ_}8iq}gz@ZvOyc<Tvta%Qo!!+a9CGU9X?(UXA0lYkff# zwtUH)D~85%>0HH&e6r)zRblZ`oj!Jvv?!@c#wMFThpcVgua@>(lIL;<*^j9l=B1m) zdTqb_cxYDJhewq|x8`}SOHx1S*CYO#H}f@}D-hVh$E|uvb33yxSn{TIci#{-dx5qh zkS}2v#w(w`)%6+fW?AoNeXW7>ky*@X_lNyD=O5qxm8@cuvPN<m%+7Melj=@rQS(zl zr;eGX$uuV*$8WS?c|-ctIP|Go{Y|(1bN;546P~|_=CoTJlV0;F-YFLE^rW#L=+XTu zTh%{jf1##XAM|wkR-Fm4HYvB~F-Oy{r?oqLYs<QU^{DP`Zmz<$t+dic4oiePN7VGB zo~6Y*+|-U2xYU{m$S)+t6cPZ*Yz|KXx=m}r`lp3%trJ|e^4ej&Q+PRC<0l!#W7_FA z*BX?j+3m!P?8~-I?4vo!$4|tHp6>jt@c#g4++9pegZIZ6FW3@EC#7RLog&WsJq63n zG;2i-viO3_$W;<6@{#=8Mh|j&)|cA8#~s7lMhiT=?>%$=dVOn~hUVHRVvab*Wsth> zIXj5%4;=A|+|_UG^xGJ2Wo@y{KJIw;#w*ROWy@>I=cz`k=anfX8!PT}m&WdXB~-N- z+nbjW0zQp`Ya;9HcGmH%x`GAEW9;!6Gx?RQT~|?Cn~d8jjWLWKrlxHw_U_KiLvH8q z<(O?zk}=8Ry}Bu+p2vY+N=>WWbeg7{8Tl=4ZcnW9ZC}YiKdmtMj^rs8aN1e%jKK&G z{{Vgc@7A`o--kNI*&Ea%p6aEC=5W8QbTRxm(=_A{t!l8uPiB-1{sa0obaJ+0mt)N3 zxVycPnrY;buvnI2b5}H5E4gG@BDS65+%n+~(e*tm)1&_Ygg;5b+ru@?tI?v3Q~v-S zId9gI^T!`$R&6&*vxU@gG=t24`{?uf)%zybyu_xW^eAb*8PsniwvlaRc-Axr%LMWV z_pnyFfA~e#B>w<TxwExgV2NiA-;e}XCwbz{R`PU?;^~+?NcabUN`qNf6U{mO<gR|E zxT<|$n^@oGcGFd*9VKLS^S^^M*Tv1mx3fkz;yBeu=Gpx*Oq=2!n`DfdR*P-AKf3n- z%)`@`>G)TgYK`TwTyjTRV4ekxak=r6^{$B4YBqvN9I~Xjnv*>e&&OK!y?HWEcOpQs zWJL+u2h0a*wdS(I%^@t~fK74U4!Ti&aKU&G^#1^X3hJet%rF-N>s@qjDBYfZDX7ZG zuGa;%jXxv!&Oakv91;l8+au;awZ~mEF1M!uc*`Hsx&=Aw$@i@`IVwMO5c%P}S80>d z7eCIrs3se`n&SM(<MA{PBPt@>NW`(g00Uboti+Lrjv1aa(q;lUzwfrf*EHp0NWsc6 zvi`2mQt;P_uXL#0^y@=;c@2&9uv>*JWO4R<3=U7>T<?o~VJ@$x+P<}<y@lk%EC3)c zA2wCF1U5zua@rrnODzXpw!C&*iD5~h5Zj%@_nSFqI2apw#%kxp4PJZ6E$(iX+1ZzJ zj2te19&6;YN^+}S5U7_gN7wNAZhZzGFO^HOIgKXn3r$;4F5rR?C{ehv=YR(!)%(8? zrMjK*rMA)k057tLN%|))fA5;%yg{kne`Z{Wr)Xm`M!R7B+L4Fj3yjtdk>RalSdmvw z)2^oua2;bifA6l9^teg5QH)W=R+MT~a%W5b010FkCg;ewno~TjDG)>u0(~9wfAQMR zJ}I8+ZGze+2l%duxMB3&@{hqqcN!PKPY~V4vg;QXx>P;P=Lhx2^{&@X_(|a11}D1J zZ*MhLg$#_bC<(yH!8ix?tz}Zn-ety>wMxf>>oe*)uCpbxPjhgp!(=;)1>^Wvx8Yq+ zhQ1bft6gl`>9a!-AL}Plxco7n(!PV!FZ8`W*;_-<?5)I#8+@^cbH@ju8LTfBYx=a> zo{byo7Yhux1zsd_s>2-!9AcV}aFs|WXRA5QH{eyJiiYtoh+>I{CL3%Q{-^X6-uO?$ z-VcTuEVSz>Z<g8jvt5vI!-7CLIpfq-G*P=IvwS0UG&Aat86m#cBK{erQk%7*xKPDG z-OXrv4~=iG^bHFBO+U-EG1@H9JSY>d7$lsZO6Km~X|7z)B$6;<GVEB^cf>7P?)So% zQO@IRaViWhGB*ysy(;p2!!)Qual4zj%Z$WEqDmVh8%R_Av8s>u(2x7(r16UX0K|fP ze|bOjAyMgW2C=ID0KT060KjUei?y3cG`X$qWfwDLa<KgSFn@@S7xArS@SJhHryF@4 zT%+de(z1L$z$M@x^Yth5u4h#7u`WfTM57tWZ#4c&LjM5c)x>MwCA_qX0dKjC?<9}C z&eBQ#>fj%t%|xb_ru6XhlWFLB_OW+n9@&k;n~P}+`ABy*Hk|M|?Z>ThI`@q2?qPdt zP=TaIPo2SF2;}Wt<NgFid5w+!y?Zq9*d(*Oh*!yqEP<X56buf%ab4Gheh+w-L`%(3 z+UVAyd3)yCa7U;A0A8?+<;?7M%L_%uDqEwTy}fHqa^Tt_p5kfP2Quf&%YSuwU&Pg& ze*Ws`Rr0i`C7;Ysnl(m|fyuxibvVab^?M%y_)kl|p7QEzy<+A^F3O-s2qfo%c{QzL z;#jTYyoX7!veDv#Qy%zKZ)|rgYagon{O?1h<DjYgHLE=9Pxx`-J$FWsYF6;*(8;s+ z_ph*E9G*D!tqUKAUJlS18&kWy)~D&R5F&>@<n<q&Z7!qzr>V|b>Pv@M*DUT^9G>Q> z_>AnfhyD8PjX2upnO7AkyCq`9zl^NmORYD;HrjTdBHFyWtq>Onpv;|)Pipg&@crzH zQXjJ{T@^g_$oIuT@ZK*oUeuE>^H{oq2O};zS8X1P5Rgc2Zbm&=0DnsG^Eq9??WgK? z;jWyMci7;x-6Hbt&f0Vd<*EfZl~s-j9A>QQeiyoxT*S87QNkpHAY+niUro}k?tCYm zEpFw#V(Sz!o(pdy@%mRetY{t}(X_d5H9aF*w<^F%b8!PKN<X?mU)MFs3T|_4O4^;x zD?JRS@a5Q$t2_$W=+XgFTX-MD*J`p_XgY<&4t_~vk0|5R9P%qF&sk}uWSS_CAPGSu zzgqf};Vz`J_-93nk+v1hde~|X?Nnzww3oY~;x>N{ylA?7kPi)9sCM0%B#lV?H&Xuq z!l?0{H@1@;VEn&5+++IJ*l(uKI))M;1Y~uryZs9GVif{<<hKI1bRm6=RJ_ldbpHSb zc;`#Gd&uI`VVBQafKAwJGMtV8Beita+NZ?3Yn7PKqp)Nv<;caC>%!+hjw{u*4-D!i zBZ$Le`;DB|3uxD{Zu4!X8%H@q#dFl9PA}muRa@PjcMhN9s4dvq>N*yf3EjTdH%2Ep zJr}v_>sZ>ow~X)1O{wel7lIS!BFh1gDL%wkq29$KEQ^*3zP+oLXj{x;U5F<=YdBY? z-zrP)Mry9QGYeN9FV@!B>Rus*#keaH$7mENIL|^o1zd_h8tT{fWjs@G*D(c1Hb4f@ zI(O|??ir!CVm&K!;b{{1uGgMaIsX91O=nW4B7CWR$iHtruFhA*Ukv<Ba%a}Hj}gm# zDkj+<1jwtBank@~o-<l{e}Mc=1aC9^NRrZd7jZ_^nM(pYkzUv0*thsMT2=3E!}PB# z)uGMWMms{E`VD!{Gp)-R8sRB>d%M`?^sfQfU4)aw{wviQ+^cMgE0X10`uwC1deG87 z4Cxx&Z6=*_s@z*7;xlmfAO;Kfz)(N^Ri|yHHkYQ`+rHLhXF|$y2~&;@dXK_rj9wRf zf;U~vdSm#j>hXDGAx;y7-o9s;u@b3K!)qTV{7LwDd>O9|I(xY;gG92dyrw}UbjkFt zZW~q8q=M$yvRp>uM1ACyVaPS^{{XbK0lZyi+<(WC{{ZN1UL$%kquRV^)|K2_*_|}z z{%3!uyGi5webUD(uAZ_;z=9cw1pYkNrdY;_xy~Bh!lZz8!RTwp^ro5oR}zoiPP%+* zOY@!=)4g?zB$m@5x@(7soU$yefrfpn=W&o+>0a6&eTHq*eVb<Ih<rUAqb%{p8r<M3 z^~Q66efX}9XzZt0%<_os?jj#H@Sq+KTF$oAq7bYz;^R-aQmG;1j^O$_@O`*7oc8l+ z+iaR|HU=hJOM%ei_)nt_IIl8LliloTJIh!7f5AE3E?DI79oqT7=~kCBNFpR0DRY8H zTw#y3X85yBUk=z_No#W?&2y>S-ItS+9Eo$EUPf!O)r4BEgL^cPt4|CsyCddS3Qy%+ zZ;gxIct68iIhK2ptj*Nm@ComZarLiD3suUk`Zucle?!m2LeRHVdEC~ksA`h5^Xv^H z6M@sI{*|L=r~d$DMKFp8nWh7C+`_ED1D*qCAN^{|jUF4-p5Yiq;bg$>Fi%?OJVU4m ztnOxjm3d_;xE{P?^5VUElc=Q_LFmp1LJBT&TBFfCFQ{MG{3p7&vq>blTZsTDKh=Yb zwlUCGl4@78-M!P?I~H-h<R_Nh$;tg{d9|%h4-K6jTcT|ze>stt=E=uH>BUj;GnS7} zUIA+;flxgG8T^2%r%T^*eNQ(P8{M-geAiKNwCA2W)r<K6R3zlG<Ad&LE!T8WOv}1O z9Dr~RD^dlXYn>a)5$*YjVnkt-G2AoI@Nr#g&S@NRh0`eN%qH;WlEeOb&b<Esz(2;a z<H?0U^gP!7v!^|dgQmdjj>&m)11bC8EF^zA!HG`!RRAFopB1vTtc2rfSkJmUn;lBi z^k4Z2K*VG$ih26gd9tp+9#5rT(ro2dNn4Ip)Ozz>5^6BiBDEJ=V%mhvIR%ags_{W1 zMyyEk*yAMht$jk(ZZBqMJAPx82bzVl6;p9gqi-kDvUPN_DpA#y;frO&K(;|(PCboS z@dU2=%*rt5JY%O6;QOv&QTI!(J-HNXtXERGSHlqaC)X6CCiP>t?%%1JsAGI1XLlTo zRM%PaFnR-w)GF!bF6{8x%}IRuF|u`TYW3z<A#60pRdO(Wvr(jlB+pLA=xL*RsmaGV z;+R|J=cnaXmvNm(+HEgoUF1eC2?|f}<JOXUUCj}MmRB1=_4-#e7B>&`n(6ek-)(LQ z7=~F-CmaOLbNyf5N-XH-Qj2XAWK>xMTe)S*f$31mZ-(UXeJVW=yI(@zG5y=9$3Mbq z*07Ins>>8OQGvi10;$t+c1(*!$C!#jCfdrty77$jR3?q1mp`3LYZ*5P26<o)=UcY^ z5%DIcBONzF)h-d6JEFCAUVZV3(hzCc8P0LF<j!=*mGcG&?kcj3$+&a_HS0QG!4DeV zz`kXbwTizvx}8-1LnrjEhfV#Q^;a?r{c_G7pkzY}%6j9z(b~6#DfT&~h@`roKaBOQ zzY3h{$(0N?HwV8J^i{9y>!8`&%Pyg+>K8s*GvPu)I2q^fS7oLAAn;a;^9?N}f0P!w zm7l2e$Q=DEXNQ!PjMg>~l9E@k;g`-0)w<kE94I4J%RUs9kR0>gv~(Mr%dI|rKEqDC zzDZ<hmP>12HwS4@0|h6aV~=Y38FXD5%G8ZBPP4Z8(35LwiTr9w$R6DDTB=#uPn7Hj zcW&RUXAH3~iJI5WrBmUD<8$VSejfOrRD`5@Uy!mcH(bt<fu1=TC-kmM#U2*B@UEw( zLwzl~%REgc$V)cG&lw~Rr#yRC)K<FYm!jIo1^%a{+Be?|B+3Z{lhBZJULEny=v?@# zPLc@>w&Kz?SvKKLM(io+j1@nPZC?!<G}}ne3yG_Ut7$zw4?4IcWRA6aP4R91nqiVA ziM)WjLjXr3IP2|Hi9TxMu4}B)E%f`nF)gnx?R@V#P4YWL8)-!g_nArOIOo?L>%_!L z2`+ft`V1^8NwrQ^IiC^urt?;_hSupL5!jzCHXu@X$0zXbQd*z%ShL5-Kb2^Br%uwX zw0jxtVvp?;V9FJhx!4IL1|%KDxFA+eqRFROz-OKiWQ8R}M$2Kk0y|em8c>u{g0eWV zw5q~y+Pl3AMTf0LB#VQST<dD#n-QTW+~YM}wSs@;m;OCnstDocH8h)mW0QCHvDZDS z-L&iT{cE27)dTLsjypI109uH(K$*-(lb^j+%Mo1M)UE)ue0Jut*7b_UrG_>!>C>;Z zGV<;@?4$~CamYPuId<-ZWd8uFZr|@yvOtc*LDV)_sbkLLTT$x)d;Yb@X!!dIV>rp{ z^{d3zi((qov8!te+O%Yq$xt)jy-=~$w#{He<81jLa1Y)0tldioREs&z1~6)+iC;3) z3V+s+GJoBr%vUP0>o9ne=KJ5RThn#W1faKB9lH9lHRt)oJ}KTk*e9)8sLAC$AHi)U zg{1J@GVf;(^0s&?J%x2ka2RlZTJk+VRd{8PGZV<<pJ81!hP#pTAJ(*t^wgbE+j)o9 zypP6rhB>u&e2^y!22VdX*A?hCx{^x0Yt6iGGotE`6BIvaTovH;^`d)S7ac{h1MRB# z+)eXz9S-{IHOokz!%(?wd4^zwl^6hQKAHajBDq-D{{U!5mj3{k#si+8IIfRemCc5w z8975DvW$+uAT`e`>2Bvn9Ywiwb~^WO^mg0*!^h!YKi@HtLrTE&_fTO+_}9Eh{{ZOE z&-}AL%D!;?Yg&yh8H9Dz*0G&!6~K>n;yRv~t5+I;ffUJZRIti}-{0w2D<h+2ScBV| zM`m^`<g*@0CnVM{mf9k$?p<w8W}H3{DE<IAtIexhvg68-H{n=Rqpcy>wzEg%?c2|p zyQgZNON)6VTxKKy{nhVT;8Kx{P@RItlC`rk6L#M&F_`@-`rcas^30j<#xq>*DQiVj zmt;$Y&YWLG5e%0RK1K)!1X7(=KDf#DkXJ2BL#Xd@do*_*8T&4!s@vIIyms+jFh^{I zjP*5^kV2;^vG)vsB<Wn$C_7F;!KqUBoS6A^vO1JT00O9ePARTq2Wxpjy&D;>a9qs4 zc4-Iv2-11a=5Hr+=Q$%aueDu?_H5g^w0S08BUDq81wkdW;9z-sdSa-3zUM#dSp9ja z?eFJp+nHEnxD}S~Lafp*M{dx@Hb)yU<d#rR=S_;;q+)IpuR<HGMz`;SyA>$BRw@JR z<G2T!u2~@yEJ8o^a!Q!sVX;I|vu<Xc!v6pe-PWfOGQjdrB6=pybNJRQ_fO`&7@-FP z6^!S5rMYlc-(t*{6Isg{lmXl(Itp#|=)0a4g?5x1j~xwWNh?YB*pfPCnvb=!-x#d9 zdb3VzXtq0BTUEcbN!XTk^gRfwmv-^FXOOXR#ZuAri8Sjr5jJwbvXBTOqie~p3(1Tr zVt5suB>CE!(9)y#Q;XEio_E8<zyceS^vze?bHTx@)5qpHGBa-VJ?coIh(=afBY&cR zN8?=_scd58`jzdjbLA|G85Q@O4o|jgLS0he%3eG#7z%k5+d&j0;njf{!}2OcwT?na z3+hU~YNJUpI<iA!)O8nZB0~O{Ucaqx#2BiV7-FE2j)xV26qfdv5xNJLyC^+9D@HwT zJbchd8_`HPtm;#D_ffqnir14Jstv4!{q4A{+2z@7Fu^;9-aA%<-q~L!TwKR09zj93 z9AljI&TAIxK?2PJ62QIzJxHwmo6)gKY022>bj@bgD`krA?skeM0Qf)$zqay_CIG_% z{QG0^u92sP>NaRM`7#1jw_F<X=zo18E_UF8O?S4lELwD77-eF}8TPE9hK)J%$Emz) z%A(|b3by(!y_E1uCX*17!*)MfyM1#atZ!<9Qdk4VM$^>SBX_RdO=<)AW6V|rfr_)H z>TbGh9Sbar8OYBWu9pQRW@4%+yTrF|48QP^w2d5_H@+B-F^(%sMG{A)#Iq|bgE<U2 z>r~*?Bz<B_Y$nxa0O043#;)o2M()WbI0b>|IO|2VEj>(4a$oqJ+P>0OJY<3IP0(&q z3(MIi`4QV7w;j*;)vZcJT}CKMsq-*$b66Uj!dToW3K9049=&Sqp^Ue=)oM}y0BGsz zE!_&13=e94j{{oAFp#M)A#sm-!qTsezJ^&y+_LTGoMyFbFAc<U6OWO~@kCX{DQwKI z65GfwWSfB-6~WIuRy^w=Uo``HOh^RQp1pcqq=cyB13l|1Cu0kM#3&gno((B0GDU)U zp(H}=ySm`@`qy9KeMKaNt=j@ncN}y-O5;4kw}JJkwE3DXLIzSt%mK%yYFfC{Y25F8 zOlI3@_J1(`^DVPIG2a!Jr8N0}v>AWY$NsZDd-d;IcNcL&iRU`5;enHk)hD`&Wj<4* zV+VttDptouMSV?+u{&F@**^Y8ew9PSb2YuD^kxSje7{=K^ESpgsyeg+>Jb|;P)Iog zC#`0lr%R8!bZ1MbOq%jrhdx=67jDtqXX{&<CDYx<a$M)joueE9!1`4KqnR~iyYq;{ ztL@wATN-Vr*yEfR-R4SPrZJkzqg@N73Ey3ejb~Px$5QeVG-dKknK{ltJbsm};rph) zvVwO|ti<p>w3nKFwav!)qE?e`&Bv}iD)yCY6|`|hZlPlbf$32>E?o^HPF(lsaoRqW zdcH9GOWQby?F`$FI%kUP{6xY%CiJ#H)jl)a;MPW$;*YfWh-t#pZ|_+*@6SVyn686f zwu);hg~8hh8FT*6dd8&Mn&vtv!Zw#9xry-hK_-ZIDBLA%g&41_z8h+=YaSU$qaZvg zA|YF&DvJ4o!nW3NT1eV+mw}&*j{WP^{s-#~6}`YV<`g$_C?$y!5I`(HO6Q$kN{Xel zzP~e<5lN~Gx;>WiaKh5@da=O+`@^^5GCwNiwTpe0+MT)$-(r*Pk&jqg?+>MQMjtWm z?GWHfziEYsWB&l_s<J|ld1H91nCF45T}bsA0RI3gyj34)z3#v8&-7;{3f|4#e_ztN zI6XT4Pcqrn<ZFup5S3mHxA%z^kMVy@j_TgqShH1;cb_u@Dp+&*8oM+T_<vakEFy)x znKqtew{D-MZE4X9sPA-(q;<8p2gr6ES3e^U(!O>x<@mQw%=%?0rmHQI*Udft?a|}D zCh=#3^<;4R7N>8jl)^}p0~uk^6$}PH;5DD1LvN~R+K!EIk_gxvfT+1?6a%_3-_ZVb zL&n}1mrc~98yFbe^NxfX($Eqxme;nsmUx&q(2DD;UM>nzAJyef@lO3$Q_Z|Z;hVn= zt?iA^?*wzi-fAh^%Dy`NYM+MuPkW-9`&(DUH_$b^ZYK$zkB?E!dpE^BJ@sD;O>t$o zZee{Z$^2L(82<p^Ipf;69|C+fy76tD?ZvgN>rDz`F&GNF@tn3tAmbc!n(o8msn??2 zeRcl;BhRZwP;r$lKjF?Icq|@BB7sVzK#3gv=~$oBim9zxEvnn?7<UDj0moXg;vF{U zLcMDn(~GFj&V%?*q4%uGF5YF@=bnS<Ud<cHDMj5LWn`k<WUR+n+eISonZl`UzfL;T zGa^LT-Vbm^SAG-<WppUL?s2&#rH-q^v&C>Gx466z2;g}UpZJQdLVZs`QOz!KfO>;m z%DD$3ug85amI2yv>L^swcVkr~^hWiqtKQru%!SBY<KDJ)t24EHvj~?s9&bV`k+jxs z?O1tIvZ}W5fc(3?cJ!z<4PNT{;bobI^*Q5#SW1-K?w*E<ag2TCsJGL)$YYR0l|4{Z zR-ZnffW<(}S+Z%lu9U1`<#sC^u_l{N&S^1JK%MI4yt_dok?&OL>ev|k==1|!r-foD zWd|b}9M)-d<Yt;ARQrk#xu~;GnT3fIpz3L)(`jh{l;CkrL=3seq`>5Npa(?dBn*sI zy|}4lmPrOAc46*wQ7mMP)3*LUjWmFo8@b>ZiOP>aDtPrvwJ1!PDsnObs(VkRCJF3l zagSkLlg)7Q+<DGHk-6Yi%Ui2AZ}hm`3H~HG=CeLw$IVHT=~E~v4K`_nM?%rK&Ts~E znqr~J+;NX;pb1_<#ZMi{#wGcOu6d=)HMEMld!y9;7<fCv+THUH6Zop%ONCUBrP4CT z{pL9RE1U7|m*JZq5lyG)8okB+y2=%9jA~=SKA`;A9l)+%a+8>tu>D0=n<{q>YH8Dw zP0DSFG_I_6HXaa;`9IM$NFU|gKwRzPJmA+WBC5Q|7!ZxOc6c>b3(4)K^I(<Zd}Auu z<I<rImn{DPF()TAG=04}KWRQ?(?p7wD_Si@F}CA@(xa~9!g?KtifwgdH)&(2+S{a- z@gKP`1cw})=b_`@isgT^rdW)UBZ$kgEA9DMj<`MXPBmjWI4#g}K3;Bz(KhyW_x}J8 z^aw7emI&HhnIe6zG3a|&(LN;b{+)ZK!)q*;1SS?g-!Ti@IO(6lzF6@U>9z50k1S^f zIl=vF+O2$6_gaOzJMp-8_pg-2*OfXdl2_(mcBi3Eo2v-&y_@|GE8_N!G>ux<%VN>8 zgarDkulUyiX81CU^}(-9`1yFUYFc^&ox92B*=xyu&?3t>b>rWfcnI@QNqPQb!%g1u z^EnMn=KkaUx;OKz`3+jsWFKyro-)MNCG5=dkfoJ9GhXE?Zdw_-iZDg3-1AFtcU??> z+Nu`NTwF=E+Bv0vNYt_YD_!*4gqhmm;I<jh@8t^L_s?I_w{THrJp9_3-glL9H>q^S zc8166im@A(P)~Zb4Xu>YGMl3-D9Q5U!2H`E%B(Q8v;d{BmyC}&$^8Z@XDI0&pqbg+ zX;u(?II+~<`#<fCCNuWn)3WpOhzD%<HOjKzPbT)W`#QHql~x4Dws0z*t*vS+9lF7A z@I&Qruzu_elfcJn=L<<Bh25E)^(5q)xKp~gR-9)o5z$_Ko;2Bas6*+t*$k)iI3KN1 z`+l7V%N@1N!5!w8YX1P=Dl_`lbWlriFi2y08~doFpVGB0JT<7^N<?s!Is3AG;r{>u z9R8K1qaE3eyAwsL&OjEn_V(E5VT=a<0KSe}^`}og%=^reOCz%NVp!t39Xr4`rY+Yp zG$Yl&c0XQ!O4pA^(KLKL<@82ObQyE~2k2{>c5g$npSrdzL9A}nnV!zTnN%SzF@w`~ z2N^gVn$7VAfz%|n7Ru{9POJkt2yxc|x%{(T6{G3NI;1c=m*AP33;3wOq}KH--8I%L zG=d~$B>8de1M;p4bm>N|)U`bd*t)Z$XB|8L09G@sJU??Lo{>vxL#P1C{{SfIkKzFI z>FHD6=(a6uZ3eM_YvtR6u}kkf{V`eke!X{ds#@AlGAl^UnV&pla0tghd*d}1k1{ps zNo4zx=Nxmfb6%Wb2&ma_bH}M#Qj>Du^ev^=hBVh7WwxH;LB>!Bm;V4A!9R&KN8@P{ z-6hiW>pMR)JcP?|d!Jh4eB=%QIXUf8+AiHT+dY17=Uq^p<;r0u)Niq^f8sq_^#1z# zU=Q%4Z6Bp__sq?yK+H)bjFZoL)95QQ^nb*{+v-1<u8SC^+tk)f?ii-e88-4{agJ+e z!+LUR7ZQmX(_)4NPrYsG9vg~FS)1)rDIk;bis*FY<8*VniZ4@yyr1mTM}P}8v_XBo zK~!XUBnV3!<GHSjQt;`!RCsR6O0Xh;M+XBR(z32JyP0&@Z4j&6K<-IR&A&Ln9Cfcf z(6+4X_SaUqi8By>f0?=fPH{{^2yMzi+m-jN2(%leLf7(J*kkx^q}cxe_s1>zQ=wfd z-NMOrb970@*pNh@_tRwm0OPc+VcfUj2~{p`-GZnQcRYRJT|76wcJH!7E!!{N`!r?o z^&^2?#9E|ww%7jv+4dH(6;b=i7tUkzf&5t?kQIl0sbAlP7ngI+tM^2b77O$p1$08I zglvv_lod&HGqcpLBU_tmneObZ+ARL>&GV!`@D$EJ5-UFM#PA*?H<usqA#T5r+)w5! zCrI$-uc*ryw6U4@5w(gHQ}~_@dLE7NPg}iFE~PELnGbU*+y4NeWAp~0&TOSZ3d+YV zqU+MhEz3o3ZEO<SR^0+(Lxex_(B;2cWv_?z9~CoQ*rPLNV+kaA828**j-1!2Tlh1< z9uE>rs%kenjKU``6e<`M1LhpyboQ!p{8!R6$bQe^j|^KPFWh_5A8)up!2Bzkq+GR( ztp4++9I^P3%xPZ;yhC|9B6%_)`^^J+p*@b?2j^b7s(1%UxV42f=F@B%Fb3Os7`gud zfT{kq<yYSod{3y~BVAsT2G#=JEXNr1;8h8{N2%Q|THwMDXue$jJ!&OFoZMwoR=!`6 zUeU#J$7Xv5iDBTaGVWU~H%gZJ<^ZnJT>Pt^*y??I*I0Fn-z#OBMPU&gEu-?u{Cd}p zd?}Ho)US)NW-)?EInT^%)?x%lgXvn%R*r;9QclJ<hiAFEy4B6((#-MRIw_Hh6<iDf zp4F?Y3}~1C0QK!B{(@^G!u;LqpY{I#0LWIxx5kC25DEVPMw9;lK{dru-D(Z#e=pF^ z(?8@NZT|pVm-!0D@jv{9^bh*^KgzVNZlbr+?c}+dC}R;X%_D^!2d#46Cf1>n`%9YN z4Hdk`Q!K0@jfOvk+X24}*ROSDbB(7ej?1aqI3)4SV)$fieOSms1-f!cJOP^IFT8lt z61LMK_WuA!P2c$Y<Np9bHOgzgAi9pi7PUkTJhC&%DD$5<UO{dF=dcu~Q752L!nb~R zYo4LwEmqcT2I31@R^g=pZ;a+N!5?}4==|%S)P5z!ZdxmMgt+7PKfe8bRs4%rpV;Zz z--&LN!v*f4A;Ry&IA5!D0=w@G`~%lzUq4^Hw9;+5lOfA~xF42l7*&+|9aF(mr>5sQ zt?Tzc+5Z4(jaB5in=aQFm-tUXfWM_<SX%3TAe|Bz?=On*0)}vz{S<yx^cRM_9pIZd zn$;wK5XlJ)#$vuuBe3HK@~yuRc!ujs(C7ZdhTBdEp;PSffWssXw4IxGV49_5`kqVS z{{VtsC7FfJt*AkxTM_>NEuWM4a6g@Parj5T`a(o?JDIhsi*m{fVmQljk&gX6E2HrR z*s#;h<=K)}-XqR(cO0B#*O}_tjB4IvvniRRbx^s+PyYa3q7`Y&;bN-lMZF8!&&CT) zA}MV=8Kv4<2r?Ptgf2ih;rjkK73SK9kG1a*YLTt&>xkHp$b^l<(2_|%opZL9<fZoM z0Ulpr&)yX64mifC0N4-B-<mz6xwL4uz2oV-e0`jIit}Aw@kYIQYD0OCD>C{Yn2PV) zSFy0Sh)pU9b^;?`na>@Rb6$6<n@wj_not5t&RGwb6~gDZ1o!M}u`fi9EG-FpM&8Ev zgq4P+c1nN@YI^(8;_X5<w^*&Eh3-+e6wd9x?_;HM8r$4xo*&d@f_Iuz<uC6};ey!4 zKgTtT;JrV@+UB5NOSQYYkz+nfEM&xuk%9ftK9yAGXegrU;gw0bH?e!c!d)Lx)$U<< zC7wGck<}Qs)dU{BGoIYi#p3&zWBK17yuly=#((<t-|4;qpF*+JZv0uQ*jXEkX%cuW zq4RDwF!{UTPvO?QU&x(gVvQ5WoP}<pxhv52iE|@*Q;b_o>%2STjcdbk+TG~Q3%q6W zw+bCjPI3Y0efrcsGx)LMe-zr>+G_6)*{6@oXhe8X$UA_~UOLw~b|$&E@?uqtm)(># z)oH&D^@$C#>X!1`MRgFC3ziuSqtJgAa=rNcYmQc<8`N4}=UcB$t0$@FdUdq2yuMs- zAyq6wDQo~TPaS~fzMl9)7Ca}V%A6I6SL;#Ud?~Qd{ukcrH!@s9smTUq5wS&O{_6mF zQ^6y?1!;UVvy#)o+FUGt^eE$ua7IOK97WrVm7a$yX(rP>*3^S-5nPe5WAdpgo{oRw zT?jpfD%7$hQJ0Z|qVh@YUYe3|xrpj3Ikl}hP7Ty_&+Yv$%-Am@E$z)mwx|5d?<biT z<c2>j_QshiN8KFP&6m+^xRF8N5;(7-ZZ&%tV&3-&x72f94SxZ7oxsLUdRLc@txujR zEp`0}l+tR)J*h-Zt9-`0PlI||wfEWst4S710;4JjBZFMNpcFy0sc)EMbmKMZUkT)~ zXr@bBQ3Tlx*^_qV!Relr%YSK7FuvsTUP&EI)=rCl%A3leA#t3MTy^G`qNT#iAiifY zE(YZT7~{2i74yXon|9K;g*La%#~fEVdeKL39IntT3mWc`qya!3c|2DJGY>jc;TS9F z+4pBv3HvnlIQ=F)K7S7BEp=~mD~QfWlnkB;Jw5Bu{3g&zqM@_}V^=A(^-?&;zt*@v z2kJKC!#Z?nePFB?E+n{BE5PbA$KzeMgKlk&g~V~q89!;2#t&63kVmNZuerrlg`pJt z_5BYw4<_n6s~$u9Mn=!AS%RuSwFf(S`_10G3s3<N<X6)_7<Acn-C_f$$0fjyA1(Z( zEzU{p^%dd1FYvyr4xc2aOTQ2Z%e1E5!iC4)&M}XA`3Y8ES(;m1=%+t@qMnW7J4n27 zdwN-?fnZdRYJxB}<a5F3YtrrB!sks%=Xk}W*kzH9)=c#3e?P*!4$ys-H$}z{2<T2n zHLEgSG?w;>EbR<xp_CJd$?I9-@NlfvosX%(=2ar3r5pbMn`g8`WNcN{N1bUeEScc? zFg=LuYf{x>iqxph&7<(j+~D>ke+v%GD+j@mLt~{FZLZoxAUH{zoa3Ak)7rJZ)&Br3 zEq`@Bc9`3Z--r1X^Yc@ToKw0!ktc0e_4=A^qeF4FYiEvTTmrHThyMU&y?(Wy;++x= zR^Li%PbS1^w?1s}F%gv=NgasD{c0%h+3qnBHkUy28*rrhfAx)7zP~qUk?OF^Zz8wu z?i1uW9*lpH{OMEYjMSsh#-%4TwK*+ENz?V83;0^r-fLC7lgN%mje&Lj%VaPd^%cZ; z-$uI9{DhttMf1r&FHxSq&b>=ryOvhd<-02~S<U8&RxQMQq+&$v!RykU9MEf4)48=P z6bt4CA9apE-~-p1@1=;98AevqT|Q?OS}n#=lQZ@22xz`4^X?tZjo>8jga81?@h{`o zgIwId3$8Uw6l)v%f3@2IZWcgQT|fj7*f>1&Bk`@&52lGkUULclZ=8JK9CXhorF9+) zX@;M@{$^=GBe>vyg>+KHREn~C92lB0vuDq`uY~+Xqu+UQXxDyd$O0HzL~-m?VAcDL zHt1Vv$o4l;v}+@@ViUOu0Dwk1^ZHk~c=F~|@XHuwk{9#Sm13u%^sYz3cAj>vZ1b~8 zDTa2-9Fc;UKdpDi6y~|Quktu!MJCTZ*AebKF<~^Z#}lpe_&^GC<;C0i)(xB~a}KU| zK4G;o5~uHBfc~Psit(R==GQI$${5Ty8B4~XK1Mm`sr>81^$j_EJFR$v!ezKfg@dd@ z;dZ+cQ2zjpZHBMyRl5HG!6tOAB`@n^j<L3kvrOcMM)?OLcjs4HR#s5C+(_V4Eq-Xg zTx65Mr!O}cV0P~q=DQ^MrKyBtWQ4nro9AuA=3-4lE9_Rl?~;9K*Dt>6RqEx=GuN$G zEU?LfPtDXGK&!Rbrb}W2Y8L9}a+y~+=eVsK?F&=5l54wdI`&&7b{=X%%%=yQaofF8 zux0+zwkh&~D&!9Qii=vA<-D8bZJW;r>sKF&!{z<o%%gP-kV&>gc+Xrqkbhc|Lyet| z(Nfu6Pi!$0kh#f?hU9Omo)6<)myKYy{{Vzn!@7hJ+ea<Qn|XzB7_QIdo|IIoqbSAo zCKTMF<=Ej!#(jM%7r?<I+Z8H|{Ktx8#(3UwTO&4lN5jvA`p=EDpY0tk=E5s=kjE#V zBP${L1v#%)(LZPV%UGmML&PvBlW;3<Y|cjY+rccq%C>)I)`G*~tIag&Y6b3DrJHKE z%7@#vdMVa)y*m3)fPJcKk2`;tX=?MgDyKPBJ$|*@3NhUCYhtP?IH#e(=zp`{g>?O| zadWBN$UB)<G05kI8U1UmvG`};?LO$Z(Cj8~iZgb;dGF89n$;TB{lt!zo*mR*YZWhb zDnf@Hz${Kb3MYRMyDyUlkz`K|z2u)940H+a-mOweoN}!xEm@;$W2e|*ENw0A#?k<{ zfly=9<rMRiGlLs90FB2v;<4qj)u)eb#=Wrr0MnJSMsdg^EMBuO^nDi0NBz60UEHZ| z4x^|rQ<2jdRqfLiq+*QRk-w^HmU?0=m-m)dQGv@(DxeuYq>oC%Ulr+c-AuL<U1|nZ zMc-j<B<en6{7MKI&U)4~o-FVliYETc8d}X79m!*Etn5c41xGo^!Ot~`s{C%!A&rtd z8^$~kXDMzD2tUNx{VPQcHYq7LqB}_SjY94h`!|QLK-xh!v!o;P%OBE}+g@oJIM+wB zhk!rUOvB~lKX6x!>z^KFit2T+ipWKl3%Q<RdEorG9XnSob@7wLI<>{3SpB8tjDpeG z!Xq+avyL}5F@ZyrS5#`KRDBPjWwO-XC6fEZQ@V*`9#x&ZaEAGZbGZISuofQ$SWS(0 zQcKN}`D`_9M9Z8WenxD5LcAsq4g5#(9H6#`s>r3c2Gq-_$?yBZ2jyK=hrvG>UrQv~ zuA!z_nH;+;Ojz=Ae&_)HbxN#uK<Oy2dPiP<Blu^-u~;>QoN|z;q}W=j^y+_vbtjK{ z^6!dT>>eQTb*=5Amxb*hz$9e}OqDn%k)CVOtbb?g9qRh7y?JoTdO;G$`mt*1w7-PD z4bkQfY^1uCx*MsB=l%jPKT5*9U1;*8ZHL5C!^@dnpEWyqWn^=O4f)g>hODZxG;v{w z$YR`rJ*(0_J7^*ee$}))2q3mgRox5`1m|;d0P1VSZ(rsqCgg5}GLS`Z`wCHZmaQJ7 zF;!(1C}^2;RMoDo;)Xvv_X;usuG60R^{k;pXDZ3Zs5GliFpO2AjOx^#Npnrzjuc!E zn|S^%zgln@rQnUNz&-scj8oLDMJp~24^d4t7Can}aZ0U+Q`ZVq53Ya4qUqA4><Iiv zr2uYd%pTcePnpybReFKzSG#ds)wJ(#I!g-`kq$wv_-7X}d8zW`{K`Ls{{V$tUC@fx zcH&EcE=Vzt&#h+Il@Dvl&zgIGyH~EIWxPdJz+f{`TgX_!lm7rMG1KWou}Ipc`qJm~ zr0jnh1SAr9k0S<F862s|=ql85vUD|{rUwzYkR98*)yf8MXxERN6V7WJ#4OhW{yn>i zm}djGHDgO+E&!`KKZkDoPpseB*vlMF7`ZaL002J2-jzzRrw1v!Bvm*{a^-@%WgBTr zY7qheKWitCLe;n82QjXpxJp+l+#v(1jt((ZbqyW#jYiRDkz{$DXB$BqLpE_v)*}x3 zi<XFd@q@T?f=6CGsludcN18gJ$`vU|!RY#(%#Z$x{{Z#O{{Sla<q*H!88z?nAM{7Z zc+Nj6@M}3tGarx^BW@2Bb>75NA~87r6G`R2_Z#x95420?GUW~k94$?6Cz*8|c}G@U zk`#;|O6KNfwnkFK?a@!;PVyXo39W5Nc_B*tyo#*^(n}nG<2!iB#T?8zQ9uJq6_{iK zN79(xDLd5?Nwkite=0&TOx#C8YFl=1G-D^$hQSTRC3@$>PlodN>RZUIZtd+F+BI@! zk9cgHV~Xd#BX}lX2>6a&D(ZM<mN?bSkb)G38R=Yb$I_iwML&5Ox=kybB_?Z1O*pEC zA%J1QBdt@C6;I<Iopvdwv73uqkWorh_6PE$#Z|-=r=zq5ni-UCW0gL>)tm+>>Gh_a zgw@Wv+Sc7<-XmSWWO34x;zUbkJ7X=kfWUHUrj2{&hFJ&_Dw0pA^{k8Qr;&=W;B*<H zQXxcim(Ivl^N?~6r?=9m$i<kGLwi$eB)Xc9je@pt53!_kxrqP_p46H7aY;-Ef(Hh+ zw3|^Sw&s@#4mOUau)3PDY-D!|vw42y_KM1+9F>!?bmXG%C38ntK^mscdC9DOtsx`| z(}m1sva2?DCQ-Vi`$C)vA&HZ)35-^hskp|>nv>?0(lT~B@<FNLX9wjSYguH1L5ZVP zLHADXDhr#4BXvllRzST)Y@=g4mBO$bvvujX^s5lV9EbSN<4U(S$2*~az}A+YkQf?R zqhu_nsi!R!!D`IyVHPN2L?jj?l6@;C;#QS`ZlC~bS6ktu1ZRxaM1u{-HH&vH)+}1w z2-|^zc+GD8tUNLHXu!|7{{T6zV!8;TWU^33?dYe2oMxhLmd3QBcI-!}ymDH<?qgD| zfwggpt)r^Qg$wP&ha6`(s<JDH)VAy#=QP`QG32Qu<`qfkOqQn7mV;C?f(d40`SDkD ztJQ+UM8oc9r+VhZ#7^9vr@c!I<zoYQ93DMrxYIQ6uH>HbH$r^4$Zfv=0OZy^(y}a@ zzm4}(p^*KdGQ<LT0;9N)$gR1HeB5N_s^mROI!Re0ni)w5aQqx{D|1frmf4;X#bjdV zcdc?t#dEZgo(>OM*U>I88X`}q=9+dSZnqFw8ePXOM-`U2AmH}NtD3~6?n<2TRqV(N z`@5k6l!k}ELPsw3&gJ<{K^tw6ThN1APbN(9;XsTWaanY%2XU)gNqui>U@P)`#FN*G ztEbt`YrM=C1~fr~o<(Tsubno~ft;d`(xkXKiLx=$uu|xEN|I}s$njnKpo$a>Fn1H! z)%{xNTxhXMn@-?|2cF`w{A8d_0q=^vqRR~WL?Yk<h;}d|Jm-pXdMOGQ=adO%k;jW7 zPyrrvV?Fv+%YAKZZ0=;AKk@S50C_z7n$r6YoZ6B|?4s{-up7C^>BVtgD={t5hSzC8 z%*O{mF+A2$aj5Lhijkcdx!mYAtxDkP3ik5Iu)Uv}pmEL#ACF4vd><^A8a=GDv3EHn zp1o_4wee&S+QAt@Ss?+5{`eez6<IuUsK+K%ZP_u6n=n6~Y9&r{jgl%Dig1@arE{v$ zd@C)th(@UQD#_<F><j91*EP`iwofZWzQ{RfnMOTGIIbg3_=dvbHM_KlZS(UKEx3I- ztJ;T;>}S*pHMy3<X5b3pxB4D&isY$IyPA`Y>~&MGhEaU7watHrEX|;leC}CWB>UF) z!ks`_>srA`=knva{q(sl8t!Jm>GGZrerr3ydiIa0`H|asc9XM!A#xXY`@W`|;oS;Z z{84`Gcwk6NImsYx&U0LK>MKoDR=Hlwo{CU+N7dSW>dj|ocN+%sTq_WNfvaLzytLYJ zv(FB>?URxE*CXHy3!8fux0tlCNSIlU8$57xT~)zrjV1B^v?zbXbNs=s>@?j<9L;NQ z%l-wA3C7dqIKLZP^KCouxob&Jw&S$^7^TskOWi9HHg2rXmItU6tK#TV<|yDDF5!`v z{qPl+`5(@>%@*~bxq^I<uZZLs{vqG-74z7;QpPs^8E^L<-WpDH6}Mm4`~dOZoHbh% zxmd_>zEp+4<$&P+Bl%aDX%Kye`QVioWQUA)YWfE5&O6B7Hc-nT-5;PmvtAkE>tkW5 z$QV3Zi6Ah>)0*S0PE_XLo{aS{brni{i>KsmLuVD-FJ~#-tP95wV5-hJ3P*5o1$oAU z@dEz<;f|@P*lDq|E2|j=%AAMC01WU4>s^hF@VmDU86I?aErWs&tz`T`@T49sk{wn( zPS)D#lVrBfjm+K1=Q#Gobmh}j<l|MezJH;XA}y;Wd!ARSc#8AIUMRGl{&{W^;^7d- zJDD~sF!@xTgPtkAH28n3czaWr-NzJAvple=d=`uo$o1pWyX{-yCFh6a(6vb7cq~>( zjM7giZO@Q$zbL>xhd)~He`vis`%A)>R}i+@tphM{e}s>iSEnq>6l>C_N!l{e>GvES z##O(Fuju<D<hgUW=A)^lR$PPF)HU?$W5JrU3Yyk7l$Ak~jMO!$rZuXrUD+HQ<eURk zmGmJyYE^h80Od%kwX1!7^0ZO5@AkUpvZ!e-hTD6EB@|FdQqoaK0*WZ60?|oJKm|TM zqn78})|o{BTTvN-GKVK4gVfc0;GQYFh!~=P9C853<Jy!l9kMC8`Egbto+Sz;jFLO% zn$?MsplJ(j`t_@udUQN`R579Rn8FEMXB9MZp==Yi`Vhku-hfbNZO7J-xy?y*RJX`J zwFsyvy)F2AYk1NWj!3}{<NjK6!8GXn+1KU3BdPVO3PyPut9JLI6)WX}KD9Qs!@c(| z1St~|GQoK@MrJ1%UiE&~@)?6GFpY@E%y|6hj@~z8iC5)4@y$5Gce*Lg7D&ip^B&aR zwXJz%1T5f&Dl^4Uvjp77wRdxz5me(S?uokH5^3>D(r=9IBam~7+_%^Dr?ZaN?JqUG zuy~d>RdrQk^9&KrY8b3SDzLorvUpZeOQInP<(OsLBc8R5erQF>tD9l@YE4Bu9);tX zOd7?6ilLZ40G=~j8h){;MRgpD6`5c`<=hIy_<*PTPRoKoe40yJm65QI3VX{aL0%0= zDN~DUxzmM}mi<nr$G#=iCDeY$sat96QHkFhIa8mg&2d*^d4iE0svsC2zU%q@YVV8h zh3=%%qeghi*&t#0)&oxNs;?t3QH2cL52aQI4xvgdD>EK<7SWG?C7gE;_L%3EZlzJe zDEx3qs%@++8T&4uWf}hTiRQQdJ(2oV>FnmXxpsotrtOdk0<>(rHFb0cmkKfTiA<RP z0KP%}Yt!aYTB1_sTb#7|t^LaFn(k(gKkCB}zxT-Es@#L;Hr_s!>G1dhU4ieJAW!;i z%OC#$6n~XUnoouF{l;t9t{fgj5r^mM+5G^fu@UHm>B#e)1}UE4h@y8SZ6#EkepR_| zqv{v36<GHH#uqH#kjbv5PZQ{tY9q7L1;#oXr{9mD=szk=W5xGpMS<-k@@1KrD#W{0 z6n|RIb8>vvE0W7rR*S&5lgAOee<CzpxRHW)>)ig8JboSU-ju}NUqLJPR9`UuyndB~ zJ^jqKku37G#zYLv#F9@no!**K+T==ci?T;yH;D8-Iwo5^HcOy8TsO)80DScQDvZ81 z)NZZ7wOFj9?2xY9S21<|rT&zfcqZB~26*jOsjb;Q)?E&k)@!ScHsbo~;u!#yBikhS zmnS5ZO!7G7k%7mpU~0EJlOHhz{{RzJgB+26tWu+=%qlHf#z-O`XpE~7xG6X^(vLD_ z#m{4JQt=hMbF^|xG}1&$00wq}+a{^sYqti(AG2*C_-R;^f;}pPR`JcNY8%nFd8`+V z=jJ@~R(wT^O*S?AvNPmksKBbDgN>Z+u<K$Pv~Q~_+26=uYpE0&l4S^0ag2lh6+eqE zJpTX?PR!(;k&aLOGg`}v;#qX|-z1Ow<?6)tHL2qGrH@_#3<ZA8Zb4L1MqxvOao5(O z+O_?3B2QGz*v1@vYf{u_Pq!Y0znxprZLcqE?yRFpW-<jUvNHH}JPeP!oS)LCeIi>c z1WVh9Em}5U=uYPcjxp=)nyL~}o!J$>8yT%xcdox~fbJX~e>%54=9v><M|XJMuKrs7 zL!Z{G+<1oQVRYq!!~xdYJ&*Xm-anmo`&;Z`7})E4D<#Ff+q+uKkjWkvNk^84p2e}6 z-?`M))sV`*n2C3Orvv%dmgskrHn5L#ERwXpDQ03xCc9|tZ)ch`f)*RO8@^oqGuQC0 zm{yddV;Ry{EL`403dUlMbLE4&Q@b2`3<~1)oqq1;F~vNq69O4yI~4x_z*htL*P+AU z9Y;_K_meH0dlg8N<mfr;&*@ez{4?PF6qdWVH}ejvfJx8e$NoE6N)nfmqDnfm$1SYx zuHS9663+d0#)XgRT}FrS>tEGEymtE(p13SW*KTq6;=K;f#(HcGExwPTz`jAid!L^N z*mNI-L8<tsSky))ypa>KL`nffXs)bMu8^8<!kaq?pZkAVf-xe1(W?#I5PAZAJ!_-V z^#1?|Xyt#i^wzqSI!NFk{QmL$>(2Dqf@{~3OvL{Hql}Zu`@+3P;clO0;w?t%8>^Wu zqm36kR1*0lW1gKWiy4fD6*r=>wJLM0uM}hWzsLH%k8GdVmbP|C;fwiEVBil?UPG;T z$6nQ>X1mnxNIg{?sQoL|{y9Nsr1+~*({%N51+A!DyJVJ<BbxAY$*gGCm3z5#Hc;h? zx!U+<KQqGGq>RUM#W^15V*db*al|<FuEWF5%i%3eagW{H5B!Fw`d1b%TrN1qb4DKb zpQu*SYTc1MGL@A}b{OWhZ6;Y0BY7P@?NriZd)vfFB6*}Lene8te*s&u=&x@XYs;vu z06*&Fe952p7bJc-uB#d9cK-kfPIW8Z5Bj0k{p$5euOYXMnrRgRj%&iSoleh3v$~Qg zZ=@_$3xnlnIpYd*kN6c))O<y*YEc1gb1YVaDkFfH$K!*O_?pV2U34##n$G8`X?pke zbeg=aYbaT6-c>S<nNy4%zj*!vu=S6LFMGbiDBonj#@koRz&v$1KcKEBN$}5zwTTuj zHrj2Z1wS!WEsxf_?FZm?sF=I)7O88b!oM*Ner$d};ZX{F?)?opR8^8Z`@|k1z8B9G zua|5i3KilXyvEV|XDmMgDyEa7Ykne|{RYnF?ngY!w2_~7Jbq@q&}m-{yfZ1exCdCg zU>h<KjsU>}ll-b3TjKPvNoxj&Wfq-iWD6mNFs{ky4`0T*Y`U^eXF>P{tGD`2y{g;k zFq51X`H$z1(>30Bf5RUS^rd?zl<QYE$t=6BK-gJ8+@O)k;C8P()xIEVH<GhLp$Yyu zSjgY4Woi0@`7qqwyF0bRyAjoiQT{b<$!K$`uU3)Tc+2Aj=9e^DcZOio^u;NLXrs)E zIsv<}?_MY39~NtR<jnfMr+0KuJ6=P#^65+Bi-B|EMvy67k;8SY3yx&ZVd+^W`m=6# zD5lkqVenUot~75ATD6!A(z2ucxhgPE6wB>?=I9e~H18)k-cKJ-O5{G>HIIbsB)4L+ zEbI9Ernj`~%bWXBZ!MbFdYnl=?$Q0%JfFig&sGiI!%vz?+f#9TNSfD{2bvfllM*&~ zKmMxc{B1miG|>w%++-M2*Ojd}bZsu;4?Y7EW%lAG!5#P~oPGkayjOF1Z62F#r@W8` zX;6qs+}x<aZ0<iy(xn)wH4^6K&w1`<+v)Pb1<}7{vke|GIm4bo&d<G$Jt>-D)7JVW zi_e+K`|jRYA3zRI<}0P}f5N{K_=Y)MEZW-Hh8laEqK-lDUWujrC-A?AV|$%*QMlG8 ziA$JmFp-Q5<8kX<5rc1Fsa7!7O!D1IEmK<8bqh;2n5s-u$%ZmRdyu_;rn^lC;QqUH z3=?Zt5NS4`aQ6+hk2&Fb=Dkb%de=y|E2djnX)!k@MFY)m_!;l?t`6tL>iV9d;pVz_ zbqex1I2(QW>s9QP?<R9RW|pS5G;Xy2015a)#z&e9-w;d!oLx59Ne#!!bKmJ$7atTg zjavHF&d@`qY5Vs5p3He+hfESWiplX#t#x;%-N`g~07j@Wfy<8d$7tk3tXaLp@T&QM zwi^LPdz!1Lw2()hq+FzXJ<p1??O#E;(zNSX;F2_Ul6>xwx-VYCo}DYrbsr3so-1n` zl{XR|M2CeQrvj^ZVt8ZJHL|m==e3lf4l+-eq+{QTg6jVO%vfY}`QeyO8$4um�`G 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%0D$dT)~bma)Gu-%jQoj=`HkBHzxnhulk0cmg=oe- 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%0D6;C zXc{HPw=r+^R>*#72;=BIO?NUrpW)p&BH}dk{y9xzl&gF79i#nhZ7If&?&<3<>v9bv zN3_u(d0s_nK>g*z@E6%@k?|J0cdpDd-5NO-+?*1>dSCvvTe)pR#aC+zDURKF2tMkM zR;e|OZ%pt-v9_>PxzwC|@7X@T;aVr{BWU}uU)F@iE;76Kr}=+h@W+Duf3}j_!WQsK zn;r|#EP!*-TxW{#Js{%m{sMoMeF5>-VmuFTB&^d(CE?5M#^P2&PCYB<-7NnArT+lH zPx7rYRca6XOyR^k&g|7R+FDBXj$opSLqG^=D598xgGC{r2C=5l)RX|VHNX1hKJ^Hx zp&vM)7U$EtKaO<P-4jK1x-+nyfP;=bYo)UBMx4r{e2PE=4H>3rmT@+pZeAGMF|Sky zu&pRG%O;I;XK#`RK3Em;Qk&-YQR&T7yS!iCSch4%ethhGK@|?6;yoVDNCn=fXA3(3 zt0c;#=Z=K>)Zf{5yKp1gI{c)H@NbXWKA{D~H&I$3u|mh~2tI5OIBrSx=Yd<$QmHi= z=$XqX)9G{D^zRmE`i1??rJd|DyaX#UFe+H*fPF?Q=WmNz{I{MZoBK}5(qOF}wDC$~ zSt7{@#d+ZJNcXM^%T&L8U|hj0qTt}=I`BO^)eEgkd$=Qxb`RyV5ZLFFpXdDR)}@4S zoyfFfsy$IYD_vQ5Z7eOJM_;ooyBBgf84ZjMgk#ec#o74RQ?%9XFQA=}!26X^hyiy3 zSLkpCJ*$<HM%WbR9Yr@dUrOkP9uaM&xSKj(7<iI@5o!|N-UVsrD(?Np7Z@181KS^s zb4=a;0F@i^y!PUlPdKfi1r=mTBvS2t+#FQ4_c&=h!gjKrJxx;$fM^HSw6Keo@&O!* ze)BIC0CO2&4^g)u_NL^XeFiC+pbNTWh?6LqGbDM*Ks)=_yZ+7BcF|b)lT_DkEtkW( zd}z|!%^J8a*$VqeJqF>&t$g^h6wcB{Mh*@$S~@|V;@;}cJLI;tWfRLBlI&TEbQtJc zlYnaC%Y>9(r`h^8jiq?UL5EfF<;~rltggm5t^q56a2Y`%ln?<JILB&_S<`i`D@vN% zWb+~1Qsx<2Lm$iEfL6T6;1`M5wYHaG=U!jWaG{=7;>yJ_l?R~DBLluYE9l!-ocZlM zvB-@fF$G+0PQdz}^`-PWo%c-fEeBoKMxz=@t6a|txK;*VG<^?TQzY@uvmL5DuWcbe z^U~QHKc#xkyWzbjSz`CLYPTQ5a|!b1U!$%Xxtre%>h^JM(=RMFIDq0UTG>ng0N>Rg z&3_s$V&gXV`_3ZE#}_vja4XnpGAn${-)AUP2EpVvwRmrgHFnf5=8!5zBzAC#z+KEw z_5M}r{{Ry8%Z)EglEVAL)_QgH>QqY{V;*1e6ku?3)A6qeypXXj$C7<%+e4O4N>+CH z8Bw++2i~=9q&`^x0D7|)A{%f7{VLot6kK_1e*;b`@v|mdnyW5HKT3gL=c@kzX(Rqw zzvOAYXF@}LFhy$wjGS3+OZcOGjk_a*fl9X#+%8i*92|<FAc@?wt7P@Tr$r=DF4PPG zIpow|T}R5Tp>W^8gsCSgaCo51lg1h|k(^d^ivu}hyb^MG6y%UZp<@KDbIGAiMct2* z>d3OhXRmsSLIRxnRcRkBk(_nroX#3N=bF+Ll-oey<EXZbJdzJ{Q>hH2%Nfb%C#6%1 zK3*}&H8cmwW0IsFddJ#X*_F%Gxv)|`)WL3q;-a^lzWu{2I#p|nr*+y!200aSs*<`A zi@3L#^MO{+d{V_AdB);#)Brlv5;HP`iegB}*+YX|-0zy^meR%eqmJ)6RsbHgX<$`N zqwbG-ucWXF;n)IC12uUhjo7NNBz3N+B`B+zO6llSn%EWLnMbhAR%>+!23G|18*5#F zR4h*bWY$8;A5m66GUo#n+xB)*cDP3J++VW#$o~NM%?8zf=c87h+CsIbz1*H-E9~ke z19NT0bDs470JBB?<NpACR*os9X=~hfGUkRg+k$hr3}&QwBOm^{y2AjF0X~%p&wst# z`cl+)DzqpLJNM?92@%W2YIZpugZb5W&i+j%%0jjtMmSuvXPVQ#k{PXSB7z%uq}#Qg zG0S|QbH!zBW5_i4f%6pb2(LbIyzX?yrz~3TEV6B8jH2aE(o|!is92Ju<-a<UP>ikQ z*zUyC8(SD3p{jA^b{93!=$*GQEOHd%ntI$yCfX!yXB>)!I49^uDx63d`sRitd+&w* z55=tbUJX#{x6n&0ioRP~ZWu9!&q3VRAMq2xqrz4iZNxgNUbW$MWb%R-_JQcf*1ZSe zpT=D$NAS#gZl!y7Z7NR8tYI<$n*xEHA4=waGkD)k@ivX1$EWIRcGmLCB&+2WWXN20 z869ip=t~;0YBRqkx6@6#*z~1Nnx`o%U)0#U_#by^9PK}e^uOJOBM}~QNdEU7E6x02 z;mgkruK6dlyM?Ec1!##GD}qivIrXogyjk%x!}rieEj&$UFqB6iEgs#YCn3ieBO{9N z--&vyrTw}aTdOOA*OR$uahx5XcRX>$JSBW&t0+~yy)F70{Z^l_buX`}#6x|(h%w^; zo(&@ED(tJ}u*t_-scrtTzua%-P{IDWKi(DeJgD++&E|cFk;W+*fZNC3IH$KG-`2D5 zU(5;t2M0L@vq-C&I8p^SXsE4Z4GQBp0~x6p)3B~10Z3GOQ0-Vdn<ta`)u`N8Hs1<5 zX0qpew;Ql|8K`$R_mAgEocE@qVKzB%Pt0&AkO15ed+|;0#Vc@66+#<D6jKmWopxh1 z+I*51#(EBvfNG4M^<b!;b&eDuk$^kr6<ai+h+skFat$ViNhB<i6eH#wVw<tL;MMoL z7ncyTBCDz@ANG!V(q2YlnWKCH4>+vU-p_LRdYSm*lNE9$3Ro}%eJPg;xCL+xUpBTA zi3*`^l{mLMb4hK(Z9MT>chUJ5*AU<X9E_il#YClNVxb$Wkz3fbn8pD0V}L8%z6JOb zTh^_wC(?DRjW1>u-qzuh%pHN+Yr_C6-!V1acnin6kAzv{(j@XyPGl;1YV)zssW@6c zhW`M2zvOyYHw4<zK8@<W;>X1=1zh+qTb9~?GU4PU{MAFZ1o878rno@tIt+S>>bz;< z+rJb|_N&QT?Hq>rzV&hYx#o-_pR@hu=9sxgGftcPyY(TsgUplU2cAt)m3By1(zX<L z1dqnEZUL6~f;lV)1Fd({li8kiSjDboxxc8Y&14<`1eh5Eza49zNoBW_a<@^>p%tt4 zhKdr7xm;i$N}^R}5u&4S0~p6z%8KPpJ&iF5T}nHfI#uetc>ARFrM|aWMo<N6%Qo#0 z=PJUlTHKizYaU0fV>oiYcpWsW`$+pbg<VQ90fz$wrcF`YbDzSuwX0Bko}r3nup}D} zJJ$Glslgaxsk(D(pptM0TvGz`oKvG?hEvhMojq5rZ+jfJxWaNh$)=G`ah{x?N{E)v zJX1ulB9o;%uUZJI5Sbf48nLIA^S)3|t}3|6Tn}2+@a>?wzexPIZs#4VI<irNY;cQ| zl1E@8i7dRra=<QE(xq*wjl=JK+UK?Bnm^pz5+g0On>|SMu4Y~SdnqR>arCb~mLJ+i z?VHq;Y00Y{w!d!Q+0qqe+&Lq)aIcbba((LkzO8ixGQ!X2MIx2Ngq(dUjFK0}0DUXE zg*Q!dvkA#b=rbJAnhBz}GZ(EX7$c$Y#U?S$Jtgwk?HL4M?i38v)8z<(B<Ok?E^Z=M zz+m4_YaqEJ1pXA6qQWjD;qs*58h-YP`to`oTA(V#GXe6*GN!eR`;yVIZFD6YTZZ?m zU8@PdcH^Eq8qu@5-SSJ<v98K>Z={T64yd@i8dgwxQMWxQtiI${=3<2uyi-riI`dI^ zO}ipP&tppCECvIuA%ALvl>5(-?@9^BtrrUt<R9Uvq(FKDNLiHgNYjA9RneQ;hSyQq zm1c#Nw<bXqBo|rvW#IbuG{w4qw7h2wgzyhhPq>Up$WSmb$9m?HQrOefv$~WwHI`p$ z;feNPQ3+%YxRJ0=tzj(&k+q*%Ow>h>YK}<?#?99QIG`+9%{7lTp10g&p&yMsZ{yF* z7=1-9X!jp0FAN^SmtyCjs>yX54jBGbH=4P|d7|sMxmhQco|L6Kg5RAqH>Eo~Q|6M{ zh+i!6hBTgAb|di={nUl1_OtF5L@XS7AI7VB-@GbOf$R0FG9D>KxSANqJ6%Txsx@Ty z3nWpKYklP+qF_1zNcvRw3nJn<hf~+Fr>H5@1Xi|E*e+7e#szBZ5(aJvp_~1YOk<2P zAPhUx<*;12*EH|p7^+Kf%bmkNN^;z(Jd9Cc7V(jqPvNLq;yfHRBsVDQ*BChFm4K3N z2PUH2J%2iV#DS9$l_sE7=jIhi2T3BvyNSrnC}7K!A-;y9nh;0IxvJp`@wJ6FCW6Oh z<4H3;x%4%J(iX0pUX~~>qx0YrZt0WKsQK1!*30rW^VH>Ntq(!M?{gGnwLcZCMT39K ztB<2oL{<l^GY_UKPJbb)Z@HtR_=57sNc$b0*!MYN;AM_`3T!qv+KZWF-ude!hdBD0 zx1hyju(5(4F}HK(k%9OQzl~_!TmczrpDLrLMIS*@=g}k8m0GnRl-`LM*47p_BQiO- zk$WNI^Zx)0Rcp;od2OW@>bB7##@NO>)4V<Qc`fc<dCI7e;T3l?e?e95=C*r#q=p@w zj23;({RJI$IsV1bnWV_ee3>Q8jkNbBy#q<`_0Fs2VOls4e9yNdKA&3RG%KrpH^def zH;^sF5y+15F~;$ck(&A!#D5TAp57s41-uqQJSDtCVt_xN<SSK##A2f*hWL@uO0E|R zO;p4_ujTh^uk_D0@V~?R%~Ctri}`kT)4We1;F1ri;8zjxYe<hp)NLYyQ6OYs-#B6z z5!W3%S5+U1?`>n$Ws7g`E?z+jx5_iz1IBC2yh(X;uj`ZCh~#H(yNBKS`c=HI6GBk0 zR(f{x{{V+L@fnsHyd4@5*G+p9ctk+r?QXoaTml{Ydvzbz=}~KWyPR8C!j7oK?v6YX zJxTQaDo+mS0`pM&EHCzn$jMlb%08|BADt(RB9_ANShARQE?yzP&tiRerOOpk_tltA zukKYlTk~W(Rjg3nEN0<kow_Vd@{g;3hHJ3Yd^vd+g)c8<jV<9=_YW`>GIRVYc|Y$O z=Dr@>+*oVz-N9|<?N(jIH*F+!8Sh?$u1GGR^ZZA0w$L}su@k;&ev8zP(zC=$tD1~g zyVaLYjX6<sa<lE!^5dr6>RNz~(k5?}2JB%+$~)Jkcy~#kLD0U-s%j$E-boC`Gz3WJ z0ALQiy{i^)6|7OEzJ+b}RAaY!!%oZWdMDvk;4LNKmPrG<51Hm{j{J8ldH4EKuUT5w zG{WKJ-!<%S>Ru*;O||koJ!m75h6K{6XZ(i){PA3c)VDYDKia2~S^ogKtXTE#YHdbT zm52>40O5eaVh2z2`c`z4vd*xD^1%5@NB5iReQTaBQrD@qN^qp?wiZ@c-UMY(6p&Ib zr{?ZzQ!**o{{S!th~w$kxvJOBWVlF`RR(e8$I0v5(_&qwB1Yg_CN=NRb6Q&IHfPT6 z(lsoQ&naDoP22>G?Tt=*pGvTpd7W~z6gb3uM!z?$K^&4@J0l=?k#pt7cE-N!d(;gu zB&a4_aftp{*Pqs~X|{BxqHi>dAT9p8X>L!+0&)7E^O}YmMZBG*1Q&86^8hNNfy$2k zf0buRF}Z0oBFeGkg2!w~eTQ>aZSKs`I=t;9ODF|Y?Il1TObm3Xj2u>k;wKp-be<cs zj@mgPmfky{>E=0}>&UiVMhu-YqaSyJR`naUyx($VYp7!eG`M)Yn^?ctw~N1CymsQb z{Wf%#_{lQc${@=5m(ENvkb3Rv&BssDx<9w8*&FMbW_Ok`<y>AaW{`h&-Vwqlzd`L> zwP5+K_x``H(CVcXXugI9y>#nxV%o||jFZfd-|(YPyC&amz*RnCfZ%Q8A=~oev#+kw zMxIBB(oNV{kmZQ$muizwadIK^lrVFYj0__Ck^D#NTBvfvQ%`IA-5t$z^RL-qw1@2u z(y}Jl$&7#3H0s29{VCog@YTkPa}~a%yKdSMGcl9QbGtYM^j@6?diAeH@J62<p1E)2 zuRP_CQ^j*%5;gd5z9DLsi4v1*9B|||?3D*`_UZ@WQ%<9mH!5LT&ZJ`}xryMIm-|tI zj*-@P#!HZHw7Vrg>e1z4o|}jR{{TU){{RL4^IUVZFYeC(R}Jw_V=s+v!vTnqBOikQ z0QKqNFDycDqB!yL<)owB`kUZw^4|O`(8{1sB^m>c<y#s2YtKJxi|pI@YRy;`Ej+9} zFc5!JT<3tidwHS!JcCZt!X$|-j`KgwfKNa>el_QQIMgq7D~O*`yPE1iNfJpRZ~^B7 zJwAfEY1B^;;*Km#<0YY6!@AAhn+?^ytkOpv&|fek72S;B5<7Bh8c7Ys%(F2p&g{gK zl5%U*KW8m6$5!}(<mz@d_mM|#@~ThDs|<6uqkc8!62#G5Opk?l<SJL8&U;r}Qs<Y* z!hGDZzTfaqrgiHZKMQ`$VeP>4wJU3Pl(T0qAyPp44Dsz=QQ&Lan>p;}hE!R>j$=Gv zJ4CDM51_{trQ!`@El1%lm1TJ)<o7U399GC2ExbrFGE@#*sL8B94cQfcvO^~4oNZ<e zv<1K{J<mak=BqfU>D=^aQ<Q5;OZx05#apv?c?+%O&FE<yn~RUXCrlH`$9^-#a$0CV zYlT&JlE7ovn(ICy8&B-5FxeG#W@5~pI0O7^kh7hjzPFVzxkzE3QCm`%H9Pv8v-fK> z%}*6UExxoo$k{hUrE)Qzwb=L`$lPe<(bRzFsp-<WFBV@t{mq>2Pt9+f;~&I&S5xrz z;b79{T%38B$8H5<Qc_ft>TQQh+DRPu#0bn<)M`$Da=G^8)<wJRvY4FhJBMDxR^N}A zXV<|~{{So#Ir^HwwHURW6V*u>_6D_s=BBP;CGX4TP=)ro5&S1`sbeHuNclNqf%P<& zk#1rSZUtPhht1SHmKgov2R*(201Dc2O6<xJZpiv)_Hfj#^j`_;e{Yj(n}jJIK4&C- z)g9{R#ZMDzy1s{Qk?HMkVI=DD%Ev7DEC)^pbC0EF`~}fr)Eeq*q}-9HR1CZv=byw^ zpnPChwEqAF#OW6Bm$u_?ayMfY$7;BWS8mJm)b!@*(ybM1zjW~SxRxm;63AX*^6t;4 z`TZ*2h&3OzrMwEb*trKCb6J;IlKN8FU*5Lw&ZX2Y)<umYL5SCAIIlVwNK>sD$D!(` z<sC}9BzZ8PpT@Q=Y|Lt}CQf}Sj+K>SPZ{R6tp(NWzh<(D<(0bukUh`0y>(7+Vxbh3 z+0xH4kA}lNudqMwlUZ6Phc9&(S#CF5+V$jNpXu%IU6+J3otiZESICmJz2^L5AU4s@ zKU(R9upNIODO9U#Td~OJX-eJy07ENH(_ylNM{J6SJqKTNSC$>!`1PwI+nkbql=cBs z<lqjbxt&htZQO?m7{b&qF(+yOPpvu5{G5f1bz|S@T&}m`FgIIic=Tz1!~AQWRj;H? zY0`3LhOw(#X=-==`r*1p4^QwF$=loNekj$FDC06ndH_C1$EoxcK7pX#YnP=V&NI4M zobaR6n)FRJz(sD_c7jY$f6LA5aAf))N>T31sZun3*zfs&U54q}6q+0+*F=svwFw5< zuH<C}JwgtkRr@VI&%?2si>EhN@^h2R6YWlGt6dV^ZFIB~Tt8ME<oo{sg<sNi$t~p< zH>2$ujDsqT$c%cQO6a8rDOqp$FY{-aIkgt%^nWJ*0Lcip?(ZGmuBGHBFj=oW_AEP^ zn^3rmPa^MCWtQixR_byS(xKG#ShUA=frZWckD5c#J%w=hSF_#T6pMAm)Q20s_7Amn zPNZpQwSU*%{{RF<Dl)6@OGW*C%kwPj`o#Cs2e@qi0CpujD6J0-_+DH6w|bd);5?*w z!$0^6*6{xTg`v}z?Ygl^CIfcX<x|}Hnycau6g=gv^vr;MQ!jJ$tlCun0C`_`)BMWz zj;HSEXqQt{Tk!?9ks8|B#*Zi%SkvyO*A<7RX*YW9$-2Figa<6`&<{aVXnr0@U^n{2 zf+$z!!yFS?ekJisUnki#@%DQq<AZ|Z-`m=)w<YZ>?a_A#Q%l^V^JU)<_?`_q*|hBg zX#N>42U2=)eSPcB3`;EGrDDaKEP9jAHQDJt9{WwWp6t9~`BcUZJx+RkE3VQsJwDpS z<q;@w4qU&?f5N#VjD{+srlhoAEe#x@hK`Eo&7T|Oj!%dBXV_t#BR2t2Bm}ysMjto7 zwRi@b{{SCD=zo=cMf*vvd_EZ^6C++_SYxM_-ks~^-9P?5htSujlYa_J`_J<{n%mse zqKYfoIl`K0O(ii2MHEm0O=@xCqhmk`NktR@;3{}WH44=5jIbG`!D{S%gQH*Bt*mgH zh{Ta7jlnp>0fIj<SLN08+YLGHuCHz5f)w2&?+0l7DHB1mS!Kjv<;g6pM&q8fnc@!z z+3Fq~zqNub(@O)0)<(*g#~JJ274V$@0KC;{bK9J0yS+<#2aEJgeGuuF(Mrzk8lfOD zD~ymj<B|E-i+J~6mtVNOhS{#Cv9e)1qo66~7C*b=A=KvsABJnrrqS+nEoj+a$t|?& z8#H8%gtjs;4<LRS$j4qPhM_tpj|6bt$tBB@XF^E|F~$yYj(HxUz3O;a^K(Jm;FYXo z+)WX`Go)c+GH>$G;{!bhr9*cyp5dPgrMoWxeJVE;+}Es<IhgIlQ%T2KOy}C0)~do4 z&U#aG=}Y`Rl-$q&I?{p3p@*dh>r4gCcpOq1N+<yhIbD@b)+6X?hOAh|vfKoBSiuDE zW5B>Bm?d{&-kW`Qr%fE1Rn5h%&ca?uq}XxSs68+%-}L_gjrvxXV{_&Gx$Yq%G>PBL z^R~yaPVC?>8;?>+ubcN^uuN?yt~tdvnszp)UBOu&XZ#xRwa1A(D%X0n=x&wVgaaF5 zTwtC_$GPfrR=yhiK#RtDHO1_aM`-ZM%E)F@@{U3E#t7v1&3ujFpAg+?5MHEL4v|R- zQ5a?cobV0-9FDp6rs!Jz){SR&wo*uv-66MfIy6o9m+`>q??*AewuO^_GqL!e;)y&{ z;%ke{k!}x{0{TV?+xVWR>s%el-yl)Zc|Tg(NCm(S%*xC%Ml*_JdNLkO!O1y4C;tFm zuiC?Fo^+dT?8A|^U}qT}dsVw%oioQ?D@sofET=1XjyuQy0N1N<=xZEmt8%;yeZx*Q zVSUMa86jzL?OPw%=e5j#_sORJ0AwHe_>1ue{3~B+9^@HddsR!hQ>HS)xBmdLTmF6_ z{eQx-wYx8}6)h}o&Dk@>XI6ggkx<`Jk;nuafF~Zb%Z4fxkT&BrU(98IDmDQbKBl0R zn<pprtff20l(ijhBR)^1NGDVqQAP;pdFHB<nkrVC7S+o}RXl}NIKTi@48$DJ7ct~^ zJm#6y(gEtk9OjzNNzFEjb{OJMG@nX9XrKudwj7R5e=3^Na|@R_#yV%MRivhyPUuY< zbLt*s45KRDJ&jOFS~uK;gX>Y}H29Y+Go88Ss#0=yVo5EIgU!TXwnj#1FYJFKQCg>& z0<>tvbR;i&p{Pjl+!f?tepS}$S~oCJyRiQNw_--l;xoYXtBWeK2Fi?#<2@@70(k(R zTGWpoM0qFX7y}v2Wlp1axpIm_%<;OgSyXn;YP8aAQHBI(^QE|fRYn&%^h&KPY5*ex zJ+V~g><J=5;k`-6r9|KB2YQTi*N;I;poSdhnx1>2tzyFPC<86}({fHdsp@mzy>h+H zIu6xW_rdz^ML(reQ^%pJm&_y(fG|~dnxaoNbNHB+!<oKp(q@~QJJi^s&AX7*crZBW zS!hn|)Z#->&=;_Ws;}ivju@n3F%ZZ1uq!{!I5^EkyuL?0b3#lFhPQ?@_f%(&DXV1w zA%Ns^IjWX`s*&%Sx~=I_{Dv&gvNk@P)^*WI2za>2=BK$&GbC`|&ZskP05=7As)`29 z3rRDG)kq3C%~&R=Th0*(0OqGzSdW+iKq5QIii{4Xv$2}Dcb8#`ueTLTVp$z2QYbxX z3910^O09}*6vR6ir0qs&Tde>#mWlx4fD(!+X#pj?hACCX2~(UN^)=1Q&ofBeZaKiJ zwI=@nln-oFxU1Zx^s#119HJe-oMQs3?_fCqk8#qPj#TA%Jt+w%tqL|9cO<nqlb)5U zsay-4UjAgrNiH4N=vd;ip-sx?o;uaEB1`FZ$(3mi8ND;q)J-eg(wls|zJKOO#e!!9 z4_s14*shDjGg{ke@mOiMZ*dcqb_EoC-n<fP6|AI>8`ecF$oY7#YIT(3%=Ed{QiG>1 zRPWq?G0CK-+*`|QCdtu}h{y~vkx^F4&0QI^)3YXwkZP1{P55!g&sLGJYORtMyNv$; zwUbRNCdiiQ8akB2U}B(_;fKGiP_>!T<}wEI4ir|7p&IN`S1X=<t0VnH%%_|j0zaKa z6r3z{VjGmy<o^J#_$N_1eW62R_n>pf-v0pW)=Mc9F3t{d#cSI`AN(U$`h;%m-f@O- z-0f^-Nc!{nRTr2w$S4N!&mN<_b5p!qOQF+=prGR}=|8-+;tRhf;xxu~rYiz)Tjy@4 znzgD;HQb8w<K^_Kb4j=kIUH9;9-kx<>T=@h`$~=U=r%8#t_D8}mQlAIdG^IN58f|9 zkyowbNw2NpnBXwlYe~zM+{O^*S{a0}e4*rXNX*BQ2jN$>OCw{bNpHJ&7%Fkp9+gT} z+ri`7sxneZq9+MOxW0sB^T?r&C?n=4u%j8yYeOPf)9#+#>(G83rkfs@s@`8q<<A>! zj1z?*so?%1y#ClbxQ%+Z0D9L;tbLbFvWCM=ytlqr#lg8qS~(Sp;Kp|jK*!R!akQOi zdnE01d^Kf8qMKK-og}_^1k4lX$sC=&^=DR!&+Lj2quenEd}o91eLj_l%EJKU5_u%` zrCs7YDF9~~&1>wnO+Adc?%m4^rO!4@5wy5c%s~62)~a6GNoy!cMhO|kbs7!D)HWCP z(WGzoh_)AI#?f53jLUQpncF8Dv6EFwa&6t{h?b7UZZbRjQl7kwRja!Rt->>HWp*g6 zr|%I_wW}<9+l9_K<0rjrr5CDV)P&R5V{HVL>`p0$il7!n0k95z#WxtDlnkC|0XU;H zW72>q5oQ?!e2?Fzd(_(+KfDgy@O>(gA26%7cR61!Q|uW309w&Yxh)s%u6F#go;w<} z=aK1D3_EkT?w;P2BocW;YviYIYU`&~+A}Ffsx9$J>_sTS0FH;H1lK{BjkIJDRd-7p zV+!&1BaBs+8R3R#Se1qs1N5kpeFm8joG|V9)fr%y%q}ni=dDesLSc**>z`cIR?s}K zvj)Rwr>$ou+vR49MRF+BgqxW2>P=jY@cS|tV}qPk8;GY|hv<DOILpL`^Q@EQdlhS< z{m07L0ZGP2c?PM+bc6+HL2U4IPKs2SOlKpw<E2*+GDy3zjQZ3~^E;ufH#JseVUT}1 zPGurABXgajKJ}Gxg@2bg`qcJ0k()9V+sLStt#k=>A&r}NUY@knKR?gYxvOT>WSHbb zo_f@BTFRvcLl);Fn!wiRV3%s)an`Ri;hHi@$FAH}IN+5ZEf_w=wXd&axsKj4BqlSQ zH_Sl#_Z6G?<78<Uc*^V%{!m5;^%WwvYOdql5l@ax$P{(1+h}qYrZ|Ppdm7DYq)<L{ z{Odwu3=jV4tWDi32^SLIp5>HhIX;!EYJYc%!GVdHGwWNHr{$*&$(d!8I^lpI^#-X) zX3^xpK_A_3=}5YQt;5AA+k?Y){A%*BAP_+wwW^i2Ce3bUD+Xe$3csPH*lP69q{9v} zJ0D7?Br?Ow=0*KQZwkw43f+cn^`?}~Jsn?_Jog@y>e|WbgjX>SOwtH|=s!xaibL!u zuv}uG6#802MSxs^??ED>!)Jyg+Lw3-gUtbBr}1&k)xYCZExzYIwRgp*{VLn>s#e@b z_}9+E;76kOWa>Gm5&WqIJJy)dKZfoVUg9Pk@W9mGAi4s@&=Hp76^*64CG5L8mBkv) zN0+#sa%s5x$4sifVPSNaf3wP}a0%qq+Z`%*g5p_JMHm2vo8=A1LF#^$KY~^!Tb=WH zPDl%~A2LJ!?_hoFppM5*u{Z5>K#)dReEEFY9mwDW7{@)%K&YKn8FHJ^qk2kjvFEpP zmzc2|amGC>L2jP$N(PagP%4EvKmB1?cTz_S6Cv}TKXpzQ^Xh*}_MZj#cT2eNMUC0G zw`-}N&5$4iX#VCs2kTl?sHnwA=Alj2=4|uLGe&!;w_V&6jy5XicT?$D-Zg|;UPTNW z(gPS^hm4<6E7P>?S4Gn=H5Z-+nt!!0hBS<H<F`Rr(|BTE5^FPR*YTKQk-k{S;YVTl zfBLExCQe+{AG(KDg5^pJSGml5GrMg+Qn|QQl$MLiUoVW3dS~#ij&BS>sq3F?)uj-% z!>Eo1Y_aXg{{R~6^xZzoOi;Gypn-zK5A%xTg~H3@D_2JjzH)*=&+%6r@o;q|QkvG= zoe+mKAwqj6`J4VW)6>G96Z7<<BDiN*{J^6s4_tazk2La5?IhDlBT4s|l3Bi_*S36P zKk-MY#(#Xg8u18Z%IpJZ&o~^KthTjE)LiO2yPWmXjaL=1EKJKJOA_q=0C$!(rm_{3 zJ7l&n0r`*9{N}2L@*)^34;>emZ>>iZphuU=A?uD^zt8y<)g{fTr8)aOE?v|vi$qnN zhuhH(S&zB@0QLSAkf6sl2_X6*$u(|Ls-#Pp(DSi*c0To0uq0(5rg|#!#MRS^O322n z+@;LP7aP@i+*l7TKBwwx!%ouy9@SOO1aa}ts~Vw|i~!ljbG163sjCpbn<thyR_A1X zzUG{x)FApAa<`gMGO{WTV_%ju_ha6mVv=yB<s+`qwFf`Sppo!)kgeDvoGSa8uGbhG z{_`9RoG9ngvy2(MswDL^1b%U2!v)FSN65cQv1E%qjBLN_41{N9;!k7k`PM4mGPz=_ zA;%lX%07a&w1-dZ#3UXaw|vJL?Z;2fiji@R%v_^6B!#`Y{?9KQP)3pjWD+pk&6CS0 zJwCK}dil(61Poy=xOY$WjyhvKhp_8UYgk2v&D%cq3P56W)1_tjYUtYPu{wZr^5CAv zp(Q$Uw?k;TLabvgnbPXx?3zS@fK}P|ag3i+?@aL1{h(NdPzsHw-xS{#OkYQKI0U#I zDgOWu@hq^XbYed-T(Y;-+|k!fzpV0kBi_6(0bOd`pHuv+!8J?OzWAf7+(6leYx#l^ z##4C%^sl8nF=%DhZPhqA&VLH|MW*`?i>3P@GAdp=sP4<Wf%%HdF1)Ep5saEu>Hh#- zQR$uw8+<^@Mn86coq2DI?nUQ{wON-TTIN7A*8zzBmG3?a@Z7f^Bvv?5VI+WaiuvnQ zm(A60WhW*{-SS2=mM5iamq|*bq`M=ZFq&>L^*x97eefvpO}*{jqGnAhDMB)!+vl(u zE4w)Z2b>Q5MQi@kT6LzGqWEIZPl6j;WR`G|#x|;qC;81E1AJxuk>D%MI>y~i>fJPk zNBf{MjB(ujrnx_jhwZRwm(2ImO6<ujw=4)}#z;&K6m}Hk*VfYW{S4|+<)zHLj!)sv zqLv;ei_enjEvH7eoLaWo9yb^&4{X;rb+#Mpm5wKkRz_7vIb;CzKH{zDdW5=Gr49AI z1hGC>BfVys9%<x~RxKm3W@FTna!q<^K4$tH(~@zD>#5dS&GyY6%T6+9%)AH7&p-zs zhFI58;H@%y`{^D6(F<F02}6SCY5sM^X);A^4V~P;4=(utZsBpy;nKYe!`Bn&vRx&# zVtD0UrIeG9200^u4S86$;Q3cy>7L#K3ZxvK{{Zk6j1Pz;u7#pPv4xKW@IToY$KhN> zxc&S@;}zL_OO+#SI>{vr&>`LJzytB(xcgulbR>L(rFP-;rFZ6eb+xShPKU=y9CsR7 zg@)5;hyXnRt*?g>3G8mmo;<;{dt$kd6JDmJe{5q-s|~tDa&x$h^X;1IJ{!vdNg^2; zXJNva7F1>aAacWL_xjY;g{deS(08obUCbXHD2v2T8gu)l9erys21wdc4%I?Q>`iU_ zLl(>8!}dwxo;HbCm_Z7J{{RjNI3uCwtzxMwWps=4?o-I<2dz4P!-<pLPt>cY{_TN& z)umz@eO5wt?|Djtj=cI-TqKCjKZ~FzwR9d9b^h3qq-BXV<dQmMWS^+VN_dHK#!~Vo zlDkOySN3<8HW%vS43We5@YT@#bePBBCV0kOZ*7=yo_xVrzXiOlH%ECcP}{8V#Uhu- zJRYBc6)(okPTJ?dw=hE?GBv~-PCX@BoIPlJ1h##?XLWj0mLW+l-=XAp47YbK%_K=7 zV$1=@B=bs;D3UnYfb$yxy4Rq|{h2krtSMz3#nh3m5k_!G9c#}%D`@cO?QJYdMZ^JR zkVAlq7_b2MKX$z6R8=R+^gVYkXYnJe(L6eDALo?A0-P5km->H=dR~X7+3EJ-En`qS zxA8aB*CpX-W4Y7qrAzCg8Z|f$2poIYL8f@C!Wu-RYCa&fX><1-z$^G*WY?RB#W+gQ z_qDZ>W_YRct(mi+sq=MDl;GUt5I@;k*;{f~Jw3^+`22t28;RkD$6K|QX_z7)Y?fkh z0IxamPmT4D73(^VgW#EDw`&>17Zb+8S@|KDH}E2n_p1kq##XGT!U<Vw^=R__tmOV1 zdY+{;UR**(IZX9Yn##VoiWrR7Wn+nX+Q<Cl)z1a#QfOK%v0hxyJl6vtTujqDF;Jv} zz#X{+fsXa(I+XVp*Nyg>3o@}#i}zOtwRd9h^{~;8wQF9+_zIE4%1J$mwXI@ZNO^Gv zirn_@-(T>nejD)1>TG3|yo*Trd0VH@d)AkR{4?jEy1y+Qu=}GQG>6e^w~=kFp^dM$ zL2iThR^$1R+}0GSx2(20u^6RIdpcYH0AG76T3ANbOK)u%wbN6Rfrp#i)^@dXaXhQ1 zT1VwZt+&gJ<&SQ0Q%ikwt6T^zq4`PqLkyAcj)Uvh-nt7rJ5LYVG}E{<5zBYaqSsv- zPPOf%dcV<sUZ<Sr>|N%sE{pYK8Xk)llM$Zbqc;I^S-$K;{A({))>`FW3lh;==l7D6 zhLisQtx#(ktKQ+J{oT5sk}!E={Y^hk(gv#GM9!$A=knL?`)9HJewEcJt146wlsSKW zh<g4;L^hW?l)?*G<l4s!%0HRtKb>|Kb~<N;tjzC^G>jHwmOjJUq|`MVFAZ3TV#~>n z6t+*}Tyn*K;w$~qmN`5xI8jpVLD_rL{1CnTO1!<;eq!p{#qPgwA%R(9&J`EEZCcn^ zcv{2l`n!gdkCHGsC(sI>;!Qun)?4HPd*>f23}^oUtSihl?N0Amy*C$bSf9GRlzIVK z)N2jz)z*Exdj9~CtvEXV>@<hwUhx--uC?2rn0at1`65%m{{RzCu(G;~N4nL~5sV+| z)%&;8zyAQQ(wpHO0rj0kK&%xIZ;<*ff1P?Lg4NmKhs%&ec40uk#}&g?b>zLXgPLbe zX;Yw;D1S)tn7mW*C9NjBO(NcFIF1?ov&ZDswcy_!YkIGU;L-JcIxAr!7HLyB1a{}W zdXdxPmfTxRLc&`jGI%HHRIWTfWu=w4FhY&b?*fAV072<b4}w_Va-9xa_2`V=GLzAz z_Z}zvLzR=kvB_wwv+cxfEIwbA@;?LHt$f2{#nAd1`h)g}^3%aBZj;8+LwmWlk_ZJ` z9CgMUy?ni6{{SCL^fm8gFyyBw_GgDx-(ymWT1xf~aVaTHB{2mhEi{?{Ml{M&X}O>R zicHdGfF#XL8LF<~KJmp|w2ye*k&I_K;-#=h*ZQ`!G*1RwUZl`@p<qnSAW%ubIX{`J zI**KWy*kpyP3Bg)Fzk(f_*ptOI*>c#IIo|4VXDV)V7KusQo$0)w+R?U--0kU_H^ex zLBQ)=?~FCwKT^<iXdt+nORHG$<(Gv750HBdW757#mIhOCZ>wj}R&?T|@3Bwf9-(P} zuHDIPZt+NNW_OUXklAuTP&mNnoSgNpT1hty@w=x3sKrL-Cm+h2yKfk;qoqw#ii+rX zlAklUt&H$7Ok<h@a%e0MP7P{6&#fr!Li3!{k4j)BbC0bxhAG>9=@@W1KJ);Yr0Ypb zoKpY;Iii<!A)p4DN_8QoAt<7XU?{74b-$l5+(rTDd(}#E@q%-V)V+p|))l}XN|qK@ zBr66gaBzAW=3u|MNJvKjXFQycD(;0W?K=gCu#@IzVbZx>J0`)M3e&e@lWik);<HSU zFZHA^OsZOiz=9pzwm|?6)oW60XAM-tc`Gq5A3IcWS(lTWnVLt)B>id$Un7rNZhBWj zk5b8FlMwA79{o9}Bsrzs$;}}-sz?S=nkjp6Ok%DuNj&;fGespa1r$+04q8enC;@4E zQc~uC6BR1RNCAFcFmc|ar^m}~JO$*?2GsYnSlGu6yz(i=NLC{mtXU;_T(pkN$EjnJ zM6bPxIm!2^>A5$clCgfm{(CrPjwfYFMi>SoHLIcCT_vk1o<O<d=QYjdrnk6wPs&dt z7|l4zp*W^>7W$p!%-iIY0mm)W)c*i$nEmI^)Ml}CshU8+i6CQxS{dTCQf(Qyq{|;@ zW7lc=sH%FKZ?lssne_@PT7yvH&PrgCxutKQ*w5hTuA<!9v6e@1=crYM5m@}acXEFt zSQqxu!tgvXy2R<Y7CGE|;<WUAZo<<?xU{;`)@zyFSM4G8l#inTG53hiKTazrQl2U0 z-ESY1zGE;#+1jU~=e{e3lqRP6uWOyP9X>_Ny@;+AObEm_{NSANilnMt!mddt0|K<7 zx{5eVHvyU_WMUL^!Q_5Mv&6n!`{5*Dou|018+^?XWy+n%*kJaegF&hwS_4r@NC&Hd zoaT}@3i&l1C=~1k_-+vAsKC!kl`bRQjPvxW5p;j|4PMfSZBjOJ!vr6@p7oUN6xpN~ zGqE+;mJmG1T>%-(DPQMV56d4?eQWe@L2&vk3daGwya^#ABg;<J@rTE#AwD#_y0)85 zWR~J&SnWs-p=4o~8Sm5{;=KGOJ=Un8<8Pn*14l*jxpqAA*8`oWKD8^%7p;2(T3+dp z>FKZdS3!?;z5!)G=KzhxNIdjEm3eoI4YazY<5^on3_@+A$s=IkZ~y~}*AI*K^R>Qr zOxCUy)2GjW!I=*zZi1pMOr)<u#}K)srN<Nj?0BF;QCID(Jll4W<yejjD1J8p3FGp` zQdI}3#U&*4L*-%t>q=<PG#;lNDS(u##U%jZfdv#%K*EYjC=j`#l8OL!Y0;7o2AN7a z(8i5VFLEPB<>czvKX$hCZE5uRRlLo!uibI;aaaOmfUl6*Bc0tdPKkWh+<4Dw;;BhZ zT6-Pvs_C@hxv}CqiEi!{t~rnny~sYbjux#-&iLL*+AuTeQIw@gdpIbST27?g*&Z+n z_NX-0{{ToGI#kl}6O-vs>AOR*ZoLI<_8Eit;$OPBjo@_2+&fl9%&$9%B_)`Q5=W(4 zyiY29<w--%ed?_4j>ljiXFQs8Vv^8xYDuRS&Wpo$VYGct!bB{$5pF~2BJvOPu6br< zm`D#G(%VQCPDu@rL8odEh%N|LS4IKVm!SrnV52wYZ(6jgVX435)8zGEcBP4D^Pz0@ z>r_j2rAc<F00W-pn8zlrG3C)5Q=c}hp4v^1oCDh#r%N$r&I+(OUV2qfaB6VD^FxZg z!ZUW|EJt$lY7t8y-s)6i9dTFgHZ#vA;4;}G>zd27c8YY7atg3uM`9|>*G&7`Tzu`g z`_>bOytz)AbN5@3MYugF(GWStGI5jIl0l8EIRs{(Z-VC|f!J2<=yAxJ-LO}IyyFKL z!L1aDE$$s&D5Z0ZJCXvQQONyjlq(SoB#_6?9A`iMYS>gantXQ=l_nK272ASTatX&? zy?v{atgOtOtnOR!$A~TbO{iPl+vv6$N0$P`(OAc^9fp2#J%7TnwCip0{KpwTDgF|D z4I;*O9r;1i6bpkBDvm&G)4kU$O<_CE?AnE5j>h>*ZEq})2VxIFSQ=FJ$$7f!LlE7$ zTo1Uzq2jJZB+^H6(f<GtAd)kHYbR5(p3-R2RAA1V#~nfMMHnq$-sY8^<#O(&tE{&c z#ZQ_bou=KlZs15b$LCOLdXwJ41S=4Z-X?8_9GsqVYcQ$Y7X%)h(uZII5=w)dXOHv6 zbgEERX>4ZZ*p!*4G5L-uz3W0lttB{&QcwUmqKYU2ZK0HO%{DReztW|KFmQ5BJ8l63 zxW;Ohq?1|P?TuDaahxl9fAy;Li=LH|%ZNZf-u{)LYQ$SdyQVlkl+^ijVrt}(OXTo* zL)+4>E*ND<BB{tdKQCHHW!Qh&uBvryqu9<-xp*JWtIrv72spvbPNXo%#T!q(b!gF+ z#(eNQ^?$<^0wmEk0c>Zn#cP%I>r5-`DjhX@mCw-6V<89cAEVWFa8=cT&N$q86{OKO zRmb8gDJ_G2;e+25oS@25Moa@^62Rja%~&WxF5=n7Ksl=oW;bMDeib-PpoUO=>p0SD zLAe<&g|UvkDqDMZ89b|Yt40((0}57?2WTt@)Z&-2SGylG=vP)&VzP~nJ$MyipxnuZ z84HSnbrP=P`DNhqoKs_Bp%z9s>{RxwCru_&Ws;7a9P!r%ogzx$738VPoYRCVSBs9m zxTzK?03jtttSplq$N9_$Z2c)>xCOx5%|+zR=!fyB);W>#W9jH>`O!8-na*RE=xY<4 zjDuT>a&wNosj~yO<v_>K@lniD(8jlK->25MZM%xC<*NW$0=M$37QOwd;`Js*Rpx%x zE<qld6}4i2(IWo<bW%$SOqg{BoeBuzR2+pEt0lXlQgKU~t9{L+pGwcPzj)O|zc}^o zDclmexurF#OV29Zn02bMd61vpjm!Pz{<U%$l2Mr0A5rO29%|z^9fQ=Orz0wIC^anh zgLddn#`NrJxsao6nRXvu#=0X}OGZyGVZ>5Xm}j~C>AgGVwGb1I)YF61Q;DR{&5Rl> zGo<lv{C!*gHAdY2>{YK7o!3wguS&_>$&4|to~FITdN1N;nwoxiHI?VcKZyQR<++gX z2tS2BQKnw-{P012Jd?sEWlxb<{Gj@g-|1DnLw6<h=wZ2vAXav7lz%`ys^^Au>r4B) zroX)VJaHgq=r?n~70PO*zS}fyD>6pqSB;|?>yFezmQ@;Cqgt)TlvCNAZ-G_`?(SoK zLE%84xr}2x^ai_ii%Ny3k`3-Q;ZO{_wmBr4@$Ct&qP$6M6p|QnN6Vbosp(R9hWZ&) zOkr7DFCwq+0Q3j=N9A0&$WAimQb~E8xLMMvMMg>7@^rMG>fw?!B<{f{v9A9B!}@*p zm8F%qylXgE^NfdYU^@Q*D(P)Bt8FhE6LhDmu*s~?6V9_rixURgMsd(qb#dx#LJH{4 zjR!|t>dxE4J{YmK5lYHrEr!buPJL?E#7$Z)7f82tiy*$mRl<SC=~`Mz{{Td{KkP5^ zu217MBI8J~R(+~2GTi$b^C(o6d0wl&r$!|zQM4EJbImVx+r3^uB$bjZ;38xX{=Ht% zAo8`1IPVXc)b-9e6^(tSMQ^CQR@X5#xO3%}eC1C=T^EP#Jh^oF_p2w`!Uh0<PaJby z)tnSP&6&@JZc|QO(bs%rIPikwKkHV!THNf9a}gNN-u@%>uW9(jk>NBW1o^98B&^84 zKGBoQ9!UCCWeWIqy#D~m#}%b!Js)5!PnVwOmNYAsjkf%NpS(s`)C6Hc0~zk!YFOJ5 zk~rg*Zoh?ktr#bEPR+I%Kt5jXIpx}^vjNlP{Yj}w45W@Y##^bX@q!y8fu6n1IH66F zFy0p&04jTYD8^5w7peN1tj+TfGgI2ZEYl=$D?7Q`v7jZG`eKPK2M4IhBrbOF6dVN| zDY3&O&Qc_2Z1e*)*Z8C1=C|Q}3s2Sch>|F8NeL2?%^omuCm2)DPQ7ZTmf>NA6+qA4 z$9l$flw){mZAz1?CZx@e3V1HuUPU&#`{G}nqC<gzGlGA^^R8aX8K$;Hh#iGXo^jH> zA6$^dp<L~aq<JDSs-VdwhB&I95A=1DPEmVm@|Di@K%}V8L(d<Ld6CA=Qk7Zjsob!1 z<y}&amPaVin|%u63xs8S7u(Q|n67_JAL-WMoDJY-@vlwsCWEQZ1n|J7D_4_q$GEl+ zxb?0dRMJk1HLflrl1Cfxq!vOBIRmHT>0Ow4-kWcBa@KM7%a>HFj*rJ0Yg>4IUH)9k zpl81ZvwQ<Ni&TFtNdN`_pTf8PDR`o7Z^QaMvqcOsTZO<x0I}Q2C%LX?!1{z5exzi% zz(-xB+oJQy$E{^f5_o49sk@&pF6jEV!qS)?>gm+4kpn({gFn)~Fhb2HNhFh!bIpB` zABucEbKyN@?KL<HwVc5~IAVOWj00aA!&8>Q!ZJ_D=f%m&t=c_T!}l85ypDF%uPusz z6>X+vVm%25HRU%EmDD9xV%sEavG2`&J@EU%Gio|Lg~Z_7K+s0poet*tN8KKt{VV66 z8C%=hc*k9W%(1tM`e@_NU7^SnU@LP}oJWqgG*?|uV*Q)^F>QaO>N=I~=TSPuu?_=m z896_VNBd4l@&5o|+Q1QP!H|Slz}mwDuci-AYU+Lg%oE|dgM|M8N|(Rh>CJE-v__@p zUuiahw+Plrk3OUi{g$~?bIdR2`UPmUO>5Zk>~zPqMh0bnsi|Y>#Ygug&tCYieH=1I zo%CTeSRIrDB&=1l&k8@OuUgTrl0OFBh*4T$ZM8@}4l$lSzSZN`Q?qI50XdCavi>8V z<z3aK(*FR$HFUCPcaWeA_354~&BasZ<WIXjTolr$R^5@o>bEjo-bp3F+;<blL-eQF z{LZ~jcpq9diA2{3Rybn$ar>kS59QvH$+pa+*PPdUm7`<Mrv2-Xz+Oweo3Y6mCy&Ou zO$$%EmN=5lTg@YO=EzZw<oXJ$;OiJKwRsj&$6~R@IL0teeXG!>Sv)4tUaV<y*KtUQ zBw>iu@TVBYG1PmJo-2zHii~S2T~4eu7s#c3&oH?=rHz<vmDhWogCz9&R)(At$+?aG zR+1J)<2?1MD-%tt`9+Ils*iq{uIoY4&Y!49FOV)}RNdwD;P5}0r(UdMX!T{#rsUgZ z7M<Yx>x~opO-Ac*Lhc>2+PgVC6xx=dX7(1+Tfwzb;F1@39C7YOYJZ3N75q1sAKEEA zT5Ny4irl{kZzFKyILD=Tw_YQP-reovRFu008vrtA(>|QnpE~s%+H${R)1iclh3(j$ zNiKAi5d}q#Fr#Ze0w@E4_*WO>KM>2QX-^zU{*4{PLRksNJGd3u+G&z%dnMI~jRz@i zr-A<W_*WzGR?F=#VPU4jHPzHilB`gsK@8hK3Oe*WR~;KF(TaCFWi*xA)bBhkYZZo{ z8(U$#<qzf_O9DN8E6aW;YB6daCDe50HsgqunZXR9Mo9;u`^T;;(>x=fU%_UMVGx#u z6_<Bb8T;II!2+=LFNPYnrQ<nuSHH80%21+M!7S{XKxOCv$Q^5!3ttaQ5h|5ub<@`C zv4$#LBKOhy8J-XEq#hcxNbX>k2xI2k&xDfev;aqJXCxf@SDSc?X0PH4yXCnvwZj+5 z1bKNpFMRgqzQlrMHZdd#0^0=wFDQ+wqo5e$6<+I9iqa1=AgLu0_W-QmXKsCKo{n={ z+drydoUhsU^-uFSYQJcbsk!rS!T<<C_r9+SYH6<>W=%fkRny{mt;dqvWt~?ss5t6d zAC-E?!=D6M_!;HByJoxA?o=5BgcXOm$REX@t!;S6$G`B8=_`7chD)8Sk;3u?#s*k| zGxLA-s}kp0xWBqfEe<n1<e+7`i1X`?{@<N`frFt65O|Djl&r3|w)RW<)cK5VCliRA zXy6xpdMj^t*Yfoy_<^fQHmjvuu%1Wrmgu6U<vSQ{40DmsrF9-1@WR_{pHPT2&+{d? z82nq%dsF-w;P~__i7#i9?YhK+_m>hK!Y`+(uAb%E7)VxZw<X;3^{)dl#KRF&o#>Xx z_Aqqb7MHPuyL!28cK-lEuAzFiM?Wl=IKckwW6%o9u+#2!d%|6bJz3kGzvuL;)*4ir zgyf8*DtUGv=bF&eHCQybe#fRFw79`NLi?XnT-2z#tJ+r5KcDs7>2*Dy?&zz_@+FpA zFAm#zaW9#0<EQaA*U(ofs_G2xXS{v#AGsJjEmpX@V>1MAI^mDb=a99#p!j7;Tg_B- z`Gjx$+x#nMQgz?6q^y_Rhsz8y=cw-|_p?7i&~CMPzSVZ`6}Rw_!C~mWgWk3^&k$Q_ z5Q`lp5XuJMlcN6s_3J;xz9?x}*l7Us^0b4~+uEb>&xYRRyxED{)trvMO1L(^b%)ix z-}w_Lb1!X)(R4*!A4a;>uFSKsW+yDiqSryH>b9O6xBDiZsV(6jF+CI9{{V$o@fV39 z(_=bzjknALYs$SB*n8Iyq_YyoEKW-knx`mXmn}VI-?yQ0r#wUFsib-@^BH<3yqTq* zMv_-KEXOq6KSg_&V#R_1%Yj=ub&r^aX~~WS<depK`qcaD@icx-xbp45#s?d}`rg&% zRbJKd$A0HhllWp_-)lokWQ$FWyr|nRApS3*@Adq#O|kfaqC(hvhPQs>?wXeHW)j|4 z?(>4!;<?L>9?s?2o+Qcdiuw$rA1n<gMp06a;&EmfI>++Qo>6h^&gS37`XrM_Dr#_V zQVQ;rR(Fp)eywO%P0jVgF4bi?ME9;vKM&f(!(?f@u6By(;?OL#J5|*6(Y*cZdpvU? z^<nAl{{ZT*Rpzmh{oHw4>%G4MVU_y`sMM2}t*-uioL9#yD6TvSYkMjG083VgNC@Qq z?Dh8;ubVAKFR|!NeJ}BWrMdWB_ScDEo@vyw5O5UkBOLl;74uc9pY04Dxj)vtT$&Sw zIjO(2&o-rRYx2|XG-)ZsVwz2RXE27EO$=%_G{A9cV>KZ(+|U7<Owv+jfE1c^YnBwT zL|oR!nQaQOdE6p_o@ym40gHBufl^$8{PgH5zn)Yw!dX!E&sw{#T&2{YM4%#`4|>f} zv{YXEjT>6FuG)dKcgwsnDmzFsxPpDp7{KjRA)9M4+J`Dv=1@mGXX{YB#zjy-0Y(M} zDT~nJvy2o~k*w-D%bAXE_IaqYlS{i4);!jU8HNYrNlLjr0W>(lrU0iO;YRK$xTT;4 zZi0^3rw_Z%DV@y#CweLA$>y3yIiNxcT1s&_6abjbHkOXGz-=uAVv>LqQ`NE5WEyQ0 z43ac=ZLxyH_5-bUi>MUSU|C@n?4^6~Mk|cBo#AlEc?UIq7i~&(`%LT^PC4iDtfrgv z2Wx6{@Z7t$*Ht64sr%merfPQ|XSHV;57Bt<Qe4cCMy^5Fi_QgN+-kXsU*p_xpp0O9 z*DPY&y@ZQ8J>-`m(n3q91T|OGui`tR2zLyr1b?4;%(IqRCfbuSbDV>YYP8PJD;Hv0 zJd<4t8ij^Sw>bwnre>F|C2WQS=8&3NYf}KFq!lf!LVJkfG9-Ru1rH(GtDfZc6bOc$ zw2%ntoKi3#;P7eN&cLGs`p^SuD58KD6qK}-018?uGe8SUPC(+DOF#gmrOhP(Dd&n@ zQ)tZqTY})l9@zf#bv3hUrZ|9Ol1^&^+~Q;;-~*Ctqf#(ED&R!NgHUP*{SE&B3Yb-W zME?MyK0T{a5sTBD{VA$GVaV%GBagaiqiB$PtU2y#S!1W_ze<nOik4xLK9vSe?25)) znk|Pg!JsZ_$O|{k*k+yeskp`}4E{AL#H8{#rbo#?TClX67=qOgtw$s%hmGHzSyO>k z=TciBkx4gjXh_jcKrP6}H2C3o<PRLAFz-!;NG7KZg`u!VrRe_v9K3O&>9N|!eQi8( zvamD28MpvrIUHbDoA`%YpI+21FRx;RUEEwtaV)5$7iVmd*P!iGZVM7OAHY;BFhxx| zFpjAeDw29Lp|$aDqit&}D*OD%OeLiyh#d6KP7Prh$$-XHPG5O!bNSRv2<kCKRB1vj zxUIr<>QqTd-oR-nqZOf$wb6Kf{{Tw2@b0^7sU+=lJW6duZG|!e!EQSO-(1%@PRGld ziNQ@-l^I#w=<oH*dGtLp(j}MAwuPc{<PdVqMmYDbb3J|OC}D$2ohZ{*iBp@Muh2?f z)RdyMGYn)=ijp+k*Z>-CFvN8AqT?G!N+_TNqLlUGl7R&jQA`D;q!dyaAsYxiK^3QL zRwz}^ddl2L0QRRejLNH%fmzNR%EpkNE4i%KDZDRVOjMsS{t!PJsb;E4M!-4EXd*B? zS0wEhWOmcM)!9P)V%+xTt6HIGCM(on)%{s^+Go{;W?9T(lpo<dF!dF^Hsw!43aLh- zOncmxxAItL2O+AhT)(zxpCEjV%HyFGPPK)ayi96yCus@}DeUhmfJtD3k6}^MgBAOv zgNz(iGGh948$~EMq^3xe@Nfs#nM*(-#4b)i1M#FN#lB;a-lC1RHbq%hVEh1k@M<2L z8pWop!b9dn2p9pek7|KVAA(1{S(LAqcJY&vzLi)~j>DSNm5O<M9JX?6TSK^#=ISeX zSnVSuvB2ZCVy{}?(rlr<kNs##7qA#Vg=2{2Q1^^w6wwXU%cq$m65)o?k-+yAk2Ix- zIVTm>Yqqv<TbQiO(w{9_Mk#=Y(?6YZOg54YEHjLw%#bMA7j3l*q=rc2U{J0IVVn<Y zoa=WIGPLO%vP1TDVljp1{A(y=9uMVGB)bup0EQjMO5M|sV!4~#mho7sJ8c`7k%3iO zf@vz!vL&#hm5zDFDFrYW9`uw@IiLik=}K1un?Q-7mtzc!jDuQpTacb)V=8$fvA`R- z>0JeksPSw@X4*0Hccj}}4%agX%7NQC2DEMCKV?9AfrC-r+U+Jt#zE@7w98riurhNQ z{VBhBXe(HJ&*Ug4J9CevIvaMmxs4)V42K@ZnW|vh0y*ZN;n@h)Y}c`neiWSCV#d|Y zk)Yqh$_V5hihstcP9E9zPK_I5I3m3TSOu<s#Hb|-{nn!kjj7GcEsbj7Vx!ECZ_Go{ zhv!CecwtD<ub`&+DfF*T+rGyaA|%J9AsH0UAogNE8fTQ_qm%WjRhLvQLR@35G>T~w z5!4^TlP)_0Puj#eSfxAWn9&EwY+{tN0fL{Arsw^ZAB7yzVP-(qm^-6lq;&699vPEx z01(}={HsH7?nwI68bGU)lk^#^<p{EqWd&zJwS!~O(x`Qm9mSBJTDBP_=nj3wLea7| zRQ~{htS3n|p=w0pLOK)q)bYy4_>ELa6#E|F)R9QOW)FJflo}|u?Y1^<;AWun&!GJ3 zX^;9l{{XscF<hQF=~96r@0*eaI-0R>!K}ESGN;zHE&7_cyFE#gEUhh9hUFecjGQ<) z!S7ZpYmm_e_xyUv^sETa-TIp8k33Zyy*(W6kL6*{wF8T`hHxz$t+9w9k0P&IY6P9L z4ulQDs7)M^NZY~W=QRs`=K4{pF3FuRaLbiB9-^4QFu@rWS4=<XY53KKkyVvg;GUI6 zWoa$iK43W?-8CA>80}%Py$=<7t6dnt#HvU=2pua}RB;PjZZp!8k=m+>#BG2}uejt@ znA|$$O?5^}TN2fc&lEmyA;2d9=M|7xo}iDyy#(pX7?=5v(wR1&ZWD2h57k9{!o<c4 zm80BvIX!N0V;$I2!zk!i^RDV07SePKQ5QalDd)phjhwo!2l?+w<Ebsf>Qy=o_%q_2 zBf{FwrFE+5&3P-tqS@60Y+z3VJqhEQJX>S`012OpuRK9<VwMq10WhyU1~(p@nz^Ov zmbM_30h>K>S1uZR<$G;D87Fm8U1D5_*Za(RDEBqVS00qPsV#e(eO5M7tI(<8dp$f` zypcoqTWBqe*5#eqxVmd=C0Q94bK!!dOa13#!jE%aeQa;1QD<vEksCf<LU0Gv`c{Uu z;cL4%puMn}64DK-_nvp&kn{3@KJXosoO;(iSSpkK?DsBMg+(q%mpn=KtKDHV_%7lA zFj*#5Wr=-xAQm3@=eB7)Md7=hGSVxk+s(1!Hk>#i<m=FM<FF?lwaJ(*=V?R{Tu9Lz z$uxuw+4iqQxbXe7Uk~jr?Vfj+%q<{i${@h#JAG<u+K(zv@G$0-mpq?iv(sA^R;T@i z{#DQXQLvj)u&|B@eBHC|Vn@oO9ZBd1*0*%S{{Rx}PX7SAU*s#C__VW0qgo^~ynCkk zikxI%))+3j^*b?ZM}O;6&$X#6ty<pJQkRzq`Al%B?VhYZN}Ef0W!1Fe>`J7|fH(`t z=sOCNMDRJ6W&Wjbwl<s<n&6OS{{XIQ)z8z9df8R7`(=zeR5uJ(buMHfpBW!B1sFm8 z&Pnw(*HUmxmY%0R8{=AaH9jyfJ`s$9NAFi8SBNoF%t#j<@=y6S?f(E6<JsX*J9(>K zJ7-}n{ISDl6tmot2bPP%jt1g;44isbR!;3h=Klag3|js3C})m8GeFNh{mpLw0ARF} zeV)oy)Na*x$Sr<op<HJzCr&xZ=rDP#>8zS7xu97hnW18hEKRy0&N3UJ9{mk^w>e6i zjz{WEAU>RP_|k;^*<r{7siaf1`_loR#y!n@*T5}0+TU88<BS*kHbbz>a!2J}4i|KQ zA6ohs;9(7`&yGLQv-*nV#J9~9j@%SejN4raJ~>B!eYViwvT3We%x@tO#^2r}rtf<4 zjW_HOe(K&=yL3N$8yEadd$-05XzsiN;#trmG!k2>U;!$3u0?$L;TwyKU$=-Rk})a> zq}ar7Qh8JD$4cX;KWPe==<WFqiudBYj+4g$qVT)P8GX#?2?X#fzxXNP`?KMzX<|n( z+$nGhoNo2cZlA4i{xO_K;R&<WLWn;<{<VD_@Qi1{8WBOkw2k*|;{iwO#duk*X~zVh zy36!E8dFp%)Tp~9_uu@F4*1;v0NIi@ofcMBlgzkayF+g01KXu|@H?CIuT1#!b}l?~ zsVcItNs62t@&FwB*O|t<vF>Zz%HrdNjrTaQGIO6ZFYDK$;@aLN<fzFx?~auZhoEbn zPSz9vmXSfo&mHP5LANcs4mN}MoYzh8p4?t|_eHm0OmbW*I0pxBf6}|3yG7W_5$CO` z+v^dXBf_`W9#cN$@&j~LXBY&Yg1ljVQ=e=K`sd^RpKPBB?x43%B_W<CNa9YXb~*g{ zHS$Kx^T!p(mPtwyliucaXYD8GeVy?C0K^vhmx1)#xV*s}Ff1(6oNg$fGkr1*d|f=v zadj-x9nT{dW1gg(n)m+z1nY04d^my`_Oh7dkw85Z<ly_)&#Dh^%8b4#RjT@F`jw20 zrk``!eh%s~+IVL2HI=rt#zK?8J4S1f__c8N8uhU_ID>*e-Zak#Yb&GabMB3uT2RMx zZB-cL=LerbSza*LV%M*sp5D;gB1TXvHugM|l5jn1N_csFW!35b006qxBPvmX-$M#D z>N*AIp7j&p%bu9P%~?^`-;8FUjQs4xZVZ1K^b_5jQd=Tv5q`>dHW^!i>s@4Qqr;|2 z5zok=j&KLz-nnawbM`fy9Iu#D0DoHP;T~^>yr4$c*a*k#n&qzT%KXmkD!eMMBb2&y zk%5R1qn>%iKH5FkF)+Z%sJymDb?u%9sLgBW`h3$wBnrSQ43fNiR`qHq#_gPRqT8__ zgrr+<5!&2Jpu-fRLy^V-?_SmMNv!oVVGJQQBr(g7xcPEPU#)qrmti_u<*X+^Fy7?} zLG|>m$49W9{0Z*kcvTT&Mk+vIfzE!F;^Si)n5b60xlcn4NwlGBo_VU-O$N30i)bVf zG2EwN0Dd*=9}n!4OD}h=ui75sH6>B@72_i$j+}yhsrsg=p=o+tO3*T0sbL``A2fE_ zJ9n-pSl2Igb}~B{E&E0rs2wtEY0cKR=66!13Jv~Uk4ldEO;T5fO^h-qJLhhSJL0Wr zcJ>;4jUvh(;kYs}89z$kd@-nMC+!g-kXp@cBv=_#JY-<~K<2kJ+bf5UH&?F$`a-<_ z0N+RY3gE?ZMm)DY3Qf;j9Ui&jXtc|BlVNR(yE_g(gVg(0Z-l-vcxOb^=e@bpH9MK+ zK(6enagKm|+zwA3^~YcMbHxN0Q%RQQGR0csda0c==uSmaZ-(A4xVC??cy{7DV<R`1 z(xJdTiOoDM93p)3sIIl$ek=UXHyetTxm0Z*pXZ_LTBpaK3SQ3|+v$3RxW~=5B;=o? zRtJo{Lnn?kZxVPu(9dP|IGybbZbMy57I((rbynvC*jF3j&xD>VxYd<Tm$D_|R%wuh zPvT*N&lT&w9`KZY9M!cwUsd}o`la;dXk#h8t-~>FmOoyA55~5ml;N>BpKpctr26R6 zYTdN;Jo>n((4gII62E`f@G5*5)Aer;cyi)j5HsnzRm5wHIdUF00}l8<a4;D;=yO{B zIq~m@;qa<WW#rwgk{B$)LQj_JaLlT3ay@<h>l?>jAdd2FGf0zl#Me_JTtcma(1Vr- zA9Nl7{Ar&L{u)AHOZ{nGAkXd)<g)^Ega_Bwz3R1T<<rq#d;HD{aCwD3cPm4qwy(ba zf00kac6zssHB&4fY`3;lb1c(HLJio+AaDi;3M<&WJ)>B7drvXh{EEX8Trk7S?7o7X zH1>KutWXGJi4=b7(kuWy+tk%tX(rUJ1lI(SoGOpx1$bG8V><O+N$VzhIBY7VNmHk! z7vueX$5p1y8Hmj&5x|@x4y<X*XxFaS7j5NR1CYHq{{TJd_V#hw#<O3#0HZ2|@V{E+ zwf%VBWX#43B<F5$TkG%bOO;ZB_Um5X=jG&O2s+gtvitu4$k5ic#*!m%rjIJ!ep4?@ z{VS4+S#Rbx+J5c1!^#FrA3;^^P4|m$zRzTa8z}fEkN~ekv$U}A#+$xYMJl|p<NW^s zT9xfFFP=`(@A@OMQic*L5Lc7w{$zSChi#+5_M5_ikY^{Nd*|2QxsMQdj(u)pX{K(D z)lM1uZmN2=uXo~`fRTrW!CQqLUb@{6LbcND{@18%iYUh5t;RmS^|dtXFJ(nqAJ*h@ zsvo|utlr40;g1a>Md#gokq?-b;J4S>s(7Eperh$2j(odu^0ceLZ)NuPsy-+2EV{dS z7Tck*Q}=U%{{UZaYUdPh#@w8o40Ug%RZ8)tA7@AA&1uk;NqbuR^j(2@<ZuUi*t4@p zqWewv$r|#gdY`ZOQ)7l5HZgH~gD3DtJ0J1)?eAGv+JoIozEglYDCj@Wt$B)0rjgj{ zv!2@;(&|y&T%fxWEux>f8vuT_)Y`v?t#r9Bp%BX)V<&kG`Rp)y8uJ-$aw@Y(N&bSn zC`nURu;+wT5?5&SXa36Zg}_C)y@qJlDG|oS9-vn#uW1r#8j!W~R&0RFuwe7h5$jeo zPZB1NVDZ}6uiBh^)4S*N$4_k2bd7S>&|F?jB)Qd}I~qj{k)Fq{b6rkxlJgZhb!f@* zuXSv{t&4HN2Zmxxok<qsQj~6n)SQU_0NQ%z?@aow)86V*lBE8A!_ZKoONpNH`JLKE z$jQq&{{RZL2=lI?S$Pb>c8<d}E~9NmCv+gGr&6Q5zpait;?J2U!%NW6zQwqOk!2@v z47oY$j1qY5fGg(hJu#^TJ+}|g*V!K!JQ1j$172EaP(A7f5`|HjKpE})Uxz-`@*S<L zk?P8_sSX)~WOwGfFc6(6$~8B=&xMArP4#V)NHI{6R&EBy0lJ=^^<AsfOp-DxHmOZU z)mIS1O*HClJAjn<qb%G4Y0yH_xK#v`*bLW07Lsjj8s_PQKpZGPdC#>(rlK=C-aVnS zg;iX!syEt)oaIph#=HURQ{PYKleKms?d$JZdp4W^depw`X$@w{rhRHcpEtDx_N8%= zfslPWQZIgK#PdnBjGACHoYPG?a5~U-nqVw>%`Z|;aY(1-q+`V(8hE4zl=P&k0o}c5 z0G{-jrBj1Q1s^ZgleT^7r25nM4WkqYjMDa_9q6V3no@bB1J;s&9LJ6+7q2u@eL0{7 z#Q>2-CTIdg3UXUGt5#Dy2h2suJSii!QU<NZc>e%qFn~H>5!R)kEZ=G`a{!IPgPi8G zx1U;ZR4P7WztbF4Og$+^DQJMP3-I_MPAQT`RdRdONO4bNkSW_yjLs;fIHal&Qc~uD z??3?Jq_vyu_c2YfIXtHh5=GpNgPbt#J*k-fRLTHxJG%k&q@&V<Kn5u(XrKh5iVsQv zD5RyP0Yxc1Qc?h-iYNhdNktR@py2eY_Sdd%7a$PCC>h~?l|r9xcHBl!K*0XA)6`8G z16M!u#DCvZNqhFIc~{HZv;&;yy=GO5QJ81I$-w?~ag%PsX(L7?1{D7QbW|5<v<wPF zxphFJ91Kv?7Z@N{L#K3Sdos<Al`f>04Fkre2#keW_<#hQf8RBb6@rGx1md==^#!%l zui@OPW<a17$v=2;>DID$e&HA8``vrjpYN)YPi~)a*3@|-*8M<-i-%sTkxdc-!O5pc zIs=Xe^{AKcXVaRcshKQogNzf-1_pCd30Jwt)}n`rqQ-jXifqlCb?M%VY8NaCll#J# z%~m}{QKh4m516>}g<ddfHJ>L0b*l7~D4^ygchA%wDXQ4(%`%Fs3gm#ZoOPyj$fu$0 z&O73o;|y`o9w`8sqLPZC0iudg#Q-~kDeSpldcCCRx`wUiE}v}EG*S7k6!I6>-m5fn z2zQv<W(UoV2c>82BHt={qWL9j+d{jh+7uUI`BS7|!>P}uBuo*qYKbP=cx5;sR0X>F z`qhIhWq9MBm07x)5D7_2X(@<WC=?T#1{9R5)MuqhaUYc-P|Dux(u?b0wwEHOD9&-% zW}8z)N0$9EcoZ;1WMdxm@f$EDK_GP_wQS3Grd<C3XU6tT8AQa!0y81(dFXv>(sd07 zMUzR>t~@KD>Qdi*ll>$+2*h#@p@A5DjP%8ERmJ-&b4PzmerIhg8>-(dw)Ou2BgrF4 zVSHhM9Y#A=<*RPD&UvoF9|LL^@JDrSAp10hLQ5opN6az-{$E<;&8t~mUE0b)cqC+E zrz0TtJ?oj`p@>aKbaulFJUiEBbN7?T<72ZB4?|T)Tvn#I2kwX-qcxY)73k7w!8DI9 zyj*HD-OF=JH`!y2m=w<*qN&xmLe0F&-*s}i=}-Z<9Py5GRU0h@JG(MEDRWIVW<!)_ zn?)1=bfqH{Q9uPt18s3hv-f=CJw-822&DBI(9oGTrvskVTN{*|dm5BTcBx;;)QF}z z)DSTzYRNuRX1Rp3F{hYhP5DMpJqa8dyEdOCjorL0DN!&+<B}Dz=zZ#~%pP7He(Qdf z+IX%@d;b6oX_LhvX`o1=V7)%?Ap4ATu3ELZXB4)Uo`&?OMsS;M%`2gNdErZ)64u80 z-p2m``z~kz7jg`wr|7&WIO8XfGh7AGk0GKxdW>|gmR}lc-`O_$QtG!UX=}Jfi7PHQ za7n@B;E~T_YnYZiD8p_&z#S`GEGJG0RCi|tBPhiwJF+D`sT;2p^@c{>g+D0Crh&$5 zs_0`XSqzdOb_A+`nFE1L9rIc`KCgGB{iSU@rdycIQY5h~ib3`u@;e@)hkk&($`RoG z>SubED9dDSRVoX7#~Jqe)Kmb5j@0T*(*aZ7lf5TOmxGgxaX=CEH2B@4Y^frcX=nqc zw;QdKaov%e=Q*lZR^}%zgCPCk>s9RUe1%ZiC{7QxYqr@C5J3cF5m~Fnm20CMuw>Xt z*m)k+KAHQCO4X0>$(n7{1qo&3cNCT+8IwGW@O>*rl%hkrJt=0)V2@1JPO|w!Qd!BE zSDu3vM^u#HgVwmH#?w0`E5OKV0{q#}G?}M@E7L~+ovFQrJdsT&rBH^9?xn|CZhmZ2 zx|(7P4034#j?^_Yo_f;|xS%4i`=j!p98=Jd!5>OYXpODVkGemVFxvy%{&i<LpnWL2 z8A+^Fx}V0FmWv$99oYI{R*$%<*AA(18Do#H*0QR-*2aXJh$Qv~vC+BwD`+TnAo2}Y zYZ!s~c=}fqW|rld@Ep#cTGzMx#;Heb2$*b>=2O!(cHf3IPFm<oGOoYl;!-+;TXv^) zv_z6gQIl2fY|?y{`CHU?t5(b9!iw0+fDKxZr0$3Gp}7>xhcU#?26pq0T8`f8JB7;d z-PHB16C&M5x(8e^$gInqJg!<IPaO?v(p8Bzin?$sYN5C}G+h-kMY|X|9Q`VMVxg4_ z6=gVO&N4D7NnR}?p(LetZD_hNzSEXLaT+%vbDnvu`|D{fXY!^19Q7Q6J*!W`cbA4# zvR8v<?3I&fBxm?Z>V1Vj#tU+{ko~J>cARY(?_3lr)s1&bZ$^4Eqe@tRYYoxN&1=r+ z@I8-8ia9nNk}kvg8o8Qeh^{ceAFX=vZaW@sk8{)}Cz^)in9WHc%|}1_y7_r4pI6YH z&;3&KQt9wL{qtKvBq+lro@oLi4|0E(Q&GXhYl`6YX$bKd7WWqj!OzS-Qmy{Z59?T} z3ae`NF|A$GNhP_~No%QUdYrZzY|#N3A>EPG9Cia6vz`FPO=eAJu3BC^C}+0QneZgI z7>uySSwP4w=vOBl>Ya|8EUd`7gpd`$!$?P8L0!j+^jpZ^MM<@%yZ-=3RgGqCq;vOF zWahJVV?s(Pt$dDYMx>f@xx;;y34^`FqAjNYusHndvy;O1&ET5}H2LR}?pvLpZf91? z^i>0`P>)Qp5M4*9-86e<$CC<!CP!S8!j9QLl}D)fu~N+FmU7EuZ5ssr!v_q#%f5bN z`g>B7saDFHdKWbwx;+avx<#&&=Etht$34WWk0RDlmHz;KL&E(zHBV9T3Dj)m(<25L z?c4>12FyE087Hf^PEBd(Qd%v;+W215U1IzI(_T(CLjeBs6VXShz^fXjkEZFmZO!xC z8|_YZ!`vX*3mozy2bn+K01C?dvb@v3Pxx-1nLPU09fp$M@h`Ssgd+a{AyVt-WxNF~ zt>>G5BQrziY&hzv(4S1zZ8R{ov^;^aLHw(#uux>|NE}vF7rP_StraWW^V_co-d;%8 znyuvOWWZw2zG(-eG3Y&oZ3_!+J4=cyC{iTcGqiGj$?j{U))2fvd*ZR~lr_34Ww65} zbQGON;?oLIO3fUP#&*wxVb5PVN9kTwq%oey?2Ok_#9TXBz;-MB-<n7M@7JfOuS@vH zeQvr<g`1tvEcwEofk!#~#bZO@iR@!_zql=ByUa)-ayRk8WBwnVdYN>+oeg@ndDy5q zRF#fB<d#_f0JGOKs*vAiw93K`I=SwB{=C+9udCc&y2U7p8+cf%Tfl)LPI};VKkXh( zdJd0g9B~zjG(qU20=%|X3wtnOk>)mkI`k;k_R@N6V~C{ba^3DvF;fZofE{Y7WM}yS z1E2)vmUbw=%A<sqbtHG|is_7Ou45@(*s}r_ln^ox2l20d_zbN2%;8j@^n7vk72($C zqHF=s{{SP3_CJ9BD>hy~k4nB(SSEp?fr-W+AoumgD~hvnlD9+C!$m@zue;msXQOz+ z$bZ7K_<<N9o9!y(XE;A0BEDkMZOqZ%0V|<a;gsXnzNXcrTki;bNwft+wcDccw<Ic` z&x7@^m592wySsuYkDAA6{{Uoyb6hw%b2^bvTin76l_|BlU;0Wc_>N1(($?PY?g^qt zRzaLdcs)gZXYii!*TDK>&gzCMXyY4who_}{XR6vu6~qSWQqsyY&cu)feQof@<^kcW zAq0&yO9+i!lVJr$GtWDFgI;eEMMn)z+Um~C6n(0z&E@)@4e@%!TX@$}e>co|t{zq- z<mHZW?bf*XV-KF$>BmD}h4Eq!wBARm$rN_&dv$Pv1yVMMm@ea!_lfE~tA~LZ*>Tq# z*QbM<`n(f**c~+H>}Ki+s_-6iz>i^yz2V!J({-y$xVLVGbdxF9h6jqJs0kYIBMdA_ zf$y4qyKF}FCy#3MFYY6rFP5h==+CG=DC?_n;H^?Ty1Uyp1a5K(9;2wQl`)OD^{)3- z@dd;9V%{0u<dS9b%N~ATe|%RCtMd%{;Cj~%UrEu7uhovcBhT)gPL+2go*%ziShte_ zRo-|5X}}f6!n?UO+xRm_xz}RUY+%_M7+K|-KuSWO5?!&-05e>+n-HB<6Yu{3I_jj< zIoXY5<uts8&vkVspKoO}^P4zXo;b2fD>gDf>63xawN=@@lt-31z!mME417PROQ7AL zwRVI}jIy1*1x4|j;oEpkpwvF!VTmvjC^8#4Cw4F@{<N9TC@9S)_dJRV8J_H0?b|MK zhT{j`v!UVh+pTFwF3G&ma>J?htmuE%A5N9L8)@8gO6b3JDvc!|4bPfn$2{?h?miu8 z>#oUfWb8295Kl}F)y7-tlisp<BO{OiJeuu16LB`3bqr9*@y<@hIKV-UNg}-bb8@2N zP5Z}w1}(~b-BFQg;cJ~rIi-OZZf(efXXWestEup)wey9o#Bzw?KwaB_SObsL{y472 z;vcqt?!v-$x3!PYXSYHEsN|D_(x^u*wWKr8A~=zqU1V?p9dZYJ^sY*^Wm??JPKRC} z2&1z%?mo|DrrF%XBA-7hA&j5h;~jpv;<+7PTD!OpZDTvl&UTNuzx{e}j`LV;)4RcI z*dDAZ$%5BUgowrusp@IQ6yqz&U7c=|QTJYl6LY1^#5L-?w++cVyVqafKZp04%-&>! z%e7v7$4%dtt!;QWLDMhh{?XSp3utXs{zxX7P`L-IWc2p$UbhChp~o7<4ymT1D8N;@ zjCA~Jt4+(@i#e2io4Q(@#D5K98#ii_u~G;EA%pGKw>19%hp;AIOQv!#L1Y+@{{XLD zWrD2HnPOog;~=32aK~@0c;)`5;!lWB+%~F`_*cU4%djH`6I|mtK)i20-2wHeh60U9 z-lEXTSjxQIyvF{qb7`PzpV|72<PgXJc=xzn>UuEA_N#U}^|kOzdn=W?eBmOE!RObG zgWkN)<HhBH@cxHyG8k_3w?ws#+&tnOVEM<fI8qn34r?RApAh7SO`1ts(#=*ii*39L zjNoSg0m(I%AB3>jr_BnDN3Gqo`;05*{>?Qww$|2Hx!ib<;!HXQwH}*ljdOQ!@x?6b zCea9Ej4I=tWFFm*u&r^fUFw%@XQAeKv$gJUWE;P_pWbiKS2N(h3F;m*xVWBZQX5yg z`%}Ejf`9@P7WBdC?OyBQ?Ke))>=<d0#cgb>wp+$sra|c#>?_xnV+<WPhQXy6t)G|m z)br<-s`HI}TfD9C^uM3=se$1C0EhN^MZmbfi7&NA`Pp3WR6pL4yPs;^)_g*ia<q1i zE-xF0nEllDTFKRQ^Kz!sOhW~(eq+(3_a?9SXTnoz471#Bg4=_+mzMTX{cFN?Ct7^8 z^>6zAnd|#FQjfH+qUgV`$cMxFW!A5DPt4?<&h6XkYr2|i9}QcA*p}&r6b`@rVN|s} za@R(LSZMK~kwyy+LZ5$ASy~pFbtxvj)F#9c<)oF*9CCRzbmFMXl9#<xzeVZ(W))>B z>z=Q>4&7M}p{UPu9Jl(5?6*z;&J=gR9nEukMwO^|%TQ)q!C@P6<ZuV4xvsK3HcuE_ zM=Qo{G%IpNym-Nv{+b`?dwbQ}{eIfX4w<2OF+*-P%WwzD7qG|Pe`>hUj9aT(S6`p} z0jxxl_H<+KehdBytsnMxhBTIn9g)KV<=FKn-_?($a90m|;;Z$NVIo@w{oFa_z0GMG zNj3Yp;DuZ4a37IdtDf>dxvrMxdk+a&hHxglkbH<bpYR^_v|}t&yuI3S`;6e$9qI2> zVp>?(_-@DTdW$abFo*|#Ks{@V@fNvptxdXb-N1(_5Ihfa>T0H~s$6P!4Rw1iPeoDD zN1zo_7^JwFRafTgf_PEt)iv=;my({5{QXYKIBrMnD!a?_^c`;*xRn!;xcsU?B>i#P zw6{T}A$ep;F4=OcoD6$=d-tlhSCMHdVuX3OY(92x;rfsLm-F<jDdjHyWQ<wgl20Vp zoq1OBkLGl!IqLQ;T-?3HhDo1uAJBRaK~$uZZ87G@PEA#wPzdKNYo;p4N{xv!*(HwR zdI8#=t+yM2=k%t=La97`Y9F=vvJ^q`j;&UiXj_S1PnILKd$vE7U9q=FAz39;wDPEW zzq$6QE#VPFl1{lKe3731=h~#h`J12SmA^`^YZrADc1yX7Zhx9a=m+roR-Th=&vhGh zA%hZo3X4s!d0!l78<~$q9;T<dze{-*3tmR<Te<hGXfA1{`<!)V>S%i)73RMaeiCTD zH3sTG_?GJG30t@j4d?x_{9j(R>6oUGUr6AjwLU4mPl7%mcpqKxW~Ce&?Y1E#`B!lQ z(M$9=>+E^1N<=Hk#yv+${TS6W>pfD+OHD@F8>?vs9x0GIpTO77zY)J@$BWIs@I<B8 zmu)_F=hqRp^go?Nr5`Zn?tIqO6B^OH)2?i8r?$O<OKXI38W~WlDfA%nDvXFh{*_8e zm0}ZCEUhKGog#r5<YRyU%}u3gw;Gk?cJ{HflDWW8E6}EU4*}ZwQS7_cBww8NkB*fy zsc&{Wf<{cXmi`pAR-a_rgsZ}j;jUj$)F;22%$>4(4#J|myO#Fl=bg*Q=OmL*nj;%( z_XftbM==YIohmt`V%X>^84g(g09tK~csb^n#|D);P#4mW5tH+0){Oenuo>f;Z`PO% zZnW*0T=7T-Taqe9I2?0Lupr44@yI!(BQ3=%@GvQW#vJiUPmKKB)R+ai6kG@mB|V4D zg{MfWqm1T@h>D6@N?<K5H@z(Y6uIJ?N+<wcywZ*jT5ozN0Z62$XCj-507_BqMHB#L zmYYUs0dY<g&}b%r9nzNo^GrQ6MF1}0=|{aLIQO7?Py*!AQW{<;0L>*XDJTG<iYNhb zMI{sfQqoaC3yMl9DS)(6Q9uht7^S2F3VQ&gVCT7};({mw&6Tg(ScY<nJL5I4rNwPy zb-Cv<WMgj3e_G2+u^c;NP^a3G)@cpGx8BbNwTp~eB1y7);;(yf$@f{w86KvfrIepJ z_hUKcn_Y2cRv9J<=hC7$#Yry>Of|(<<7Y%Z5L^TJ)7CIo2BWD}vTG(#iynCdgH348 zdXrP9-4qH}W3d5*`qK{_Ri@xnK2tX;Gfh6?u@yVZY-bfBMBBL?Ij2nf7Xuv8&Dbs6 ztxJq`6uqe#iR)BIp{XgzySjFvy3!cRDg9|NOhajDzyZ`#l|f$JXl!Sxct2j#?6nIh z&5($+F(&Im7|Zf1IAPoqla4tixNTz9Z6wrShkun7!zeqtuHqEq>E5zFc;p2cw=Y5s zOnl3kS)`en*>@Iq!EAe;wZl&csnm57-rs@UEMGk(O|7;mnkn)}v9L(|4rq=&?bj9D za1CEd6lXN|jH6)Eh{~?x(2gh>8S71@J!vQqQA<c^fKu{LttAwIT6}v!3~|E`m)4n@ zN-R5_1X^r1?_o5OgB{3XFhSXaj%(2Lj~F(SG%NO7X_6cagU&sEwd65*l1PZ8mM3bA z)q?GBIr9~_gUH?9xT|3!TivUv-A5YeU)d{M_Ye44mU?cH<t2imNY1O29W#-feQV3@ zLQ7{g!-KfUNf^goMPOfQGTnfWff}5ufzqQ}xsKI&9H2jUJXTcl+H_O)dS7#*S){5- zbE0ihZ#9qsfHU~h3}JNe&Z8x`G|SmmLm6H$Fhyu-rYnnIB2K-){&m#fBondA742%Y z-ss3-v~W&7l%K=X@vZC2B#y|4KmmC>zs9h0)DD$&sw#@;%Do9uX-G;+S}RilD5s{) z$9N=Q^!29ppe?1urKF%xA=C;FwF9*U5~fBrnzVj(NeRx=f-0KxTF}Bi#z|GlUZ7Sr zWTR$nN-j>vp!gp~(sf23+7qfpB7jo>XYn<$sA=Euid*Y8dK{nHuwKL<Du^VJV`Xr2 zjNpTeAEkMHjIsz(3pO#4gI%YCJX@%EZslK2WsQ2cEy4PZ_4D|QJ!n*Ogs!`6wDUbk zI7$|_rz?4<M{{o!miG4xDK7H{goQx8aDKd3H*}s{xY)~{KqChRyF~GpvumkeGF&4x zl1f;C@}DvHzaE74r137JujyKah3>0wY|n9Qvs=3&$Qm~2cqa$-uTqSwMe^A->;C`% z;He6B(c9#4-)Nurje?><wDHtcc3cj*tu1?4xYjQP&CyN!cH%VJ0Ib~h$=m7cUC>U~ zYRu(nCuB1ey(m3tD?&-_XZt*%$_WQdH+qG+Ilvh`Jt@!Bj!J`pnq?%F?gCLoJYX+F zoYN4>T+vBD1ukhQpallBwEN#Je76T^@99~&!J{-aai?RU+$DrJP{OFRsSU<go_Q4n zcUvG4=OB;Dt7k9I6)0@)w7Z3x*-9Ma^r*EEFmugO@a*c)vK9r$PHN@x$gCi(Gh6$H zYp)#AD&xI5z3CJ&Zk5`Z&tV&ZP5Fw7eAz|ys6$nzO^0NTXb;Y5Ngb%bWjO+wA)i`d zJk#<jHsJA8fO7N2Ouu)nK&OoHPwhhlI#lx)ACDBPkhrMxntJCIHYku&8}EAYP6nco z;z9JRu^+=>9miv8k_96ZjzOi7u?rt+tM;;27_TuV_BBX*DBY3(KDeh_j4`N<$de0$ zoQj?fSk)`7$&x2peIktXr-4?aW>>xc0FPNe-8IjlAgKg{o}#)*<MU#4&N4BY!JgVn zW>CW`o}#vlu2gn3ptw7JetE`f!mweLIUdzU5(<&;RaRGqLQwmW*wcjG%p5PVuxX@I zV3*E_J~2d%w;AtSre$Fm1J<RwySTVmmgZKJMlhw0NFQGHRL#yYOlfk!V--p%wApG> zifKK@NprK3r>+f5k}QjqITcd){_K5fnmJYlNd~RUmDm!xJ(2s<CTy)r;}s0Od9Rz1 z^)fBSKX$af3(zd}D11V<7K82cE6Hawe+#K3W9yD-@UQMyK9!#*h+y#T!>*reEK<w< zzIBipo75r4$UcKUrnxGq$}TkbW;LT0z3)Glm|OT}SaCkO%WU6yU|77{mjj%H{t@oj ztr@&R`uw)`+BEidMI$IAc>tSdp^)RRpsq_u(QY-ntBIw(zYjVy+oTERv&eqx6OqTM z89vpuWANx{HxbL?4M?QOLz^p5ts-;(04-RZu@A0KwJAcE{JfvMw$t=EJ)^o-8R56H zYj?8#$hy|Gn}<GKs;MX!`^}DVpZ@?|YH8mOv^_b#)2mLmvm<Vj8+(AZu{T|hH#tA- zCQrRz@YUta_BS?mFcTD$66AjFCmpwu^GH1uoPKrGS!mIQj$79^QlP>HP!b{Ni_u5Y zvXxmzPnETO_5Q4MtQ)B=d2C_o*K_J>pJlYSiqTlQDYp?@hCjxTjv2q&Y?`HSXs}!v zrg<(d7;`MAbABFzx{Yf;AjcT4dg?`u#(LwYb3`nbhS8@OX%lk6^8$Nfx~(ky#|lp! ztBlj%<xij$+vy`b_S;y~(KC9B-DEnl7gZ0_JqJ(zy=G2Kc5Y8%G3{EKjH=v!wTk9^ zNvXxA==0ApJ9mW`^(PgaQ*ERXv}1O7H;whL?M+rh`M+~<HquT;3-SD`(0miD&#ic3 zU$vHLZHSL?Imp|^c-_(St|ggzGqV7F&3b3TdxdQ`Q?<a(M48Vgj91fCr5bb;S3}0b z&Qp`WQ)5X%<(zugj@}$zO~yZX-_E_OM@*(Y>&3MpjY8ue^VPqZtgtWNk~PeChtKp9 zW&Z$0+`|re{3&dqB@X5*zvWPCVQrv$RFYCd2zs7_0<x{f{IMjrRp-B@bw;N&T1PaY zX_`<-H!BbaJGy>9&{tUxh_Cd!ix{k<wT2ZbaMC0oNizoA13z304l9pdi@{KMC(|5O z<;+uD%>*{lJkp>8YAGb=r}C~UsdFp&o}D>0O|{a;v}szu_V>dd9_n`h3zj}+RBgn9 zFbN)<=ku?cWs!u9f=4~8-8=yMJ`niA7v!@tk)QS({42sI7?u9Do>vsA)?RD!M>Z`u zsI|41<cZ;)@ldkMaFLO;uwn<&zP0#rMYn~mOswxLF(Er&%x45-V0}L-`D0a`%o=<w z5FmwEDvn3g*VCQ@M32LEs`G9U#E!~N(T$_{zq|TZnZ(}p7-`h>^2qy@Rqwazc&Cf? zX*C-yJ~`QQZ6x@?&Udo5fBN--Z!0{7ReNNUPuC;{^_h7a*@gx?)Hb9<`EElT<a3Jj zsli5)NSprgmGu_Hv8!aZ2`8@^sN+qrZtJ<Z_NVGwh4VQh447PY=9v~kGM+~m<EgHd z@GBgW)^6uXdu*daz#J+D1KzofpdH84BZ}U<@df6irii}S&<kus_E>=iQI1&S9AoR( z(<Zs}Vn$n>@kA*~n)kYr=NZZJo`dlJ0L1IzzYh4MOKxTwhMhH<6&$NN<0SeD=DaTj zocGWq(N5^j2@!PzHKXC(H|>5K)Gjx%@~23=u(=8dKRX`WfCsNWm5XB}mNUJ)c=MIE z1_u~9tft$Gh~sZ;oR>rI-wMTHABOEBv<lEHU^f7r*ylL!Unc(0_qt{8j;@7)-aNHQ z5BFI~1P{`)<(FSw7T7~|cNM!cNV{E?Um$$L)EeWbvuX7ej&~U{eE$6N_|&@atfJzr z%479(t2Y;Wn7%_Yw$yBNA4=Tt7lCz87V4(YO^r*aEM3kN53P4TB=G&Eo!yzVx{7F8 z5wT-A0I0(;#~ijt;a0piWpx0R;J!;pW(6HoZ4SQYHOnl^nvXR-E_Bnt`$>J}Z6Cua zq9pcnI=$xhQ!Gpl+;9S#&(Lh+wAb!!?`53F1msEb;m8DMuYR??e==!5yn^9Dg~3@@ zj1kin$W3)~sJ-3LxguMM+2nG5W0FZfT-G$_DW=;!N>mjly6AKohl?9c%vOo!T*$jh zq0b(N6`f~yJhOeHQ^eAqRX>G3f~eYCM*t!)2|ZZyO-VHC4;(a@;8Tm`(g#Jp)Qzj1 ze8jxQ2|WgS*EQll5Kgc>YE~`749eMVdk;#_)i06_$X|Wg=Yzs~AMmbr_6wO9$#EoX z%yYQ5Nv}f-N9{=SaoDz(BsVl0TDkigz+OWRF^me6OxR+Q?ikFnLc?lEpr%hkeQ|@u zWXO{BV;Em<YtTF$p+_ErHRiQ&Z0y;x7>v7}q+u64u+DRwDIlLp>#55K>Tpw(<tv@f z!t>&2^|-YyOJlYBsz_%Hvr8XSA_2|-=z3SIc;jET&@`hf#U<sz8AKCpY>!4cApJ6H z!*4uYJ?-6<y{o>RrU_M;?=3yf58jcwzw*yPUXP;Bed29e?(Xu<<AzAQn_FyTs+{D> z?s75*pszlrBiY5epCoijpE^ywk2UbNk*N5R^7d<UEv1FjZw&KD#hyiSH!Aec9A~Gl zYtpozg?=5<<4e2Ml~iCxg||DOVcgeYXKx0RB3fHUmzs_^iGJim{jcz-HC<jkB4>A% zZ}nyvEKX7%U_YgKldn>G$=P=Q06zYOS;|Y3f9vjEWy`58wCfm+>`85EgD_Y{)&1Ys z{PnI^Qq?atyOj(v7&wk391o!GX_gimkBGG_D3W=Y<(axD^(U~eRPeustu$0=11#47 zeD@4EUuB|=8knbO=^p<8*Yt|1R-O%A>o3dt``MM@KLxG2E#|YdZNn@lp#K1S{3<UK zc%`nL?KHw;gnX?i;F0a<DLiH3K`47@Wb*Bki61?EzP0Bu4MW8Gf@%6pQb!4FJeWN_ zN&c0!TG5L7z87w<t23iYRUg(dFVnI*9}n9}uWFxT)7ucnf6^{}2crA?*Q8$QmeSfs zZ#h^rdt5P$ouXnpxBEWb>McIgOYl#Jf;@>Fajs-`AQx<Yr21Et_@Bgjhm3B;t*X2h z3V1+I&F<gfT``S34K}5$nS<xYRJ?S8>TP(Z#<Ob@oh!qEqP0`9N!N|Trx@t9r{Vtq z5B;z}u%U|95rA8qw|4&kc>2_y4De#wTdQ1#!(i>lE%o-TKNEPKJuwZIm$1$T)^+=_ z`wx1>LDl{KCE7okqncKsg<AJ5YdX!JhHZY!W{D-)A9p`@-M_-T;`Z`en})c$c9K^; zNy$FnN~<)?((ZC(mCkuNC*0Kb7O!%}w-AGdmmip4>Us*wzAv3?q*lo5p@)qHecRoO zYXrA5;dbsFPBIhQ-My*WhNT^f8(lnt{{Y7>bA#-ApMKrFYQCqdBE)TU{{WI8HnXoA zy>a#Kd)6B^Ij%&VSIs@Uf0@%5b88}3kb|BHq?U4Hk7(xyKb=yNLfH%X)SUgDG1<@Z z7148|+Y&1tfaK?zT&M97^)*-K+rY=;)~lE&mAO1;pzl<qim|DclPkvByKie9uGe_M zkn)k^r@8+C3WD0->_JR}ea12qs86rIwOWfP+?~?5Q=H<5I~8RGc22vumUF&V=bqJ} zXKBt(Gt`sNQ!FiuF(ixumSfNghg82Ce4BiP9Zz%aX+`yCB&6o`*qZ+UQdErFz(`Ex zkN1zgQnQVvl}{v-nxStSOD+c_n%%ZVjxYvD?ti?0!n&!?-b}ttFVOb*`HfGopwn&6 zZQ?$v59v;?s%OJXaVaTHB_SM-#1Dts7mh6atzOtI$_zz!5CT~L03PGm@n0%@Nccsq z_%d%gKy?iuo-ZJbpDcgVLHs{nmG;dxL=L4`22wCV83a^H7u5oGJ{`QhweVGsk)d7M z>K9irhLZ9zlNclJk6+OGiu0?f<hYTgmD$~nOEJkNzPI?D@E1<kOt;<^X!OhW%Qe2- zK_T`7?jNTf)$;d<{5z@mZ%}&;X3A?zl;aXE1CLcb$GEIx?mII1uc3k)Z98^ok}?k! z7b3N~0owz(s2OriO1a57=qaPENXDO(U{Ztf{b{F@NJStTM-;*;P$`X6fMrRy7y_6$ zJk;H&U`AIRI@CF-4Ixf4DWC|>GfGL$2YN-|A8JLQLh=bTu0dWYy=kQWbXZ~zywiyt zhAFK@F%BamG=rr%Da^Y^6kG^s`cP;Tz;=B_A;G57&;dX+l%jwGN7jr{KnQ4~G*bYg zml>p>0+x(of@uJhx%A?aiU3};Qc}<X(vqD?KnX=14Jf7p?c<J3A>*wrBLkX1B^0>k zoS+&m1awrpMl;PdhI(d;fZ8acfC?z0fEJR9C;>sOEkDa;Vv0e}AoE$OGQfJ8)3cAs zTpSiBwOltQTNJK|`%IjlylO4S)YO+h@$o17#-gFNG)Ub?(uL(tezcMvpGrkOzLgB5 zICLhQr;0@4B2TR%u&Eg;9H^zuGA>q}+)`yBSm%oL&xhX!{6phk4;@=bn!zoEtq$4c zWo)Pfo)nC8US-@@(SNgszjJ5c{dU==ouP|#u*DjK^A$NA4<x9sGc?4{3y1cw?!O%k z=u&lM%N@>c=k|K>v{Nd^x@64JvjCT1WDI(N>MO+N1WLhy1aLvGtS@|9acAOv0$8A! zNfn*bfz?;{sg2kJBaHqczErzrlHzvA`J9va)a7`}n3UZ`Z9i7OLtIuB(OSO0*XUKE zl#Fm`E7&~pXn)nUMtab>=qV`-wFCv@+@QD9trJ^3X}E$9a7A<<5462kQ`OaUNM(}l zG97jyU^(i21$xBa2E1vlUq>#x50Z@@#+cya1As+&xQxOWdW}u1C3oF?taZZ<rw(qX z&){c0uro~nlYz}@Yx-oGRrJ=Ap;_Q|ibW%%DIor$sKXDK4{j^ajY!Ho>tmWwaeS;T z##^4$=|J89rMHd4oaBF6ZsJb-ebYx#bR#R*8KD@jN|69!L7l#i5=?rJ%A@`qpTej_ z@=2CLMt)k3NjF9p(xcs;Pij_d{c%kp3^Y+mOa;v)6i@+06i@+06i@^AV`*QNhjCT} zZE{p5=9HeFTB%(Zh3{auX&Piwp#YO7k~6^rzAHJtRdCeAxPZin$EhV)9;!!Lg?iS% zif$sf)nsdZrZMuVUWXNrRGrXlO>?u%{oZ!tp{B;=8zPzBiY=vzzPB_b)I8|Q%B;YF zz-)6=4mrmj)RcFn7%1q9ok>*ILQzE(MqntSiU260l|gQUJ?Vm1(Ez7lU{i2MS}6?< z;8m+)LY(A}P1wa#tp!0XugSwNKU&Ui%TroP??Kkmg<!H1u=meHTTtFZXa}Ef=Ak3! zjAM^_=Gl+&9M#=EbRfqlUYrx$S3PP<O6=a1TRR<}g{&gf^!xI#Li0&5mLPa4Na>Nz zIjk*T?J><HXHSmNqsVn6zfWD?O0S{a{gg-pg)01f*r_~4bN21C?*+iaVY=5GDb6^D zeN+4nm&+<Q>T{SR;M9g7igIXxpEI_7O)bM^fFF%|w|BATv{y8g{3!<{gV<uDt3(ga z6xt}D0)a`I0iXq>G_+6xQAQ}HAfk#W0H-e}xu(<9;eiyG(Wj$J<jgjzmSc{3ir>6^ z0pE(}w7j(6A&fVpjDuU3Ib;EV2OYXrA8ghu-DCvh(u1^)w9s$`F7HuWre|STgVg4k zg*_@<2I<8{R1b4jq)Uk&Flk8}c%Y9{QOVzFtrH=j&oqt2Fm<IZ&sxzEC~Q-d=Ar4* zo*x6HRe+d#QtqfkoKe5MPb&+P?#Eh+Sf!RkXt@j5oKd?pN1wDOKj$@$AzGVbi`1F8 zjq}t4n#o_iTG9f>P&<+;pV%B8BnRtWWjQ3ZH!H$_dY@X<w;wUA^|qEdRGbhGIISB| z_o>utbW2A60Eew@ua+w*qhywAprYkQN(0*+{+X)!y@AuUD~mRjB{4Bw>Ie<^9+gtY z?rSTk+s&0B5{WW|jier=R;P%rXS~(txVn}prk4z4je{%XaB?`VC@IFPclTfBYb8z6 z{`Q}*`~xV8jzkQkFa&qVshfe%6xJ2n861XOmZmaVE=Q$jvH6JaTkI;W#kxv4;Py4N zS#h1W8Dswd9^>mxyy&^9ZR0V=wDvUx;xiGovF+H_@MJ%TX8aC$6xSUYdH$6FVjF4T z{{T8^b{vC)n%!xr>@DMFG4-m7d8=|dOuJj<Z(6G)Z5w>wohduJjdnevL-SM()a3W8 z5)84bt}qYQzIghX^fe(FjD0Jy_;um^8$puqQSoM#Yk1cYmV(kqgpwTdzhTY?`$rzt z#UVagf$3I$&o#mYxsW6u^TscfKf8LG<EbgZrw3wPA8qcN*V^n5thQbpwOe&;tF@!y zn0h?mbpHTlIQOjBbicHOYvPQGa<=g<*p&L?x%WJpwG3+=xJc9m9e~Cv+(QeR<4Hw( zrjel<ZpgW(DyPbd0384r0=E@FP*~trJ%{fJ*gO5>(zhXtjGkN6_N;Gd9WP@CTrBLN zM$wL^t#emM5r#YUJ<W9*@!2mvmCRi>_FC$vsyQC!jfe1l72av@<y-DPmBH!cZ;rcX z`D~EZa5kE}Ov}rK0G2rCaQfF5@gEX+BK@4OAgclgeAhv(xKIfo4D*`cJ|OtFPi+qG zNQI1-5-8<HLi5m?+YL?1s^wis#VD*au6X+W?0Q#Q@Z$UITFHs}Tq#e_rE*9|na|d@ z{4_#p_Hy(BQJVTZN5IoOTp#gK>v{sN?6){1;=D6eIn?gR?<@ZRGhW%D$`x4fPc`9} zg#D&m{q5&Jop9yr_mcS?8J><GKhWLN?pN%{;X!p9GTe@xaa1o8%i2N7XU$pBEoQuo zM{b3Vc_)BArndY+;rp8{5*ycoLu$|EiHhM%;E|7Bm8+>5b$MId#!1SbH=&UXd!%6@ z;hP{1bI{kY{2}m8nc`oxUg}pDDE4jwq!Jj}f#;U=uO5X9+6LnwmmRq2Ud8Z3RQn=o zh!C{x3b^Nkw>*Be!;h4sP0BB0(V~>8$v)R5j>;V)LGi|yYJbt=yp<#QvxV~C13sgt z;a&qUCgGHFOJnt~TJYwfHQ$6a>8=PfPbAYh0~sY8pXXj*1Pt1QZz?#9KbY9+6dan< zEl!;3^F4I@jB%0u-4*0ds7h>g?Awr9+=Wu(cmxkaUqg5&Bf+-!5?q^^1Ya^peq_ku zSFdgddilEPUR$ZA3U;#><+H)}?_TF^a4obrBUBLPDB`$KyBxMUh3bDy=DBm)eDtZs zJ#KnAE#&I_zf;O}X#u>**s6I7q+xmJDhqZY_NY8^J*wWQLhgz$EW#vW7$_`%;8@8O zMmO^0QZRjMz8?jV$tP)QE$AL}Bd#;wie!CodgS!)Pt@Kpu^u-A@`8JPDQxo6DQ*Tc zkAF(F@Frx{=FyIK9ZFmun7Jn%I@QEKE`1IKQ)VC9ll1_*06!YP#@t{42RwB((Nm$x zL1_I=ts>~7M;O?ufc@S#l5>;mj)I@y`!&DuG#2*-rSm4*K{?!W(3+vAqrA9;WS&Vq z2iMZC=tb;&MQtQ%W^}n$AwlPmYl^q<<f8Rx>4I*gTRnr~@?2YKz8i+sjB{LCmYOvf zF-8ap$6|hO%i6fOye)ZqZwgt$-)TQE1mhzaB$Lv+Jr~1Q`qrc_^%tGi;#{$OlY)Aa z*jGd2jTXzp*C`FAqa5G5%gAs7{x#yltEEXQdn+gEcF>~~`DMGm$n$M8PPzLu52#5n z%I#1_K2UiBKKZM9{;z5^DO$<~Mw1?0oNc)y<DmZlIjAPTy45D~EM0^OHnr1d0B559 zbo)ztTNFM)8_(X6{_|BY`g!*rlwg;`_nDGumse9P+KUIrD-xa-vh;To&wUuq7D8~N zr_5^Si2P9olsvQody+B~9vAEW73YnrNa_2s(MZIXC`b*Hob|6m4~D5)4ZRmbj}eTe zhnkJ<`knpMtsW37Jcoe2{{R}p)O7~4l*Jk}w=6QuISK4N>k{iwxYZ55@IP@FLRW%7 z^);t#k{DJwZa;MiG6gMw*~WTuK?1rXgpDm^?sCr_>fJ$D$q<=IVyqMloONSM4Ybmq zmI++;3Z&O*rCZy}rQAz>X$f1Db+<?q2iySw;OAoQJ#o$sI#(Z}YuDOr`|T%^2{#50 z8x<gra4Vrsa!o~Qjwt&sWTeiUNz!d}-!9`?%-19<#bpr%xyCo2;rey{b*+7SFN!sB zEv1yJA}Yj?$l;=d58?m-0_UK|AMI28HDdbqvu$^LCW;N4Ou-MCD-nT#-|LQR+WbG@ zn`<>$^(efU-0w>?;h)|^{Kx+Qs=UljEk)UDFTowkoM4l0PM&8k@b|!Jx=Z`1ckSj| z$+%VZ3Qzw4uDiz70fp@J1^Yd+j7xa@5<QP$PVp@E)7r$7xQf~n^2Zkna5z;R{<X|a zaW<my+uOuaAaIbzGn3msro4KUTb17Z{{XFyig5Od?P>Tg^DUiV*~2ZilvzZaNjdxP z^z;>@;hzf2sK#$^<QA3*$(hDCf4iQ)TIhT~;LCkB6usB*(i|A=M<tJ7eNVMryu6!H z)uXnyRhsB<v5sQ49;6Nn57N3LLz3RvYtldG+tYXCVLVcm<3r~oOGy!^j>}B9`!%jN z5O|V7{r5eG>T4^<zAS@Hwfi(r&p;IN$JkXr7<ii3!d3%MvL^3RxcQuumUI2){{Uw- zjo}{zTxwSz+a4%(8_xBK_#}1bJqNdX?VRT&>><0}-<PLzlASth_w{z^-IY8mp<ei= zMxN~+D{s1h4gl|r^*;6K7uUL9f;5zkLM)6vL|kOxa!>U%z9;bopM<n^b%;Fje(u`c zcrMuE=%T#KO3?Km7T!&$YWEDb?AxT7@&kXeo`^eo(_EM}Xlqga0nIF1=c}bP)45*a zeN)GJuplzqBViN+f$#ONOYpyjZM2xi(2`1{Ea&d8<5qOLJ54^oh(g*~M!9((<t#tL z+PLo&_^9d;n_Df=SObqno9pdZ+B8?SudC?0{{UV7;_p)w{gowXuj~3ozlnTQ8DzB5 zq#{4NEzWswWv&`|L$=s}1P)0(J<U>UmXw{ODk$Z<FVo((H0yMV&E>p~B!dBmnhx(` zdGFYDHNl9*N^hQ{^k3w6!jv%a<%j$^6^)3RGc3$f-y4~j?JhpOm*4WL8pf_}Khf<$ zOJ(DZH+~(5pr>m3yI(Jsk?e_EBP?=$y}q86jOPNejpX^JZwS5QA$~a~p(k-WN3k^4 z1^q{@I1a<5Z_aLz{{WR#l0XBg^r>LHi|iMBHUj}xc49v1$j?1_#dF>#)fUD8gcfEU zF~J{R-Rlp+*Pm;**A`iTGC}5*Q}T>Rxxvq&JXXq!t4*`b%rP91aE|A(#iBztpB%^; z51p}<oEAMsN91ce$ZH#qCun=Zc7Qq9eZA_phxIE7ZY`4JMHFCUO6)fb4hRQ;ypMBK zH6IdvuJGJCM{GCYR4zBE`=tIMfybqCLDY+PIk1_GsmqqGxBLw(h~}0yKfh9Qn%1z> z%#L=r;de1ThH+fhhjQ}9#_9qYRz=GWjf}3}Q;hzVv!^VtYL=?zLGxq)Nj!{>J-8X~ zQ0p}<N~xK1N%lp(Liq7%Zj7j>@nh<J>b142%_6F+Gcmvz>s?KSpxbQHGc*N(a=;ME zPH~@nS3+%(BWYlX66s*ckPe(@AbQrct2&J)XHPUzl8aV2>-{zzHYJUu-*ir73(<es z`uft^#8_@Vnfg|fX|FaIgX_|??DVs+e5c!r?WUzBcgqzR!&Y~eM>bF&Sk(TNEk|g( zZ6F7zMIWU|YwYiRPm!0z#?ebf1c^lyPyj_?_^-nHM~bx8vDYl3xrBqYn0)b{Rc@fx z<rJDyR$@Lt_?_@4R`B<md!QLLT{LBiDrRVhx{tg)$K_rnG-Yxe5Jwn2MShhquP^wU z@Owe=uIrf8X}Ycn`%DpxNPpX3;`;ThB~DS+I---Y@arj5IOjCYr)fO#*V?<!AN)Mm zd=Yda)$NJ5V~Fmd1)_h)vGzRI32}|s85E->B9plhYAL^3UDcoqoKP2baY;`@j<kj| zW(G|nx66Z1b_b<IPo*`e3fcVWi%-sYG@E<ptpG8OO#pgQ8gh!D3~^08X?GezC_)gL zdarX$9CfGXJc<|%ZX3NBJ85&pDuK72^ni|xW}LtdohiAh0kjHM>q*jp7Zg!PQ9ubx z){Id=2}KlB0dtB<DRW2zG?Y<H0*WZ40Yxa{lN12Cpil=i@Il9F0BNM9VM&?*Tw;v+ zP)>T&XaUENDFq$r%G~it1YUiqTZ$Za9q9p}0gigpO)2J#am_Fimo$|9ppL&<0B>3` zMlgMiE@%M<t!UVsvojD*GuJhn+NZOWMIkMm5sIa_n3o^%@hAJnqNyk5X%D_?Iv5X- zWRImZ3>ua2Q3>>@X*Fh27x;e~Oi*Z~1LJMKN^?t+Jt{D9NlL@HV|PLZYq0Qkux&Iu z>k{eY-b|3Zd+@|CVYm-t&TEJadsV9zxAP(wCDk{L&Ye|z@!qh+QH@w~%lfg=hOX&U z=0~Sk>bC3R2azV7wph-LQ1m82jPv(T;%m#M$dKc?uGVcCG|fVESNmPNX^nD{7TR&R zbJv_6#<-cBcKj=o0|+ME9o3iWcVp)%FMmgO`Vha~G}?0m)c2$Uy)(#rlUo72?jTab zhr;aK{kft@!YLU9<2375^5hN%@H&Dj9?hcGj?C2X-luh@Y7v`x=7v4Mvqo@npVq#| zi{k~Q#*1iXF)PUunKHn9zzl#o`q##A#3i^{0+`~);tA+|KAzR0+Op3cB?B;=D|i0@ z^;e0RV4f}el5y!Pbx))2dKh_AQMFZjY;1g2yN2V%H@9}L9Jcphun3_-p4~Vn9sR40 zvco_W47N^c)~RvkTkPtff)A}$u~m35-J65lS9ThgvBSn4v@owVI<2Bez%0W(1qz^? z5DhPuRktH<KkXW>_W>K_A2*?`)#b5^({ilJ(YZs~qbE42CUYZX(*OlJ^Ia=#OkUzq zK&2gNh)POYN&qOLmp+sLlu<<>3Mit03Vd=jM3l$O3Y?7bO|FwoirV+Xv&6|4+L5x2 zfE$i~#<QtY*`(SkF>-cCHJs5~H(Db{^G7Na@_f_JOm(bks8o#A;wKj!!j}}3RS0n% zF-@ndl?Mcj(>l;L6j4Pn1r$+03IRF5&N(zGAn-_}qJjDj&m7Q34J9og60X>iLB@Vw zO;WDY!a8;3ydwHoRkW$q<GBn!fG$b@00Zk>_?oJeIhogjqU%*rU+~8-c@ak4u&6z; zP^)7cb6sWjp3~f@4odl9pqe}nH9h(2Iv;w;n@y4_51N8i=Ylq$u0>~06A2`lrF<me zCuCEJkVvn_*Ek@KhOca1%D<Im+(OVvj!K?EtB_7vRH>?|8>J|b2WQI2F0Gmu+yUC% zJ*uN0QIqRia@+%ML#FZV!4-(Wl1>d>DLF5A&a_f~=^3J&;0$d!^`_S7=V3~q5ObXN zq@sWUjB`nfDQFN<QJN?LX(*$$F@+hTiUbtl?@h%mAT@N>Rx9^FQ<6<<&e>2!bGBG# z3d}&yL0haaxXC`1gq5UpQ`HR33~@tK80Mcaa)j4LB*|Qi;-LFlp2SkhyW?f`6`l_n zt*F{tnN_;6q36a=Qa=h`HGjM+fHdsq8R=ar2*%~jz@DS?s9latMc#~xsH+H&6OWW* z)Km9-qM*ph!9LVv`kJvaeg6P8F8=_V8LHr#cH`HI1X%{Ah9;AhV!M4Rh?I__HL;+b zGU23;Q`9bW;lW8Gw)52bh8uw&hHIY^r7CSla-hBKMODDd9G>QkflRr*wcR2^E69M5 zu4CJ{dgHZKHz+^~*!*k9RBCmLI{nq9$J|4?_lX@(pgdKJMfpyBYP&k%Vnsaq)yqs2 z;L@Kmj-qK2flpKlFgO$i=~7~{xs-@zT%iWFyg_9g6B#YQKq6OU1F7dYth<x87H>hC z(0lQJwW25s#u35i@uD<qRho2F?f(D(+JvaaGF7^nPJ}9!1$QW6k&J`T)GRAr@(|uY zZJ;D67hbp+&0}vnoHCUv*skRol=P_^9L_6vyNOXuq1Oad#+YkpvI=rV^5Onr>)cgY zBV--feMef-YOO!+{$=AmYiP)5&vnluntV(`u<ON0c1YY|ANkWZl`I%zBk-*vHhUrE zjZ~YfW1ecmHX8tDtHcXAEPo35#j*937RBpvlD|sUUe%Kc!-~-XoMiA6X9K-)Vjig9 z476qK$7jp)9{z%^!xnM7=JxGWES%3{ynwmrJu6Pd!3Z0vHOStDRFAx9>A3UCwQcYt z&!uy4%oay%^dt(_GZwQM;FDbM;<)*4lR4iO-G#Vl#|Lg}mY(o3fH>?b{{V^R^DmBh zz{PU%$``4xUX3-#jw!n{u+eS<nEB*mHQDO=jkcYl#XaQ76nVy6<0lp7ULU(woFb4; zM|!#OqF{U(rVhEf$No5L2-5xHhjwdPrzIyea=tS0MaHvq_WEgLv|R3#ai9LRdG*X# zh{?D!Zuy8guT1!T;0<raaRH`UM-H7D{`UIiNQ@rgc=?a8{KahiP4LEv;Ew_uYe+@r zrF(F(wdBeRBCpJL58(#@e_He@SA_|xQCheCT=6SPb#FOzJUT!W0zUU0D@M+2My=g` zW6ylnY=^cxVy{`TnQ#VK0Q_s!s;?bPsJlx;cE?oJVq>!BkM~7#Qe?<UIXTHS>-rys z5B6*78nYWaB#+D>ug>Ec;2wbbn(`OM_Qyhau52A>RFt`TSl1aD#l=nB^^btIRu_7u z*uRZtc+fmiG5J{MxbIz`$H^W~3EJ))3#7mt05-Q3=YIrAUlk*QLac09XBn=G<My?_ z9Wuft9$9lc5PFfoAE~b@y*DhzDSDlCEU=M(*5{g9?o<c?Cu0@Q73tp%$Cqz+cDw@; zt8wX)Ys@U>5;E>Panw|r#nr{Ft+aO1$dJmrpJ^ZyU3iKPyY^SL%_-JY={fFpo)2$3 zLDmX@1FY--?eZGK`+BI2^y>&>+JkW_T(QOuaoqZITAl{Axw!Df@V1aEDJ6(KR4F8W zBDh7qc-*GYM_lBJR4J)dsF!qIT8f=2SL|-y-r7k4nJwai_9Wc9Fhd_-e_ncGy)R3& zOG^`dGp)qN&?_{}%c1=8eSiAZ;;A`FWH(mrx>Xx_+l9|=J*(NYo5G$GnW4;(7>>x; zWs?I9gXnsGHOre*aEub~{zrCEQB+kVyuVY+^&%G=PC+LJJP}o58}@^Po-<C>S-*6y zNFyY#OlF&GLE3ZQ@~=iQv=cnZO7m!j?m}-P7#m|;^Tu;pS{!Y2e*|OiJK!ASHI;OP zUq5ky!LG;Qt%F_bkiJUC5i&8_P5|ehK~svFr$NuMD+rwHYux4hPhs{S7T?=~t>w;f zuyN~Fw7Vv|-k_k`jQ;?<IOKL7{*~3+rM|b~ZASO(tF^d{5s&}@S3abH&*MyA3|`w^ z3yWy?#~TJ=yJ<Z3u4!XZil;YeB);<S_!-;UHFV!Y4#wMLhBq6Pm=BcTX1xnWHd=eD z#bzUP#_1J-$KzJ5blB}Ri1iI(2<BUJyxUwI$WAy1&<{$?x7IZG)SJw@xVExTcRVrf z$D*qDB=<j+dG%|3X<9!s(!<gAI?Ww?-aPvzp0TF%B;X+X&(j=!AFXq@OD?Z3pRGRF zv$<`M{{ZM!k7sOc<$0ubp5f2>$vGqGR(#SMp(NKjgem_34}RxNV7F+}n(8RySm7IE zQH53D3g`9xNL$<FI;lArJ-tm$d3hbRyUjUORRr>J{!MwFqdP3}Y>&!~-^~$`%IA@q z?!e&vv%A?Gd4@CU@w9h;fjP2UtDVebP~SN$a9DcOZKo%ghz{Q|zEaQ7d(}3(mSS0s zO(>RG8>!rQ&wBUyCc2+H%?7nvofel3y}pJekgu5X?f{Il4ub=@>s2LL9%PK==5R3> z<d01M0EJl6t!9Z2kmSv`1<B$iJxA8Ibln-qKWE@K4DHnV{{Z@{lBH<UZc=ANVJfza z6^#ui!@7;Pg_pz;T(d0K4QSTNx!j}?ociQ+>^ZG}0DL!^{^?6Zvq-onBlmIplU{}3 zzX(dc8-hJm<g(L|%z!cd<mCSVb!)csQ7w|v(gAH~Izpd${oL1!iO0%KsOX>G&e&0n zH>R)h{{S(%9fgO7;l;3y=I9JH(<s89M5_AMwdD=2H_F`X&nMlFr?pYP)XK4r)Q6A^ z7X#%__kYH<JTu_QZ`bX*tF^tUJHBD_%k<Clt_UgCmnAj+NZG~|*R-tv0L_?sPLrv4 zmr(uCEPsj52^jvB=^9?0r}$buma4Z}i!NGLzz98a{44KCbF13fShN~gj@HUAm8QoG zeH4EaR~2=AajVX@mQlLK&-XLAq;FR}&*&?uN}d<s_ov_dzt8S5m$b!cFLn#3`Tqcs zt$VB8YS-Uo(^=-8Q?aHTaojKWSKq0tx7RD-iy@|H(S5gAx%pVA&p(Gn2DLPuEBg}7 zptUO3vM=waJtOu8s^4B|z7w;O>T|y4UZt_U{k@mkx1~yI@{`xA?c}`9S=Nl2g*A22 zclGi!G#?7t_<POPyh(DGT7#YCpOCHo&U&9uPSvqKvJ%JpTT@4l*c@8guP-wmoO++q zu>3n|d*Z9XtZPciWf{%AzCQP%#ckf)Db*FBM7qCK*%%zhY`)(9rnTpdID3lA@qgE1 zDkmx0Ztt&U<Y7ai%YA-zpB56=GjRoybFn)nJw0oGQopy-?GooxDvCIQ);{Ohcly?T zv8uu0<=r_qy0pV8g!K15gWJ7%_M@rIb#*1x<lb!Os}u5|^jhbsH9@y6X8LyZ{{SPZ zjviQxN$C~+Y5r!Oxvkx5b9t9-vN8Uy-3}24srUD;ep$@fXL2Ja8SdV~p=nxD3+}@l zEPurzKDE6B5oqxJw^Ma#`bQe`vAq}lq1^Oeb6#a??LKOb?Vg{zPAV~9A@+8W*u(a{ zPiiWC$Vdli>@nTFyLB~<s_OFJOf&Mvr{zF%_><iFny0I3GhfIim7`P~Bud|KeLa0o zdd%|<GoRL`5O?RE{{WGsDLv)6peF;0P5aMYD?CbE`+qvO6hzy}aNjIZ^(Xj`p!GGS z&3RHMByD1N0A$pb+h&W+0EGiMJZ8BqV_pxw*4+;L5X8RSpHbGS=^CxuPSz7k8EJBY zMt!VzarDo6=#Cn4jm+J6Q<CN<@ia2eFr5{Z+XW8oyG9REIrZRrQ#1_=`#V)DzA&1F ztcw(c<L{~3KLQ6|Ow`RJmtJZYRt$(&9F|;wTiZUKl~}q7QsOyGuM~k*G8u>s$-(W% zYU!mZN8GvKV(Kc=Y}e9mqTFn?m4wFjMPnd<6@~y}f1l@9wA~oVs1tPR(%eGM%s>Mt z0FVb@Yo61hn^4q@NDEurNWdc|GBb>TdCoTQeSPbyf_R0TR_;~2n2ZTBlEOzSaCYN@ z4nG>_ucnLUc~9Pz{SJag5m>A-Vn~4lVw1-kNG;ib#w$-o(<YK_*5?zm9G~Imo(TNv zw}y;YkjZfrg?@3i22p`3kGt*<YU#9jW3`QDB<_w7%zl~U@)hLrI+L`HnsnW6YkyZZ zEg^3@J@{YUwo!NmxXuUTTUu-^$~S;WMO^&_a+eyyjYbI&VHHOo%-2QX{YDF!R6^PP z+V1x}=DkcU2+C<+V@kLhk*2CO(9_d&{{WfM#|Uwpb*;GJNR$Op^~FbO;hC{k4Sj|Z zgdr5x=M7qto78EhnsKI@?JyA0MF7wNNlt2Q02EP00t#y_I)gwM-Y8UU0{ls39r*H_ z7SbSfl=e~&TKR{?9|j}w7Mpdgcq$ofCWdJuxYDjqD(lonaqqx2^*zR0D1V7b2i~j4 z0_~CFKQxjCVg@os2l5rkQdfE{Mvu;oLr=QX?&P(<x0c@GHrnyZg;`IyHBFGOBELd> zYv3IV#=3Xe^{YiPs3I$vz!JywZ%<>ze5vt6;KsM$&G&Y|>RL`o`$RFF(U1CQy}rFE zsY)vHLsIT|{(0t}PC%uaGOg1U0b(mvsbq`>JJfB`o(@Gcj(DL6ZgJ~Q2P;az^HJiM z3yjmq#yF;6k)KL4=|IOe0XU@39MTih^Q3A7FSR0^Q)p3>ObH12ayrrXR-}p56ov%% zrcBD;bP-FfhB2^uQUDDg;L`OxQxKTVDdcfUM>N1~6jIOuM_LU4&;n+PQ;JGp6j4P0 zFSR8oqL2k8Dd(jn02EP4Ob2FxNlQopxin+*r4*eF02_G86sH)c1G^y8NFAvFlu<@$ zfS}~j#&b#ZpaN(CNbUtHkU2claz!R+0oVhjB>>ZpGyvXDN_Nt{MIbn(R{UuI-KLdr z8=U?WbZR&>?gOL+oSu26;QCVLjGtOyNwB<2j=40RLsQ@<%<G!A+gGUqqI1_l8H8Z- zNm5OX#Eyj1Dv-pYmWp5{6tqwTk<TP;m39YdUcHY|QRh6GN-0?nnd~oGUYNn=o0=#K zj%F-!#FOtrv0jvvlnUhk0BDofijR5Db4;R@kolQrYo9Sl+m12TrLmMEFg<gcqf44j z3f3u2-sY;ldUvWfDT2fgU}`j#m!&I8SYfJT2NqMBk|Uyn^rE#g3O<xkKm{c&B>*iY z6jA`9ib`NDAfk!@T6}mXu083MwV~Tv+*#ZUgxq%xq#j2Vok^%inUw|1>{E*NM21w# zvNUEq6YJbnoo*;uEoHXjas+7h<7)xUXIw?(%E_GI^{tdkL2GaJcy1o!8^abGPxpWr z&P8QAMX4idadi`2i~=x7Jw+&|PyV^^bMqQybZ(3`Vz9yX>rJQb`F5U^(lJ643MnY2 z0?|bj08vF00O5eS=Q*K{=FKK(q*e4KwpiLsOh{Z4#e08-=CIX#KlXT#G(Ja@BCj1( zXYsEL92)7oE8<CXXzwG5S-j-J5AMHXUS4U0mLi&PdaaLNC&oh!RjQjv=yk}_+G#Ya zxY{dwOLdmkQah;XeVFil>z;e?;B5hk<M(V2=rLPb?z3^OcxuHYL|NsN$aw|{IL3aN zI5_sMR{KJd+4iLIyAhD5hBcNF3zpTim-%k~XI?(6r7n6Z@v`#tUm^E?RmyzFYj)f) zYdQuf#Sa|!sg1UwZ^%h;kKy@+R+>WL8G?u89jl7;sm995-5xbbU*2lWyEE-h{MCD@ zOo{*iDu6SJMwfIWzhg~M8F}0f(wt*$Su&OMMRP?t*9=FcHnuQfMHEmWD5Dfp5K%=G z08vF008vFPAVE%1o~IPrRrjD|*3ae07=T7=UKifL4wa3+a)FGIS2Wo?xii@ItfsWN ztR0<=GAWD>YUh``2ALvu$Kza{(Cg$Wky+9%$(?=jKN{L}09JOP9@j@5{!?8TdY=7^ zD^vGwRi`NRr25nTwdtmChk8m;miGG65ynU7RFDTsOwo!dh(8($q@*U84$TzNPY0Sn zFEn+fUVS}zqJatrttuWcdr||>H8q;YB*7%lJqR=ai1UMpr)BiZ98|w=yMeZ|s~l&h zYTD`JaAn>AKb>aXrrU>C;j{P}iO_@ANNRU24PI`Jk)O;`A`75oD67xmS;vDyk~s$z zFrx}?@m3w#rRB(&3p0;EB&lGX5w_1KIZ{h~Yb;z!5BkRZ>9R;%{Mh^kDORMa$Ca|7 zHwSZi;jP1|f@s__<b^+U`c+xqU>H4XB9F)NtYL-ZR=vVf=9TPSX`~H|53NHqj@S*1 zQxZ}J4@yG0&&*Fb=C9c@Zb3InSngRy7;hf*vc$)wLQ#imR+LIsXSC-AqJ;8INXL_k zh8p?U?0s!Tsx1dQz+YP33U(j&YX(2{%k{34Z^+;7*A6Y&qo)Y8kli<pJb$}ZBb&=~ zKAeiLW-+J5j`aBeQ<8gE8h5#@<L?t%7I}~7TY^v_88}hzTy4*YEHudFdx_8$A9Nm1 zwQycC@syg_g5OMOA&EI+TLq7$ZA%GKytGzb%&{1nbR&5^dm8>R@jb1+r7}W%=LCWj z=M}-<Yj+n82+F8Az^Xe*Y`5Mlc|4O|li_cLTCM(;bzOJ<C3<c$v9b{~y~Y9PJ<WT~ z(857Ev*xN+safb?d^x(FO<*S0W&6(EarLitT_;Z0Ah3^Bw-dz#zG}wD<s=T{xc09% zx3{#@CXO38#8C$yQ|?cC+SI-vS!oc*1(J)qp`78qL_X)g(!AVPHEHuwN419Kg&tb3 zW8Af07T9Rs7bspESdlgXkgB5`06(CwmA)x>xcGy~f;gRRBapZ*a9sU6Q*L#QLsY#> z>)Fh*qi&8oWmleoYQMzITUzlQ+*+=`cC$-?5+DIYYB~T11HEg6!@@NbsyoQx#WbnE zX0&^pGZm746KWDjQO+uDHH>$O$x)xutEoO_M_$!=8D;Y1BMdks3ihgPW6q=TC)oAR z9O`br56fG~0!)b?JmpB_SBd`s4r|doLw0Vw0}M+Z)r6=S^d0NY@$%sD&2Z&#<fkR( z&WyqHS8VKl97^%)Nb!=pP6-~Et3MQP4eqaQxd+LMuhiBLh9s6Ni6n(x+B7e*0XfA~ zxw)Q6-r`am%6aEMg+F6_Q>)n4a=soAdkf5JjrX(EV>M$~B`quroSYGl%9{&GBJigK zfCe$eN3Rm=XOV(Kch7p(I@F4P)`2z4MooEsXQuojQT>akrzHOH;~(rA@_UpapNt%4 zy>H;+xzq1e@Nyp*^w?|3e~gDYJu8z2{{Y%+e2sC-_cWK06|9BlPchi^0G8?L_*bM` zYATvMkYLOkaea$|Nf<yg)N|6j&e22MDxT)OcTdy9_<uKXD1}DE+Q)mYGR!#W0OaEW zt2A+3yvI&U&Ftz}^W8^lGDaJazgkNad1hFGl1Tso%`UAQ!*sl3a*SuVuSWP+rfIX- z>sp4T8I)VvlNbY|5OdFC#YQI*u-JcS_5T2Zb4EPX8+{Hd#M*3@cSsj&VkF$^bIInq zKMCs%YolFh_6Qp4_YtvCle4{UXtptUg5u$gy2%r4i4j++0Q4Tb*3N;U={im1vZOB3 z6f1yu9-mHY$;4xz>3b@Q?OX3Xd_E1P=AMkT;J*)8!8B27H!W`ykY$xub}%{tPu8yN zwC(KjH<ywl82OxMiqF(F)8`vqEG+YMXU<VRxaw-Xm4?|(#oWGq$^JPx+w}CWIj28< zxBM6WNc2*+kpBRHQl2ZVW&F$C*7n)Rh##4~%{KE#f?II}d!vn8cgS#SX}(s;ITYu% zmT318v4100tI91~L=Kv3Qw^C@8`)8Fjm&T=-;uU6>PKpgbg6Fj0X(QhtUSUA`{(=L zN|#i+i%)>P%XRyxKJ$N{TDI=gW2+QxX5?}o67-vkQDJh^hSTPYg18u$8;X&hdvo=! z9^Te#hE+#{Ex_M0FY`3*RjwZ9UpLGVyp1BGF(732s&TWdi?@<GXPWx_EhkQrjn9w8 zR8=cVR%fJmSK%J9tZDITH`jJrUYjEMf?1>t+dsdw9v9q^R6H}`IQ&84X!QR84&Tbi zp_trD7Q!&Z0-zI;Mh~`qvst!w+OLQjG`XHxmNiudMkFGEfHUknSG#;8@IB6ulV8gt z*+C?GJog)2$6t_od)J?d#=1AWuKO;BOfax<yH;O|IIj-)Lhklxtm290x!_2#jsfYC zdwpxzG!F~u{u|oTtXKMyjt~#sK!2$p=M}LJ+4TEAvgvVsmfZd4{{WVfpHgbP&ag&2 z#Unp<CF<W=`MTJ-P2DrTF>PAbU5C#z$nSA;C}tg9zUuoZtk3kVR$*&l6u5KHa?kbl zt$i;-y4IN7T)JD?$H{l$6nlD_zb3B@iXAt?@UuxL%J(E?-|6j2ofjqTD!WDf@6mp{ z1E`|;X{)vOFH%cAA5HMYVtq)*cYX#|5Jt$)zp1D-ePZF0&eh$3eqytX?Pg#0y%+h` zO~uq&OP2Ez^HJ|1JCY;q>fZH_Zw2P7dA1~Z=aA&&=ka4(O1_@XnzHTwec#|rC|&zX zuKs`J*%Du0TxvHl`I%x!NMVV8B}K1C@ZO4=1)!GeX}^?XY7CV29;ejPd^O?NJUf}? zVRfoVzchqlVIGU={LOOyG4X_vUA~{9gL8i;%JMJGx7cH)dK7Tc!$;d!dj9}lkK}RJ zuPjscG(Hvmea5egyj^XfMo<)%@AzX=kcZUzn&rGJ;k(ZiUN*PlM`*P8&h=S1-`~G# z)9{yo);W%);#pQ{$L|bGepvhEiLLv&W$_e&6E@mJ2Z8r|%y&M5j%ieB$!XF($&8^% zIMRCcm-!B#QlC~L&qzaWW}hznV3GcNRz9<?Tj&UEG=P)ek^9L>%EWz1?%v(1hlo5> zsT=9uA7LCSHp_H4_QL-Fjd3#EBxn^>kWWtTYYLV9oA+Jk-=gwIULOg}xvDEiw^!!L zr@6yPB$6}8=PxUqvzn;ROsEWH<zfVJ4tYH@>S=}9r&W!7%a-|7RA*^D03Nl`M-8`z ztWDMHd6#dL8#<HSRQh%-K7zcuwN*Vgli7DY2~OG$iYt8r*{^Tk%)4)uLY_hHs(WLh zZrukp$?AH9`kl#|V~h+csKTJ^0PHF~M^KwmxM{8%C!(krR9?YHU_C0XLJYB@oi@Gc z{6>`N+gG@tW0vNTiDvfejM8U!J*s#kF5G>7m7`|q652uM5?F+d23fzu)YeywwOMWA zg4Wt3F5vA)Z<RnDPpLoBw^-r~D8NC$^*+B^^WPC&hu1Ep!Z3;>5(iz}^v(`_&2NN< zJxzKOh`lO&-Av1X(p@-Jk93TrNI1bKlk}}fw9z};5wq<EP3~B4%7L7dQEB>kFNliF zj$?4-b?isAbT>BXYi?CQS9ekfJcTN9ztX$vRkKPbh~X)>Dee7iXGLz%YFX34G0g*% zP;hWT;QEj{eJPe=>K`>^LLz`;axTz!X8`-0AIi2GIAXfBGB9S1b}<7OY#ihDtEMEn zw`FtYT=^bay7P=4{{W~p%<9|XN0#8@E4_$(GiM*4c?_V#aVU5SV|SLv7;bqP$j8vv zL#F9|O3P@Gg`PNF3!YkD;m6dIk4y@}@Z5-#$*4}#EUt>ot^WWmCjS0UeZcgt?@nur zdp|x_m1UAfcgfCr8p9POv8ERc>q^Yg@THt_-^kNO%BAp0<AcZ@J$ij=YxZ@WqfQWl zfM>Q%ED|!?pnsI01OeZigH`2ZW#F6-zQ@0?u2mIek?moy$y9Ucx2=R!1RhS_g#Q40 z*jGW}i-m&eq;8u^;C8N2t_+fGUKDZ1Z))GuWk__}i8d=pj(`>!z&Xu&SSTeszJ=o( zoOur3-&G3RR1$p$G@6%)1-xB-v7DOo9dpGqY4;FEvMClt$nyqv^VoE(ZF^h2xv(B% zhyWpySzBk|ImsT}*V0t1ojCG6&z!GGMtq%*q4eo|{XW`n`RN+B<|=bsN5gLyT58%| zwTzAy?iiH;=(6BrBR%W6#d?ic;hbDunA%EqD5OBzDRD+90Vt<plN11$qL&n6fGb=m z2<!$(Qb4NGLQT5iijYAB8oMw%a-$?u0tq2>_bc?Na8FPOPUmsP+yV%tnKwqj0CEVy z=xHtE*h>syhJPBGs1KI@GJF`biq_*=@cf=krff-QMkC2T!;ic4>GZFhb!{^5OuA?< zt);h^bLGl~PCFbP)%s1SY4-Q9G*iZ|j8%vusRtswkH)_Z)5a29okYN7g&H_qj4<2N zBzM6FkIK0mWl8C(1nhiFawt>lU7w4*6LX|*mfAseZm!9N`|FP6cOLb|6(I9lCYfW) zaYACEr3V!31f=Jhi1p%}8;V8mnh*lg6YWkvEjO(&5K*5>6jG6nw7@e-npNh89QslL z7}Ik~MI|s5c%w9em8bw5(siPY&;n6QK%fMooJMK108vXo9<;y(A*Er8F`7UnD58_S z04q{aMF0TepPbWifk~PGR^t?P=nWa-m+_zhMrih=rT3r&%_S+O&;rx<Qz)PXCXfPX z?@z{g6adbYw2UxmezX8lMrkMk_{pU0Nt$uz>qrE_G=PytX>vU&fZ)V7X;q6c$;N4+ zdyayJBsWSx5-D6~(=^(Vn{x6x3XOR*&;~I{PRFm|LN4w*(Qq4@DM-Pj#V{OC7^cxh z02EU7#V54@IhPow)UC{7qRjvlQAGe0v<{;^DeQp0BhC*@ngEW9DQEy3no3eJMF13( zw3Gl*MHGMy0+%$D02)eDM`~atEhQ8Hwb1C-@YrcCvm}M2e1f>?T=hxjx0Dd_2Ty9s ztu<0StZ{aCEo#?sUR;SGkINfCBi^cSIT&NdPikc<jIbW{QHqMP8*RIYXr*C>Bi@p! z5*;Yb6jK1AiYNgoX(*rtqLzw4B`p+CFsoW!<5*wAa)}&<HVv{tl|NdntGTh#uEPp0 z2_Tx8wQDlr7TwVkT}UM|M(r8Sep8B@`%>kcGq;%FU@`mH`i^QjVFdiVA4;ys$lbxI z<%GH#xzvWt)3eo*29=r?W{~BZetE3fm@{Q_&nGn=DqWkn9YqQei*j8>I@75Y32cTb zDnY5*;PJQhs`#fU8}QXlE~ZJfu=z6QcTDk7I_+$Ar#9d*+L}o|^w2RVpizoq3Q8$x z5K%=a9A<zKfEdBg6i|B5yqW+?S{PvZ(Lf81C>&A>S_U{iIUH51av8TSOLrAYujz84 z-64=<bRhSro{H$GZ)2^~qZbxwARVe%?Lsc>AImjEN7iAq((WziA3oAzBiwhWA=RAd z_YX7W_Z1oU730d4UT50pu}*aPVw>0ejNL-z1k55QZgJQ1sHUA>(`h+yLCt2f7VGt- z$;K<yjU=t)d2+1VWzaz!P+ShY)kvJKd8I5(b^A>YUQ*a_-qaISRj@`gQ|}(4wS`3Y zCC0<43H7AjJ9A7YCa&VJ?B;<<ih7S)XaJsaDRags1DagW0<LjN)Gx81{b&Kb>Q#)z zaj}_L<EpkrM!YaRX<kC@^I&7pPy<#+R{3O=PqPt<lgxP!?-;)v7CETN`LY1@sg$cO zN8?E#V_895bJvQ6l&w$A2b!T=ubL_3GKTrEzJ%0uBNmd1N&qQC5yw$UnlnHWsmS?F zE;IfW2Tq-8MR0g(X^8d1$*AqCLeoyUy_8353(DkY2)U|uY4fl@!^M2$Fx3)Gtr_+8 zDo2{h>Ps2K!|Po^AG>e&Yn4O(k^cY!uBPHoE(aiFmBo12{UnjugZNoo%(nV$Y{vi$ z*PD3L#k0)cXR%`ve-kTmNA#;6KGiNFhAU)tMB9^tjMoiiX?3XFEEaG{JnnE27bd&1 z{2Y0GwL{L#v2y2hsg5l2AZc5c=Pg}F!|w%ZzAV(ldQ?GU2M=*C5vS?T(!Ec>-v#X> zvrSLM_X1{B43<$h%0J(3{{TT<m5#A#qe%W&k!x!Z`BY&M0IYF&ykea?3h%#9_$MwG z11gQV^fGjxhWaOhKF=#T)O9w)H1nv!ANCiw*R3bSZy7|Eu-KzCmlAN0$MZWLoC?I( zwas_M7CMF3hAgC(+fIzhb$2NUv91Zo$r<1iUO!{4Y8E=27xvS)+aZ)plAc&N=yO=% zuvKvqRdFoVtm#sUr5({@U%%8L)C@YUl-9*_n^}WK-1ICsHIr!zNJcmwwV|wPSGxAC zKBcVPNq2iQW#o~wpF#8;F;0b8j@YkaQczZj#R$qy@@4xFA;3;s)~a}{i8S;ser0|+ zsg_NP=~;dtpUk(8J^*Ix>0I<;-Biu1OZROY`ljdU+qGJch{;e+O8ly7C`QIC+@>*+ z*wvdK-A?Lx7|5?qqtN4}9Z|dDE8j0ej?y&)?uiKOahz8vA<oh}@_x0kdaU|R!U<(< z#Gd~3lHYkr=);Q33l$hycQnMeDviG*bHTdR{(-5r?vrrV_ll)e)j>Es9t~qfBN<D! z;#rPzJJgzSWSN)`;WZ3086BDMS=hJG*1W#-pP)L8!Vzz4nkyJ(k9(b^uyA>;O+xO< z+eJ$$G~|;}X*Yeb+a)6(!WXtrddhF!G-o(=JmaNkYP#HTX)@`N*;-2X1svVFW?0op z`BeV^2tEC4nMKNRN23ang(<e}x0&Dk7XJWIe7NdgANR$10B^icTJ{fu2hCffz$b9P z{{VKpgsxnwe-Xg%T=*yc)m=aC{-k5o=~1_0JuMxs)W$N)^A0jU8uk4uZAV(t-5r7? zsC@qbD<Mj+Bp+U%jd{CTHJzMcT%GNf3)ARLcP*)0N8$J_ZDR7%0z4-L*!qt3mSp{} zD}w94W3wxRrAnkF*q6l~7n4x4(c4K>20KObH*vYLbNwnW4fu1!(p%hE!7^Lg%B0Gk zW&^5ne;V`;1=vArr^LGPx!5Blh;UmyhdnB;rK(2-$JwkQH#dC(uOWV>yhV<y%j{!m zB;Wiw-mo;(xt-Je3tDZ+@U5arZE|glXv!QBxw1LOxvVRzskJT~wT+aAfznAwx&9UR z{&ji{EH%SiYPSnL-|(*+WA*^nvRO2?{!iK!RPspS&$(bhu0LxSYTe)UvE9qo#zvC` zxVL12duv5jCjv#wd*nCssjTe8vp>qrhwivyHy=<BZ+`W>x<by#ZWugK;HquoBfmMx zt8zmc3?iN0x&C#G@fU90M4H&hwbJ5(UDB&cdf>6eUAvOnIK-C`M;uAX8;%dx)~V{+ zgj#far(n__x`6V3z<bvVsa$GW?Az_jDxvu>NCW79t!YMtt3GKvF6HuRc6GXjxn;4Q z(`z#DQ=mid@9kXG#-IJ8C))3r0n{r~^o=caOE-JzVv+N>Dz5<g4^vRf4d%H%z>OR^ z;TNk9dhDkN({I_mJDAGNYL0tRhCA6zR(yfMkgh=bR_}#;0WP6&8Y~h>&FH@|_x2w3 z=)M>53c+r-y0nRDZoLbyE$pE6KK0&N$d>lPEjAyq+73%F{p7yvYU<<MBg+?M__NQc zC^=p#@4rN4_;<oOc7d=oF<xFM`C>)Pai3G_Yhvbinrz5Svi4EYX%g;MR#-wu7x6hj zGgIlBUCyby?BHA44rHDFP<?S;YE>~3<&M5a4%F{DkNW<)B97-X2<_L;xo$Wa3+xB2 zY{#V8=&=jEeor=b`IgyES-;-)rrm2+9v^_gqac=YFxM^zKkoZ~o@<_1B)nF?)#rHa z$>Vn~DfCc!{cEZ)r$<dCFZ`8%pXKUBr&V41vHV~88W-A?{{V?5^3!F#j3XpxY;mut ztgFpVD@a<?N)7|?^K^NAhobvZT-{u1&X!trzn5-@<%v2E_L%oIX4(sVF6k}cjOseH zog+?FJ<04n>0WhZ%-!4SYiLqdw%tEgL#60XHrcOR%SIq7<K}N*E7A1ZD}4^f%w>)f zjGX@f54k<fP>n78HEP<Hw>r&!!wM8|pX{LPpVZeet=&ngLH6$v%G!RHozYqY``Ny= z>C&YP8t0n4`Yzt5nO3CnQnmfx&(KXzSkydGG`2n;Cf>zK6W=kw$9x~_R(v6*Sz4Ee zLmPts0I$?!9es8m>sh*7cb+YgEv?}G&(UE#M1vd0`(C_PVX9qe+7^@qtRl0IfTJ0e zhhRI>glCACw42d&{{S!ZDzEO+r4_Gl^X|sq7-qF5c|OmjLG!V5w9I`^x1j24&%9CM zht=aY)_bkA#5myLru@5mR&R;CLq4}CdyrwYL;I_BJ$=uq_pWwprj|{sjDgShP*ydu z9M^>{y1xGa<c|C{3)$Lrb$z>k!7WX3ji`&y@Sy%?sjS<sQYie%CXs>@8t!AB$||GJ zy!kfU*_`7&S<j%Zvd$ZC3~BOR-oE>${n-e4&v=yf{{U%+PJ^1@tx-`*%I)lqxl8XX z`Fd}KplgX_l367?2LpNj@~=Gy@i0Ea*0{}5{q-y9Cz4liPs$i=3cY{;kO1_mnx3OR zr+C*lYzaIn4iy)&sO$%&Ra>ana)LK6-Wlmd3_Fj%J!JPPojzS&<>M;EzcT^&)mOVm zx{Vq{3$(XTIIPW1=sZmreWk_=1B_QRZ{<so45xCj+S?9(T!Kf_wR#kAuA;e~BNVAw zTIZu$pR@$>zHQjR9ZhJeh{;4?*x<0~pK9gwy>b|%AG>s#P<Ep7PaJ_-DSq<Fv0@O0 ziP$uaz-8kfo_Ozy=amU3vFK5$3v(xSY}_o7X|p<l%&1&$=%XD^^!2VYSkr{pky=_Q z+W!DMf3$h#wRH`1e123z{Sg3DmdTKieqwu*jtADXEl@6@Y@mmq%O5c055}$vRDI(* zt~H|W^y~get)b~_X$`V246XCvkGc*rKRVo-OpGerf>j}4$Ni!Ma6dY^r`ji+CzXP% zB(K+=gWjRlFGQ{7#sZ+=0n)LZS0{F2sHtk!oe{~EhFiyw4XTBd4_s$I&-AEtDKi{O ztn0pcB4r2oT%Y7ft2VaINNCA1{J{E>1!;IsNs7};lJ4qA?k0Bq)IZ%~a9O>pUuhm! zbI+-PbtQDPG<0n}*^~EsTGYf;x{a(xOE14ao@=b);uc^rwaLfys+Vfe*~cVg41|=Z zAH?A2{{TU!q=ZIP9iyu^r?=L*YOi^wRtF5_`@~t6e5L^aDurhB_8#@0adKq&6NMac z+M>9e$b7i|RbD=y{d#l}WQ=i8rg~GdRz%jwMh$64a1!1~yEz}FK?)3DR%eLrq|<Hf z?ve(UHJ4$>10)<~y71DJr^_C<{Ej+~DP4~70&aUmitp^V4U(a7BrEH<@&<h>rlFu& z!3wscG9hK&WS(3T_?qA*`(@?q)5@&UyAn(L{{Tw6K9?-^u{FtZF~xh)pyL?Fn`6n9 zXi81;m*dwo-74s<n@c%Um5~n82<e9Fiua#|UNe)#dQ{(T084JloBsg4k>0*u`!g(p zNI5D<*uy5hTj36`1*}*0^UASYN>Py*?JbP(J$j1tqgl|V)OANR=_;<xSo$xoB8*o% zqw6UpvN||;&IelPA(bYKG8}_lG^<9cyiYo%8B=<W#TchiTA5gGD8)L80>g1l#Ys&f zm<%->YqY<+W~DV=XDIwo2{)n}YWuv^Jypp3J5==8@Bs$}`1;s<V3Ex_fj!2^!4R`_ z117nRO6~^N?Vwo+X;smb9stG<<Ng)X%BACF2LyrvuRhjv9a0TJbrfy#CL1y2^gS`# zii!D%@Lv`q$9r*V?U5X=pbnfIarsv-s(2>TQqk_UUq9^ftBr^knN+sY-uNU}srZ{h zYrBaiD&|%~<Q2gul6ds%T;v$K@ZHXxVv$2}5NW0+V7riP-JJgbx)^iofz4o_H>xSi zL%_A|Ds3B4YfGKJU{!pn;Hv;Z{D`dD==^_a_CFF#nIAGEmFMNkgSVdJrE_(>b|_xi zr432^KaC|YB*sYerzzOPjt@#`&lD5ZkPRoEC?=e^3!e0Z&;rNzxuMTJ>8nm-(vF}q zl9bX@Kon6&dH_1niVqY}0B95%C;;g}q@sWnP-!V>0HZXN0YC*P8KkA?C;&7lEO?}+ zFUyQj1AUD<G>z9ZlmO+&dQOy|N(}%hqLPXLDJf{}KnF@(<Wf)p#%KVg%_S57<}}(m znp_h=3ZNQ0Q$fWP0Hca`xD?Lx?D2{KO!3b&<M()`QuGu6xu)iojW&QJUbOf&EQifq z!v_OB>LE@f5lMiO9I+r^6TtMVHZd%uhHO<rd8uKP#Bu=ip{+Cm#ncMFbmad4DuDtF ztc*vY9MtMb*y_89;D9Q;qY^M60zD~5uncT&`FS4n%5kKt0HTU00dvMFC=^fuMHEm2 zfc$C4b9>V|Q9uD0HD^wZCZEfb_=X3yR`1O{a}z1+-_n}gPh)4qc#b=0R|P=Ge^5BZ zWX%YTW8A|m0U&k;uIqElD?#SQtA)z2{vdF3Rb*G0hT_cOdCzY_T2!->u`tmhwlaA9 zDM}S=5JCD>u#KchFm{}fYR$>{heEB-UPWe+S1B}X21w)9niY>gPtvZ<aE_#G?SaRo zMmO8-Vb?#EHjrGew6ES$KBAC{)w$IS_Nfq!Q*T@j4O5Ms?cNndI6Jeqk?oosQrU?& z)T=@1P79JT$u(syl@=GdEV0Pn!_*I2NjI?Du}+H6YilB)t;DO7fC_`hsph6SI59Mk z-a#U$CkG&$RjN~aB6-(SDvDZ4%&?#w9EvEQ1t%volu<wlOGOj_Q9z=A3QAfi0Hvg* z?LY}jMLlqMKUzaVpu!g1Juyd_oDWK2IjdH1?rqF42kTQNlDjm8<oQt~L)M(?2<D%R z92!PA9M(6xxzg3NWl16+<aDJ|(2k;}+zminZmkn+!bvTHMBp;?Kcz(3EJ>@TG8Y|G z`coDt&r(65NKOo{&y@n0Pc<a5?Q@LaR9WYWwQhtv^&YCyXK3lk#W2-~rz&z3bj31Z z&T=#DNjG~ktgUh!P$^9zsvs%Y6oaiiU}rR#48~XvnW5ukY%n1D5sEv|PpvSp+9~C- zpb$8w(Q#N&NlQsU1syt3MF3j!K3&5Y=s~EZbB(#<tw*@++uJ#$sZlA(hG}!gJ!z(j zq6MW>Nl8Ew5Gk~yni~{~9}jw$fmM)|U~19F)K^6(rHrMxZlwmBoE|A_VnxrbFM4e# z6kG@A-i*>vC=gMCY0AF54|*BBI#U6xDHVzwtt~XrGG%bxI{H+W?4m*>$4r1~nf9qr z?(5WdrkI*7+)D(5YN1x?(}PtSE_mbE(#Tt`26Na@tZ8l=EeMDzdEnAjhQoEJSuxY4 c0dbU3MInVH2dxUM?5cpXF~9(1lSmK$*~eQ`y8r+H literal 0 HcmV?d00001 diff --git a/src/dashboard/src/pages/SignIn/image2.jpeg b/src/dashboard/src/pages/SignIn/image2.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..1fbcc64661ebeeebc454354e4db7d8aba2cbcad0 GIT binary patch literal 401649 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%0AY2HU- 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%0A|VZu=x)`?N$C6 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�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�?f;mr`guZFvg*mG5V@<2)4#a&~L{ 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 literal 0 HcmV?d00001 diff --git a/src/dashboard/src/pages/SignIn/image3.jpeg b/src/dashboard/src/pages/SignIn/image3.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..50562452306ce426d9c791ea3013e5cc47a50c63 GIT binary patch literal 255458 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 $|&`t2#&oUcf3^1&3;pvE|Z-R0r>H%pDny9 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�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 literal 0 HcmV?d00001 diff --git a/src/dashboard/src/pages/SignIn/index.tsx b/src/dashboard/src/pages/SignIn/index.tsx new file mode 100644 index 000000000..24b2363b8 --- /dev/null +++ b/src/dashboard/src/pages/SignIn/index.tsx @@ -0,0 +1,76 @@ +import React from "react"; + +import { RouteComponentProps } from "react-router-dom" + +import { Box, Grid, Paper, Typography, Button, CircularProgress } from "@material-ui/core"; +import { createStyles, makeStyles } from "@material-ui/core/styles"; + +import image1 from "./image1.jpeg"; +import image2 from "./image2.jpeg"; +import image3 from "./image3.jpeg"; + +import UserContext from "../../contexts/User"; + +const useStyles = makeStyles(() => createStyles({ + container: { + backgroundImage: `url(${[image1, image2, image3][Date.now() % 3]})`, + backgroundSize: "cover", + backgroundPosition: "right" + } +})); + +const SignIn: React.FC<RouteComponentProps> = ({ history }) => { + const { email } = React.useContext(UserContext); + const [ signIn, setSignIn ] = React.useState(false); + const onButtonClick = React.useCallback(() => { + setSignIn(true); + }, []); + React.useEffect(() => { + if (email !== undefined) { + history.replace('/'); + } + }, [email, history]) + + const styles = useStyles(); + + return ( + <Grid container justify="flex-end" className={styles.container}> + <Grid + item xl={4} lg={5} md={6} sm={8} xs={12} zeroMinWidth + container alignItems="stretch" justify="flex-end" + > + <Paper square elevation={6}> + <Box paddingTop={10} paddingRight={5} paddingBottom={10} paddingLeft={5} minHeight="100%" display="flex"> + <Grid container direction="column" spacing={10} alignItems="center" justify="center"> + <Grid item> + <Typography variant="h2" component="h1" align="center"> + Deep Learning Training Service + </Typography> + </Grid> + <Grid item> + <Button + variant="outlined" + color="primary" + href="/api/authenticate" + disabled={signIn} + onClick={onButtonClick} + > + { signIn ? <CircularProgress size={24}/> : 'Sign in with corp account' } + </Button> + </Grid> + <Grid item> + <Typography variant="body2"> + {"Built with "} + <span role="img" aria-label="heart">❤️</span> + {" by ..."} + </Typography> + </Grid> + </Grid> + </Box> + </Paper> + </Grid> + </Grid> + ); +}; + +export default SignIn; diff --git a/src/dashboard/src/pages/Submission/DataJob.tsx b/src/dashboard/src/pages/Submission/DataJob.tsx new file mode 100644 index 000000000..b15ebefd9 --- /dev/null +++ b/src/dashboard/src/pages/Submission/DataJob.tsx @@ -0,0 +1,255 @@ +import React, {useEffect, useState} from "react"; +import { TransitionProps } from '@material-ui/core/transitions'; +import { + Card, + CardHeader, + CardContent, + CardActions, + Grid, + Container, + TextField, + Button, + Divider, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions, +} from "@material-ui/core"; +import { makeStyles, createStyles } from "@material-ui/core/styles"; +import ClusterSelectField from "./components/ClusterSelectField"; +import { DirectoryPathTextField } from '../Home/GPUCard' +import ClustersContext from "../../contexts/Clusters"; +import UserContext from "../../contexts/User"; +import TeamsContext from "../../contexts/Teams"; +import {Link} from "react-router-dom"; +import Slide from "@material-ui/core/Slide"; +import {green} from "@material-ui/core/colors"; +import useFetch from "use-http/dist"; +import formats from '../../Configuration/foldFormat.json'; +const useStyles = makeStyles(() => + createStyles({ + container: { + margin: "auto" + }, + submitButton: { + marginLeft: "auto" + }, + dialogText: { + color:green[400] + } + }) +); + +const Transition = React.forwardRef<unknown, TransitionProps>(function Transition(props, ref) { + return <Slide direction="down" ref={ref} {...props} />; +}); + +const DataJob: React.FC = (props: any) => { + const styles = useStyles(); + const [azureDataStorage, setAzureDataStorage] = useState(''); + const [nfsDataStorage, setNFSDataStorage] = useState(''); + const [openDialog, setOpenDialog] = useState(false); + const[dialogContentText, setDialogContentText] = useState(''); + const [submittable, setSubmittable] = useState(true); + const {email,uid } = React.useContext(UserContext); + const {teams, selectedTeam} = React.useContext(TeamsContext); + const { selectedCluster,saveSelectedCluster } = React.useContext(ClustersContext); + const [workStorage, setWorkStorage ] = useState(''); + const [dataStorage, setDataStorage] = useState(''); + + const team = React.useMemo(() => { + if (teams == null) return; + if (selectedTeam == null) return; + return teams.filter((team: any) => team.id === selectedTeam)[0]; + }, [teams, selectedTeam]); + const cluster = React.useMemo(() => { + if (team == null) return; + if (selectedCluster == null) return; + return team.clusters.filter((cluster: any) => cluster.id === selectedCluster)[0]; + }, [team, selectedCluster]); + const gpuModel = React.useMemo(() => { + if (cluster == null) return; + return Object.keys(cluster.gpus)[0]; + }, [cluster]); + + const handleClose = () => { + setOpenDialog(false); + } + const fetchDiretoryUrl = `/api/clusters/${selectedCluster}`; + const request = useFetch(fetchDiretoryUrl); + const fetchStorage = async () => { + const data = await request.get('/'); + const name = typeof email === 'string' ? email.split('@', 1)[0] : email; + setDataStorage(data.dataStorage); + setWorkStorage(`${data.workStorage}/${name}`); + } + useEffect(()=>{ + const { cluster } = props.location.state || ''; + if (cluster) {saveSelectedCluster(cluster)} + fetchStorage(); + },[selectedCluster, props.location.state, email, saveSelectedCluster]) + + const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { + if (event.target.name === 'azureDataStorage') { + setAzureDataStorage(event.target.value); + } + if (event.target.name === 'nfsDataStorage') { + setNFSDataStorage(event.target.value); + } + if (nfsDataStorage.length > 0 && azureDataStorage.length > 0 || event.target.value.length > 0) { + setSubmittable(false) + } else { + setSubmittable(true) + } + + } + + const convertURI = (type: string, folder: string) => { + if (type === "adls") { + if (folder.match(/^adl:\/\//)) { + // adl://example.com/file + return folder; + } else if (folder.match(/^\/\//i)) { + // //example.com/file + return "adl:" + folder; + } else if (folder.match(/^\//i)) { + // /example.com/file + return "adl:/" + folder; + } else { + // example.com/file + return "adl://" + folder; + } + } else if (type === "nfs") { + if (folder.match(/^\//)) { + // /dir/file + return folder; + } else { + // dir/file + return "/" + folder; + } + } + return folder; + } + const covert = (dataJob: any) => { + dataJob.vcName = selectedTeam; + dataJob.jobName = "Data Job @ " + new Date().toISOString(); + if (azureDataStorage) {dataJob.fromFolder = azureDataStorage;} + if (nfsDataStorage) {dataJob.toFolder = nfsDataStorage;} + dataJob.userName = email; + dataJob.jobType = 'training'; + dataJob.jobtrainingtype = "RegularJob"; + dataJob.gpuType = gpuModel; + dataJob.runningasroot = "1"; + dataJob.resourcegpu = 0; + dataJob.containerUserId = 0; + dataJob.userId = uid; + dataJob.image = "indexserveregistry.azurecr.io/dlts-data-transfer-image"; + dataJob.cmd = [ + "cd /DataUtils && ./copy_data.sh", + convertURI("adls", dataJob.fromFolder), + convertURI("nfs", dataJob.toFolder), + "False 33554432 4 8 2>/dev/null" + ].join(" "); + return dataJob; + } + const[currentJobId, setCurrentJobId] = useState(''); + const postDataJob = () => { + let dataJob: any = {}; + dataJob = covert(dataJob); + fetch(`/api/clusters/${selectedCluster}/jobs`,{ + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body:JSON.stringify(dataJob) + }).then(async (res: any) => { + const data = await res.json(); + const { jobId } = data; + if (jobId) { + setDialogContentText(`${jobId} successfully submitted`); + setCurrentJobId(jobId); + setOpenDialog(true); + } + }) + } + const onSubmit = () => { + if (!submittable){ + postDataJob() + } + } + + return ( + <Container maxWidth="md" className={styles.container}> + <Card> + <CardHeader title="Manage Data"/> + <Divider/> + <CardContent> + <Grid + container + wrap="wrap" + spacing={1} + > + <Grid item xs={12} sm={6}> + <ClusterSelectField + fullWidth + cluster={selectedCluster} + onClusterChange={saveSelectedCluster} + /> + </Grid> + <DirectoryPathTextField + label="Work Directory" + value={workStorage} + /> + <DirectoryPathTextField + label="Data Directory" + value={dataStorage} + /> + <TextField + error={ !azureDataStorage} + name={"azureDataStorage"} + onChange={handleChange} + id="outlined-error" + label="From Folder of Azure Data Lake Storage *" + defaultValue={formats.azureDataStorage} + placeholder={formats.azureDataStorage} + fullWidth + margin="dense" + /> + <TextField + error={!nfsDataStorage} + name={"nfsDataStorage"} + onChange={handleChange} + id="outlined-error" + defaultValue={formats.nfsDataStorage} + placeholder={formats.nfsDataStorage} + label="To NFS Data Folder *" + fullWidth + margin="dense" + /> + </Grid> + </CardContent> + <CardActions> + <Button type="submit" disabled ={submittable} color="primary" variant="contained" className={styles.submitButton} onClick={postDataJob}>Submit</Button> + </CardActions> + </Card> + <Dialog + open={openDialog} + TransitionComponent={Transition} + onClose={handleClose} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + <DialogTitle id="alert-dialog-title">{"Info"}</DialogTitle> + <DialogContent> + <DialogContentText id="alert-dialog-description" className={styles.dialogText}> + { dialogContentText } + </DialogContentText> + <DialogActions> + <Button component={Link} + to={ `/job/${selectedCluster}/${currentJobId}` } + color="secondary" + > + ok + </Button> + </DialogActions> + </DialogContent> + </Dialog> + </Container> + ) +} + +export default DataJob; diff --git a/src/dashboard/src/pages/Submission/Training.tsx b/src/dashboard/src/pages/Submission/Training.tsx new file mode 100644 index 000000000..92527a337 --- /dev/null +++ b/src/dashboard/src/pages/Submission/Training.tsx @@ -0,0 +1,1025 @@ +import React, {useCallback, useState} from "react"; + +import { + Card, + CardHeader, + CardContent, + CardActions, + Grid, + Container, + TextField, + FormControlLabel, + Checkbox, + Button, + Divider, + Chip, + Collapse, + Typography, + Table, + TableHead, + TableRow, + TableCell, + TableBody, + Switch, + MenuItem, + SvgIcon, DialogActions, useMediaQuery, Snackbar, SnackbarContent +} from "@material-ui/core"; +import Tooltip from '@material-ui/core/Tooltip'; +import { makeStyles, createStyles } from "@material-ui/core/styles"; +import { Info, Delete, Add } from "@material-ui/icons"; +import { withRouter } from "react-router"; +import IconButton from '@material-ui/core/IconButton'; +import { useGet, usePost, usePut } from "use-http"; +import { join } from 'path'; + +import ClusterSelectField from "./components/ClusterSelectField"; +import UserContext from "../../contexts/User"; +import ClustersContext from '../../contexts/Clusters'; +import TeamsContext from "../../contexts/Teams"; +import theme, { Provider as MonospacedThemeProvider } from "../../contexts/MonospacedTheme"; +import useFetch, {useDelete} from "use-http/dist"; +import Dialog from '@material-ui/core/Dialog'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import _ from 'lodash'; +import {BarChart, Bar, XAxis, YAxis, CartesianGrid} from "recharts"; +import Paper, { PaperProps } from '@material-ui/core/Paper'; +import Draggable from 'react-draggable' +import {TransitionProps} from "@material-ui/core/transitions"; +import Slide from "@material-ui/core/Slide"; +import {green, grey, red} from "@material-ui/core/colors"; + +interface EnvironmentVariable { + name: string; + value: string; +} + +const useStyles = makeStyles(() => + createStyles({ + container: { + margin: "auto" + }, + submitButton: { + marginLeft: "auto" + } + }) +); +const PaperComponent = (props: PaperProps) => { + return ( + <Draggable cancel={'[class*="MuiDialogContent-root"]'}> + <Paper {...props} /> + </Draggable> + ); +} +const sanitizePath = (path: string) => { + path = join('/', path); + path = join('.', path); + return path; +} +const Training: React.ComponentClass = withRouter(({ history }) => { + const { selectedCluster,saveSelectedCluster } = React.useContext(ClustersContext); + const { email, uid } = React.useContext(UserContext); + const { teams, selectedTeam }= React.useContext(TeamsContext); + //const team = 'platform'; + const [showGPUFragmentation, setShowGPUFragmentation] = React.useState(false) + const [prometheusUrl, setPrometheusUrl] = React.useState(''); + const [name, setName] = React.useState(""); + const [gpuFragmentation, setGpuFragmentation] = React.useState<any[]>([]); + const onNameChange = React.useCallback( + (event: React.ChangeEvent<HTMLInputElement>) => { + setName(event.target.value); + }, + [setName] + ); + const team = React.useMemo(() => { + if (teams == null) return; + if (selectedTeam == null) return; + return teams.filter((team: any) => team.id === selectedTeam)[0]; + }, [teams, selectedTeam]); + const cluster = React.useMemo(() => { + if (team == null) return; + if (selectedCluster == null) return; + return team.clusters.filter((cluster: any) => cluster.id === selectedCluster)[0]; + }, [team, selectedCluster]); + const gpuModel = React.useMemo(() => { + if (cluster == null) return; + return Object.keys(cluster.gpus)[0]; + }, [cluster]); + const gpusPerNode = React.useMemo(() => { + if (cluster == null || gpuModel == null) return; + return cluster.gpus[gpuModel].perNode; + }, [cluster, gpuModel]); + + const [templates, templatesLoading, templatesError, getTemplates] = useGet('/api'); + React.useEffect(() => { + getTemplates(`/teams/${selectedTeam}/templates`); + }, [getTemplates, selectedTeam]); + + const [type, setType] = React.useState("RegularJob"); + const onTypeChange = React.useCallback( + (event: React.ChangeEvent<{ value: unknown }>) => { + setType(event.target.value as string); + }, + [setType] + ); + + const [preemptible, setPreemptible] = React.useState(false); + const onPreemptibleChange = React.useCallback( + (event: React.ChangeEvent<{ value: unknown }>) => { + setPreemptible(event.target.value === 'true'); + }, + [setPreemptible] + ); + + const [gpus, setGpus] = React.useState(0); + const onGpusChange = React.useCallback( + (event: React.ChangeEvent<HTMLInputElement>) => { + let value = event.target.valueAsNumber || 0; + if (value < 0) { value = 0; } + if (value > 0) { value = 26; } + setGpus(event.target.valueAsNumber); + }, + [setGpus] + ); + + const [workers, setWorkers] = React.useState(0); + const onWorkersChange = React.useCallback( + (event: React.ChangeEvent<HTMLInputElement>) => { + let value = event.target.valueAsNumber || 0; + if (value < 0) { value = 0; } + if (value > 0) { value = 26; } + setWorkers(event.target.valueAsNumber); + }, + [setWorkers] + ); + + const [image, setImage] = React.useState(""); + const onImageChange = React.useCallback( + (event: React.ChangeEvent<HTMLInputElement>) => { + setImage(event.target.value); + }, + [setImage] + ); + + const [command, setCommand] = React.useState(""); + const onCommandChange = React.useCallback( + (event: React.ChangeEvent<HTMLInputElement>) => { + setCommand(event.target.value); + }, + [setCommand] + ); + + const [interactivePorts, setInteractivePorts] = React.useState(""); + const onInteractivePortsChange = React.useCallback( + (event: React.ChangeEvent<HTMLInputElement>) => { + setInteractivePorts(event.target.value); + }, + [setInteractivePorts] + ); + + const [ssh, setSsh] = React.useState(false); + const onSshChange = React.useCallback( + (event: unknown, checked: boolean) => { + setSsh(checked); + }, + [setSsh] + ); + + const [ipython, setIpython] = React.useState(false); + const onIpythonChange = React.useCallback( + (event: unknown, checked: boolean) => { + setIpython(checked); + }, + [setIpython] + ); + + const [tensorboard, setTensorboard] = React.useState(false); + const onTensorboardChange = React.useCallback( + (event: unknown, checked: boolean) => { + setTensorboard(checked); + }, + [setTensorboard] + ); + + const [advanced, setAdvanced] = React.useState(false); + const onAdvancedClick = () => { + setAdvanced(!advanced); + } + + const [workPath, setWorkPath] = React.useState(""); + const onWorkPathChange = React.useCallback( + (event: React.ChangeEvent<HTMLInputElement>) => { + setWorkPath(event.target.value); + }, + [setWorkPath] + ) + + const [enableWorkPath, setEnableWorkPath] = React.useState(true); + const onEnableWorkPathChange = React.useCallback( + (event: unknown, checked: boolean) => { + setEnableWorkPath(checked); + }, + [setEnableWorkPath] + ); + + const [dataPath, setDataPath] = React.useState(""); + const onDataPathChange = React.useCallback( + (event: React.ChangeEvent<HTMLInputElement>) => { + setDataPath(event.target.value); + }, + [setDataPath] + ) + + const [enableDataPath, setEnableDataPath] = React.useState(true); + const onEnableDataPathChange = React.useCallback( + (event: unknown, checked: boolean) => { + setEnableDataPath(checked); + }, + [setEnableDataPath] + ); + + const [jobPath, setJobPath] = React.useState(""); + const onJobPathChange = React.useCallback( + (event: React.ChangeEvent<HTMLInputElement>) => { + setJobPath(event.target.value); + }, + [setJobPath] + ) + + const [enableJobPath, setEnableJobPath] = React.useState(true); + const onEnableJobPathChange = React.useCallback( + (event: unknown, checked: boolean) => { + setEnableJobPath(checked); + }, + [setEnableJobPath] + ); + const [showSaveTemplate, setSaveTemplate] = React.useState(false); + const [environmentVariables, setEnvironmentVariables] = React.useState<EnvironmentVariable[]>([]); + const onEnvironmentVariableNameChange = React.useCallback( + (index: number) => (event: React.ChangeEvent<HTMLInputElement>) => { + const newEnvironmentVariables = environmentVariables.slice() + environmentVariables[index].name = event.target.value; + setEnvironmentVariables(newEnvironmentVariables); + }, + [environmentVariables] + ); + const onEnvironmentVariableValueChange = React.useCallback( + (index: number) => (event: React.ChangeEvent<HTMLInputElement>) => { + const newEnvironmentVariables = environmentVariables.slice() + environmentVariables[index].value = event.target.value; + setEnvironmentVariables(newEnvironmentVariables); + }, + [environmentVariables] + ); + const onRemoveEnvironmentVariableClick = React.useCallback( + (index: number) => () => { + const newEnvironmentVariables = environmentVariables.slice(); + newEnvironmentVariables.splice(index, 1); + setEnvironmentVariables(newEnvironmentVariables) + }, + [environmentVariables] + ) + const onAddEnvironmentVariableClick = React.useCallback(() => { + setEnvironmentVariables( + environmentVariables.concat( + [{ name: "", value: "" }])); + }, [environmentVariables]); + + const [database, setDatabase] = React.useState(false); + // const onDatabaseClick = React.useCallback(() => { + // setDatabase(true); + // }, []); + const onDatabaseClick = () => { + setDatabase(!database); + } + + + const [saveTemplateName, setSaveTemplateName] = React.useState(""); + const onSaveTemplateNameChange = React.useCallback( + (event: React.ChangeEvent<HTMLInputElement>) => { + setSaveTemplateName(event.target.value); + }, + [setSaveTemplateName] + ); + + const [saveTemplateDatabase, setSaveTemplateDatabase] = React.useState("user"); + const onSaveTemplateDatabaseChange = React.useCallback( + (event: React.ChangeEvent<{ value: unknown }>) => { + setSaveTemplateDatabase(event.target.value as string); + }, + [setSaveTemplateDatabase] + ); + const { put: saveTemplate } = usePut('/api') + const {delete: deleteTemplate} = useDelete('/api'); + const onSaveTemplateClick = async () => { + try { + const template = { + name, + type, + gpus, + workers, + image, + command, + workPath, + enableWorkPath, + dataPath, + enableDataPath, + jobPath, + enableJobPath, + environmentVariables, + ssh, + ipython, + tensorboard, + }; + const url = `/teams/${selectedTeam}/templates/${saveTemplateName}?database=${saveTemplateDatabase}`; + await saveTemplate(url, template); + setSaveTemplate(true) + } catch (error) { + alert('Failed to save the template, check console (F12) for technical details.') + console.error(error); + } + }; + const [showDeleteTemplate, setShowDeleteTemplate] = useState(false) + const onDeleteTemplateClick = async () => { + try { + const template = { + name, + type, + gpus, + workers, + image, + command, + workPath, + enableWorkPath, + dataPath, + enableDataPath, + jobPath, + enableJobPath, + environmentVariables, + ssh, + ipython, + tensorboard, + }; + const url = `/teams/${selectedTeam}/templates/${saveTemplateName}?database=${saveTemplateDatabase}`; + await deleteTemplate(url); + console.log(await deleteTemplate(url,template)) + setShowDeleteTemplate(true) + } catch (error) { + alert('Failed to delete the template, check console (F12) for technical details.') + console.error(error); + } + } + const [json, setJson] = React.useState('-1'); + const onTemplateChange = React.useCallback( + (event: React.ChangeEvent<{ value: unknown }>) => { + setJson(event.target.value as string) + if (event.target.value == -1) { + setName(""); + setType("RegularJob"); + setGpus(0); + setWorkers(0); + setImage(""); + setCommand(""); + setWorkPath(""); + setEnableWorkPath(true); + setDataPath(""); + setEnableDataPath(true); + setJobPath(""); + setEnableJobPath(true); + setEnvironmentVariables([]); + setSsh(false); + setIpython(false); + setTensorboard(false); + } else { + const { + name, + type, + gpus, + workers, + image, + command, + workPath, + enableWorkPath, + dataPath, + enableDataPath, + jobPath, + enableJobPath, + environmentVariables, + ssh, + ipython, + tensorboard, + } = JSON.parse(event.target.value as string); + console.log('jobpath', jobPath) + if (name !== undefined) setName(name); + if (type !== undefined) setType(type); + if (gpus !== undefined) setGpus(gpus); + if (workers !== undefined) setWorkers(workers); + if (image !== undefined) setImage(image); + if (command !== undefined) setCommand(command); + if (workPath !== undefined) setWorkPath(workPath); + if (enableWorkPath !== undefined) setEnableWorkPath(enableWorkPath); + if (dataPath !== undefined) setDataPath(dataPath); + if (enableDataPath !== undefined) setEnableDataPath(enableDataPath); + if (jobPath !== undefined) setJobPath(jobPath); + if (enableJobPath !== undefined) setEnableJobPath(enableJobPath); + if (environmentVariables !== undefined) setEnvironmentVariables(environmentVariables); + if (ssh !== undefined) setSsh(ssh); + if (ipython !== undefined) setIpython(ipython); + if (tensorboard !== undefined) setTensorboard(tensorboard); + } + }, + [] + ); + + const [ + postJobData, + postJobLoading, + postJobError, + postJob + ] = usePost('/api'); + const [ + postEndpointsData, + postEndpointsLoading, + postEndpointsError, + postEndpoints + ] = usePost('/api'); + + const submittable = React.useMemo(() => { + if (!gpuModel) return false; + if (!selectedTeam) return false; + if (!name) return false; + if (!image) return false; + if (!command.trim()) return false; + return true; + }, [gpuModel, selectedTeam, name, image, command]); + const Transition = React.forwardRef<unknown, TransitionProps>(function Transition(props, ref) { + return <Slide direction="down" ref={ref} {...props} />; + }); + const [open, setOpen] = React.useState(false); + const onSubmit = (event: React.FormEvent) => { + event.preventDefault(); + if (!submittable) return; + + const job: any = { + userName: email, + userId: uid, + jobType: 'training', + gpuType: gpuModel, + vcName: selectedTeam, + containerUserId: 0, + jobName: name, + jobtrainingtype: type, + preemptionAllowed: preemptible ? 'True' : 'False', + image, + cmd: command, + workPath: sanitizePath(workPath || ''), + enableworkpath: enableWorkPath, + dataPath: sanitizePath(dataPath || ''), + enabledatapath: enableDataPath, + jobPath: jobPath || '', + enablejobpath: enableJobPath, + env: environmentVariables, + hostNetwork : type === 'PSDistJob' ? true : false, + isPrivileged : type === 'PSDistJob' ? true : false, + }; + + let totalGpus = gpus; + if (type === 'PSDistJob') { + job.numps = 1; + job.resourcegpu = gpusPerNode; + job.numpsworker = workers; + totalGpus = gpusPerNode * workers; + } else { + job.resourcegpu = gpus; + } + + // if (totalGpus > (cluster.userQuota)) { + // if (!window.confirm('Your job will be using gpus more than the quota.\nProceed?')) { + // return; + // } + // } + + if (type === 'PSDistJob') { + // Check GPU fragmentation + let workersNeeded = workers; + for (const { metric, value } of gpuFragmentation) { + if (Number(metric['gpu_available']) >= gpusPerNode) { + workersNeeded -= (Number(value[1]) || 0); + } + if (workersNeeded <= 0) break; + } + if (workersNeeded > 0) { + if (!window.confirm('There won\'t be enough workers match your request.\nProceed?')) { + return; + } + } + } + postJob(`/clusters/${selectedCluster}/jobs`, job); + }; // Too many dependencies, do not cache. + + const jobId = React.useRef<string>(); + + React.useEffect(() => { + if (postJobData == null) return; + + jobId.current = postJobData['jobId']; + const endpoints = []; + + for (const port of interactivePorts.split(',')) { + const portNumber = Number(port) + if (portNumber >= 40000 && portNumber <= 49999) { + endpoints.push({ + name: `port-${portNumber}`, + podPort: portNumber + }); + } + } + + if (ssh) endpoints.push('ssh'); + if (ipython) endpoints.push('ipython'); + if (tensorboard) endpoints.push('tensorboard'); + + if (endpoints.length > 0) { + postEndpoints(`/clusters/${selectedCluster}/jobs/${jobId.current}/endpoints`, { endpoints }); + } else { + history.push(`/job/${selectedCluster}/${jobId.current}`); + } + }, [postJobData, postEndpoints, ssh, ipython, tensorboard, interactivePorts, history, selectedCluster]); + const fetchPrometheusUrl = `/api/clusters`; + const request = useFetch(fetchPrometheusUrl); + const fetchPrometheus = async () => { + const {prometheus} = await request.get(`/${selectedCluster}`); + setPrometheusUrl(prometheus); + } + const handleCloseGPUGramentation = () => { + setShowGPUFragmentation(false); + } + + React.useEffect(() => { + fetchPrometheus() + if (postEndpointsData) { + setOpen(true); + setTimeout(()=>{ + history.push(`/job/${selectedCluster}/${jobId.current}`); + },2000) + + } + }, [ history, postEndpointsData, selectedCluster,setOpen, open]) + + React.useEffect(() => { + if (postJobError) { + alert('Job submission failed') + } + }, [postJobError]) + + React.useEffect(() => { + if (postEndpointsError) { + alert('Enable endpoints failed') + } + }, [postEndpointsError]) + + + const handleClickOpen = () => { + setShowGPUFragmentation(true) + } + const handleClose = () => { + setOpen(false) + setSaveTemplate(false) + setShowDeleteTemplate(false) + } + React.useEffect(() => { + if (!prometheusUrl) return; + let getNodeGpuAva = `${prometheusUrl}/prometheus/api/v1/query?`; + const params = new URLSearchParams({ + query:'count_values("gpu_available", k8s_node_gpu_available)' + }); + fetch(getNodeGpuAva+params).then(async (res: any) => { + const {data} = await res.json(); + const result = data['result']; + const sortededResult = result.sort((a: any, b: any)=>a['metric']['gpu_available'] - b['metric']['gpu_available']); + setGpuFragmentation(sortededResult) + }) + }, [prometheusUrl]) + + const styles = useStyles(); + + const isDesktop = useMediaQuery(theme.breakpoints.up("sm")); + + const showMessage = (open: boolean,showDeleteTemplate: boolean,showSaveTemplate: boolean) => { + let message = ''; + if (open) { + message = 'Job Submission Successfully'; + } + if (showDeleteTemplate) { + message = 'Template Deleted'; + } + if (showSaveTemplate) { + message = 'Template Saved'; + } + return message; + } + + const renderWarn = (open: boolean,showDeleteTemplate: boolean,showSaveTemplate: boolean) => { + return ( + <Snackbar + anchorOrigin={{ vertical: 'top', horizontal: 'right' }} + open={open || showSaveTemplate || showDeleteTemplate} + autoHideDuration={1000} + onClose={handleClose} + ContentProps={{ + 'aria-describedby': 'message-id', + }} + > + <SnackbarContent + style={{backgroundColor:showDeleteTemplate ? red[400] : green[400]}} + aria-describedby="client-snackbar" + message={<span id="message-id" >{showMessage(open,showDeleteTemplate,showSaveTemplate)}</span>} + /> + </Snackbar> + ) + } + + return ( + + <Container maxWidth={isDesktop ? 'lg' : 'xs'}> + <Dialog + open={showGPUFragmentation} + onClose={handleCloseGPUGramentation} + PaperComponent={PaperComponent} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + <DialogTitle style={{ cursor: 'move' }} id="alert-dialog-title">{"View Cluster GPU Status Per Node"}</DialogTitle> + <DialogContent> + <DialogContentText id="alert-dialog-description"> + <BarChart width={500} height={600} data={gpuFragmentation}> + <CartesianGrid strokeDasharray="10 10"/> + <XAxis dataKey={"metric['gpu_available']"} label={{value: 'Available gpu count', offset:0,position:'insideBottom'}}> + </XAxis> + <YAxis label={{value: 'Node count', angle: -90, position: 'insideLeft'}} /> + <Bar dataKey="value[1]" fill="#8884d8" /> + </BarChart> + </DialogContentText> + </DialogContent> + </Dialog> + <form onSubmit={onSubmit}> + <Card> + <CardHeader title="Submit Training Job"/> + <Divider/> + <CardContent> + <Grid + container + wrap="wrap" + spacing={1} + > + <Grid item xs={12} sm={6}> + <ClusterSelectField + fullWidth + cluster={selectedCluster} + onClusterChange={saveSelectedCluster} + /> + <Tooltip title="View Cluster GPU Status Per Node"> + <IconButton color="secondary" size="small" onClick={handleClickOpen} aria-label="delete"> + <SvgIcon> + <path d="M5 9.2h3V19H5zM10.6 5h2.8v14h-2.8zm5.6 8H19v6h-2.8z"/><path fill="none" d="M0 0h24v24H0z"/> + </SvgIcon> + </IconButton> + </Tooltip> + </Grid> + <Grid item xs={12} sm={6}> + <TextField + label="Job Name" + fullWidth + variant="filled" + value={name} + onChange={onNameChange} + /> + </Grid> + <Grid item xs={12}> + <TextField + disabled={!Array.isArray(templates)} + select + label="Job Template" + fullWidth + variant="filled" + value={json} + onChange={onTemplateChange} + > + <MenuItem value={-1} divider>None (Apply a Template)</MenuItem> + {Array.isArray(templates) && templates.map(({ name, json }: any, index: number) => ( + <MenuItem key={index} value={json}>{name}</MenuItem> + ))} + </TextField> + </Grid> + <Grid item xs={12} sm={6}> + <TextField + select + label="Job Type" + fullWidth + variant="filled" + value={type} + onChange={onTypeChange} + > + <MenuItem value="RegularJob">Regular Job</MenuItem> + <MenuItem value="PSDistJob">Distirbuted Job</MenuItem> + </TextField> + </Grid> + <Grid item xs={12} sm={6}> + <TextField + select + label="Preemptible Job" + fullWidth + variant="filled" + value={String(preemptible)} + onChange={onPreemptibleChange} + > + <MenuItem value="false">NO</MenuItem> + <MenuItem value="true">YES</MenuItem> + </TextField> + </Grid> + { type === 'RegularJob' && ( + <Grid item xs={12}> + <TextField + type="number" + error={gpus > cluster.userQuota} + label="Number of GPUs" + fullWidth + variant="filled" + value={gpus} + onChange={onGpusChange} + /> + </Grid> + )} + { type === 'PSDistJob' && ( + <Grid item xs={12} sm={6}> + <TextField + type="number" + label="Number of Nodes" + fullWidth + variant="filled" + value={workers} + onChange={onWorkersChange} + /> + </Grid> + )} + { type === 'PSDistJob' && ( + <Grid item xs={12} sm={6}> + <TextField + disabled + type="number" + label="Total Number of GPUs" + value = {workers * gpusPerNode} + fullWidth + variant="filled" + /> + </Grid> + )} + <Grid item xs={12}> + <TextField + label="Docker Image" + fullWidth + variant="filled" + value={image} + onChange={onImageChange} + /> + </Grid> + <Grid item xs={12}> + <MonospacedThemeProvider> + <TextField + multiline + label="Command" + fullWidth + variant="filled" + rows="10" + value={command} + onChange={onCommandChange} + /> + </MonospacedThemeProvider> + </Grid> + <Grid item xs={12}> + <TextField + label="Interactive Ports" + placeholder="40000 - 49999. Separated by comma." + fullWidth + variant="filled" + rows="10" + value={interactivePorts} + onChange={onInteractivePortsChange} + /> + </Grid> + <Grid item xs={4} container justify="center"> + <FormControlLabel + control={<Checkbox />} + label="SSH" + checked={ssh} + onChange={onSshChange} + /> + </Grid> + <Grid item xs={4} container justify="center"> + <FormControlLabel + control={<Checkbox />} + label="iPython" + checked={ipython} + onChange={onIpythonChange} + /> + </Grid> + <Grid item xs={4} container justify="center"> + <FormControlLabel + control={<Checkbox />} + label={<>{"Tensorboard "}<Info fontSize="inherit"/></>} + checked={tensorboard} + onChange={onTensorboardChange} + /> + </Grid> + <Grid item xs={12} container justify="flex-end"> + <Chip + icon={<Info/>} + label="Tensorboard will listen on directory ~/tensorboard/<JobId>/logs inside docker container." + /> + </Grid> + </Grid> + </CardContent> + <Collapse in={advanced}> + <Divider/> + <CardContent> + <Typography component="span" variant="h6">Mount Directories</Typography> + <Table size="small"> + <TableHead> + <TableRow> + <TableCell>Path in Container</TableCell> + <TableCell>Path on Host Machine / Storage Server</TableCell> + <TableCell align="center">Enable</TableCell> + </TableRow> + </TableHead> + <TableBody> + <TableRow> + <TableCell>/work</TableCell> + <TableCell> + <TextField + label="Work Path" + fullWidth + margin="dense" + variant="filled" + value={workPath} + onChange={onWorkPathChange} + /> + </TableCell> + <TableCell align="center"> + <Switch + value={enableWorkPath} + checked={enableWorkPath} + onChange={onEnableWorkPathChange} + /> + </TableCell> + <TableCell align="center"> + </TableCell> + </TableRow> + <TableRow> + <TableCell>/data</TableCell> + <TableCell> + <TextField + label="Data Path" + fullWidth + margin="dense" + variant="filled" + value={dataPath} + onChange={onDataPathChange} + /> + </TableCell> + <TableCell align="center"> + <Switch + value={enableDataPath} + checked={enableDataPath} + onChange={onEnableDataPathChange} + /> + </TableCell> + </TableRow> + <TableRow> + <TableCell>/job</TableCell> + <TableCell> + <TextField + label="Job Path" + fullWidth + margin="dense" + variant="filled" + value={jobPath} + onChange={onJobPathChange} + /> + </TableCell> + <TableCell align="center"> + <Switch + value={enableJobPath} + checked={enableDataPath} + onChange={onEnableJobPathChange} + /> + </TableCell> + </TableRow> + </TableBody> + </Table> + </CardContent> + <CardContent> + <Typography component="span" variant="h6">Environment Variables</Typography> + <Table size="small"> + <TableHead> + <TableRow> + <TableCell>Name</TableCell> + <TableCell>Value</TableCell> + <TableCell/> + </TableRow> + </TableHead> + <TableBody> + { + environmentVariables.map(({ name, value }, index) => ( + <TableRow key={index}> + <TableCell> + <TextField + label="Environment Variable Name" + fullWidth + margin="dense" + variant="filled" + value={name} + onChange={onEnvironmentVariableNameChange(index)} + /> + </TableCell> + <TableCell> + <TextField + label="Environment Variable Value" + fullWidth + margin="dense" + variant="filled" + value={value} + onChange={onEnvironmentVariableValueChange(index)} + /> + </TableCell> + <TableCell align="center"> + <IconButton size="small" color="secondary" onClick={onRemoveEnvironmentVariableClick(index)}> + <Delete/> + </IconButton> + </TableCell> + </TableRow> + )) + } + <TableRow> + <TableCell/> + <TableCell/> + <TableCell align="center"> + <IconButton size="small" color="secondary" onClick={onAddEnvironmentVariableClick}> + <Add/> + </IconButton> + </TableCell> + </TableRow> + </TableBody> + </Table> + </CardContent> + </Collapse> + <Collapse in={database}> + <Divider/> + <CardContent> + <Typography component="span" variant="h6">Database Management</Typography> + <Grid container wrap="wrap" spacing={1}> + <Grid item xs={12} sm={6}> + <TextField + label="Template name" + fullWidth + variant="filled" + value={saveTemplateName} + onChange={onSaveTemplateNameChange} + /> + </Grid> + <Grid item xs> + <TextField + label="Database" + select + fullWidth + variant="filled" + value={saveTemplateDatabase} + onChange={onSaveTemplateDatabaseChange} + > + <MenuItem value="user">user</MenuItem> + <MenuItem value="team">team</MenuItem> + </TextField> + </Grid> + <Button type="button" color="primary" onClick={onSaveTemplateClick}>Save</Button> + <Button type="button" color="secondary" onClick={onDeleteTemplateClick}>Delete</Button> + </Grid> + </CardContent> + </Collapse> + <Divider/> + <CardActions> + <Grid item xs={12} container justify="space-between"> + <Grid item xs container> + <Button type="button" color="secondary" onClick={onAdvancedClick}>Advanced</Button> + <Button type="button" color="secondary" onClick={onDatabaseClick}>Database</Button> + </Grid> + <Button type="submit" color="primary" variant="contained" disabled={!submittable || postJobLoading || postEndpointsLoading}>Submit</Button> + </Grid> + </CardActions> + </Card> + </form> + {renderWarn(open,showDeleteTemplate,showSaveTemplate)} + </Container> + ); +}); + +export default Training; diff --git a/src/dashboard/src/pages/Submission/components/ClusterSelectField.tsx b/src/dashboard/src/pages/Submission/components/ClusterSelectField.tsx new file mode 100644 index 000000000..bc977dc1c --- /dev/null +++ b/src/dashboard/src/pages/Submission/components/ClusterSelectField.tsx @@ -0,0 +1,83 @@ +import React from "react"; + +import { MenuItem, TextField } from "@material-ui/core"; +import { BaseTextFieldProps } from "@material-ui/core/TextField"; + +import ClustersContext from "../../../contexts/Clusters"; +import TeamsContext from "../../../contexts/Teams"; +import useFetch from "use-http/dist"; +import _ from "lodash"; + +interface ClusterSelectFieldProps { + cluster: string | undefined; + onClusterChange(value: string): void; +} + +const ClusterSelectField: React.FC<ClusterSelectFieldProps & BaseTextFieldProps> = ( + { cluster, onClusterChange, variant="standard", ...props } +) => { + const { clusters,selectedCluster, saveSelectedCluster } = React.useContext(ClustersContext); + const { selectedTeam } = React.useContext(TeamsContext); + const fetchVcStatusUrl = `/api`; + const[helperText, setHelperText] = React.useState(''); + + const request = useFetch(fetchVcStatusUrl,{ + onMount: true + }); + const fetchVC = async () => { + const response = await request.get(`/teams/${selectedTeam}/clusters/${selectedCluster}`); + return response; + } + const onChange = React.useCallback( + (event: React.ChangeEvent<HTMLSelectElement>) => { + saveSelectedCluster(event.target.value); + }, + [saveSelectedCluster] + ); + const isEmpty = (obj: object) => { + if (obj === undefined) return true; + for(let key in obj) { + if(obj.hasOwnProperty(key)) + return false; + } + return true; + } + React.useEffect(() => { + fetchVC().then((res)=>{ + let clusterName = ''; + if (!isEmpty(res)) { + clusterName = (String)(Object.keys(res['gpu_capacity'])[0]) + } + const gpuCapacity = isEmpty(res) ? 0 : (String)(Object.values(res['gpu_capacity'])[0]); + const gpuAvailable = isEmpty(res) ? 0 : (String)(Object.values(res['gpu_avaliable'])[0]); + setHelperText(`${clusterName} (${gpuAvailable} / ${gpuCapacity} to use)`); + }) + if (selectedCluster) { + onClusterChange(selectedCluster); + } + }, [clusters, onClusterChange, selectedCluster]); + + if (cluster === undefined) { + return null; + } + + return ( + <TextField + select + label="Cluster" + helperText={helperText} + value={cluster} + onChange={onChange} + variant="filled" + {...props} + > + {//const filterclusters = clusters.filter((cluster)=>(boolean)cluster["admin"]); + clusters && _.map(clusters,'id').map(cluster => ( + <MenuItem key={cluster} value={cluster}>{cluster}</MenuItem> + )) + } + </TextField> + ); +} + +export default ClusterSelectField; diff --git a/src/dashboard/src/pages/Submission/index.tsx b/src/dashboard/src/pages/Submission/index.tsx new file mode 100644 index 000000000..cdcae12da --- /dev/null +++ b/src/dashboard/src/pages/Submission/index.tsx @@ -0,0 +1,19 @@ +import React from "react"; + +import { Redirect, Route, Switch, withRouter } from "react-router-dom"; + +import Training from "./Training"; +import DataJob from "./DataJob"; + +const Submit = withRouter(({ match }) => { + return ( + <Switch> + <Route exact path={`${match.path}/training`} component={Training}/> + <Route exact path={`${match.path}/training-cluster`} component={Training}/> + <Route exact path={`${match.path}/data`} component={DataJob}/> + <Redirect to="/"/> + </Switch> + ); +}); + +export default Submit; diff --git a/src/dashboard/src/react-app-env.d.ts b/src/dashboard/src/react-app-env.d.ts new file mode 100644 index 000000000..6431bc5fc --- /dev/null +++ b/src/dashboard/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// <reference types="react-scripts" /> diff --git a/src/dashboard/src/setupProxy.js b/src/dashboard/src/setupProxy.js new file mode 100644 index 000000000..048ebf5ab --- /dev/null +++ b/src/dashboard/src/setupProxy.js @@ -0,0 +1,3 @@ +module.exports = (app) => { + app.use('/api', require('../server/api').callback()) +} diff --git a/src/dashboard/tsconfig.json b/src/dashboard/tsconfig.json new file mode 100644 index 000000000..0980b23fa --- /dev/null +++ b/src/dashboard/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve" + }, + "include": [ + "src" + ] +} diff --git a/src/dashboard/yarn.lock b/src/dashboard/yarn.lock new file mode 100644 index 000000000..b79eb32df --- /dev/null +++ b/src/dashboard/yarn.lock @@ -0,0 +1,14259 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@7.0.0", "@babel/code-frame@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" + integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== + dependencies: + "@babel/highlight" "^7.0.0" + +"@babel/core@7.4.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.4.3.tgz#198d6d3af4567be3989550d97e068de94503074f" + integrity sha512-oDpASqKFlbspQfzAE7yaeTmdljSH2ADIvBlb0RwbStltTuWa0+7CCI1fYVINNv9saHPa1W7oaKeuNuKj+RQCvA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/generator" "^7.4.0" + "@babel/helpers" "^7.4.3" + "@babel/parser" "^7.4.3" + "@babel/template" "^7.4.0" + "@babel/traverse" "^7.4.3" + "@babel/types" "^7.4.0" + convert-source-map "^1.1.0" + debug "^4.1.0" + json5 "^2.1.0" + lodash "^4.17.11" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/core@^7.1.0", "@babel/core@^7.1.6", "@babel/core@^7.4.3": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.4.4.tgz#84055750b05fcd50f9915a826b44fa347a825250" + integrity sha512-lQgGX3FPRgbz2SKmhMtYgJvVzGZrmjaF4apZ2bLwofAKiSjxU0drPh4S/VasyYXwaTs+A1gvQ45BN8SQJzHsQQ== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/generator" "^7.4.4" + "@babel/helpers" "^7.4.4" + "@babel/parser" "^7.4.4" + "@babel/template" "^7.4.4" + "@babel/traverse" "^7.4.4" + "@babel/types" "^7.4.4" + convert-source-map "^1.1.0" + debug "^4.1.0" + json5 "^2.1.0" + lodash "^4.17.11" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.4.0", "@babel/generator@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.4.4.tgz#174a215eb843fc392c7edcaabeaa873de6e8f041" + integrity sha512-53UOLK6TVNqKxf7RUh8NE851EHRxOOeVXKbK2bivdb+iziMyk03Sr4eaE9OELCbyZAAafAKPDwF2TPUES5QbxQ== + dependencies: + "@babel/types" "^7.4.4" + jsesc "^2.5.1" + lodash "^4.17.11" + source-map "^0.5.0" + trim-right "^1.0.1" + +"@babel/helper-annotate-as-pure@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32" + integrity sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz#6b69628dfe4087798e0c4ed98e3d4a6b2fbd2f5f" + integrity sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-builder-react-jsx@^7.3.0": + version "7.3.0" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.3.0.tgz#a1ac95a5d2b3e88ae5e54846bf462eeb81b318a4" + integrity sha512-MjA9KgwCuPEkQd9ncSXvSyJ5y+j2sICHyrI0M3L+6fnS4wMSNDc1ARXsbTfbb2cXHn17VisSnU/sHFTCxVxSMw== + dependencies: + "@babel/types" "^7.3.0" + esutils "^2.0.0" + +"@babel/helper-call-delegate@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.4.4.tgz#87c1f8ca19ad552a736a7a27b1c1fcf8b1ff1f43" + integrity sha512-l79boDFJ8S1c5hvQvG+rc+wHw6IuH7YldmRKsYtpbawsxURu/paVy57FZMomGK22/JckepaikOkY0MoAmdyOlQ== + dependencies: + "@babel/helper-hoist-variables" "^7.4.4" + "@babel/traverse" "^7.4.4" + "@babel/types" "^7.4.4" + +"@babel/helper-create-class-features-plugin@^7.4.0": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.4.4.tgz#fc3d690af6554cc9efc607364a82d48f58736dba" + integrity sha512-UbBHIa2qeAGgyiNR9RszVF7bUHEdgS4JAUNT8SiqrAN6YJVxlOxeLr5pBzb5kan302dejJ9nla4RyKcR1XT6XA== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-member-expression-to-functions" "^7.0.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.4.4" + "@babel/helper-split-export-declaration" "^7.4.4" + +"@babel/helper-define-map@^7.4.0", "@babel/helper-define-map@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.4.4.tgz#6969d1f570b46bdc900d1eba8e5d59c48ba2c12a" + integrity sha512-IX3Ln8gLhZpSuqHJSnTNBWGDE9kdkTEWl21A/K7PQ00tseBwbqCHTvNLHSBd9M0R5rER4h5Rsvj9vw0R5SieBg== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/types" "^7.4.4" + lodash "^4.17.11" + +"@babel/helper-explode-assignable-expression@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz#537fa13f6f1674df745b0c00ec8fe4e99681c8f6" + integrity sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA== + dependencies: + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-function-name@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" + integrity sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw== + dependencies: + "@babel/helper-get-function-arity" "^7.0.0" + "@babel/template" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-get-function-arity@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3" + integrity sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-hoist-variables@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.4.4.tgz#0298b5f25c8c09c53102d52ac4a98f773eb2850a" + integrity sha512-VYk2/H/BnYbZDDg39hr3t2kKyifAm1W6zHRfhx8jGjIHpQEBv9dry7oQ2f3+J703TLu69nYdxsovl0XYfcnK4w== + dependencies: + "@babel/types" "^7.4.4" + +"@babel/helper-member-expression-to-functions@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz#8cd14b0a0df7ff00f009e7d7a436945f47c7a16f" + integrity sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-module-imports@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d" + integrity sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-module-transforms@^7.1.0", "@babel/helper-module-transforms@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.4.4.tgz#96115ea42a2f139e619e98ed46df6019b94414b8" + integrity sha512-3Z1yp8TVQf+B4ynN7WoHPKS8EkdTbgAEy0nU0rs/1Kw4pDgmvYH3rz3aI11KgxKCba2cn7N+tqzV1mY2HMN96w== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-simple-access" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/template" "^7.4.4" + "@babel/types" "^7.4.4" + lodash "^4.17.11" + +"@babel/helper-optimise-call-expression@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz#a2920c5702b073c15de51106200aa8cad20497d5" + integrity sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g== + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-plugin-utils@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" + integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== + +"@babel/helper-regex@^7.0.0", "@babel/helper-regex@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.4.4.tgz#a47e02bc91fb259d2e6727c2a30013e3ac13c4a2" + integrity sha512-Y5nuB/kESmR3tKjU8Nkn1wMGEx1tjJX076HBMeL3XLQCu6vA/YRzuTW0bbb+qRnXvQGn+d6Rx953yffl8vEy7Q== + dependencies: + lodash "^4.17.11" + +"@babel/helper-remap-async-to-generator@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz#361d80821b6f38da75bd3f0785ece20a88c5fe7f" + integrity sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-wrap-function" "^7.1.0" + "@babel/template" "^7.1.0" + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-replace-supers@^7.1.0", "@babel/helper-replace-supers@^7.4.0", "@babel/helper-replace-supers@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.4.4.tgz#aee41783ebe4f2d3ab3ae775e1cc6f1a90cefa27" + integrity sha512-04xGEnd+s01nY1l15EuMS1rfKktNF+1CkKmHoErDppjAAZL+IUBZpzT748x262HF7fibaQPhbvWUl5HeSt1EXg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.0.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/traverse" "^7.4.4" + "@babel/types" "^7.4.4" + +"@babel/helper-simple-access@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz#65eeb954c8c245beaa4e859da6188f39d71e585c" + integrity sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w== + dependencies: + "@babel/template" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-split-export-declaration@^7.4.0", "@babel/helper-split-export-declaration@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" + integrity sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q== + dependencies: + "@babel/types" "^7.4.4" + +"@babel/helper-wrap-function@^7.1.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa" + integrity sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/template" "^7.1.0" + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.2.0" + +"@babel/helpers@^7.4.3", "@babel/helpers@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.4.4.tgz#868b0ef59c1dd4e78744562d5ce1b59c89f2f2a5" + integrity sha512-igczbR/0SeuPR8RFfC7tGrbdTbFL3QTvH6D+Z6zNxnTe//GyqmtHmDkzrqDmyZ3eSwPqB/LhyKoU5DXsp+Vp2A== + dependencies: + "@babel/template" "^7.4.4" + "@babel/traverse" "^7.4.4" + "@babel/types" "^7.4.4" + +"@babel/highlight@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" + integrity sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw== + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.4.tgz#5977129431b8fe33471730d255ce8654ae1250b6" + integrity sha512-5pCS4mOsL+ANsFZGdvNLybx4wtqAZJ0MJjMHxvzI3bvIsz6sQvzW8XX92EYIkiPtIvcfG3Aj+Ir5VNyjnZhP7w== + +"@babel/plugin-proposal-async-generator-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e" + integrity sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-remap-async-to-generator" "^7.1.0" + "@babel/plugin-syntax-async-generators" "^7.2.0" + +"@babel/plugin-proposal-class-properties@7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.4.0.tgz#d70db61a2f1fd79de927eea91f6411c964e084b8" + integrity sha512-t2ECPNOXsIeK1JxJNKmgbzQtoG27KIlVE61vTqX0DKR9E9sZlVVxWUtEW9D5FlZ8b8j7SBNCHY47GgPKCKlpPg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.4.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-proposal-decorators@7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.0.tgz#8e1bfd83efa54a5f662033afcc2b8e701f4bb3a9" + integrity sha512-d08TLmXeK/XbgCo7ZeZ+JaeZDtDai/2ctapTRsWWkkmy7G/cqz8DQN/HlWG7RR4YmfXxmExsbU3SuCjlM7AtUg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.4.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-decorators" "^7.2.0" + +"@babel/plugin-proposal-json-strings@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317" + integrity sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-json-strings" "^7.2.0" + +"@babel/plugin-proposal-object-rest-spread@7.4.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.3.tgz#be27cd416eceeba84141305b93c282f5de23bbb4" + integrity sha512-xC//6DNSSHVjq8O2ge0dyYlhshsH4T7XdCVoxbi5HzLYWfsC5ooFlJjrXk8RcAT+hjHAK9UjBXdylzSoDK3t4g== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + +"@babel/plugin-proposal-object-rest-spread@^7.4.3", "@babel/plugin-proposal-object-rest-spread@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.4.4.tgz#1ef173fcf24b3e2df92a678f027673b55e7e3005" + integrity sha512-dMBG6cSPBbHeEBdFXeQ2QLc5gUpg4Vkaz8octD4aoW/ISO+jBOcsuxYL7bsb5WSu8RLP6boxrBIALEHgoHtO9g== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + +"@babel/plugin-proposal-optional-catch-binding@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5" + integrity sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" + +"@babel/plugin-proposal-unicode-property-regex@^7.4.0", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78" + integrity sha512-j1NwnOqMG9mFUOH58JTFsA/+ZYzQLUZ/drqWUqxCYLGeu2JFZL8YrNC9hBxKmWtAuOCHPcRpgv7fhap09Fb4kA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.4.4" + regexpu-core "^4.5.4" + +"@babel/plugin-syntax-async-generators@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz#69e1f0db34c6f5a0cf7e2b3323bf159a76c8cb7f" + integrity sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-decorators@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz#c50b1b957dcc69e4b1127b65e1c33eef61570c1b" + integrity sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-dynamic-import@7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz#69c159ffaf4998122161ad8ebc5e6d1f55df8612" + integrity sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-flow@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.2.0.tgz#a765f061f803bc48f240c26f8747faf97c26bf7c" + integrity sha512-r6YMuZDWLtLlu0kqIim5o/3TNRAlWb073HwT3e2nKf9I8IIvOggPrnILYPsrrKilmn/mYEMCf/Z07w3yQJF6dg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-json-strings@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470" + integrity sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-jsx@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz#0b85a3b4bc7cdf4cc4b8bf236335b907ca22e7c7" + integrity sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" + integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c" + integrity sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-typescript@^7.2.0": + version "7.3.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz#a7cc3f66119a9f7ebe2de5383cce193473d65991" + integrity sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-arrow-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550" + integrity sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-async-to-generator@^7.4.0", "@babel/plugin-transform-async-to-generator@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.4.4.tgz#a3f1d01f2f21cadab20b33a82133116f14fb5894" + integrity sha512-YiqW2Li8TXmzgbXw+STsSqPBPFnGviiaSp6CYOq55X8GQ2SGVLrXB6pNid8HkqkZAzOH6knbai3snhP7v0fNwA== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-remap-async-to-generator" "^7.1.0" + +"@babel/plugin-transform-block-scoped-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190" + integrity sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-block-scoping@^7.4.0", "@babel/plugin-transform-block-scoping@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.4.4.tgz#c13279fabf6b916661531841a23c4b7dae29646d" + integrity sha512-jkTUyWZcTrwxu5DD4rWz6rDB5Cjdmgz6z7M7RLXOJyCUkFBawssDGcGh8M/0FTSB87avyJI1HsTwUXp9nKA1PA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + lodash "^4.17.11" + +"@babel/plugin-transform-classes@7.4.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.3.tgz#adc7a1137ab4287a555d429cc56ecde8f40c062c" + integrity sha512-PUaIKyFUDtG6jF5DUJOfkBdwAS/kFFV3XFk7Nn0a6vR7ZT8jYw5cGtIlat77wcnd0C6ViGqo/wyNf4ZHytF/nQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-define-map" "^7.4.0" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.4.0" + "@babel/helper-split-export-declaration" "^7.4.0" + globals "^11.1.0" + +"@babel/plugin-transform-classes@^7.4.3", "@babel/plugin-transform-classes@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.4.4.tgz#0ce4094cdafd709721076d3b9c38ad31ca715eb6" + integrity sha512-/e44eFLImEGIpL9qPxSRat13I5QNRgBLu2hOQJCF7VLy/otSM/sypV1+XaIw5+502RX/+6YaSAPmldk+nhHDPw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-define-map" "^7.4.4" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.4.4" + "@babel/helper-split-export-declaration" "^7.4.4" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da" + integrity sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-destructuring@7.4.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.3.tgz#1a95f5ca2bf2f91ef0648d5de38a8d472da4350f" + integrity sha512-rVTLLZpydDFDyN4qnXdzwoVpk1oaXHIvPEOkOLyr88o7oHxVc/LyrnDx+amuBWGOwUb7D1s/uLsKBNTx08htZg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-destructuring@^7.4.3", "@babel/plugin-transform-destructuring@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.4.tgz#9d964717829cc9e4b601fc82a26a71a4d8faf20f" + integrity sha512-/aOx+nW0w8eHiEHm+BTERB2oJn5D127iye/SUQl7NjHy0lf+j7h4MKMMSOwdazGq9OxgiNADncE+SRJkCxjZpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-dotall-regex@^7.4.3", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz#361a148bc951444312c69446d76ed1ea8e4450c3" + integrity sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.4.4" + regexpu-core "^4.5.4" + +"@babel/plugin-transform-duplicate-keys@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz#d952c4930f312a4dbfff18f0b2914e60c35530b3" + integrity sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-exponentiation-operator@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008" + integrity sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-flow-strip-types@7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.4.0.tgz#f3c59eecff68c99b9c96eaafe4fe9d1fa8947138" + integrity sha512-C4ZVNejHnfB22vI2TYN4RUp2oCmq6cSEAg4RygSvYZUECRqUu9O4PMEMNJ4wsemaRGg27BbgYctG4BZh+AgIHw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-flow" "^7.2.0" + +"@babel/plugin-transform-for-of@^7.4.3", "@babel/plugin-transform-for-of@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556" + integrity sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-function-name@^7.4.3", "@babel/plugin-transform-function-name@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.4.4.tgz#e1436116abb0610c2259094848754ac5230922ad" + integrity sha512-iU9pv7U+2jC9ANQkKeNF6DrPy4GBa4NWQtl6dHB4Pb3izX2JOEvDTFarlNsBj/63ZEzNNIAMs3Qw4fNCcSOXJA== + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-literals@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1" + integrity sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-member-expression-literals@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.2.0.tgz#fa10aa5c58a2cb6afcf2c9ffa8cb4d8b3d489a2d" + integrity sha512-HiU3zKkSU6scTidmnFJ0bMX8hz5ixC93b4MHMiYebmk2lUVNGOboPsqQvx5LzooihijUoLR/v7Nc1rbBtnc7FA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-modules-amd@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz#82a9bce45b95441f617a24011dc89d12da7f4ee6" + integrity sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw== + dependencies: + "@babel/helper-module-transforms" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-modules-commonjs@^7.4.3", "@babel/plugin-transform-modules-commonjs@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.4.4.tgz#0bef4713d30f1d78c2e59b3d6db40e60192cac1e" + integrity sha512-4sfBOJt58sEo9a2BQXnZq+Q3ZTSAUXyK3E30o36BOGnJ+tvJ6YSxF0PG6kERvbeISgProodWuI9UVG3/FMY6iw== + dependencies: + "@babel/helper-module-transforms" "^7.4.4" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-simple-access" "^7.1.0" + +"@babel/plugin-transform-modules-systemjs@^7.4.0", "@babel/plugin-transform-modules-systemjs@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.4.4.tgz#dc83c5665b07d6c2a7b224c00ac63659ea36a405" + integrity sha512-MSiModfILQc3/oqnG7NrP1jHaSPryO6tA2kOMmAQApz5dayPxWiHqmq4sWH2xF5LcQK56LlbKByCd8Aah/OIkQ== + dependencies: + "@babel/helper-hoist-variables" "^7.4.4" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-modules-umd@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz#7678ce75169f0877b8eb2235538c074268dd01ae" + integrity sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw== + dependencies: + "@babel/helper-module-transforms" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.4.2", "@babel/plugin-transform-named-capturing-groups-regex@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.4.4.tgz#5611d96d987dfc4a3a81c4383bb173361037d68d" + integrity sha512-Ki+Y9nXBlKfhD+LXaRS7v95TtTGYRAf9Y1rTDiE75zf8YQz4GDaWRXosMfJBXxnk88mGFjWdCRIeqDbon7spYA== + dependencies: + regexp-tree "^0.1.0" + +"@babel/plugin-transform-new-target@^7.4.0", "@babel/plugin-transform-new-target@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz#18d120438b0cc9ee95a47f2c72bc9768fbed60a5" + integrity sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-object-super@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz#b35d4c10f56bab5d650047dad0f1d8e8814b6598" + integrity sha512-VMyhPYZISFZAqAPVkiYb7dUe2AsVi2/wCT5+wZdsNO31FojQJa9ns40hzZ6U9f50Jlq4w6qwzdBB2uwqZ00ebg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.1.0" + +"@babel/plugin-transform-parameters@^7.4.3", "@babel/plugin-transform-parameters@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz#7556cf03f318bd2719fe4c922d2d808be5571e16" + integrity sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw== + dependencies: + "@babel/helper-call-delegate" "^7.4.4" + "@babel/helper-get-function-arity" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-property-literals@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.2.0.tgz#03e33f653f5b25c4eb572c98b9485055b389e905" + integrity sha512-9q7Dbk4RhgcLp8ebduOpCbtjh7C0itoLYHXd9ueASKAG/is5PQtMR5VJGka9NKqGhYEGn5ITahd4h9QeBMylWQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-react-constant-elements@7.2.0", "@babel/plugin-transform-react-constant-elements@^7.0.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.2.0.tgz#ed602dc2d8bff2f0cb1a5ce29263dbdec40779f7" + integrity sha512-YYQFg6giRFMsZPKUM9v+VcHOdfSQdz9jHCx3akAi3UYgyjndmdYGSXylQ/V+HswQt4fL8IklchD9HTsaOCrWQQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-react-display-name@7.2.0", "@babel/plugin-transform-react-display-name@^7.0.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz#ebfaed87834ce8dc4279609a4f0c324c156e3eb0" + integrity sha512-Htf/tPa5haZvRMiNSQSFifK12gtr/8vwfr+A9y69uF0QcU77AVu4K7MiHEkTxF7lQoHOL0F9ErqgfNEAKgXj7A== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-react-jsx-self@^7.0.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.2.0.tgz#461e21ad9478f1031dd5e276108d027f1b5240ba" + integrity sha512-v6S5L/myicZEy+jr6ielB0OR8h+EH/1QFx/YJ7c7Ua+7lqsjj/vW6fD5FR9hB/6y7mGbfT4vAURn3xqBxsUcdg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@babel/plugin-transform-react-jsx-source@^7.0.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.2.0.tgz#20c8c60f0140f5dd3cd63418d452801cf3f7180f" + integrity sha512-A32OkKTp4i5U6aE88GwwcuV4HAprUgHcTq0sSafLxjr6AW0QahrCRCjxogkbbcdtpbXkuTOlgpjophCxb6sh5g== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@babel/plugin-transform-react-jsx@^7.0.0": + version "7.3.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.3.0.tgz#f2cab99026631c767e2745a5368b331cfe8f5290" + integrity sha512-a/+aRb7R06WcKvQLOu4/TpjKOdvVEKRLWFpKcNuHhiREPgGRB4TQJxq07+EZLS8LFVYpfq1a5lDUnuMdcCpBKg== + dependencies: + "@babel/helper-builder-react-jsx" "^7.3.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@babel/plugin-transform-regenerator@^7.4.3", "@babel/plugin-transform-regenerator@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.4.4.tgz#5b4da4df79391895fca9e28f99e87e22cfc02072" + integrity sha512-Zz3w+pX1SI0KMIiqshFZkwnVGUhDZzpX2vtPzfJBKQQq8WsP/Xy9DNdELWivxcKOCX/Pywge4SiEaPaLtoDT4g== + dependencies: + regenerator-transform "^0.13.4" + +"@babel/plugin-transform-reserved-words@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.2.0.tgz#4792af87c998a49367597d07fedf02636d2e1634" + integrity sha512-fz43fqW8E1tAB3DKF19/vxbpib1fuyCwSPE418ge5ZxILnBhWyhtPgz8eh1RCGGJlwvksHkyxMxh0eenFi+kFw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-runtime@7.4.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.4.3.tgz#4d6691690ecdc9f5cb8c3ab170a1576c1f556371" + integrity sha512-7Q61bU+uEI7bCUFReT1NKn7/X6sDQsZ7wL1sJ9IYMAO7cI+eg6x9re1cEw2fCRMbbTVyoeUKWSV1M6azEfKCfg== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + resolve "^1.8.1" + semver "^5.5.1" + +"@babel/plugin-transform-shorthand-properties@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0" + integrity sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-spread@^7.2.0": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz#3103a9abe22f742b6d406ecd3cd49b774919b406" + integrity sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-sticky-regex@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1" + integrity sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.0.0" + +"@babel/plugin-transform-template-literals@^7.2.0", "@babel/plugin-transform-template-literals@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz#9d28fea7bbce637fb7612a0750989d8321d4bcb0" + integrity sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-typeof-symbol@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz#117d2bcec2fbf64b4b59d1f9819894682d29f2b2" + integrity sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-typescript@^7.3.2": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.4.4.tgz#93e9c3f2a546e6d3da1e9cc990e30791b807aa9f" + integrity sha512-rwDvjaMTx09WC0rXGBRlYSSkEHOKRrecY6hEr3SVIPKII8DVWXtapNAfAyMC0dovuO+zYArcAuKeu3q9DNRfzA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-typescript" "^7.2.0" + +"@babel/plugin-transform-unicode-regex@^7.4.3", "@babel/plugin-transform-unicode-regex@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.4.4.tgz#ab4634bb4f14d36728bf5978322b35587787970f" + integrity sha512-il+/XdNw01i93+M9J9u4T7/e/Ue/vWfNZE4IRUQjplu2Mqb/AFTDimkw2tdEdSH50wuQXZAbXSql0UphQke+vA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.4.4" + regexpu-core "^4.5.4" + +"@babel/preset-env@7.4.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.4.3.tgz#e71e16e123dc0fbf65a52cbcbcefd072fbd02880" + integrity sha512-FYbZdV12yHdJU5Z70cEg0f6lvtpZ8jFSDakTm7WXeJbLXh4R0ztGEu/SW7G1nJ2ZvKwDhz8YrbA84eYyprmGqw== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-async-generator-functions" "^7.2.0" + "@babel/plugin-proposal-json-strings" "^7.2.0" + "@babel/plugin-proposal-object-rest-spread" "^7.4.3" + "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.0" + "@babel/plugin-syntax-async-generators" "^7.2.0" + "@babel/plugin-syntax-json-strings" "^7.2.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" + "@babel/plugin-transform-arrow-functions" "^7.2.0" + "@babel/plugin-transform-async-to-generator" "^7.4.0" + "@babel/plugin-transform-block-scoped-functions" "^7.2.0" + "@babel/plugin-transform-block-scoping" "^7.4.0" + "@babel/plugin-transform-classes" "^7.4.3" + "@babel/plugin-transform-computed-properties" "^7.2.0" + "@babel/plugin-transform-destructuring" "^7.4.3" + "@babel/plugin-transform-dotall-regex" "^7.4.3" + "@babel/plugin-transform-duplicate-keys" "^7.2.0" + "@babel/plugin-transform-exponentiation-operator" "^7.2.0" + "@babel/plugin-transform-for-of" "^7.4.3" + "@babel/plugin-transform-function-name" "^7.4.3" + "@babel/plugin-transform-literals" "^7.2.0" + "@babel/plugin-transform-member-expression-literals" "^7.2.0" + "@babel/plugin-transform-modules-amd" "^7.2.0" + "@babel/plugin-transform-modules-commonjs" "^7.4.3" + "@babel/plugin-transform-modules-systemjs" "^7.4.0" + "@babel/plugin-transform-modules-umd" "^7.2.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.4.2" + "@babel/plugin-transform-new-target" "^7.4.0" + "@babel/plugin-transform-object-super" "^7.2.0" + "@babel/plugin-transform-parameters" "^7.4.3" + "@babel/plugin-transform-property-literals" "^7.2.0" + "@babel/plugin-transform-regenerator" "^7.4.3" + "@babel/plugin-transform-reserved-words" "^7.2.0" + "@babel/plugin-transform-shorthand-properties" "^7.2.0" + "@babel/plugin-transform-spread" "^7.2.0" + "@babel/plugin-transform-sticky-regex" "^7.2.0" + "@babel/plugin-transform-template-literals" "^7.2.0" + "@babel/plugin-transform-typeof-symbol" "^7.2.0" + "@babel/plugin-transform-unicode-regex" "^7.4.3" + "@babel/types" "^7.4.0" + browserslist "^4.5.2" + core-js-compat "^3.0.0" + invariant "^2.2.2" + js-levenshtein "^1.1.3" + semver "^5.5.0" + +"@babel/preset-env@^7.1.6": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.4.4.tgz#b6f6825bfb27b3e1394ca3de4f926482722c1d6f" + integrity sha512-FU1H+ACWqZZqfw1x2G1tgtSSYSfxJLkpaUQL37CenULFARDo+h4xJoVHzRoHbK+85ViLciuI7ME4WTIhFRBBlw== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-async-generator-functions" "^7.2.0" + "@babel/plugin-proposal-json-strings" "^7.2.0" + "@babel/plugin-proposal-object-rest-spread" "^7.4.4" + "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-syntax-async-generators" "^7.2.0" + "@babel/plugin-syntax-json-strings" "^7.2.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" + "@babel/plugin-transform-arrow-functions" "^7.2.0" + "@babel/plugin-transform-async-to-generator" "^7.4.4" + "@babel/plugin-transform-block-scoped-functions" "^7.2.0" + "@babel/plugin-transform-block-scoping" "^7.4.4" + "@babel/plugin-transform-classes" "^7.4.4" + "@babel/plugin-transform-computed-properties" "^7.2.0" + "@babel/plugin-transform-destructuring" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/plugin-transform-duplicate-keys" "^7.2.0" + "@babel/plugin-transform-exponentiation-operator" "^7.2.0" + "@babel/plugin-transform-for-of" "^7.4.4" + "@babel/plugin-transform-function-name" "^7.4.4" + "@babel/plugin-transform-literals" "^7.2.0" + "@babel/plugin-transform-member-expression-literals" "^7.2.0" + "@babel/plugin-transform-modules-amd" "^7.2.0" + "@babel/plugin-transform-modules-commonjs" "^7.4.4" + "@babel/plugin-transform-modules-systemjs" "^7.4.4" + "@babel/plugin-transform-modules-umd" "^7.2.0" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.4.4" + "@babel/plugin-transform-new-target" "^7.4.4" + "@babel/plugin-transform-object-super" "^7.2.0" + "@babel/plugin-transform-parameters" "^7.4.4" + "@babel/plugin-transform-property-literals" "^7.2.0" + "@babel/plugin-transform-regenerator" "^7.4.4" + "@babel/plugin-transform-reserved-words" "^7.2.0" + "@babel/plugin-transform-shorthand-properties" "^7.2.0" + "@babel/plugin-transform-spread" "^7.2.0" + "@babel/plugin-transform-sticky-regex" "^7.2.0" + "@babel/plugin-transform-template-literals" "^7.4.4" + "@babel/plugin-transform-typeof-symbol" "^7.2.0" + "@babel/plugin-transform-unicode-regex" "^7.4.4" + "@babel/types" "^7.4.4" + browserslist "^4.5.2" + core-js-compat "^3.0.0" + invariant "^2.2.2" + js-levenshtein "^1.1.3" + semver "^5.5.0" + +"@babel/preset-react@7.0.0", "@babel/preset-react@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.0.0.tgz#e86b4b3d99433c7b3e9e91747e2653958bc6b3c0" + integrity sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-transform-react-display-name" "^7.0.0" + "@babel/plugin-transform-react-jsx" "^7.0.0" + "@babel/plugin-transform-react-jsx-self" "^7.0.0" + "@babel/plugin-transform-react-jsx-source" "^7.0.0" + +"@babel/preset-typescript@7.3.3": + version "7.3.3" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz#88669911053fa16b2b276ea2ede2ca603b3f307a" + integrity sha512-mzMVuIP4lqtn4du2ynEfdO0+RYcslwrZiJHXu4MGaC1ctJiW2fyaeDrtjJGs7R/KebZ1sgowcIoWf4uRpEfKEg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-transform-typescript" "^7.3.2" + +"@babel/runtime-corejs2@^7.4.4": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.5.5.tgz#c3214c08ef20341af4187f1c9fbdc357fbec96b2" + integrity sha512-FYATQVR00NSNi7mUfpPDp7E8RYMXDuO8gaix7u/w3GekfUinKgX1AcTxs7SoiEmoEW9mbpjrwqWSW6zCmw5h8A== + dependencies: + core-js "^2.6.5" + regenerator-runtime "^0.13.2" + +"@babel/runtime@7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0.tgz#adeb78fedfc855aa05bc041640f3f6f98e85424c" + integrity sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA== + dependencies: + regenerator-runtime "^0.12.0" + +"@babel/runtime@7.4.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.3.tgz#79888e452034223ad9609187a0ad1fe0d2ad4bdc" + integrity sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/runtime@^7.0.0", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d" + integrity sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" + integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/runtime@^7.4.0": + version "7.4.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.5.tgz#582bb531f5f9dc67d2fcb682979894f75e253f12" + integrity sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237" + integrity sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.4.4" + "@babel/types" "^7.4.4" + +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.4.4.tgz#0776f038f6d78361860b6823887d4f3937133fe8" + integrity sha512-Gw6qqkw/e6AGzlyj9KnkabJX7VcubqPtkUQVAwkc0wUMldr3A/hezNB3Rc5eIvId95iSGkGIOe5hh1kMKf951A== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/generator" "^7.4.4" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/parser" "^7.4.4" + "@babel/types" "^7.4.4" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.11" + +"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.4.4.tgz#8db9e9a629bb7c29370009b4b779ed93fe57d5f0" + integrity sha512-dOllgYdnEFOebhkKCjzSVFqw/PmmB8pH6RGOWkY4GsboQNd47b1fBThBSwlHAq9alF9vc1M3+6oqR47R50L0tQ== + dependencies: + esutils "^2.0.2" + lodash "^4.17.11" + to-fast-properties "^2.0.0" + +"@cnakazawa/watch@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef" + integrity sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@csstools/convert-colors@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" + integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== + +"@csstools/normalize.css@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-9.0.1.tgz#c27b391d8457d1e893f1eddeaf5e5412d12ffbb5" + integrity sha512-6It2EVfGskxZCQhuykrfnALg7oVeiI6KclWSmGDqB0AiInVrTGB9Jp9i4/Ad21u9Jde/voVQz6eFX/eSg/UsPA== + +"@date-io/core@^1.3.9": + version "1.3.9" + resolved "https://registry.yarnpkg.com/@date-io/core/-/core-1.3.9.tgz#c8483399782ab6cff776b50a0c05edecf9744219" + integrity sha512-pGZK4DJcXUKHFDWws/smJXgmeHyaVyOFPQqDsYXoxg6rYTU/S+NEVTQnoYOFgrlJ8dkgjZ89D9d3Qx1V925OGw== + +"@date-io/date-fns@^1.1.0": + version "1.3.9" + resolved "https://registry.yarnpkg.com/@date-io/date-fns/-/date-fns-1.3.9.tgz#b71cc18a8d289227731aebca9c860138e80a0809" + integrity sha512-beAqMd9csIQFOQeLu6noOXAcKdjpDjnIql1wyDOkkRop9o1G+b1HdDhzXAis88ADai7GcsCSvOp3VpB6UmCRqg== + dependencies: + "@date-io/core" "^1.3.9" + dayjs "^1.8.12" + lerna "^3.10.2" + typescript "^3.2.2" + +"@emotion/hash@^0.7.1": + version "0.7.2" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.7.2.tgz#53211e564604beb9befa7a4400ebf8147473eeef" + integrity sha512-RMtr1i6E8MXaBWwhXL3yeOU8JXRnz8GNxHvaUfVvwxokvayUY0zoBeWbKw1S9XkufmGEEdQd228pSZXFkAln8Q== + +"@evocateur/libnpmaccess@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@evocateur/libnpmaccess/-/libnpmaccess-3.1.2.tgz#ecf7f6ce6b004e9f942b098d92200be4a4b1c845" + integrity sha512-KSCAHwNWro0CF2ukxufCitT9K5LjL/KuMmNzSu8wuwN2rjyKHD8+cmOsiybK+W5hdnwc5M1SmRlVCaMHQo+3rg== + dependencies: + "@evocateur/npm-registry-fetch" "^4.0.0" + aproba "^2.0.0" + figgy-pudding "^3.5.1" + get-stream "^4.0.0" + npm-package-arg "^6.1.0" + +"@evocateur/libnpmpublish@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@evocateur/libnpmpublish/-/libnpmpublish-1.2.2.tgz#55df09d2dca136afba9c88c759ca272198db9f1a" + integrity sha512-MJrrk9ct1FeY9zRlyeoyMieBjGDG9ihyyD9/Ft6MMrTxql9NyoEx2hw9casTIP4CdqEVu+3nQ2nXxoJ8RCXyFg== + dependencies: + "@evocateur/npm-registry-fetch" "^4.0.0" + aproba "^2.0.0" + figgy-pudding "^3.5.1" + get-stream "^4.0.0" + lodash.clonedeep "^4.5.0" + normalize-package-data "^2.4.0" + npm-package-arg "^6.1.0" + semver "^5.5.1" + ssri "^6.0.1" + +"@evocateur/npm-registry-fetch@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@evocateur/npm-registry-fetch/-/npm-registry-fetch-4.0.0.tgz#8c4c38766d8d32d3200fcb0a83f064b57365ed66" + integrity sha512-k1WGfKRQyhJpIr+P17O5vLIo2ko1PFLKwoetatdduUSt/aQ4J2sJrJwwatdI5Z3SiYk/mRH9S3JpdmMFd/IK4g== + dependencies: + JSONStream "^1.3.4" + bluebird "^3.5.1" + figgy-pudding "^3.4.1" + lru-cache "^5.1.1" + make-fetch-happen "^5.0.0" + npm-package-arg "^6.1.0" + safe-buffer "^5.1.2" + +"@evocateur/pacote@^9.6.3": + version "9.6.3" + resolved "https://registry.yarnpkg.com/@evocateur/pacote/-/pacote-9.6.3.tgz#bcd7adbd3c2ef303aa89bd24166f06dd9c080d89" + integrity sha512-ExqNqcbdHQprEgKnY/uQz7WRtyHRbQxRl4JnVkSkmtF8qffRrF9K+piZKNLNSkRMOT/3H0e3IP44QVCHaXMWOQ== + dependencies: + "@evocateur/npm-registry-fetch" "^4.0.0" + bluebird "^3.5.3" + cacache "^12.0.0" + figgy-pudding "^3.5.1" + get-stream "^4.1.0" + glob "^7.1.4" + lru-cache "^5.1.1" + make-fetch-happen "^5.0.0" + minimatch "^3.0.4" + minipass "^2.3.5" + mississippi "^3.0.0" + mkdirp "^0.5.1" + normalize-package-data "^2.5.0" + npm-package-arg "^6.1.0" + npm-packlist "^1.4.4" + npm-pick-manifest "^2.2.3" + osenv "^0.1.5" + promise-inflight "^1.0.1" + promise-retry "^1.1.1" + protoduck "^5.0.1" + rimraf "^2.6.3" + safe-buffer "^5.2.0" + semver "^5.7.0" + ssri "^6.0.1" + tar "^4.4.10" + unique-filename "^1.1.1" + which "^1.3.1" + +"@hapi/address@2.x.x": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a" + integrity sha512-mV6T0IYqb0xL1UALPFplXYQmR0twnXG0M6jUswpquqT2sD12BOiCiLy3EvMp/Fy7s3DZElC4/aPjEjo2jeZpvw== + +"@hapi/hoek@6.x.x": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-6.2.1.tgz#d3a66329159af879bfdf0b0cff2229c43c5a3451" + integrity sha512-+ryw4GU9pjr1uT6lBuErHJg3NYqzwJTvZ75nKuJijEzpd00Uqi6oiawTGDDf5Hl0zWmI7qHfOtaqB0kpQZJQzA== + +"@hapi/joi@^15.0.0": + version "15.0.2" + resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.0.2.tgz#2989041a06ee2941cf6dd247ffff8032640d16bb" + integrity sha512-c3NwWBHzUnEavcaCpGaepOcygS17pSnOh5ZYUBz+sfqCP7kC9haLcRnd3U8KFC4TbLFmRwKnmYglsc47m9yapg== + dependencies: + "@hapi/address" "2.x.x" + "@hapi/hoek" "6.x.x" + "@hapi/topo" "3.x.x" + +"@hapi/topo@3.x.x": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.0.tgz#5c47cd9637c2953db185aa957a27bcb2a8b7a6f8" + integrity sha512-gZDI/eXOIk8kP2PkUKjWu9RW8GGVd2Hkgjxyr/S7Z+JF+0mr7bAlbw+DkTRxnD580o8Kqxlnba9wvqp5aOHBww== + dependencies: + "@hapi/hoek" "6.x.x" + +"@jest/console@^24.7.1": + version "24.7.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.7.1.tgz#32a9e42535a97aedfe037e725bd67e954b459545" + integrity sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg== + dependencies: + "@jest/source-map" "^24.3.0" + chalk "^2.0.1" + slash "^2.0.0" + +"@jest/core@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.8.0.tgz#fbbdcd42a41d0d39cddbc9f520c8bab0c33eed5b" + integrity sha512-R9rhAJwCBQzaRnrRgAdVfnglUuATXdwTRsYqs6NMdVcAl5euG8LtWDe+fVkN27YfKVBW61IojVsXKaOmSnqd/A== + dependencies: + "@jest/console" "^24.7.1" + "@jest/reporters" "^24.8.0" + "@jest/test-result" "^24.8.0" + "@jest/transform" "^24.8.0" + "@jest/types" "^24.8.0" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-changed-files "^24.8.0" + jest-config "^24.8.0" + jest-haste-map "^24.8.0" + jest-message-util "^24.8.0" + jest-regex-util "^24.3.0" + jest-resolve-dependencies "^24.8.0" + jest-runner "^24.8.0" + jest-runtime "^24.8.0" + jest-snapshot "^24.8.0" + jest-util "^24.8.0" + jest-validate "^24.8.0" + jest-watcher "^24.8.0" + micromatch "^3.1.10" + p-each-series "^1.0.0" + pirates "^4.0.1" + realpath-native "^1.1.0" + rimraf "^2.5.4" + strip-ansi "^5.0.0" + +"@jest/environment@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.8.0.tgz#0342261383c776bdd652168f68065ef144af0eac" + integrity sha512-vlGt2HLg7qM+vtBrSkjDxk9K0YtRBi7HfRFaDxoRtyi+DyVChzhF20duvpdAnKVBV6W5tym8jm0U9EfXbDk1tw== + dependencies: + "@jest/fake-timers" "^24.8.0" + "@jest/transform" "^24.8.0" + "@jest/types" "^24.8.0" + jest-mock "^24.8.0" + +"@jest/fake-timers@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.8.0.tgz#2e5b80a4f78f284bcb4bd5714b8e10dd36a8d3d1" + integrity sha512-2M4d5MufVXwi6VzZhJ9f5S/wU4ud2ck0kxPof1Iz3zWx6Y+V2eJrES9jEktB6O3o/oEyk+il/uNu9PvASjWXQw== + dependencies: + "@jest/types" "^24.8.0" + jest-message-util "^24.8.0" + jest-mock "^24.8.0" + +"@jest/reporters@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.8.0.tgz#075169cd029bddec54b8f2c0fc489fd0b9e05729" + integrity sha512-eZ9TyUYpyIIXfYCrw0UHUWUvE35vx5I92HGMgS93Pv7du+GHIzl+/vh8Qj9MCWFK/4TqyttVBPakWMOfZRIfxw== + dependencies: + "@jest/environment" "^24.8.0" + "@jest/test-result" "^24.8.0" + "@jest/transform" "^24.8.0" + "@jest/types" "^24.8.0" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.2" + istanbul-lib-coverage "^2.0.2" + istanbul-lib-instrument "^3.0.1" + istanbul-lib-report "^2.0.4" + istanbul-lib-source-maps "^3.0.1" + istanbul-reports "^2.1.1" + jest-haste-map "^24.8.0" + jest-resolve "^24.8.0" + jest-runtime "^24.8.0" + jest-util "^24.8.0" + jest-worker "^24.6.0" + node-notifier "^5.2.1" + slash "^2.0.0" + source-map "^0.6.0" + string-length "^2.0.0" + +"@jest/source-map@^24.3.0": + version "24.3.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.3.0.tgz#563be3aa4d224caf65ff77edc95cd1ca4da67f28" + integrity sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.1.15" + source-map "^0.6.0" + +"@jest/test-result@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.8.0.tgz#7675d0aaf9d2484caa65e048d9b467d160f8e9d3" + integrity sha512-+YdLlxwizlfqkFDh7Mc7ONPQAhA4YylU1s529vVM1rsf67vGZH/2GGm5uO8QzPeVyaVMobCQ7FTxl38QrKRlng== + dependencies: + "@jest/console" "^24.7.1" + "@jest/types" "^24.8.0" + "@types/istanbul-lib-coverage" "^2.0.0" + +"@jest/test-sequencer@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.8.0.tgz#2f993bcf6ef5eb4e65e8233a95a3320248cf994b" + integrity sha512-OzL/2yHyPdCHXEzhoBuq37CE99nkme15eHkAzXRVqthreWZamEMA0WoetwstsQBCXABhczpK03JNbc4L01vvLg== + dependencies: + "@jest/test-result" "^24.8.0" + jest-haste-map "^24.8.0" + jest-runner "^24.8.0" + jest-runtime "^24.8.0" + +"@jest/transform@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.8.0.tgz#628fb99dce4f9d254c6fd9341e3eea262e06fef5" + integrity sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^24.8.0" + babel-plugin-istanbul "^5.1.0" + chalk "^2.0.1" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.1.15" + jest-haste-map "^24.8.0" + jest-regex-util "^24.3.0" + jest-util "^24.8.0" + micromatch "^3.1.10" + realpath-native "^1.1.0" + slash "^2.0.0" + source-map "^0.6.1" + write-file-atomic "2.4.1" + +"@jest/types@^24.7.0", "@jest/types@^24.8.0": + version "24.8.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.8.0.tgz#f31e25948c58f0abd8c845ae26fcea1491dea7ad" + integrity sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^12.0.9" + +"@lerna/add@3.16.2": + version "3.16.2" + resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.16.2.tgz#90ecc1be7051cfcec75496ce122f656295bd6e94" + integrity sha512-RAAaF8aODPogj2Ge9Wj3uxPFIBGpog9M+HwSuq03ZnkkO831AmasCTJDqV+GEpl1U2DvnhZQEwHpWmTT0uUeEw== + dependencies: + "@evocateur/pacote" "^9.6.3" + "@lerna/bootstrap" "3.16.2" + "@lerna/command" "3.16.0" + "@lerna/filter-options" "3.16.0" + "@lerna/npm-conf" "3.16.0" + "@lerna/validation-error" "3.13.0" + dedent "^0.7.0" + npm-package-arg "^6.1.0" + p-map "^2.1.0" + semver "^6.2.0" + +"@lerna/batch-packages@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/batch-packages/-/batch-packages-3.16.0.tgz#1c16cb697e7d718177db744cbcbdac4e30253c8c" + integrity sha512-7AdMkANpubY/FKFI01im01tlx6ygOBJ/0JcixMUWoWP/7Ds3SWQF22ID6fbBr38jUWptYLDs2fagtTDL7YUPuA== + dependencies: + "@lerna/package-graph" "3.16.0" + npmlog "^4.1.2" + +"@lerna/bootstrap@3.16.2": + version "3.16.2" + resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-3.16.2.tgz#be268d940221d3c3270656b9b791b492559ad9d8" + integrity sha512-I+gs7eh6rv9Vyd+CwqL7sftRfOOsSzCle8cv/CGlMN7/p7EAVhxEdAw8SYoHIKHzipXszuqqy1Y3opyleD0qdA== + dependencies: + "@lerna/batch-packages" "3.16.0" + "@lerna/command" "3.16.0" + "@lerna/filter-options" "3.16.0" + "@lerna/has-npm-version" "3.16.0" + "@lerna/npm-install" "3.16.0" + "@lerna/package-graph" "3.16.0" + "@lerna/pulse-till-done" "3.13.0" + "@lerna/rimraf-dir" "3.14.2" + "@lerna/run-lifecycle" "3.16.2" + "@lerna/run-parallel-batches" "3.16.0" + "@lerna/symlink-binary" "3.16.2" + "@lerna/symlink-dependencies" "3.16.2" + "@lerna/validation-error" "3.13.0" + dedent "^0.7.0" + get-port "^4.2.0" + multimatch "^3.0.0" + npm-package-arg "^6.1.0" + npmlog "^4.1.2" + p-finally "^1.0.0" + p-map "^2.1.0" + p-map-series "^1.0.0" + p-waterfall "^1.0.0" + read-package-tree "^5.1.6" + semver "^6.2.0" + +"@lerna/changed@3.16.4": + version "3.16.4" + resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-3.16.4.tgz#c3e727d01453513140eee32c94b695de577dc955" + integrity sha512-NCD7XkK744T23iW0wqKEgF4R9MYmReUbyHCZKopFnsNpQdqumc3SOIvQUAkKCP6hQJmYvxvOieoVgy/CVDpZ5g== + dependencies: + "@lerna/collect-updates" "3.16.0" + "@lerna/command" "3.16.0" + "@lerna/listable" "3.16.0" + "@lerna/output" "3.13.0" + "@lerna/version" "3.16.4" + +"@lerna/check-working-tree@3.14.2": + version "3.14.2" + resolved "https://registry.yarnpkg.com/@lerna/check-working-tree/-/check-working-tree-3.14.2.tgz#5ce007722180a69643a8456766ed8a91fc7e9ae1" + integrity sha512-7safqxM/MYoAoxZxulUDtIJIbnBIgo0PB/FHytueG+9VaX7GMnDte2Bt1EKa0dz2sAyQdmQ3Q8ZXpf/6JDjaeg== + dependencies: + "@lerna/collect-uncommitted" "3.14.2" + "@lerna/describe-ref" "3.14.2" + "@lerna/validation-error" "3.13.0" + +"@lerna/child-process@3.14.2": + version "3.14.2" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-3.14.2.tgz#950240cba83f7dfe25247cfa6c9cebf30b7d94f6" + integrity sha512-xnq+W5yQb6RkwI0p16ZQnrn6HkloH/MWTw4lGE1nKsBLAUbmSU5oTE93W1nrG0X3IMF/xWc9UYvNdUGMWvZZ4w== + dependencies: + chalk "^2.3.1" + execa "^1.0.0" + strong-log-transformer "^2.0.0" + +"@lerna/clean@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-3.16.0.tgz#1c134334cacea1b1dbeacdc580e8b9240db8efa1" + integrity sha512-5P9U5Y19WmYZr7UAMGXBpY7xCRdlR7zhHy8MAPDKVx70rFIBS6nWXn5n7Kntv74g7Lm1gJ2rsiH5tj1OPcRJgg== + dependencies: + "@lerna/command" "3.16.0" + "@lerna/filter-options" "3.16.0" + "@lerna/prompt" "3.13.0" + "@lerna/pulse-till-done" "3.13.0" + "@lerna/rimraf-dir" "3.14.2" + p-map "^2.1.0" + p-map-series "^1.0.0" + p-waterfall "^1.0.0" + +"@lerna/cli@3.13.0": + version "3.13.0" + resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-3.13.0.tgz#3d7b357fdd7818423e9681a7b7f2abd106c8a266" + integrity sha512-HgFGlyCZbYaYrjOr3w/EsY18PdvtsTmDfpUQe8HwDjXlPeCCUgliZjXLOVBxSjiOvPeOSwvopwIHKWQmYbwywg== + dependencies: + "@lerna/global-options" "3.13.0" + dedent "^0.7.0" + npmlog "^4.1.2" + yargs "^12.0.1" + +"@lerna/collect-uncommitted@3.14.2": + version "3.14.2" + resolved "https://registry.yarnpkg.com/@lerna/collect-uncommitted/-/collect-uncommitted-3.14.2.tgz#b5ed00d800bea26bb0d18404432b051eee8d030e" + integrity sha512-4EkQu4jIOdNL2BMzy/N0ydHB8+Z6syu6xiiKXOoFl0WoWU9H1jEJCX4TH7CmVxXL1+jcs8FIS2pfQz4oew99Eg== + dependencies: + "@lerna/child-process" "3.14.2" + chalk "^2.3.1" + figgy-pudding "^3.5.1" + npmlog "^4.1.2" + +"@lerna/collect-updates@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-3.16.0.tgz#6db3ce8a740a4e2b972c033a63bdfb77f2553d8c" + integrity sha512-HwAIl815X2TNlmcp28zCrSdXfoZWNP7GJPEqNWYk7xDJTYLqQ+SrmKUePjb3AMGBwYAraZSEJLbHdBpJ5+cHmQ== + dependencies: + "@lerna/child-process" "3.14.2" + "@lerna/describe-ref" "3.14.2" + minimatch "^3.0.4" + npmlog "^4.1.2" + slash "^2.0.0" + +"@lerna/command@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/command/-/command-3.16.0.tgz#ba3dba49cb5ce4d11b48269cf95becd86e30773f" + integrity sha512-u7tE4GC4/gfbPA9eQg+0ulnoJ+PMoMqomx033r/IxqZrHtmJR9+pF/37S0fsxJ2hX/RMFPC7c9Q/i8NEufSpdQ== + dependencies: + "@lerna/child-process" "3.14.2" + "@lerna/package-graph" "3.16.0" + "@lerna/project" "3.16.0" + "@lerna/validation-error" "3.13.0" + "@lerna/write-log-file" "3.13.0" + dedent "^0.7.0" + execa "^1.0.0" + is-ci "^2.0.0" + lodash "^4.17.14" + npmlog "^4.1.2" + +"@lerna/conventional-commits@3.16.4": + version "3.16.4" + resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-3.16.4.tgz#bf464f11b2f6534dad204db00430e1651b346a04" + integrity sha512-QSZJ0bC9n6FVaf+7KDIq5zMv8WnHXnwhyL5jG1Nyh3SgOg9q2uflqh7YsYB+G6FwaRfnPaKosh6obijpYg0llA== + dependencies: + "@lerna/validation-error" "3.13.0" + conventional-changelog-angular "^5.0.3" + conventional-changelog-core "^3.1.6" + conventional-recommended-bump "^5.0.0" + fs-extra "^8.1.0" + get-stream "^4.0.0" + lodash.template "^4.5.0" + npm-package-arg "^6.1.0" + npmlog "^4.1.2" + pify "^4.0.1" + semver "^6.2.0" + +"@lerna/create-symlink@3.16.2": + version "3.16.2" + resolved "https://registry.yarnpkg.com/@lerna/create-symlink/-/create-symlink-3.16.2.tgz#412cb8e59a72f5a7d9463e4e4721ad2070149967" + integrity sha512-pzXIJp6av15P325sgiIRpsPXLFmkisLhMBCy4764d+7yjf2bzrJ4gkWVMhsv4AdF0NN3OyZ5jjzzTtLNqfR+Jw== + dependencies: + "@zkochan/cmd-shim" "^3.1.0" + fs-extra "^8.1.0" + npmlog "^4.1.2" + +"@lerna/create@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-3.16.0.tgz#4de841ec7d98b29bb19fb7d6ad982e65f7a150e8" + integrity sha512-OZApR1Iz7awutbmj4sAArwhqCyKgcrnw9rH0aWAUrkYWrD1w4TwkvAcYAsfx5GpQGbLQwoXhoyyPwPfZRRWz3Q== + dependencies: + "@evocateur/pacote" "^9.6.3" + "@lerna/child-process" "3.14.2" + "@lerna/command" "3.16.0" + "@lerna/npm-conf" "3.16.0" + "@lerna/validation-error" "3.13.0" + camelcase "^5.0.0" + dedent "^0.7.0" + fs-extra "^8.1.0" + globby "^9.2.0" + init-package-json "^1.10.3" + npm-package-arg "^6.1.0" + p-reduce "^1.0.0" + pify "^4.0.1" + semver "^6.2.0" + slash "^2.0.0" + validate-npm-package-license "^3.0.3" + validate-npm-package-name "^3.0.0" + whatwg-url "^7.0.0" + +"@lerna/describe-ref@3.14.2": + version "3.14.2" + resolved "https://registry.yarnpkg.com/@lerna/describe-ref/-/describe-ref-3.14.2.tgz#edc3c973f5ca9728d23358c4f4d3b55a21f65be5" + integrity sha512-qa5pzDRK2oBQXNjyRmRnN7E8a78NMYfQjjlRFB0KNHMsT6mCiL9+8kIS39sSE2NqT8p7xVNo2r2KAS8R/m3CoQ== + dependencies: + "@lerna/child-process" "3.14.2" + npmlog "^4.1.2" + +"@lerna/diff@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-3.16.0.tgz#6d09a786f9f5b343a2fdc460eb0be08a05b420aa" + integrity sha512-QUpVs5TPl8vBIne10/vyjUxanQBQQp7Lk3iaB8MnCysKr0O+oy7trWeFVDPEkBTCD177By7yPGyW5Yey1nCBbA== + dependencies: + "@lerna/child-process" "3.14.2" + "@lerna/command" "3.16.0" + "@lerna/validation-error" "3.13.0" + npmlog "^4.1.2" + +"@lerna/exec@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-3.16.0.tgz#2b6c033cee46181b6eede0eb12aad5c2c0181e89" + integrity sha512-mH3O5NXf/O88jBaBBTUf+d56CUkxpg782s3Jxy7HWbVuSUULt3iMRPTh+zEXO5/555etsIVVDDyUR76meklrJA== + dependencies: + "@lerna/child-process" "3.14.2" + "@lerna/command" "3.16.0" + "@lerna/filter-options" "3.16.0" + "@lerna/run-topologically" "3.16.0" + "@lerna/validation-error" "3.13.0" + p-map "^2.1.0" + +"@lerna/filter-options@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-3.16.0.tgz#b1660b4480c02a5c6efa4d0cd98b9afde4ed0bba" + integrity sha512-InIi1fF8+PxpCwir9bIy+pGxrdE6hvN0enIs1eNGCVS1TTE8osNgiZXa838bMQ1yaEccdcnVX6Z03BNKd56kNg== + dependencies: + "@lerna/collect-updates" "3.16.0" + "@lerna/filter-packages" "3.16.0" + dedent "^0.7.0" + +"@lerna/filter-packages@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/filter-packages/-/filter-packages-3.16.0.tgz#7d34dc8530c71016263d6f67dc65308ecf11c9fc" + integrity sha512-eGFzQTx0ogkGDCnbTuXqssryR6ilp8+dcXt6B+aq1MaqL/vOJRZyqMm4TY3CUOUnzZCi9S2WWyMw3PnAJOF+kg== + dependencies: + "@lerna/validation-error" "3.13.0" + multimatch "^3.0.0" + npmlog "^4.1.2" + +"@lerna/get-npm-exec-opts@3.13.0": + version "3.13.0" + resolved "https://registry.yarnpkg.com/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-3.13.0.tgz#d1b552cb0088199fc3e7e126f914e39a08df9ea5" + integrity sha512-Y0xWL0rg3boVyJk6An/vurKzubyJKtrxYv2sj4bB8Mc5zZ3tqtv0ccbOkmkXKqbzvNNF7VeUt1OJ3DRgtC/QZw== + dependencies: + npmlog "^4.1.2" + +"@lerna/get-packed@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/get-packed/-/get-packed-3.16.0.tgz#1b316b706dcee86c7baa55e50b087959447852ff" + integrity sha512-AjsFiaJzo1GCPnJUJZiTW6J1EihrPkc2y3nMu6m3uWFxoleklsSCyImumzVZJssxMi3CPpztj8LmADLedl9kXw== + dependencies: + fs-extra "^8.1.0" + ssri "^6.0.1" + tar "^4.4.8" + +"@lerna/github-client@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/github-client/-/github-client-3.16.0.tgz#619874e461641d4f59ab1b3f1a7ba22dba88125d" + integrity sha512-IVJjcKjkYaUEPJsDyAblHGEFFNKCRyMagbIDm14L7Ab94ccN6i4TKOqAFEJn2SJHYvKKBdp3Zj2zNlASOMe3DA== + dependencies: + "@lerna/child-process" "3.14.2" + "@octokit/plugin-enterprise-rest" "^3.6.1" + "@octokit/rest" "^16.28.4" + git-url-parse "^11.1.2" + npmlog "^4.1.2" + +"@lerna/gitlab-client@3.15.0": + version "3.15.0" + resolved "https://registry.yarnpkg.com/@lerna/gitlab-client/-/gitlab-client-3.15.0.tgz#91f4ec8c697b5ac57f7f25bd50fe659d24aa96a6" + integrity sha512-OsBvRSejHXUBMgwWQqNoioB8sgzL/Pf1pOUhHKtkiMl6aAWjklaaq5HPMvTIsZPfS6DJ9L5OK2GGZuooP/5c8Q== + dependencies: + node-fetch "^2.5.0" + npmlog "^4.1.2" + whatwg-url "^7.0.0" + +"@lerna/global-options@3.13.0": + version "3.13.0" + resolved "https://registry.yarnpkg.com/@lerna/global-options/-/global-options-3.13.0.tgz#217662290db06ad9cf2c49d8e3100ee28eaebae1" + integrity sha512-SlZvh1gVRRzYLVluz9fryY1nJpZ0FHDGB66U9tFfvnnxmueckRQxLopn3tXj3NU1kc3QANT2I5BsQkOqZ4TEFQ== + +"@lerna/has-npm-version@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/has-npm-version/-/has-npm-version-3.16.0.tgz#55764a4ce792f0c8553cf996a17f554b9e843288" + integrity sha512-TIY036dA9J8OyTrZq9J+it2DVKifL65k7hK8HhkUPpitJkw6jwbMObA/8D40LOGgWNPweJWqmlrTbRSwsR7DrQ== + dependencies: + "@lerna/child-process" "3.14.2" + semver "^6.2.0" + +"@lerna/import@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/import/-/import-3.16.0.tgz#b57cb453f4acfc60f6541fcbba10674055cb179d" + integrity sha512-trsOmGHzw0rL/f8BLNvd+9PjoTkXq2Dt4/V2UCha254hMQaYutbxcYu8iKPxz9x86jSPlH7FpbTkkHXDsoY7Yg== + dependencies: + "@lerna/child-process" "3.14.2" + "@lerna/command" "3.16.0" + "@lerna/prompt" "3.13.0" + "@lerna/pulse-till-done" "3.13.0" + "@lerna/validation-error" "3.13.0" + dedent "^0.7.0" + fs-extra "^8.1.0" + p-map-series "^1.0.0" + +"@lerna/init@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/init/-/init-3.16.0.tgz#31e0d66bbededee603338b487a42674a072b7a7d" + integrity sha512-Ybol/x5xMtBgokx4j7/Y3u0ZmNh0NiSWzBFVaOs2NOJKvuqrWimF67DKVz7yYtTYEjtaMdug64ohFF4jcT/iag== + dependencies: + "@lerna/child-process" "3.14.2" + "@lerna/command" "3.16.0" + fs-extra "^8.1.0" + p-map "^2.1.0" + write-json-file "^3.2.0" + +"@lerna/link@3.16.2": + version "3.16.2" + resolved "https://registry.yarnpkg.com/@lerna/link/-/link-3.16.2.tgz#6c3a5658f6448a64dddca93d9348ac756776f6f6" + integrity sha512-eCPg5Lo8HT525fIivNoYF3vWghO3UgEVFdbsiPmhzwI7IQyZro5HWYzLtywSAdEog5XZpd2Bbn0CsoHWBB3gww== + dependencies: + "@lerna/command" "3.16.0" + "@lerna/package-graph" "3.16.0" + "@lerna/symlink-dependencies" "3.16.2" + p-map "^2.1.0" + slash "^2.0.0" + +"@lerna/list@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/list/-/list-3.16.0.tgz#883c00b2baf1e03c93e54391372f67a01b773c2f" + integrity sha512-TkvstoPsgKqqQ0KfRumpsdMXfRSEhdXqOLq519XyI5IRWYxhoqXqfi8gG37UoBPhBNoe64japn5OjphF3rOmQA== + dependencies: + "@lerna/command" "3.16.0" + "@lerna/filter-options" "3.16.0" + "@lerna/listable" "3.16.0" + "@lerna/output" "3.13.0" + +"@lerna/listable@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-3.16.0.tgz#e6dc47a2d5a6295222663486f50e5cffc580f043" + integrity sha512-mtdAT2EEECqrJSDm/aXlOUFr1MRE4p6hppzY//Klp05CogQy6uGaKk+iKG5yyCLaOXFFZvG4HfO11CmoGSDWzw== + dependencies: + "@lerna/query-graph" "3.16.0" + chalk "^2.3.1" + columnify "^1.5.4" + +"@lerna/log-packed@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/log-packed/-/log-packed-3.16.0.tgz#f83991041ee77b2495634e14470b42259fd2bc16" + integrity sha512-Fp+McSNBV/P2mnLUYTaSlG8GSmpXM7krKWcllqElGxvAqv6chk2K3c2k80MeVB4WvJ9tRjUUf+i7HUTiQ9/ckQ== + dependencies: + byte-size "^5.0.1" + columnify "^1.5.4" + has-unicode "^2.0.1" + npmlog "^4.1.2" + +"@lerna/npm-conf@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-conf/-/npm-conf-3.16.0.tgz#1c10a89ae2f6c2ee96962557738685300d376827" + integrity sha512-HbO3DUrTkCAn2iQ9+FF/eisDpWY5POQAOF1m7q//CZjdC2HSW3UYbKEGsSisFxSfaF9Z4jtrV+F/wX6qWs3CuA== + dependencies: + config-chain "^1.1.11" + pify "^4.0.1" + +"@lerna/npm-dist-tag@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-3.16.0.tgz#b2184cee5e1f291277396854820e1117a544b7ee" + integrity sha512-MQrBkqJJB9+eNphuj9w90QPMOs4NQXMuSRk9NqzeFunOmdDopPCV0Q7IThSxEuWnhJ2n3B7G0vWUP7tNMPdqIQ== + dependencies: + "@evocateur/npm-registry-fetch" "^4.0.0" + "@lerna/otplease" "3.16.0" + figgy-pudding "^3.5.1" + npm-package-arg "^6.1.0" + npmlog "^4.1.2" + +"@lerna/npm-install@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-install/-/npm-install-3.16.0.tgz#8ec76a7a13b183bde438fd46296bf7a0d6f86017" + integrity sha512-APUOIilZCzDzce92uLEwzt1r7AEMKT/hWA1ThGJL+PO9Rn8A95Km3o2XZAYG4W0hR+P4O2nSVuKbsjQtz8CjFQ== + dependencies: + "@lerna/child-process" "3.14.2" + "@lerna/get-npm-exec-opts" "3.13.0" + fs-extra "^8.1.0" + npm-package-arg "^6.1.0" + npmlog "^4.1.2" + signal-exit "^3.0.2" + write-pkg "^3.1.0" + +"@lerna/npm-publish@3.16.2": + version "3.16.2" + resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-3.16.2.tgz#a850b54739446c4aa766a0ceabfa9283bb0be676" + integrity sha512-tGMb9vfTxP57vUV5svkBQxd5Tzc+imZbu9ZYf8Mtwe0+HYfDjNiiHLIQw7G95w4YRdc5KsCE8sQ0uSj+f2soIg== + dependencies: + "@evocateur/libnpmpublish" "^1.2.2" + "@lerna/otplease" "3.16.0" + "@lerna/run-lifecycle" "3.16.2" + figgy-pudding "^3.5.1" + fs-extra "^8.1.0" + npm-package-arg "^6.1.0" + npmlog "^4.1.2" + pify "^4.0.1" + read-package-json "^2.0.13" + +"@lerna/npm-run-script@3.14.2": + version "3.14.2" + resolved "https://registry.yarnpkg.com/@lerna/npm-run-script/-/npm-run-script-3.14.2.tgz#8c518ea9d241a641273e77aad6f6fddc16779c3f" + integrity sha512-LbVFv+nvAoRTYLMrJlJ8RiakHXrLslL7Jp/m1R18vYrB8LYWA3ey+nz5Tel2OELzmjUiemAKZsD9h6i+Re5egg== + dependencies: + "@lerna/child-process" "3.14.2" + "@lerna/get-npm-exec-opts" "3.13.0" + npmlog "^4.1.2" + +"@lerna/otplease@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/otplease/-/otplease-3.16.0.tgz#de66aec4f3e835a465d7bea84b58a4ab6590a0fa" + integrity sha512-uqZ15wYOHC+/V0WnD2iTLXARjvx3vNrpiIeyIvVlDB7rWse9mL4egex/QSgZ+lDx1OID7l2kgvcUD9cFpbqB7Q== + dependencies: + "@lerna/prompt" "3.13.0" + figgy-pudding "^3.5.1" + +"@lerna/output@3.13.0": + version "3.13.0" + resolved "https://registry.yarnpkg.com/@lerna/output/-/output-3.13.0.tgz#3ded7cc908b27a9872228a630d950aedae7a4989" + integrity sha512-7ZnQ9nvUDu/WD+bNsypmPG5MwZBwu86iRoiW6C1WBuXXDxM5cnIAC1m2WxHeFnjyMrYlRXM9PzOQ9VDD+C15Rg== + dependencies: + npmlog "^4.1.2" + +"@lerna/pack-directory@3.16.4": + version "3.16.4" + resolved "https://registry.yarnpkg.com/@lerna/pack-directory/-/pack-directory-3.16.4.tgz#3eae5f91bdf5acfe0384510ed53faddc4c074693" + integrity sha512-uxSF0HZeGyKaaVHz5FroDY9A5NDDiCibrbYR6+khmrhZtY0Bgn6hWq8Gswl9iIlymA+VzCbshWIMX4o2O8C8ng== + dependencies: + "@lerna/get-packed" "3.16.0" + "@lerna/package" "3.16.0" + "@lerna/run-lifecycle" "3.16.2" + figgy-pudding "^3.5.1" + npm-packlist "^1.4.4" + npmlog "^4.1.2" + tar "^4.4.10" + temp-write "^3.4.0" + +"@lerna/package-graph@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-3.16.0.tgz#909c90fb41e02f2c19387342d2a5eefc36d56836" + integrity sha512-A2mum/gNbv7zCtAwJqoxzqv89As73OQNK2MgSX1SHWya46qoxO9a9Z2c5lOFQ8UFN5ZxqWMfFYXRCz7qzwmFXw== + dependencies: + "@lerna/prerelease-id-from-version" "3.16.0" + "@lerna/validation-error" "3.13.0" + npm-package-arg "^6.1.0" + npmlog "^4.1.2" + semver "^6.2.0" + +"@lerna/package@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/package/-/package-3.16.0.tgz#7e0a46e4697ed8b8a9c14d59c7f890e0d38ba13c" + integrity sha512-2lHBWpaxcBoiNVbtyLtPUuTYEaB/Z+eEqRS9duxpZs6D+mTTZMNy6/5vpEVSCBmzvdYpyqhqaYjjSLvjjr5Riw== + dependencies: + load-json-file "^5.3.0" + npm-package-arg "^6.1.0" + write-pkg "^3.1.0" + +"@lerna/prerelease-id-from-version@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/prerelease-id-from-version/-/prerelease-id-from-version-3.16.0.tgz#b24bfa789f5e1baab914d7b08baae9b7bd7d83a1" + integrity sha512-qZyeUyrE59uOK8rKdGn7jQz+9uOpAaF/3hbslJVFL1NqF9ELDTqjCPXivuejMX/lN4OgD6BugTO4cR7UTq/sZA== + dependencies: + semver "^6.2.0" + +"@lerna/project@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/project/-/project-3.16.0.tgz#2469a4e346e623fd922f38f5a12931dfb8f2a946" + integrity sha512-NrKcKK1EqXqhrGvslz6Q36+ZHuK3zlDhGdghRqnxDcHxMPT01NgLcmsnymmQ+gjMljuLRmvKYYCuHrknzX8VrA== + dependencies: + "@lerna/package" "3.16.0" + "@lerna/validation-error" "3.13.0" + cosmiconfig "^5.1.0" + dedent "^0.7.0" + dot-prop "^4.2.0" + glob-parent "^5.0.0" + globby "^9.2.0" + load-json-file "^5.3.0" + npmlog "^4.1.2" + p-map "^2.1.0" + resolve-from "^4.0.0" + write-json-file "^3.2.0" + +"@lerna/prompt@3.13.0": + version "3.13.0" + resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-3.13.0.tgz#53571462bb3f5399cc1ca6d335a411fe093426a5" + integrity sha512-P+lWSFokdyvYpkwC3it9cE0IF2U5yy2mOUbGvvE4iDb9K7TyXGE+7lwtx2thtPvBAfIb7O13POMkv7df03HJeA== + dependencies: + inquirer "^6.2.0" + npmlog "^4.1.2" + +"@lerna/publish@3.16.4": + version "3.16.4" + resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-3.16.4.tgz#4cd55d8be9943d9a68e316e930a90cda8590500e" + integrity sha512-XZY+gRuF7/v6PDQwl7lvZaGWs8CnX6WIPIu+OCcyFPSL/rdWegdN7HieKBHskgX798qRQc2GrveaY7bNoTKXAw== + dependencies: + "@evocateur/libnpmaccess" "^3.1.2" + "@evocateur/npm-registry-fetch" "^4.0.0" + "@evocateur/pacote" "^9.6.3" + "@lerna/check-working-tree" "3.14.2" + "@lerna/child-process" "3.14.2" + "@lerna/collect-updates" "3.16.0" + "@lerna/command" "3.16.0" + "@lerna/describe-ref" "3.14.2" + "@lerna/log-packed" "3.16.0" + "@lerna/npm-conf" "3.16.0" + "@lerna/npm-dist-tag" "3.16.0" + "@lerna/npm-publish" "3.16.2" + "@lerna/otplease" "3.16.0" + "@lerna/output" "3.13.0" + "@lerna/pack-directory" "3.16.4" + "@lerna/prerelease-id-from-version" "3.16.0" + "@lerna/prompt" "3.13.0" + "@lerna/pulse-till-done" "3.13.0" + "@lerna/run-lifecycle" "3.16.2" + "@lerna/run-topologically" "3.16.0" + "@lerna/validation-error" "3.13.0" + "@lerna/version" "3.16.4" + figgy-pudding "^3.5.1" + fs-extra "^8.1.0" + npm-package-arg "^6.1.0" + npmlog "^4.1.2" + p-finally "^1.0.0" + p-map "^2.1.0" + p-pipe "^1.2.0" + semver "^6.2.0" + +"@lerna/pulse-till-done@3.13.0": + version "3.13.0" + resolved "https://registry.yarnpkg.com/@lerna/pulse-till-done/-/pulse-till-done-3.13.0.tgz#c8e9ce5bafaf10d930a67d7ed0ccb5d958fe0110" + integrity sha512-1SOHpy7ZNTPulzIbargrgaJX387csN7cF1cLOGZiJQA6VqnS5eWs2CIrG8i8wmaUavj2QlQ5oEbRMVVXSsGrzA== + dependencies: + npmlog "^4.1.2" + +"@lerna/query-graph@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/query-graph/-/query-graph-3.16.0.tgz#e6a46ebcd9d5b03f018a06eca2b471735353953c" + integrity sha512-p0RO+xmHDO95ChJdWkcy9TNLysLkoDARXeRHzY5U54VCwl3Ot/2q8fMCVlA5UeGXDutEyyByl3URqEpcQCWI7Q== + dependencies: + "@lerna/package-graph" "3.16.0" + figgy-pudding "^3.5.1" + +"@lerna/resolve-symlink@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/resolve-symlink/-/resolve-symlink-3.16.0.tgz#37fc7095fabdbcf317c26eb74e0d0bde8efd2386" + integrity sha512-Ibj5e7njVHNJ/NOqT4HlEgPFPtPLWsO7iu59AM5bJDcAJcR96mLZ7KGVIsS2tvaO7akMEJvt2P+ErwCdloG3jQ== + dependencies: + fs-extra "^8.1.0" + npmlog "^4.1.2" + read-cmd-shim "^1.0.1" + +"@lerna/rimraf-dir@3.14.2": + version "3.14.2" + resolved "https://registry.yarnpkg.com/@lerna/rimraf-dir/-/rimraf-dir-3.14.2.tgz#103a49882abd85d42285d05cc76869b89f21ffd2" + integrity sha512-eFNkZsy44Bu9v1Hrj5Zk6omzg8O9h/7W6QYK1TTUHeyrjTEwytaNQlqF0lrTLmEvq55sviV42NC/8P3M2cvq8Q== + dependencies: + "@lerna/child-process" "3.14.2" + npmlog "^4.1.2" + path-exists "^3.0.0" + rimraf "^2.6.2" + +"@lerna/run-lifecycle@3.16.2": + version "3.16.2" + resolved "https://registry.yarnpkg.com/@lerna/run-lifecycle/-/run-lifecycle-3.16.2.tgz#67b288f8ea964db9ea4fb1fbc7715d5bbb0bce00" + integrity sha512-RqFoznE8rDpyyF0rOJy3+KjZCeTkO8y/OB9orPauR7G2xQ7PTdCpgo7EO6ZNdz3Al+k1BydClZz/j78gNCmL2A== + dependencies: + "@lerna/npm-conf" "3.16.0" + figgy-pudding "^3.5.1" + npm-lifecycle "^3.1.2" + npmlog "^4.1.2" + +"@lerna/run-parallel-batches@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/run-parallel-batches/-/run-parallel-batches-3.16.0.tgz#5ace7911a2dd31dfd1e53c61356034e27df0e1fb" + integrity sha512-2J/Nyv+MvogmQEfC7VcS21ifk7w0HVvzo2yOZRPvkCzGRu/rducxtB4RTcr58XCZ8h/Bt1aqQYKExu3c/3GXwg== + dependencies: + p-map "^2.1.0" + p-map-series "^1.0.0" + +"@lerna/run-topologically@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/run-topologically/-/run-topologically-3.16.0.tgz#39e29cfc628bbc8e736d8e0d0e984997ac01bbf5" + integrity sha512-4Hlpv4zDtKWa5Z0tPkeu0sK+bxZEKgkNESMGmWrUCNfj7xwvAJurcraK8+a2Y0TFYwf0qjSLY/MzX+ZbJA3Cgw== + dependencies: + "@lerna/query-graph" "3.16.0" + figgy-pudding "^3.5.1" + p-queue "^4.0.0" + +"@lerna/run@3.16.0": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@lerna/run/-/run-3.16.0.tgz#1ea568c6f303e47fa00b3403a457836d40738fd2" + integrity sha512-woTeLlB1OAAz4zzjdI6RyIxSGuxiUPHJZm89E1pDEPoWwtQV6HMdMgrsQd9ATsJ5Ez280HH4bF/LStAlqW8Ufg== + dependencies: + "@lerna/command" "3.16.0" + "@lerna/filter-options" "3.16.0" + "@lerna/npm-run-script" "3.14.2" + "@lerna/output" "3.13.0" + "@lerna/run-topologically" "3.16.0" + "@lerna/timer" "3.13.0" + "@lerna/validation-error" "3.13.0" + p-map "^2.1.0" + +"@lerna/symlink-binary@3.16.2": + version "3.16.2" + resolved "https://registry.yarnpkg.com/@lerna/symlink-binary/-/symlink-binary-3.16.2.tgz#f98a3d9da9e56f1d302dc0d5c2efeb951483ee66" + integrity sha512-kz9XVoFOGSF83gg4gBqH+mG6uxfJfTp8Uy+Cam40CvMiuzfODrGkjuBEFoM/uO2QOAwZvbQDYOBpKUa9ZxHS1Q== + dependencies: + "@lerna/create-symlink" "3.16.2" + "@lerna/package" "3.16.0" + fs-extra "^8.1.0" + p-map "^2.1.0" + +"@lerna/symlink-dependencies@3.16.2": + version "3.16.2" + resolved "https://registry.yarnpkg.com/@lerna/symlink-dependencies/-/symlink-dependencies-3.16.2.tgz#91d9909d35897aebd76a03644a00cd03c4128240" + integrity sha512-wnZqGJQ+Jvr1I3inxrkffrFZfmQI7Ta8gySw/UWCy95QtZWF/f5yk8zVIocCAsjzD0wgb3jJE3CFJ9W5iwWk1A== + dependencies: + "@lerna/create-symlink" "3.16.2" + "@lerna/resolve-symlink" "3.16.0" + "@lerna/symlink-binary" "3.16.2" + fs-extra "^8.1.0" + p-finally "^1.0.0" + p-map "^2.1.0" + p-map-series "^1.0.0" + +"@lerna/timer@3.13.0": + version "3.13.0" + resolved "https://registry.yarnpkg.com/@lerna/timer/-/timer-3.13.0.tgz#bcd0904551db16e08364d6c18e5e2160fc870781" + integrity sha512-RHWrDl8U4XNPqY5MQHkToWS9jHPnkLZEt5VD+uunCKTfzlxGnRCr3/zVr8VGy/uENMYpVP3wJa4RKGY6M0vkRw== + +"@lerna/validation-error@3.13.0": + version "3.13.0" + resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-3.13.0.tgz#c86b8f07c5ab9539f775bd8a54976e926f3759c3" + integrity sha512-SiJP75nwB8GhgwLKQfdkSnDufAaCbkZWJqEDlKOUPUvVOplRGnfL+BPQZH5nvq2BYSRXsksXWZ4UHVnQZI/HYA== + dependencies: + npmlog "^4.1.2" + +"@lerna/version@3.16.4": + version "3.16.4" + resolved "https://registry.yarnpkg.com/@lerna/version/-/version-3.16.4.tgz#b5cc37f3ad98358d599c6196c30b6efc396d42bf" + integrity sha512-ikhbMeIn5ljCtWTlHDzO4YvTmpGTX1lWFFIZ79Vd1TNyOr+OUuKLo/+p06mCl2WEdZu0W2s5E9oxfAAQbyDxEg== + dependencies: + "@lerna/check-working-tree" "3.14.2" + "@lerna/child-process" "3.14.2" + "@lerna/collect-updates" "3.16.0" + "@lerna/command" "3.16.0" + "@lerna/conventional-commits" "3.16.4" + "@lerna/github-client" "3.16.0" + "@lerna/gitlab-client" "3.15.0" + "@lerna/output" "3.13.0" + "@lerna/prerelease-id-from-version" "3.16.0" + "@lerna/prompt" "3.13.0" + "@lerna/run-lifecycle" "3.16.2" + "@lerna/run-topologically" "3.16.0" + "@lerna/validation-error" "3.13.0" + chalk "^2.3.1" + dedent "^0.7.0" + minimatch "^3.0.4" + npmlog "^4.1.2" + p-map "^2.1.0" + p-pipe "^1.2.0" + p-reduce "^1.0.0" + p-waterfall "^1.0.0" + semver "^6.2.0" + slash "^2.0.0" + temp-write "^3.4.0" + +"@lerna/write-log-file@3.13.0": + version "3.13.0" + resolved "https://registry.yarnpkg.com/@lerna/write-log-file/-/write-log-file-3.13.0.tgz#b78d9e4cfc1349a8be64d91324c4c8199e822a26" + integrity sha512-RibeMnDPvlL8bFYW5C8cs4mbI3AHfQef73tnJCQ/SgrXZHehmHnsyWUiE7qDQCAo+B1RfTapvSyFF69iPj326A== + dependencies: + npmlog "^4.1.2" + write-file-atomic "^2.3.0" + +"@material-ui/core@^4.1.0": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.2.1.tgz#18255c01d039ff856bfdb2f955fec6c9ae64a464" + integrity sha512-hasPQUFAb9OxKng7UX2+SjUWtVZbnkVJ/jHZWXTivVcU+UzvNIpA9AyRRQvZ8SPV6swP/HD2VzUBzoMEeRR6wg== + dependencies: + "@babel/runtime" "^7.2.0" + "@material-ui/styles" "^4.2.0" + "@material-ui/system" "^4.3.0" + "@material-ui/types" "^4.1.1" + "@material-ui/utils" "^4.1.0" + "@types/react-transition-group" "^2.0.16" + clsx "^1.0.2" + convert-css-length "^2.0.1" + deepmerge "^4.0.0" + hoist-non-react-statics "^3.2.1" + is-plain-object "^3.0.0" + normalize-scroll-left "^0.2.0" + popper.js "^1.14.1" + prop-types "^15.7.2" + react-transition-group "^4.0.0" + warning "^4.0.1" + +"@material-ui/icons@^4.1.0": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-4.2.1.tgz#fe2f1c4f60c24256d244a69d86d0c00e8ed4037e" + integrity sha512-FvSD5lUBJ66frI4l4AYAPy2CH14Zs2Dgm0o3oOMr33BdQtOAjCgbdOcvPBeaD1w6OQl31uNW3CKOE8xfPNxvUQ== + dependencies: + "@babel/runtime" "^7.2.0" + +"@material-ui/pickers@^3.2.2": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@material-ui/pickers/-/pickers-3.2.2.tgz#9b7a67f5a8f21f8183853ebc1848e5d8dd680d4c" + integrity sha512-on/J1yyKeJ4CkLnItpf/jPDKMZVWvHDklkh5FS7wkZ0s1OPoqTsPubLWfA7eND6xREnVRyLFzVTlE3VlWYdQWw== + dependencies: + "@babel/runtime" "^7.2.0" + "@types/styled-jsx" "^2.2.8" + clsx "^1.0.2" + react-transition-group "^4.0.0" + rifm "^0.7.0" + tslib "^1.9.3" + +"@material-ui/styles@^4.2.0": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.2.1.tgz#b07383ffeaa840bcb6969eb17c5f0e3b734e8e5b" + integrity sha512-1KSOZ17LBWBqIyPRsEpyb4snT/wRIfQTPi0x66UvSzznVK9MPAfJx3/s5lVT4vrGFObs/nj6Pet6Nhrdl2WCrg== + dependencies: + "@babel/runtime" "^7.2.0" + "@emotion/hash" "^0.7.1" + "@material-ui/types" "^4.1.1" + "@material-ui/utils" "^4.1.0" + clsx "^1.0.2" + csstype "^2.5.2" + deepmerge "^4.0.0" + hoist-non-react-statics "^3.2.1" + jss "10.0.0-alpha.17" + jss-plugin-camel-case "10.0.0-alpha.17" + jss-plugin-default-unit "10.0.0-alpha.17" + jss-plugin-global "10.0.0-alpha.17" + jss-plugin-nested "10.0.0-alpha.17" + jss-plugin-props-sort "10.0.0-alpha.17" + jss-plugin-rule-value-function "10.0.0-alpha.17" + jss-plugin-vendor-prefixer "10.0.0-alpha.17" + prop-types "^15.7.2" + warning "^4.0.1" + +"@material-ui/system@^4.3.0": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.3.1.tgz#5fe508d4ca94cdf1d76f7fe535413fcc949b23d9" + integrity sha512-Krrc/p/A3rod4M3FYcsWSqE5KxpoyMzYuUHhs0Pns3KH+5kcFyBU+aYbIzMfUz58rhbHkqrShf1fjj7EKcgY0g== + dependencies: + "@babel/runtime" "^7.2.0" + deepmerge "^4.0.0" + prop-types "^15.7.2" + warning "^4.0.1" + +"@material-ui/types@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-4.1.1.tgz#b65e002d926089970a3271213a3ad7a21b17f02b" + integrity sha512-AN+GZNXytX9yxGi0JOfxHrRTbhFybjUJ05rnsBVjcB+16e466Z0Xe5IxawuOayVZgTBNDxmPKo5j4V6OnMtaSQ== + dependencies: + "@types/react" "*" + +"@material-ui/utils@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.1.0.tgz#45fd6698db49f3528fe45c922c496235021d76ec" + integrity sha512-muwmVU799tzPjzb+Q5E/CTDle0rXwkCAdvMVyU0BfbJhenkUsFmuYiCmbvMVOU1m6F1S5HWfXz8EP4pXwwAvrw== + dependencies: + "@babel/runtime" "^7.2.0" + prop-types "^15.7.2" + react-is "^16.8.0" + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@octokit/endpoint@^5.1.0": + version "5.3.2" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-5.3.2.tgz#2deda2d869cac9ba7f370287d55667be2a808d4b" + integrity sha512-gRjteEM9I6f4D8vtwU2iGUTn9RX/AJ0SVXiqBUEuYEWVGGAVjSXdT0oNmghH5lvQNWs8mwt6ZaultuG6yXivNw== + dependencies: + deepmerge "4.0.0" + is-plain-object "^3.0.0" + universal-user-agent "^3.0.0" + url-template "^2.0.8" + +"@octokit/plugin-enterprise-rest@^3.6.1": + version "3.6.2" + resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-3.6.2.tgz#74de25bef21e0182b4fa03a8678cd00a4e67e561" + integrity sha512-3wF5eueS5OHQYuAEudkpN+xVeUsg8vYEMMenEzLphUZ7PRZ8OJtDcsreL3ad9zxXmBbaFWzLmFcdob5CLyZftA== + +"@octokit/request-error@^1.0.1", "@octokit/request-error@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.0.4.tgz#15e1dc22123ba4a9a4391914d80ec1e5303a23be" + integrity sha512-L4JaJDXn8SGT+5G0uX79rZLv0MNJmfGa4vb4vy1NnpjSnWDLJRy6m90udGwvMmavwsStgbv2QNkPzzTCMmL+ig== + dependencies: + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^5.0.0": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.0.2.tgz#59a920451f24811c016ddc507adcc41aafb2dca5" + integrity sha512-z1BQr43g4kOL4ZrIVBMHwi68Yg9VbkRUyuAgqCp1rU3vbYa69+2gIld/+gHclw15bJWQnhqqyEb7h5a5EqgZ0A== + dependencies: + "@octokit/endpoint" "^5.1.0" + "@octokit/request-error" "^1.0.1" + deprecation "^2.0.0" + is-plain-object "^3.0.0" + node-fetch "^2.3.0" + once "^1.4.0" + universal-user-agent "^3.0.0" + +"@octokit/rest@^16.28.4": + version "16.28.7" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.28.7.tgz#a2c2db5b318da84144beba82d19c1a9dbdb1a1fa" + integrity sha512-cznFSLEhh22XD3XeqJw51OLSfyL2fcFKUO+v2Ep9MTAFfFLS1cK1Zwd1yEgQJmJoDnj4/vv3+fGGZweG+xsbIA== + dependencies: + "@octokit/request" "^5.0.0" + "@octokit/request-error" "^1.0.2" + atob-lite "^2.0.0" + before-after-hook "^2.0.0" + btoa-lite "^1.0.0" + deprecation "^2.0.0" + lodash.get "^4.4.2" + lodash.set "^4.3.2" + lodash.uniq "^4.5.0" + octokit-pagination-methods "^1.1.0" + once "^1.4.0" + universal-user-agent "^3.0.0" + url-template "^2.0.8" + +"@svgr/babel-plugin-add-jsx-attribute@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1" + integrity sha512-j7KnilGyZzYr/jhcrSYS3FGWMZVaqyCG0vzMCwzvei0coIkczuYMcniK07nI0aHJINciujjH11T72ICW5eL5Ig== + +"@svgr/babel-plugin-remove-jsx-attribute@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-4.2.0.tgz#297550b9a8c0c7337bea12bdfc8a80bb66f85abc" + integrity sha512-3XHLtJ+HbRCH4n28S7y/yZoEQnRpl0tvTZQsHqvaeNXPra+6vE5tbRliH3ox1yZYPCxrlqaJT/Mg+75GpDKlvQ== + +"@svgr/babel-plugin-remove-jsx-empty-expression@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-4.2.0.tgz#c196302f3e68eab6a05e98af9ca8570bc13131c7" + integrity sha512-yTr2iLdf6oEuUE9MsRdvt0NmdpMBAkgK8Bjhl6epb+eQWk6abBaX3d65UZ3E3FWaOwePyUgNyNCMVG61gGCQ7w== + +"@svgr/babel-plugin-replace-jsx-attribute-value@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-4.2.0.tgz#310ec0775de808a6a2e4fd4268c245fd734c1165" + integrity sha512-U9m870Kqm0ko8beHawRXLGLvSi/ZMrl89gJ5BNcT452fAjtF2p4uRzXkdzvGJJJYBgx7BmqlDjBN/eCp5AAX2w== + +"@svgr/babel-plugin-svg-dynamic-title@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-4.2.0.tgz#43f0f689a5347a894160eb51b39a109889a4df20" + integrity sha512-gH2qItapwCUp6CCqbxvzBbc4dh4OyxdYKsW3EOkYexr0XUmQL0ScbdNh6DexkZ01T+sdClniIbnCObsXcnx3sQ== + +"@svgr/babel-plugin-svg-em-dimensions@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-4.2.0.tgz#9a94791c9a288108d20a9d2cc64cac820f141391" + integrity sha512-C0Uy+BHolCHGOZ8Dnr1zXy/KgpBOkEUYY9kI/HseHVPeMbluaX3CijJr7D4C5uR8zrc1T64nnq/k63ydQuGt4w== + +"@svgr/babel-plugin-transform-react-native-svg@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-4.2.0.tgz#151487322843359a1ca86b21a3815fd21a88b717" + integrity sha512-7YvynOpZDpCOUoIVlaaOUU87J4Z6RdD6spYN4eUb5tfPoKGSF9OG2NuhgYnq4jSkAxcpMaXWPf1cePkzmqTPNw== + +"@svgr/babel-plugin-transform-svg-component@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-4.2.0.tgz#5f1e2f886b2c85c67e76da42f0f6be1b1767b697" + integrity sha512-hYfYuZhQPCBVotABsXKSCfel2slf/yvJY8heTVX1PCTaq/IgASq1IyxPPKJ0chWREEKewIU/JMSsIGBtK1KKxw== + +"@svgr/babel-preset@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-4.2.0.tgz#c9fc236445a02a8cd4e750085e51c181de00d6c5" + integrity sha512-iLetHpRCQXfK47voAs5/uxd736cCyocEdorisjAveZo8ShxJ/ivSZgstBmucI1c8HyMF5tOrilJLoFbhpkPiKw== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^4.2.0" + "@svgr/babel-plugin-remove-jsx-attribute" "^4.2.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^4.2.0" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^4.2.0" + "@svgr/babel-plugin-svg-dynamic-title" "^4.2.0" + "@svgr/babel-plugin-svg-em-dimensions" "^4.2.0" + "@svgr/babel-plugin-transform-react-native-svg" "^4.2.0" + "@svgr/babel-plugin-transform-svg-component" "^4.2.0" + +"@svgr/core@^4.1.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-4.2.0.tgz#f32ef8b9d05312aaa775896ec30ae46a6521e248" + integrity sha512-nvzXaf2VavqjMCTTfsZfjL4o9035KedALkMzk82qOlHOwBb8JT+9+zYDgBl0oOunbVF94WTLnvGunEg0csNP3Q== + dependencies: + "@svgr/plugin-jsx" "^4.2.0" + camelcase "^5.3.1" + cosmiconfig "^5.2.0" + +"@svgr/hast-util-to-babel-ast@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-4.2.0.tgz#dd743435a5f3a8e84a1da067f27b5fae3d7b6b63" + integrity sha512-IvAeb7gqrGB5TH9EGyBsPrMRH/QCzIuAkLySKvH2TLfLb2uqk98qtJamordRQTpHH3e6TORfBXoTo7L7Opo/Ow== + dependencies: + "@babel/types" "^7.4.0" + +"@svgr/plugin-jsx@^4.1.0", "@svgr/plugin-jsx@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-4.2.0.tgz#15a91562c9b5f90640ea0bdcb2ad59d692ee7ae9" + integrity sha512-AM1YokmZITgveY9bulLVquqNmwiFo2Px2HL+IlnTCR01YvWDfRL5QKdnF7VjRaS5MNP938mmqvL0/8oz3zQMkg== + dependencies: + "@babel/core" "^7.4.3" + "@svgr/babel-preset" "^4.2.0" + "@svgr/hast-util-to-babel-ast" "^4.2.0" + rehype-parse "^6.0.0" + unified "^7.1.0" + vfile "^4.0.0" + +"@svgr/plugin-svgo@^4.0.3": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-4.2.0.tgz#2a594a2d3312955e75fd87dc77ae51f377c809f3" + integrity sha512-zUEKgkT172YzHh3mb2B2q92xCnOAMVjRx+o0waZ1U50XqKLrVQ/8dDqTAtnmapdLsGurv8PSwenjLCUpj6hcvw== + dependencies: + cosmiconfig "^5.2.0" + merge-deep "^3.0.2" + svgo "^1.2.1" + +"@svgr/webpack@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-4.1.0.tgz#20c88f32f731c7b1d4711045b2b993887d731c28" + integrity sha512-d09ehQWqLMywP/PT/5JvXwPskPK9QCXUjiSkAHehreB381qExXf5JFCBWhfEyNonRbkIneCeYM99w+Ud48YIQQ== + dependencies: + "@babel/core" "^7.1.6" + "@babel/plugin-transform-react-constant-elements" "^7.0.0" + "@babel/preset-env" "^7.1.6" + "@babel/preset-react" "^7.0.0" + "@svgr/core" "^4.1.0" + "@svgr/plugin-jsx" "^4.1.0" + "@svgr/plugin-svgo" "^4.0.3" + loader-utils "^1.1.0" + +"@types/accepts@*": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" + integrity sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ== + dependencies: + "@types/node" "*" + +"@types/babel__core@^7.1.0": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.1.tgz#ce9a9e5d92b7031421e1d0d74ae59f572ba48be6" + integrity sha512-+hjBtgcFPYyCTo0A15+nxrCVJL7aC6Acg87TXd5OW3QhHswdrOLoles+ldL2Uk8q++7yIfl4tURtztccdeeyOw== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.0.2.tgz#d2112a6b21fad600d7674274293c85dce0cb47fc" + integrity sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" + integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.6.tgz#328dd1a8fc4cfe3c8458be9477b219ea158fd7b2" + integrity sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw== + dependencies: + "@babel/types" "^7.3.0" + +"@types/body-parser@*": + version "1.17.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c" + integrity sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/config@^0.0.34": + version "0.0.34" + resolved "https://registry.yarnpkg.com/@types/config/-/config-0.0.34.tgz#123f91bdb5afdd702294b9de9ca04d9ea11137b0" + integrity sha512-jWi9DXx77hnzN4kHCNEvP/kab+nchRLTg9yjXYxjTcMBkuc5iBb3QuwJ4sPrb+nzy1GQjrfyfMqZOdR4i7opRQ== + +"@types/connect@*": + version "3.4.32" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28" + integrity sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg== + dependencies: + "@types/node" "*" + +"@types/cookies@*": + version "0.7.2" + resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.7.2.tgz#5e0560d46ed9998082dce799af1058dd6a49780a" + integrity sha512-jnihWgshWystcJKrz8C9hV+Ot9lqOUyAh2RF+o3BEo6K6AS2l4zYCb9GYaBuZ3C6Il59uIGqpE3HvCun4KKeJA== + dependencies: + "@types/connect" "*" + "@types/express" "*" + "@types/keygrip" "*" + "@types/node" "*" + +"@types/d3-path@*": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.8.tgz#48e6945a8ff43ee0a1ce85c8cfa2337de85c7c79" + integrity sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA== + +"@types/d3-shape@*": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.1.tgz#1b4f92b7efd7306fe2474dc6ee94c0f0ed2e6ab6" + integrity sha512-usqdvUvPJ7AJNwpd2drOzRKs1ELie53p2m2GnPKr076/ADM579jVTJ5dPsoZ5E/CMNWk8lvPWYQSvilpp6jjwg== + dependencies: + "@types/d3-path" "*" + +"@types/events@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + +"@types/express-serve-static-core@*": + version "4.16.7" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.7.tgz#50ba6f8a691c08a3dd9fa7fba25ef3133d298049" + integrity sha512-847KvL8Q1y3TtFLRTXcVakErLJQgdpFSaq+k043xefz9raEf0C7HalpSY7OW5PyjCnY8P7bPW5t/Co9qqp+USg== + dependencies: + "@types/node" "*" + "@types/range-parser" "*" + +"@types/express@*": + version "4.17.0" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.0.tgz#49eaedb209582a86f12ed9b725160f12d04ef287" + integrity sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/serve-static" "*" + +"@types/glob@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + +"@types/history@*": + version "4.7.2" + resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.2.tgz#0e670ea254d559241b6eeb3894f8754991e73220" + integrity sha512-ui3WwXmjTaY73fOQ3/m3nnajU/Orhi6cEu5rzX+BrAAJxa3eITXZ5ch9suPqtM03OWhAHhPSyBGCN4UKoxO20Q== + +"@types/http-assert@*": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.0.tgz#56c95c69b51e7168b0d6727005d1fb2a00aaef94" + integrity sha512-8CBLG8RmxSvoY07FE6M/QpvJ7J5KzeKqF8eWN7Dq6Ks+lBTQae8Roc2G81lUu2Kw5Ju1gymOuvgyUsussbjAaA== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" + integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== + +"@types/istanbul-lib-report@*": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#e5471e7fa33c61358dd38426189c037a58433b8c" + integrity sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz#7a8cbf6a406f36c8add871625b278eaf0b0d255a" + integrity sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA== + dependencies: + "@types/istanbul-lib-coverage" "*" + "@types/istanbul-lib-report" "*" + +"@types/jsonwebtoken@^8.3.2": + version "8.3.2" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.3.2.tgz#e3d5245197152346fae7ee87d5541aa5a92d0362" + integrity sha512-Mkjljd9DTpkPlrmGfTJvcP4aBU7yO2QmW7wNVhV4/6AEUxYoacqU7FJU/N0yFEHTsIrE4da3rUrjrR5ejicFmA== + dependencies: + "@types/node" "*" + +"@types/keygrip@*": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.1.tgz#ff540462d2fb4d0a88441ceaf27d287b01c3d878" + integrity sha1-/1QEYtL7TQqIRBzq8n0oewHD2Hg= + +"@types/koa-bodyparser@^4.3.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@types/koa-bodyparser/-/koa-bodyparser-4.3.0.tgz#54ecd662c45f3a4fa9de849528de5fc8ab269ba5" + integrity sha512-aB/vwwq4G9FAtKzqZ2p8UHTscXxZvICFKVjuckqxCtkX1Ro7F5KHkTCUqTRZFBgDoEkmeca+bFLI1bIsdPPZTA== + dependencies: + "@types/koa" "*" + +"@types/koa-compose@*", "@types/koa-compose@^3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.4.tgz#76a461634a59c3e13449831708bb9b355fb1548e" + integrity sha512-ioou0rxkuWL+yBQYsHUQAzRTfVxAg8Y2VfMftU+Y3RA03/MzuFL0x/M2sXXj3PkfnENbHsjeHR1aMdezLYpTeA== + dependencies: + "@types/koa" "*" + +"@types/koa-pino-logger@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/koa-pino-logger/-/koa-pino-logger-2.1.5.tgz#7cf13cb200558993fa84fc1b2b6659ad2359ca08" + integrity sha512-uSJP8uXoA8gDyBODb9J4lLIpaaiUxreK/vHdf0a296frhYTsnVNZpE/811gYVfMiAxp/q2w/6GWVnyOUinOV4g== + dependencies: + "@types/koa" "*" + "@types/node" "*" + "@types/pino" "*" + "@types/pino-http" "*" + +"@types/koa-router@^7.0.42": + version "7.0.42" + resolved "https://registry.yarnpkg.com/@types/koa-router/-/koa-router-7.0.42.tgz#0e5c01d4d0a2873d402d432114f08372d7c50eb1" + integrity sha512-mggrNY7Ywwjt7QjaMAlbb1ixE+v7AFskOeyKdmZT/NvPVEAo48gYUxIcF8ILlMc3eg1bo6SxNoUcbxhTv7edrA== + dependencies: + "@types/koa" "*" + +"@types/koa@*", "@types/koa@^2.0.49": + version "2.0.49" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.0.49.tgz#8ffc2ddbdd715a2c392a218c67e116cb07007234" + integrity sha512-WQWpCH8O4Dslk8IcXfazff40aM1jXX7BQRbADIj/fKozVPu76P/wQE4sRe2SCWMn8yNkOcare2MkDrnZqLMkPQ== + dependencies: + "@types/accepts" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + +"@types/lodash@^4.14.136": + version "4.14.136" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.136.tgz#413e85089046b865d960c9ff1d400e04c31ab60f" + integrity sha512-0GJhzBdvsW2RUccNHOBkabI8HZVdOXmXbXhuKlDEd5Vv12P7oAVGfomGp3Ne21o5D/qu1WmthlNKFaoZJJeErA== + +"@types/luxon@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.15.2.tgz#528f11f7d6dc08cec0445d4bea8065a5bb6989b2" + integrity sha512-zHPoyVrLvNaiMRYdhmh88Rn489ZgAgbc6iLxR5Yi0VCNfeNYHcszbhJV2vDHLNrVGy35BPtWBRn4OP2F9BBvFw== + +"@types/mime@*": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d" + integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw== + +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + +"@types/node-fetch@^2.3.7": + version "2.3.7" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.3.7.tgz#b7212e895100f8642dbdab698472bab5f3c1d2f1" + integrity sha512-+bKtuxhj/TYSSP1r4CZhfmyA0vm/aDRQNo7vbAgf6/cZajn0SAniGGST07yvI4Q+q169WTa2/x9gEHfJrkcALw== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "12.0.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.0.tgz#d11813b9c0ff8aaca29f04cbc12817f4c7d656e5" + integrity sha512-Jrb/x3HT4PTJp6a4avhmJCDEVrPdqLfl3e8GGMbpkGGdwAV5UGlIs4vVEfsHHfylZVOKZWpOqmqFH8CbfOZ6kg== + +"@types/node@^12.0.8": + version "12.0.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.8.tgz#551466be11b2adc3f3d47156758f610bd9f6b1d8" + integrity sha512-b8bbUOTwzIY3V5vDTY1fIJ+ePKDUBqt2hC2woVGotdQQhG/2Sh62HOKHrT7ab+VerXAcPyAiTEipPu/FsreUtg== + +"@types/pino-http@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/pino-http/-/pino-http-4.0.2.tgz#e01994d1eb0cf5261b14048c11575b4cff382509" + integrity sha512-746TwLXL52SEg6sdjkOaAIKeOsaZFoE7N3MThvXr8SXrRfIcUdQW7XhtMqw1C710zlcxlS8+XrobvLx3gdov1g== + dependencies: + "@types/pino" "*" + +"@types/pino@*": + version "5.8.8" + resolved "https://registry.yarnpkg.com/@types/pino/-/pino-5.8.8.tgz#930eb30f1f1eb76b97792647477aab59e5852574" + integrity sha512-SxAdLtEpPkVUdnI3iCUjyFC7WVLFiebyzqypvkuJVo7dyK6BPuQ4lTOuZRKpbEdgG386l5HgfqpymL3oWLOUXg== + dependencies: + "@types/node" "*" + "@types/sonic-boom" "*" + +"@types/prop-types@*": + version "15.7.1" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6" + integrity sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg== + +"@types/q@^1.5.1": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" + integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== + +"@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + +"@types/react-dom@^16.8.4": + version "16.8.4" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.4.tgz#7fb7ba368857c7aa0f4e4511c4710ca2c5a12a88" + integrity sha512-eIRpEW73DCzPIMaNBDP5pPIpK1KXyZwNgfxiVagb5iGiz6da+9A5hslSX6GAQKdO7SayVCS/Fr2kjqprgAvkfA== + dependencies: + "@types/react" "*" + +"@types/react-router-dom@^4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.3.tgz#7837e3e9fefbc84a8f6c8a51dca004f4e83e94e3" + integrity sha512-xj0DmFjgvAqRfh/kJPO7apD5G30yPQe+8slu/dugioQOkdKpyzc4Fgk4hoTelm6CSHz7pI2PPsW5+Y6GRBF2zw== + dependencies: + "@types/history" "*" + "@types/react" "*" + "@types/react-router" "*" + +"@types/react-router@*": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.0.1.tgz#9f4548c75755c55b0cffdd743080e5afa87da6dd" + integrity sha512-vOyVO0u3Cs0w6G5DzYqNVqcTsurEnDgOmmkJf2s7VwtunWzpPgI6dHsCBX68vXqeICpP6jCfojgJcHkm5BV7hQ== + dependencies: + "@types/history" "*" + "@types/react" "*" + +"@types/react-swipeable-views@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@types/react-swipeable-views/-/react-swipeable-views-0.13.0.tgz#9e5f2efa51380886f3f73014ac8b15546d337210" + integrity sha512-orrreCcXev6IUXDuHf07RDDCAoIZRMSr95eyWmYNRfjic7w/O+68iPu0NCysVls+UygRNvoqZMuXI72N/58E1w== + dependencies: + "@types/react" "*" + +"@types/react-transition-group@^2.0.16": + version "2.9.2" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.9.2.tgz#c48cf2a11977c8b4ff539a1c91d259eaa627028d" + integrity sha512-5Fv2DQNO+GpdPZcxp2x/OQG/H19A01WlmpjVD9cKvVFmoVLOZ9LvBgSWG6pSXIU4og5fgbvGPaCV5+VGkWAEHA== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "16.8.23" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.23.tgz#ec6be3ceed6353a20948169b6cb4c97b65b97ad2" + integrity sha512-abkEOIeljniUN9qB5onp++g0EY38h7atnDHxwKUFz1r3VH1+yG1OKi2sNPTyObL40goBmfKFpdii2lEzwLX1cA== + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + +"@types/react@^16.8.19": + version "16.8.19" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.19.tgz#629154ef05e2e1985cdde94477deefd823ad9be3" + integrity sha512-QzEzjrd1zFzY9cDlbIiFvdr+YUmefuuRYrPxmkwG0UQv5XF35gFIi7a95m1bNVcFU0VimxSZ5QVGSiBmlggQXQ== + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + +"@types/recharts-scale@*": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/recharts-scale/-/recharts-scale-1.0.0.tgz#348c9220d6d9062c44a9d585d686644a97f7e25d" + integrity sha512-HR/PrCcxYb2YHviTqH7CMdL1TUhUZLTUKzfrkMhxm1HTa5mg/QtP8XMiuSPz6dZ6wecazAOu8aYZ5DqkNlgHHQ== + +"@types/recharts@^1.1.16": + version "1.1.16" + resolved "https://registry.yarnpkg.com/@types/recharts/-/recharts-1.1.16.tgz#54ea319cd688c4cb72f3b5d664cee5a364d3ca00" + integrity sha512-thAgSzw7UEJTN0xjZ9lASo52ZkXdwtC1qNe0/Pp2Wz835nBN//725tq7JvXC8pRejFSLofqTZAH0r1wbeEuG9w== + dependencies: + "@types/d3-shape" "*" + "@types/react" "*" + "@types/recharts-scale" "*" + +"@types/serve-static@*": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48" + integrity sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q== + dependencies: + "@types/express-serve-static-core" "*" + "@types/mime" "*" + +"@types/sonic-boom@*": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@types/sonic-boom/-/sonic-boom-0.6.2.tgz#5f6c7bf6b4a0994f9339d778da6a7adcc3d37080" + integrity sha512-vP9Sn1tuz/BTh8L1o776Cbzr+WH4dZGmRXOjQ5L+IVQx40hUmvOS2wfIkqUsID1vL62tThWdlXWIqijwewu3mw== + dependencies: + "@types/node" "*" + +"@types/stack-utils@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" + integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== + +"@types/styled-jsx@^2.2.8": + version "2.2.8" + resolved "https://registry.yarnpkg.com/@types/styled-jsx/-/styled-jsx-2.2.8.tgz#b50d13d8a3c34036282d65194554cf186bab7234" + integrity sha512-Yjye9VwMdYeXfS71ihueWRSxrruuXTwKCbzue4+5b2rjnQ//AtyM7myZ1BEhNhBQ/nL/RE7bdToUoLln2miKvg== + dependencies: + "@types/react" "*" + +"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" + integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== + +"@types/uuid@^3.4.5": + version "3.4.5" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.5.tgz#d4dc10785b497a1474eae0ba7f0cb09c0ddfd6eb" + integrity sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA== + dependencies: + "@types/node" "*" + +"@types/vfile-message@*": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/vfile-message/-/vfile-message-1.0.1.tgz#e1e9895cc6b36c462d4244e64e6d0b6eaf65355a" + integrity sha512-mlGER3Aqmq7bqR1tTTIVHq8KSAFFRyGbrxuM8C/H82g6k7r2fS+IMEkIu3D7JHzG10NvPdR8DNx0jr0pwpp4dA== + dependencies: + "@types/node" "*" + "@types/unist" "*" + +"@types/vfile@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/vfile/-/vfile-3.0.2.tgz#19c18cd232df11ce6fa6ad80259bc86c366b09b9" + integrity sha512-b3nLFGaGkJ9rzOcuXRfHkZMdjsawuDD0ENL9fzTophtBg8FJHSGbH7daXkEpcwy3v7Xol3pAvsmlYyFhR4pqJw== + dependencies: + "@types/node" "*" + "@types/unist" "*" + "@types/vfile-message" "*" + +"@types/yargs@^12.0.2", "@types/yargs@^12.0.9": + version "12.0.12" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.12.tgz#45dd1d0638e8c8f153e87d296907659296873916" + integrity sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw== + +"@typescript-eslint/eslint-plugin@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.6.0.tgz#a5ff3128c692393fb16efa403ec7c8a5593dab0f" + integrity sha512-U224c29E2lo861TQZs6GSmyC0OYeRNg6bE9UVIiFBxN2MlA0nq2dCrgIVyyRbC05UOcrgf2Wk/CF2gGOPQKUSQ== + dependencies: + "@typescript-eslint/parser" "1.6.0" + "@typescript-eslint/typescript-estree" "1.6.0" + requireindex "^1.2.0" + tsutils "^3.7.0" + +"@typescript-eslint/parser@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.6.0.tgz#f01189c8b90848e3b8e45a6cdad27870529d1804" + integrity sha512-VB9xmSbfafI+/kI4gUK3PfrkGmrJQfh0N4EScT1gZXSZyUxpsBirPL99EWZg9MmPG0pzq/gMtgkk7/rAHj4aQw== + dependencies: + "@typescript-eslint/typescript-estree" "1.6.0" + eslint-scope "^4.0.0" + eslint-visitor-keys "^1.0.0" + +"@typescript-eslint/typescript-estree@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.6.0.tgz#6cf43a07fee08b8eb52e4513b428c8cdc9751ef0" + integrity sha512-A4CanUwfaG4oXobD5y7EXbsOHjCwn8tj1RDd820etpPAjH+Icjc2K9e/DQM1Hac5zH2BSy+u6bjvvF2wwREvYA== + dependencies: + lodash.unescape "4.0.1" + semver "5.5.0" + +"@webassemblyjs/ast@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" + integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== + dependencies: + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + +"@webassemblyjs/floating-point-hex-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" + integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== + +"@webassemblyjs/helper-api-error@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" + integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== + +"@webassemblyjs/helper-buffer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" + integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== + +"@webassemblyjs/helper-code-frame@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" + integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== + dependencies: + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/helper-fsm@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" + integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== + +"@webassemblyjs/helper-module-context@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" + integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== + dependencies: + "@webassemblyjs/ast" "1.8.5" + mamacro "^0.0.3" + +"@webassemblyjs/helper-wasm-bytecode@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" + integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== + +"@webassemblyjs/helper-wasm-section@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" + integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + +"@webassemblyjs/ieee754@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" + integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" + integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" + integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== + +"@webassemblyjs/wasm-edit@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" + integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/helper-wasm-section" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-opt" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/wasm-gen@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" + integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" + +"@webassemblyjs/wasm-opt@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" + integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + +"@webassemblyjs/wasm-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" + integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" + +"@webassemblyjs/wast-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" + integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/floating-point-hex-parser" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-code-frame" "1.8.5" + "@webassemblyjs/helper-fsm" "1.8.5" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" + integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +"@zkochan/cmd-shim@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@zkochan/cmd-shim/-/cmd-shim-3.1.0.tgz#2ab8ed81f5bb5452a85f25758eb9b8681982fd2e" + integrity sha512-o8l0+x7C7sMZU3v9GuJIAU10qQLtwR1dtRQIOmlNMtyaqhmpXOzx1HWiYoWfmmf9HHZoAkXpc9TM9PQYF9d4Jg== + dependencies: + is-windows "^1.0.0" + mkdirp-promise "^5.0.1" + mz "^2.5.0" + +JSONStream@^1.0.4, JSONStream@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +abab@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" + integrity sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +accepts@^1.3.5, accepts@~1.3.4, accepts@~1.3.5: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-dynamic-import@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948" + integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw== + +acorn-globals@^4.1.0, acorn-globals@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.2.tgz#4e2c2313a597fd589720395f6354b41cd5ec8006" + integrity sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ== + dependencies: + acorn "^6.0.1" + acorn-walk "^6.0.1" + +acorn-jsx@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" + integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== + +acorn-walk@^6.0.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913" + integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw== + +acorn@^5.5.3: + version "5.7.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" + integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== + +acorn@^6.0.1, acorn@^6.0.4, acorn@^6.0.5, acorn@^6.0.7: + version "6.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" + integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== + +address@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" + integrity sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg== + +address@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/address/-/address-1.1.0.tgz#ef8e047847fcd2c5b6f50c16965f924fd99fe709" + integrity sha512-4diPfzWbLEIElVG4AnqP+00SULlPzNuyJFNnmMrLgyaxG6tZXJ1sn7mjBu4fHrJE+Yp/jgylOweJn2xsLMFggQ== + +agent-base@4, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +agent-base@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== + dependencies: + es6-promisify "^5.0.0" + +agentkeepalive@^3.4.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" + integrity sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ== + dependencies: + humanize-ms "^1.2.1" + +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + +ajv-keywords@^3.1.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.0.tgz#4b831e7b531415a7cc518cd404e73f6193c6349d" + integrity sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw== + +ajv@^6.1.0, ajv@^6.5.5, ajv@^6.9.1: + version "6.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" + integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^6.10.2: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +alphanum-sort@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= + +ansi-align@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" + integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38= + dependencies: + string-width "^2.0.0" + +ansi-colors@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== + +ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-escapes@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.2.1.tgz#4dccdb846c3eee10f6d64dea66273eab90c37228" + integrity sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q== + dependencies: + type-fest "^0.5.2" + +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.0.0, ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +any-promise@^1.0.0, any-promise@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +aproba@^1.0.3, aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +aproba@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +aria-query@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" + integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= + dependencies: + ast-types-flow "0.0.7" + commander "^2.11.0" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-differ@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-2.1.0.tgz#4b9c1c3f14b906757082925769e8ab904f4801b1" + integrity sha512-KbUpJgx909ZscOc/7CLATBFam7P1Z1QRQInvgT0UztM9Q72aGKCunKASAl7WNW0tnPmPyEMeMhdsfWhfmW037w== + +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= + +array-filter@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-flatten@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +array-ify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" + integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= + +array-includes@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" + integrity sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + +array-map@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI= + +array-reduce@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= + +array-union@^1.0.1, array-union@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + +asap@^2.0.0, asap@~2.0.3, asap@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + +asn1.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assert@^1.1.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +ast-types-flow@0.0.7, ast-types-flow@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" + integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== + +async@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob-lite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/atob-lite/-/atob-lite-2.0.0.tgz#0fef5ad46f1bd7a8502c65727f0367d5ee43d696" + integrity sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY= + +atob@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +autoprefixer@^9.4.9: + version "9.5.1" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.5.1.tgz#243b1267b67e7e947f28919d786b50d3bb0fb357" + integrity sha512-KJSzkStUl3wP0D5sdMlP82Q52JLy5+atf2MHAre48+ckWkXgixmfHyWmA77wFDy6jTHU6mIgXv6hAQ2mf1PjJQ== + dependencies: + browserslist "^4.5.4" + caniuse-lite "^1.0.30000957" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.14" + postcss-value-parser "^3.3.1" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== + +axobject-query@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9" + integrity sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww== + dependencies: + ast-types-flow "0.0.7" + +babel-code-frame@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-eslint@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed" + integrity sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" + eslint-scope "3.7.1" + eslint-visitor-keys "^1.0.0" + +babel-extract-comments@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz#0a2aedf81417ed391b85e18b4614e693a0351a21" + integrity sha512-qWWzi4TlddohA91bFwgt6zO/J0X+io7Qp184Fw0m2JYRSTZnJbFR8+07KmzudHCZgOiKRCrjhylwv9Xd8gfhVQ== + dependencies: + babylon "^6.18.0" + +babel-jest@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.8.0.tgz#5c15ff2b28e20b0f45df43fe6b7f2aae93dba589" + integrity sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw== + dependencies: + "@jest/transform" "^24.8.0" + "@jest/types" "^24.8.0" + "@types/babel__core" "^7.1.0" + babel-plugin-istanbul "^5.1.0" + babel-preset-jest "^24.6.0" + chalk "^2.4.2" + slash "^2.0.0" + +babel-loader@8.0.5: + version "8.0.5" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.5.tgz#225322d7509c2157655840bba52e46b6c2f2fe33" + integrity sha512-NTnHnVRd2JnRqPC0vW+iOQWU5pchDbYXsG2E6DMXEpMfUcQKclF9gmf3G3ZMhzG7IG9ji4coL0cm+FxeWxDpnw== + dependencies: + find-cache-dir "^2.0.0" + loader-utils "^1.0.2" + mkdirp "^0.5.1" + util.promisify "^1.0.0" + +babel-plugin-dynamic-import-node@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.2.0.tgz#c0adfb07d95f4a4495e9aaac6ec386c4d7c2524e" + integrity sha512-fP899ELUnTaBcIzmrW7nniyqqdYWrWuJUyPWHxFa/c7r7hS6KC8FscNfLlBNIoPSc55kYMGEEKjPjJGCLbE1qA== + dependencies: + object.assign "^4.1.0" + +babel-plugin-istanbul@^5.1.0: + version "5.1.4" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.4.tgz#841d16b9a58eeb407a0ddce622ba02fe87a752ba" + integrity sha512-dySz4VJMH+dpndj0wjJ8JPs/7i1TdSPb1nRrn56/92pKOF9VKC1FMFJmMXjzlGGusnCAqujP6PBCiKq0sVA+YQ== + dependencies: + find-up "^3.0.0" + istanbul-lib-instrument "^3.3.0" + test-exclude "^5.2.3" + +babel-plugin-jest-hoist@^24.6.0: + version "24.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz#f7f7f7ad150ee96d7a5e8e2c5da8319579e78019" + integrity sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w== + dependencies: + "@types/babel__traverse" "^7.0.6" + +babel-plugin-macros@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.5.1.tgz#4a119ac2c2e19b458c259b9accd7ee34fd57ec6f" + integrity sha512-xN3KhAxPzsJ6OQTktCanNpIFnnMsCV+t8OloKxIL72D6+SUZYFn9qfklPgef5HyyDtzYZqqb+fs1S12+gQY82Q== + dependencies: + "@babel/runtime" "^7.4.2" + cosmiconfig "^5.2.0" + resolve "^1.10.0" + +babel-plugin-named-asset-import@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.2.tgz#20978ed446b8e1bf4a2f42d0a94c0ece85f75f4f" + integrity sha512-CxwvxrZ9OirpXQ201Ec57OmGhmI8/ui/GwTDy0hSp6CmRvgRC0pSair6Z04Ck+JStA0sMPZzSJ3uE4n17EXpPQ== + +babel-plugin-syntax-object-rest-spread@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + integrity sha1-/WU28rzhODb/o6VFjEkDpZe7O/U= + +babel-plugin-transform-object-rest-spread@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06" + integrity sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY= + dependencies: + babel-plugin-syntax-object-rest-spread "^6.8.0" + babel-runtime "^6.26.0" + +babel-plugin-transform-react-remove-prop-types@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" + integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== + +babel-preset-jest@^24.6.0: + version "24.6.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz#66f06136eefce87797539c0d63f1769cc3915984" + integrity sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw== + dependencies: + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + babel-plugin-jest-hoist "^24.6.0" + +babel-preset-react-app@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-9.0.0.tgz#703108142bc9dd7173bde6a1a0138a762abc76f9" + integrity sha512-YVsDA8HpAKklhFLJtl9+AgaxrDaor8gGvDFlsg1ByOS0IPGUovumdv4/gJiAnLcDmZmKlH6+9sVOz4NVW7emAg== + dependencies: + "@babel/core" "7.4.3" + "@babel/plugin-proposal-class-properties" "7.4.0" + "@babel/plugin-proposal-decorators" "7.4.0" + "@babel/plugin-proposal-object-rest-spread" "7.4.3" + "@babel/plugin-syntax-dynamic-import" "7.2.0" + "@babel/plugin-transform-classes" "7.4.3" + "@babel/plugin-transform-destructuring" "7.4.3" + "@babel/plugin-transform-flow-strip-types" "7.4.0" + "@babel/plugin-transform-react-constant-elements" "7.2.0" + "@babel/plugin-transform-react-display-name" "7.2.0" + "@babel/plugin-transform-runtime" "7.4.3" + "@babel/preset-env" "7.4.3" + "@babel/preset-react" "7.0.0" + "@babel/preset-typescript" "7.3.3" + "@babel/runtime" "7.4.3" + babel-plugin-dynamic-import-node "2.2.0" + babel-plugin-macros "2.5.1" + babel-plugin-transform-react-remove-prop-types "0.4.24" + +babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== + +bail@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.4.tgz#7181b66d508aa3055d3f6c13f0a0c720641dde9b" + integrity sha512-S8vuDB4w6YpRhICUDET3guPlQpaJl7od94tpZ0Fvnyp+MKW/HyDTcRDck+29C9g+d/qQHnddRH3+94kZdrW0Ww== + +balanced-match@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + integrity sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg= + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +before-after-hook@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" + integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +bluebird@^3.5.1, bluebird@^3.5.5: + version "3.5.5" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" + integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== + +bluebird@^3.5.3: + version "3.5.4" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.4.tgz#d6cc661595de30d5b3af5fcedd3c0b3ef6ec5714" + integrity sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +body-parser@1.18.3: + version "1.18.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" + integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ= + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "~1.6.3" + iconv-lite "0.4.23" + on-finished "~2.3.0" + qs "6.5.2" + raw-body "2.3.3" + type-is "~1.6.16" + +bonjour@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= + dependencies: + array-flatten "^2.1.0" + deep-equal "^1.0.1" + dns-equal "^1.0.0" + dns-txt "^2.0.2" + multicast-dns "^6.0.1" + multicast-dns-service-types "^1.1.0" + +boolbase@^1.0.0, boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +boxen@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" + integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw== + dependencies: + ansi-align "^2.0.0" + camelcase "^4.0.0" + chalk "^2.0.1" + cli-boxes "^1.0.0" + string-width "^2.0.0" + term-size "^1.2.0" + widest-line "^2.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browser-process-hrtime@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" + integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== + +browser-resolve@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" + integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== + dependencies: + resolve "1.1.7" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.5.4.tgz#166c4ecef3b51737a42436ea8002aeea466ea2c7" + integrity sha512-rAjx494LMjqKnMPhFkuLmLp8JWEX0o8ADTGeAbOqaF+XCvYLreZrG5uVjnPBlAQ8REZK4pzXGvp0bWgrFtKaag== + dependencies: + caniuse-lite "^1.0.30000955" + electron-to-chromium "^1.3.122" + node-releases "^1.1.13" + +browserslist@^4.0.0, browserslist@^4.1.1, browserslist@^4.4.2, browserslist@^4.5.2, browserslist@^4.5.4: + version "4.5.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.5.6.tgz#ea42e8581ca2513fa7f371d4dd66da763938163d" + integrity sha512-o/hPOtbU9oX507lIqon+UvPYqpx3mHc8cV3QemSBTXwkG8gSQSK6UKvXcE/DcleU3+A59XTUHyCvZ5qGy8xVAg== + dependencies: + caniuse-lite "^1.0.30000963" + electron-to-chromium "^1.3.127" + node-releases "^1.1.17" + +bser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" + integrity sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk= + dependencies: + node-int64 "^0.4.0" + +btoa-lite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" + integrity sha1-M3dm2hWAEhD92VbCLpxokaudAzc= + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg= + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + +builtins@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" + integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= + +byline@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= + +byte-size@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-5.0.1.tgz#4b651039a5ecd96767e71a3d7ed380e48bed4191" + integrity sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw== + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cacache@^11.0.2: + version "11.3.2" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.2.tgz#2d81e308e3d258ca38125b676b98b2ac9ce69bfa" + integrity sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg== + dependencies: + bluebird "^3.5.3" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.3" + graceful-fs "^4.1.15" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.2" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cacache@^12.0.0: + version "12.0.2" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.2.tgz#8db03205e36089a3df6954c66ce92541441ac46c" + integrity sha512-ifKgxH2CKhJEg6tNdAwziu6Q33EvuG26tYcda6PT3WKisZcYDXsnEdnRv67Po3yCzFfaSoMjGZzJyD2c3DT1dg== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cache-content-type@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-content-type/-/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c" + integrity sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA== + dependencies: + mime-types "^2.1.18" + ylru "^1.2.0" + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@3.0.x: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase-keys@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" + integrity sha1-oqpfsa9oh1glnDLBQUJteJI7m3c= + dependencies: + camelcase "^4.1.0" + map-obj "^2.0.0" + quick-lru "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + +camelcase@^4.0.0, camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + +camelcase@^5.0.0, camelcase@^5.2.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000939, caniuse-lite@^1.0.30000955, caniuse-lite@^1.0.30000957, caniuse-lite@^1.0.30000963: + version "1.0.30000967" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000967.tgz#a5039577806fccee80a04aaafb2c0890b1ee2f73" + integrity sha512-rUBIbap+VJfxTzrM4akJ00lkvVb5/n5v3EGXfWzSH5zT8aJmGzjA8HWhJ4U6kCpzxozUSnB+yvAYDRPY6mRpgQ== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +capture-stack-trace@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d" + integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw== + +case-sensitive-paths-webpack-plugin@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.2.0.tgz#3371ef6365ef9c25fa4b81c16ace0e9c7dc58c3e" + integrity sha512-u5ElzokS8A1pm9vM3/iDgTcI3xqHxuCao94Oz8etI3cf0Tio0p8izkDYbTIn09uP3yUUr6+veaE6IkjnTYS46g== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +ccount@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.4.tgz#9cf2de494ca84060a2a8d2854edd6dfb0445f386" + integrity sha512-fpZ81yYfzentuieinmGnphk0pLkOTMm6MZdVqwd77ROvhko6iujLNGrHH5E7utq3ygWklwfmwuG+A7P+NpqT6w== + +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.5.tgz#0ae8434d962281a5f56c72869e79cb6d9d86ad4d" + integrity sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chokidar@^2.1.5: + version "2.1.6" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" + integrity sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chownr@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" + integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== + +chrome-trace-event@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48" + integrity sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A== + dependencies: + tslib "^1.9.0" + +ci-info@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +classnames@^2.2.5, classnames@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" + integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== + +clean-css@4.2.x: + version "4.2.1" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17" + integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g== + dependencies: + source-map "~0.6.0" + +cli-boxes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" + integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= + +clipboard-copy@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/clipboard-copy/-/clipboard-copy-3.1.0.tgz#4c59030a43d4988990564a664baeafba99f78ca4" + integrity sha512-Xsu1NddBXB89IUauda5BIq3Zq73UWkjkaQlPQbLNvNsd5WBMnTWPNKYR6HGaySOxGYZ+BKxP2E9X4ElnI3yiPA== + +cliui@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + +clone-deep@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" + integrity sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY= + dependencies: + for-own "^0.1.3" + is-plain-object "^2.0.1" + kind-of "^3.0.2" + lazy-cache "^1.0.3" + shallow-clone "^0.1.2" + +clone-deep@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-2.0.2.tgz#00db3a1e173656730d1188c3d6aced6d7ea97713" + integrity sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ== + dependencies: + for-own "^1.0.0" + is-plain-object "^2.0.4" + kind-of "^6.0.0" + shallow-clone "^1.0.0" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + +clsx@^1.0.2, clsx@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.0.4.tgz#0c0171f6d5cb2fe83848463c15fcc26b4df8c2ec" + integrity sha512-1mQ557MIZTrL/140j+JVdRM6e31/OA4vTYxXgqIIZlndyfjHpyawKZia1Im05Vp9BWmImkcNrNtFYQMyFcgJDg== + +co-body@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/co-body/-/co-body-6.0.0.tgz#965b9337d7f5655480787471f4237664820827e3" + integrity sha512-9ZIcixguuuKIptnY8yemEOuhb71L/lLf+Rl5JfJEUiDNJk0e02MBt7BPxR2GEh5mw8dPthQYR4jPI/BnS1MQgw== + dependencies: + inflation "^2.0.0" + qs "^6.5.2" + raw-body "^2.3.3" + type-is "^1.6.16" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" + integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0, color-convert@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/color/-/color-3.1.1.tgz#7abf5c0d38e89378284e873c207ae2172dcc8a61" + integrity sha512-PvUltIXRjehRKPSy89VnDWFKY58xyhTLyxIg21vwQBI6qLwZNPmC8k3C1uytIgFKEpOIzN4y32iPm8231zFHIg== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + +columnify@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" + integrity sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs= + dependencies: + strip-ansi "^3.0.0" + wcwidth "^1.0.0" + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" + integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== + dependencies: + delayed-stream "~1.0.0" + +comma-separated-tokens@^1.0.0: + version "1.0.6" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.6.tgz#3cd3d8adc725ab473843db338bcdfd4a7bb087bf" + integrity sha512-f20oA7jsrrmERTS70r3tmRSxR8IJV2MTN7qe6hzgX+3ARfXrdMJFvGWvWQK0xpcBurg9j9eO2MiqzZ8Y+/UPCA== + +commander@2.17.x: + version "2.17.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== + +commander@^2.11.0, commander@^2.19.0, commander@~2.20.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" + integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== + +commander@~2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== + +common-tags@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" + integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + +compare-func@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-1.3.2.tgz#99dd0ba457e1f9bc722b12c08ec33eeab31fa648" + integrity sha1-md0LpFfh+bxyKxLAjsM+6rMfpkg= + dependencies: + array-ify "^1.0.0" + dot-prop "^3.0.0" + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +compressible@~2.0.16: + version "2.0.17" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1" + integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw== + dependencies: + mime-db ">= 1.40.0 < 2" + +compression@^1.5.2: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.5.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +concat-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" + integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.0.2" + typedarray "^0.0.6" + +config-chain@^1.1.11: + version "1.1.12" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" + integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +config@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/config/-/config-3.2.1.tgz#c687cdd0ba22433422ff4f6e3edffbe3930aa141" + integrity sha512-EMA/IU0gBI3OZHi41B2JaosXEc6tJMN8RT3Pm5cHuRfbtfAQbNmYB6bFq0JK8tRu8F2WZ8s+5tnUX7acEy37xw== + dependencies: + json5 "^1.0.1" + +configstore@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f" + integrity sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw== + dependencies: + dot-prop "^4.1.0" + graceful-fs "^4.1.2" + make-dir "^1.0.0" + unique-string "^1.0.0" + write-file-atomic "^2.0.0" + xdg-basedir "^3.0.0" + +confusing-browser-globals@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.7.tgz#5ae852bd541a910e7ffb2dbb864a2d21a36ad29b" + integrity sha512-cgHI1azax5ATrZ8rJ+ODDML9Fvu67PimB6aNxBrc/QwSaDaM9eTfIEUHx3bBLJJ82ioSb+/5zfsMCCEJax3ByQ== + +connect-history-api-fallback@^1.3.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA= + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= + +content-disposition@~0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@^1.0.4, content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +conventional-changelog-angular@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz#299fdd43df5a1f095283ac16aeedfb0a682ecab0" + integrity sha512-YD1xzH7r9yXQte/HF9JBuEDfvjxxwDGGwZU1+ndanbY0oFgA+Po1T9JDSpPLdP0pZT6MhCAsdvFKC4TJ4MTJTA== + dependencies: + compare-func "^1.3.1" + q "^1.5.1" + +conventional-changelog-core@^3.1.6: + version "3.2.3" + resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-3.2.3.tgz#b31410856f431c847086a7dcb4d2ca184a7d88fb" + integrity sha512-LMMX1JlxPIq/Ez5aYAYS5CpuwbOk6QFp8O4HLAcZxe3vxoCtABkhfjetk8IYdRB9CDQGwJFLR3Dr55Za6XKgUQ== + dependencies: + conventional-changelog-writer "^4.0.6" + conventional-commits-parser "^3.0.3" + dateformat "^3.0.0" + get-pkg-repo "^1.0.0" + git-raw-commits "2.0.0" + git-remote-origin-url "^2.0.0" + git-semver-tags "^2.0.3" + lodash "^4.2.1" + normalize-package-data "^2.3.5" + q "^1.5.1" + read-pkg "^3.0.0" + read-pkg-up "^3.0.0" + through2 "^3.0.0" + +conventional-changelog-preset-loader@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.2.0.tgz#571e2b3d7b53d65587bea9eedf6e37faa5db4fcc" + integrity sha512-zXB+5vF7D5Y3Cb/rJfSyCCvFphCVmF8mFqOdncX3BmjZwAtGAPfYrBcT225udilCKvBbHgyzgxqz2GWDB5xShQ== + +conventional-changelog-writer@^4.0.6: + version "4.0.7" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.7.tgz#e4b7d9cbea902394ad671f67108a71fa90c7095f" + integrity sha512-p/wzs9eYaxhFbrmX/mCJNwJuvvHR+j4Fd0SQa2xyAhYed6KBiZ780LvoqUUvsayP4R1DtC27czalGUhKV2oabw== + dependencies: + compare-func "^1.3.1" + conventional-commits-filter "^2.0.2" + dateformat "^3.0.0" + handlebars "^4.1.2" + json-stringify-safe "^5.0.1" + lodash "^4.2.1" + meow "^4.0.0" + semver "^6.0.0" + split "^1.0.0" + through2 "^3.0.0" + +conventional-commits-filter@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz#f122f89fbcd5bb81e2af2fcac0254d062d1039c1" + integrity sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ== + dependencies: + lodash.ismatch "^4.4.0" + modify-values "^1.0.0" + +conventional-commits-parser@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.0.3.tgz#c3f972fd4e056aa8b9b4f5f3d0e540da18bf396d" + integrity sha512-KaA/2EeUkO4bKjinNfGUyqPTX/6w9JGshuQRik4r/wJz7rUw3+D3fDG6sZSEqJvKILzKXFQuFkpPLclcsAuZcg== + dependencies: + JSONStream "^1.0.4" + is-text-path "^2.0.0" + lodash "^4.2.1" + meow "^4.0.0" + split2 "^2.0.0" + through2 "^3.0.0" + trim-off-newlines "^1.0.0" + +conventional-recommended-bump@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-5.0.1.tgz#5af63903947b6e089e77767601cb592cabb106ba" + integrity sha512-RVdt0elRcCxL90IrNP0fYCpq1uGt2MALko0eyeQ+zQuDVWtMGAy9ng6yYn3kax42lCj9+XBxQ8ZN6S9bdKxDhQ== + dependencies: + concat-stream "^2.0.0" + conventional-changelog-preset-loader "^2.1.1" + conventional-commits-filter "^2.0.2" + conventional-commits-parser "^3.0.3" + git-raw-commits "2.0.0" + git-semver-tags "^2.0.3" + meow "^4.0.0" + q "^1.5.1" + +convert-css-length@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/convert-css-length/-/convert-css-length-2.0.1.tgz#90a76bde5bfd24d72881a5b45d02249b2c1d257c" + integrity sha512-iGpbcvhLPRKUbBc0Quxx7w/bV14AC3ItuBEGMahA5WTYqB8lq9jH0kTXFheCBASsYnqeMFZhiTruNxr1N59Axg== + +convert-source-map@^1.1.0, convert-source-map@^1.4.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" + integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A== + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= + +cookies@~0.7.1: + version "0.7.3" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.3.tgz#7912ce21fbf2e8c2da70cf1c3f351aecf59dadfa" + integrity sha512-+gixgxYSgQLTaTIilDHAdlNPZDENDQernEMiIcZpYYP14zgHsCt4Ce1FEjFtcp6GefhozebB6orvhAAWx/IS0A== + dependencies: + depd "~1.1.2" + keygrip "~1.0.3" + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +copy-to@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/copy-to/-/copy-to-2.0.1.tgz#2680fbb8068a48d08656b6098092bdafc906f4a5" + integrity sha1-JoD7uAaKSNCGVrYJgJK9r8kG9KU= + +core-js-compat@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.0.1.tgz#bff73ba31ca8687431b9c88f78d3362646fb76f0" + integrity sha512-2pC3e+Ht/1/gD7Sim/sqzvRplMiRnFQVlPpDVaHtY9l7zZP7knamr3VRD6NyGfHd84MrDC0tAM9ulNxYMW0T3g== + dependencies: + browserslist "^4.5.4" + core-js "3.0.1" + core-js-pure "3.0.1" + semver "^6.0.0" + +core-js-pure@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.0.1.tgz#37358fb0d024e6b86d443d794f4e37e949098cbe" + integrity sha512-mSxeQ6IghKW3MoyF4cz19GJ1cMm7761ON+WObSyLfTu/Jn3x7w4NwNFnrZxgl4MTSvYYepVLNuRtlB4loMwJ5g== + +core-js@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.1.tgz#1343182634298f7f38622f95e73f54e48ddf4738" + integrity sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew== + +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= + +core-js@^2.4.0: + version "2.6.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895" + integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A== + +core-js@^2.5.1, core-js@^2.6.5: + version "2.6.9" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" + integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cosmiconfig@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-4.0.0.tgz#760391549580bbd2df1e562bc177b13c290972dc" + integrity sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ== + dependencies: + is-directory "^0.3.1" + js-yaml "^3.9.0" + parse-json "^4.0.0" + require-from-string "^2.0.1" + +cosmiconfig@^5.0.0, cosmiconfig@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.0.tgz#45038e4d28a7fe787203aede9c25bca4a08b12c8" + integrity sha512-nxt+Nfc3JAqf4WIWd0jXLjTJZmsPLrA9DDc4nRw2KFJQJK7DNooqSXrNI7tzLG50CF8axczly5UV929tBmh/7g== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.0" + parse-json "^4.0.0" + +cosmiconfig@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +create-ecdh@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-error-class@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" + integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y= + dependencies: + capture-stack-trace "^1.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +crypto-random-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" + integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= + +css-blank-pseudo@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5" + integrity sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w== + dependencies: + postcss "^7.0.5" + +css-box-model@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.1.3.tgz#aa1ac5853d2c9358783e67fe0b1ca22ecd82b232" + integrity sha512-qBsLTX8giq7dnep+kXlh5YvDLofO65PDvigAdJbU5zBsmzuCl6uxfTkGexkyIhfnMoZ/bXrCHcXmHeVRHzbs3A== + dependencies: + tiny-invariant "^1.0.5" + +css-color-names@0.0.4, css-color-names@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= + +css-declaration-sorter@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" + integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== + dependencies: + postcss "^7.0.1" + timsort "^0.3.0" + +css-has-pseudo@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz#3c642ab34ca242c59c41a125df9105841f6966ee" + integrity sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^5.0.0-rc.4" + +css-loader@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea" + integrity sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w== + dependencies: + camelcase "^5.2.0" + icss-utils "^4.1.0" + loader-utils "^1.2.3" + normalize-path "^3.0.0" + postcss "^7.0.14" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^2.0.6" + postcss-modules-scope "^2.1.0" + postcss-modules-values "^2.0.0" + postcss-value-parser "^3.3.0" + schema-utils "^1.0.0" + +css-prefers-color-scheme@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz#6f830a2714199d4f0d0d0bb8a27916ed65cff1f4" + integrity sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg== + dependencies: + postcss "^7.0.5" + +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" + integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== + +css-select@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-select@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.0.2.tgz#ab4386cec9e1f668855564b17c3733b43b2a5ede" + integrity sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ== + dependencies: + boolbase "^1.0.0" + css-what "^2.1.2" + domutils "^1.7.0" + nth-check "^1.0.2" + +css-tree@1.0.0-alpha.28: + version "1.0.0-alpha.28" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.28.tgz#8e8968190d886c9477bc8d61e96f61af3f7ffa7f" + integrity sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w== + dependencies: + mdn-data "~1.1.0" + source-map "^0.5.3" + +css-tree@1.0.0-alpha.29: + version "1.0.0-alpha.29" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.29.tgz#3fa9d4ef3142cbd1c301e7664c1f352bd82f5a39" + integrity sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg== + dependencies: + mdn-data "~1.1.0" + source-map "^0.5.3" + +css-unit-converter@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" + integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY= + +css-url-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/css-url-regex/-/css-url-regex-1.1.0.tgz#83834230cc9f74c457de59eebd1543feeb83b7ec" + integrity sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w= + +css-vendor@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-2.0.5.tgz#949c58fd5307e79a9417daa0939506f0e5d0a187" + integrity sha512-36w+4Cg0zqFIt5TAkaM3proB6XWh5kSGmbddRCPdrRLQiYNfHPTgaWPOlCrcuZIO0iAtrG+5wsHJZ6jj8AUULA== + dependencies: + "@babel/runtime" "^7.3.1" + is-in-browser "^1.0.2" + +css-what@2.1, css-what@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== + +cssdb@^4.3.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0" + integrity sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ== + +cssesc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703" + integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-default@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" + integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== + dependencies: + css-declaration-sorter "^4.0.1" + cssnano-util-raw-cache "^4.0.1" + postcss "^7.0.0" + postcss-calc "^7.0.1" + postcss-colormin "^4.0.3" + postcss-convert-values "^4.0.1" + postcss-discard-comments "^4.0.2" + postcss-discard-duplicates "^4.0.2" + postcss-discard-empty "^4.0.1" + postcss-discard-overridden "^4.0.1" + postcss-merge-longhand "^4.0.11" + postcss-merge-rules "^4.0.3" + postcss-minify-font-values "^4.0.2" + postcss-minify-gradients "^4.0.2" + postcss-minify-params "^4.0.2" + postcss-minify-selectors "^4.0.2" + postcss-normalize-charset "^4.0.1" + postcss-normalize-display-values "^4.0.2" + postcss-normalize-positions "^4.0.2" + postcss-normalize-repeat-style "^4.0.2" + postcss-normalize-string "^4.0.2" + postcss-normalize-timing-functions "^4.0.2" + postcss-normalize-unicode "^4.0.1" + postcss-normalize-url "^4.0.1" + postcss-normalize-whitespace "^4.0.2" + postcss-ordered-values "^4.1.2" + postcss-reduce-initial "^4.0.3" + postcss-reduce-transforms "^4.0.2" + postcss-svgo "^4.0.2" + postcss-unique-selectors "^4.0.1" + +cssnano-util-get-arguments@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" + integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= + +cssnano-util-get-match@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" + integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= + +cssnano-util-raw-cache@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" + integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== + dependencies: + postcss "^7.0.0" + +cssnano-util-same-parent@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" + integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== + +cssnano@^4.1.0: + version "4.1.10" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" + integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== + dependencies: + cosmiconfig "^5.0.0" + cssnano-preset-default "^4.0.7" + is-resolvable "^1.0.0" + postcss "^7.0.0" + +csso@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/csso/-/csso-3.5.1.tgz#7b9eb8be61628973c1b261e169d2f024008e758b" + integrity sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg== + dependencies: + css-tree "1.0.0-alpha.29" + +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0", cssom@^0.3.4: + version "0.3.6" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.6.tgz#f85206cee04efa841f3c5982a74ba96ab20d65ad" + integrity sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A== + +cssstyle@^1.0.0, cssstyle@^1.1.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.2.2.tgz#427ea4d585b18624f6fdbf9de7a2a1a3ba713077" + integrity sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow== + dependencies: + cssom "0.3.x" + +csstype@^2.2.0, csstype@^2.5.2: + version "2.6.6" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.6.tgz#c34f8226a94bbb10c32cc0d714afdf942291fc41" + integrity sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg== + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= + dependencies: + array-find-index "^1.0.1" + +cyclist@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" + integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= + +d3-array@^1.2.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" + integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== + +d3-collection@1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e" + integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A== + +d3-color@1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.2.3.tgz#6c67bb2af6df3cc8d79efcc4d3a3e83e28c8048f" + integrity sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw== + +d3-format@1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.3.2.tgz#6a96b5e31bcb98122a30863f7d92365c00603562" + integrity sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ== + +d3-interpolate@1, d3-interpolate@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.3.2.tgz#417d3ebdeb4bc4efcc8fd4361c55e4040211fd68" + integrity sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w== + dependencies: + d3-color "1" + +d3-path@1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.7.tgz#8de7cd693a75ac0b5480d3abaccd94793e58aae8" + integrity sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA== + +d3-scale@^2.1.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f" + integrity sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw== + dependencies: + d3-array "^1.2.0" + d3-collection "1" + d3-format "1" + d3-interpolate "1" + d3-time "1" + d3-time-format "2" + +d3-shape@^1.2.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.5.tgz#e81aea5940f59f0a79cfccac012232a8987c6033" + integrity sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg== + dependencies: + d3-path "1" + +d3-time-format@2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.3.tgz#ae06f8e0126a9d60d6364eac5b1533ae1bac826b" + integrity sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA== + dependencies: + d3-time "1" + +d3-time@1: + version "1.0.11" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.11.tgz#1d831a3e25cd189eb256c17770a666368762bbce" + integrity sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw== + +damerau-levenshtein@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514" + integrity sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ= + +dargs@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17" + integrity sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc= + dependencies: + number-is-nan "^1.0.0" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-urls@^1.0.0, data-urls@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" + integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== + dependencies: + abab "^2.0.0" + whatwg-mimetype "^2.2.0" + whatwg-url "^7.0.0" + +date-fns@^2.0.0-alpha.27: + version "2.0.0-beta.4" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-beta.4.tgz#3e1bf33a15da69481f81972c4a50aad762a81f2c" + integrity sha512-xekjYm7ZDBuzePM/GBodhi3hW3P8dd2RbuIOLBjet2E6EGFR82wHTTXCSGuDEoapqlDvsx88ymRsq85lbM7dDw== + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= + +dateformat@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" + integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== + +dayjs@^1.8.12: + version "1.8.15" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.15.tgz#7121bc04e6a7f2621ed6db566be4a8aaf8c3913e" + integrity sha512-HYHCI1nohG52B45vCQg8Re3hNDZbMroWPkhz50yaX7Lu0ATyjGsTdoYZBpjED9ar6chqTx2dmSmM8A51mojnAg== + +debounce@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131" + integrity sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg== + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@3.1.0, debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^3.1.0, debug@^3.2.5, debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +debuglog@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= + +decamelize-keys@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decamelize@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-2.0.0.tgz#656d7bbc8094c4c788ea53c5840908c9c7d063c7" + integrity sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg== + dependencies: + xregexp "4.0.0" + +decimal.js-light@^2.4.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.0.tgz#ca7faf504c799326df94b0ab920424fdfc125348" + integrity sha512-b3VJCbd2hwUpeRGG3Toob+CRo8W22xplipNhP3tN7TSVB/cyMX71P1vM2Xjc9H74uV6dS2hDDmo/rHq8L87Upg== + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + +deep-equal@^1.0.1, deep-equal@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +deepmerge@4.0.0, deepmerge@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.0.0.tgz#3e3110ca29205f120d7cb064960a39c3d2087c09" + integrity sha512-YZ1rOP5+kHor4hMAH+HRQnBQHg+wvS1un1hAOuIcxcBy0hzcUf6Jg2a1w65kpoOUnurOfZbERwjI1TfZxNjcww== + +default-gateway@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== + dependencies: + execa "^1.0.0" + ip-regex "^2.1.0" + +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +del@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5" + integrity sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU= + dependencies: + globby "^6.1.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + p-map "^1.1.1" + pify "^3.0.0" + rimraf "^2.2.8" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +depd@^1.1.2, depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +deprecation@^2.0.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@^1.0.4, destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-indent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +detect-newline@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= + +detect-node@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== + +detect-port-alt@1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" + integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== + dependencies: + address "^1.0.1" + debug "^2.6.0" + +dezalgo@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" + integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY= + dependencies: + asap "^2.0.0" + wrappy "1" + +diff-sequences@^24.3.0: + version "24.3.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.3.0.tgz#0f20e8a1df1abddaf4d9c226680952e64118b975" + integrity sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag== + dependencies: + arrify "^1.0.1" + path-type "^3.0.0" + +dir-glob@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= + +dns-packet@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" + integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= + dependencies: + buffer-indexof "^1.0.0" + +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-converter@^0.2: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-helpers@^3.2.1, dom-helpers@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" + integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== + dependencies: + "@babel/runtime" "^7.1.2" + +dom-serializer@0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" + integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== + dependencies: + domelementtype "^1.3.0" + entities "^1.1.1" + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + +domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + +domexception@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" + integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== + dependencies: + webidl-conversions "^4.0.2" + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1, domutils@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + +dot-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177" + integrity sha1-G3CK8JSknJoOfbyteQq6U52sEXc= + dependencies: + is-obj "^1.0.0" + +dot-prop@^4.1.0, dot-prop@^4.1.1, dot-prop@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" + integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== + dependencies: + is-obj "^1.0.0" + +dotenv-expand@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-4.2.0.tgz#def1f1ca5d6059d24a766e587942c21106ce1275" + integrity sha1-3vHxyl1gWdJKdm5YeULCEQbOEnU= + +dotenv@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" + integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +duplexer@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= + +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.122, electron-to-chromium@^1.3.127: + version "1.3.133" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.133.tgz#c47639c19b91feee3e22fad69f5556142007008c" + integrity sha512-lyoC8aoqbbDqsprb6aPdt9n3DpOZZzdz/T4IZKsR0/dkZIxnJVUjjcpOSwA66jPRIOyDAamCTAUqweU05kKNSg== + +elliptic@^6.0.0: + version "6.4.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a" + integrity sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emoji-regex@^7.0.1, emoji-regex@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s= + dependencies: + iconv-lite "~0.4.13" + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" + integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + tapable "^1.0.0" + +entities@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +env-paths@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" + integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA= + +err-code@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" + integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA= + +errno@^0.1.3, errno@~0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +error-inject@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/error-inject/-/error-inject-1.0.0.tgz#e2b3d91b54aed672f309d950d154850fa11d4f37" + integrity sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc= + +es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.5.1, es-abstract@^1.7.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" + integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-keys "^1.0.12" + +es-to-primitive@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + +escape-html@^1.0.3, escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escodegen@^1.11.0, escodegen@^1.9.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510" + integrity sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw== + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-react-app@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-4.0.1.tgz#23fd0fd7ea89442ef1e733f66a7207674b23c8db" + integrity sha512-ZsaoXUIGsK8FCi/x4lT2bZR5mMkL/Kgj+Lnw690rbvvUr/uiwgFiD8FcfAhkCycm7Xte6O5lYz4EqMx2vX7jgw== + dependencies: + confusing-browser-globals "^1.0.7" + +eslint-config-standard@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-12.0.0.tgz#638b4c65db0bd5a41319f96bba1f15ddad2107d9" + integrity sha512-COUz8FnXhqFitYj4DTqHzidjIL/t4mumGZto5c7DrBpvWoie+Sn3P4sLEzUGeYhRElWuFEf8K1S1EfvD1vixCQ== + +eslint-import-resolver-node@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" + integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q== + dependencies: + debug "^2.6.9" + resolve "^1.5.0" + +eslint-loader@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.1.2.tgz#453542a1230d6ffac90e4e7cb9cadba9d851be68" + integrity sha512-rA9XiXEOilLYPOIInvVH5S/hYfyTPyxag6DZhoQOduM+3TkghAEQ3VcFO8VnX4J4qg/UIBzp72aOf/xvYmpmsg== + dependencies: + loader-fs-cache "^1.0.0" + loader-utils "^1.0.2" + object-assign "^4.0.1" + object-hash "^1.1.4" + rimraf "^2.6.1" + +eslint-module-utils@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz#8b93499e9b00eab80ccb6614e69f03678e84e09a" + integrity sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw== + dependencies: + debug "^2.6.8" + pkg-dir "^2.0.0" + +eslint-module-utils@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz#7b4675875bf96b0dbf1b21977456e5bb1f5e018c" + integrity sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw== + dependencies: + debug "^2.6.8" + pkg-dir "^2.0.0" + +eslint-plugin-es@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.0.tgz#475f65bb20c993fc10e8c8fe77d1d60068072da6" + integrity sha512-XfFmgFdIUDgvaRAlaXUkxrRg5JSADoRC8IkKLc/cISeR3yHVMefFHQZpcyXXEUUPHfy5DwviBcrfqlyqEwlQVw== + dependencies: + eslint-utils "^1.3.0" + regexpp "^2.0.1" + +eslint-plugin-flowtype@2.50.1: + version "2.50.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.50.1.tgz#36d4c961ac8b9e9e1dc091d3fba0537dad34ae8a" + integrity sha512-9kRxF9hfM/O6WGZcZPszOVPd2W0TLHBtceulLTsGfwMPtiCCLnCW0ssRiOOiXyqrCA20pm1iXdXm7gQeN306zQ== + dependencies: + lodash "^4.17.10" + +eslint-plugin-import@2.16.0: + version "2.16.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.16.0.tgz#97ac3e75d0791c4fac0e15ef388510217be7f66f" + integrity sha512-z6oqWlf1x5GkHIFgrSvtmudnqM6Q60KM4KvpWi5ubonMjycLjndvd5+8VAZIsTlHC03djdgJuyKG6XO577px6A== + dependencies: + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.3.0" + has "^1.0.3" + lodash "^4.17.11" + minimatch "^3.0.4" + read-pkg-up "^2.0.0" + resolve "^1.9.0" + +eslint-plugin-import@^2.18.2: + version "2.18.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz#02f1180b90b077b33d447a17a2326ceb400aceb6" + integrity sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ== + dependencies: + array-includes "^3.0.3" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.0" + has "^1.0.3" + minimatch "^3.0.4" + object.values "^1.1.0" + read-pkg-up "^2.0.0" + resolve "^1.11.0" + +eslint-plugin-jsx-a11y@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.1.tgz#4ebba9f339b600ff415ae4166e3e2e008831cf0c" + integrity sha512-cjN2ObWrRz0TTw7vEcGQrx+YltMvZoOEx4hWU8eEERDnBIU00OTq7Vr+jA7DFKxiwLNv4tTh5Pq2GUNEa8b6+w== + dependencies: + aria-query "^3.0.0" + array-includes "^3.0.3" + ast-types-flow "^0.0.7" + axobject-query "^2.0.2" + damerau-levenshtein "^1.0.4" + emoji-regex "^7.0.2" + has "^1.0.3" + jsx-ast-utils "^2.0.1" + +eslint-plugin-node@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-9.1.0.tgz#f2fd88509a31ec69db6e9606d76dabc5adc1b91a" + integrity sha512-ZwQYGm6EoV2cfLpE1wxJWsfnKUIXfM/KM09/TlorkukgCAwmkgajEJnPCmyzoFPQQkmvo5DrW/nyKutNIw36Mw== + dependencies: + eslint-plugin-es "^1.4.0" + eslint-utils "^1.3.1" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + +eslint-plugin-promise@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" + integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== + +eslint-plugin-react-hooks@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.6.0.tgz#348efcda8fb426399ac7b8609607c7b4025a6f5f" + integrity sha512-lHBVRIaz5ibnIgNG07JNiAuBUeKhEf8l4etNx5vfAEwqQ5tcuK3jV9yjmopPgQDagQb7HwIuQVsE3IVcGrRnag== + +eslint-plugin-react@7.12.4: + version "7.12.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.12.4.tgz#b1ecf26479d61aee650da612e425c53a99f48c8c" + integrity sha512-1puHJkXJY+oS1t467MjbqjvX53uQ05HXwjqDgdbGBqf5j9eeydI54G3KwiJmWciQ0HTBacIKw2jgwSBSH3yfgQ== + dependencies: + array-includes "^3.0.3" + doctrine "^2.1.0" + has "^1.0.3" + jsx-ast-utils "^2.0.1" + object.fromentries "^2.0.0" + prop-types "^15.6.2" + resolve "^1.9.0" + +eslint-plugin-standard@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.0.tgz#f845b45109c99cd90e77796940a344546c8f6b5c" + integrity sha512-OwxJkR6TQiYMmt1EsNRMe5qG3GsbjlcOhbGUBY4LtavF9DsLaTcoR+j2Tdjqi23oUwKNUqX7qcn5fPStafMdlA== + +eslint-scope@3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" + integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug= + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-scope@^4.0.0, eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.0.tgz#e2c3c8dba768425f897cf0f9e51fe2e241485d4c" + integrity sha512-7ehnzPaP5IIEh1r1tkjuIrxqhNkzUJa9z3R92tLJdZIVdWaczEhr3EbhGtsMrVxi1KeR8qA7Off6SWc5WNQqyQ== + dependencies: + eslint-visitor-keys "^1.0.0" + +eslint-utils@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512" + integrity sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q== + +eslint-visitor-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" + integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ== + +eslint@^5.16.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + +esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== + dependencies: + estraverse "^4.1.0" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= + +esutils@^2.0.0, esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eventemitter3@^3.0.0, eventemitter3@^3.1.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + +events@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" + integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== + +eventsource@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" + integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== + dependencies: + original "^1.0.0" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +exec-sh@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b" + integrity sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg== + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-24.8.0.tgz#471f8ec256b7b6129ca2524b2a62f030df38718d" + integrity sha512-/zYvP8iMDrzaaxHVa724eJBCKqSHmO0FA7EDkBiRHxg6OipmMn1fN+C8T9L9K8yr7UONkOifu6+LLH+z76CnaA== + dependencies: + "@jest/types" "^24.8.0" + ansi-styles "^3.2.0" + jest-get-type "^24.8.0" + jest-matcher-utils "^24.8.0" + jest-message-util "^24.8.0" + jest-regex-util "^24.3.0" + +express@^4.16.2: + version "4.16.4" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" + integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== + dependencies: + accepts "~1.3.5" + array-flatten "1.1.1" + body-parser "1.18.3" + content-disposition "0.5.2" + content-type "~1.0.4" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.1.1" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.2" + path-to-regexp "0.1.7" + proxy-addr "~2.0.4" + qs "6.5.2" + range-parser "~1.2.0" + safe-buffer "5.1.2" + send "0.16.2" + serve-static "1.13.2" + setprototypeof "1.1.0" + statuses "~1.4.0" + type-is "~1.6.16" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" + integrity sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + +fast-glob@^2.0.2: + version "2.2.6" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.6.tgz#a5d5b697ec8deda468d85a74035290a025a95295" + integrity sha512-0BvMaZc1k9F+MeWWMe8pL6YltFzZYcJsYU7D4JyDA6PAczaXvxqQQ/z+mDF7/4Mw01DeUc+i3CTKajnkANkV4w== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-glob@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-json-parse@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" + integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw== + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-safe-stringify@^1.0.8, fast-safe-stringify@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-1.2.3.tgz#9fe22c37fb2f7f86f06b8f004377dbf8f1ee7bc1" + integrity sha512-QJYT/i0QYoiZBQ71ivxdyTqkwKkQ0oxACXHYxH2zYHJEgzi2LsbjgvtzTbLi1SZcF190Db2YP7I7eTsU2egOlw== + +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= + dependencies: + websocket-driver ">=0.5.1" + +faye-websocket@~0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38" + integrity sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg= + dependencies: + websocket-driver ">=0.5.1" + +fb-watchman@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" + integrity sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg= + dependencies: + bser "^2.0.0" + +fbjs@^0.8.4: + version "0.8.17" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" + integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90= + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + +figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" + integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +figures@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.0.0.tgz#756275c964646163cc6f9197c7a0295dbfd04de9" + integrity sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +file-loader@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-3.0.1.tgz#f8e0ba0b599918b51adfe45d66d1e771ad560faa" + integrity sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw== + dependencies: + loader-utils "^1.0.2" + schema-utils "^1.0.0" + +filefy@0.1.9: + version "0.1.9" + resolved "https://registry.yarnpkg.com/filefy/-/filefy-0.1.9.tgz#9f342c2f3f804505cb5011107fcb345378b282bf" + integrity sha512-M+9HcR+Hu0LTh5qYTHRnmA2dm+WBHCG6WU+Biw/84RamiV41DzEArSLockUQ0qCSV1fJI0BoCpnniVG/Ch5tMw== + +filesize@3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +finalhandler@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" + integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.4.0" + unpipe "~1.0.0" + +find-cache-dir@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" + integrity sha1-yN765XyKUqinhPnjHFfHQumToLk= + dependencies: + commondir "^1.0.1" + mkdirp "^0.5.1" + pkg-dir "^1.0.0" + +find-cache-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatstr@^1.0.5: + version "1.0.12" + resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.12.tgz#c2ba6a08173edbb6c9640e3055b95e287ceb5931" + integrity sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw== + +flatted@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916" + integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg== + +flatten@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" + integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= + +flush-write-stream@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +follow-redirects@^1.0.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" + integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== + dependencies: + debug "^3.2.6" + +for-in@^0.1.3: + version "0.1.8" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" + integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE= + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +for-own@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= + dependencies: + for-in "^1.0.1" + +for-own@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" + integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= + dependencies: + for-in "^1.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +fork-ts-checker-webpack-plugin@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.1.1.tgz#caf2a210778fb1e171b6993ca0a40f9b6589e3b7" + integrity sha512-gqWAEMLlae/oeVnN6RWCAhesOJMswAN1MaKNqhhjXHV5O0/rTUjWI4UbgQHdlrVbCnb+xLotXmJbBlC66QmpFw== + dependencies: + babel-code-frame "^6.22.0" + chalk "^2.4.1" + chokidar "^2.0.4" + micromatch "^3.1.10" + minimatch "^3.0.4" + semver "^5.6.0" + tapable "^1.0.0" + worker-rpc "^0.1.0" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2, fresh@~0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-extra@7.0.1, fs-extra@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ== + dependencies: + minipass "^2.2.1" + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.6.tgz#87b19df0bfb4a1a51d7ddb51b01b5f3bedb40c33" + integrity sha512-vfmKZp3XPM36DNF0qhW+Cdxk7xm7gTEHY1clv1Xq1arwRQuKZgAhw+NZNWbJBtuaNxzNXwhfdPYRrvIbjfS33A== + +fsevents@^1.2.7: + version "1.2.9" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" + integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== + dependencies: + nan "^2.12.1" + node-pre-gyp "^0.12.0" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +genfun@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" + integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz#b877b49a5c16aefac3655f2ed2ea5b684df8d203" + integrity sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg== + +get-pkg-repo@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz#c73b489c06d80cc5536c2c853f9e05232056972d" + integrity sha1-xztInAbYDMVTbCyFP54FIyBWly0= + dependencies: + hosted-git-info "^2.1.4" + meow "^3.3.0" + normalize-package-data "^2.3.0" + parse-github-repo-url "^1.3.0" + through2 "^2.0.0" + +get-port@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119" + integrity sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw== + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^4.0.0, get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +git-raw-commits@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.0.tgz#d92addf74440c14bcc5c83ecce3fb7f8a79118b5" + integrity sha512-w4jFEJFgKXMQJ0H0ikBk2S+4KP2VEjhCvLCNqbNRQC8BgGWgLKNCO7a9K9LI+TVT7Gfoloje502sEnctibffgg== + dependencies: + dargs "^4.0.1" + lodash.template "^4.0.2" + meow "^4.0.0" + split2 "^2.0.0" + through2 "^2.0.0" + +git-remote-origin-url@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" + integrity sha1-UoJlna4hBxRaERJhEq0yFuxfpl8= + dependencies: + gitconfiglocal "^1.0.0" + pify "^2.3.0" + +git-semver-tags@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-2.0.3.tgz#48988a718acf593800f99622a952a77c405bfa34" + integrity sha512-tj4FD4ww2RX2ae//jSrXZzrocla9db5h0V7ikPl1P/WwoZar9epdUhwR7XHXSgc+ZkNq72BEEerqQuicoEQfzA== + dependencies: + meow "^4.0.0" + semver "^6.0.0" + +git-up@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.1.tgz#cb2ef086653640e721d2042fe3104857d89007c0" + integrity sha512-LFTZZrBlrCrGCG07/dm1aCjjpL1z9L3+5aEeI9SBhAqSc+kiA9Or1bgZhQFNppJX6h/f5McrvJt1mQXTFm6Qrw== + dependencies: + is-ssh "^1.3.0" + parse-url "^5.0.0" + +git-url-parse@^11.1.2: + version "11.1.2" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-11.1.2.tgz#aff1a897c36cc93699270587bea3dbcbbb95de67" + integrity sha512-gZeLVGY8QVKMIkckncX+iCq2/L8PlwncvDFKiWkBn9EtCfYDbliRTTp6qzyQ1VMdITUfq7293zDzfpjdiGASSQ== + dependencies: + git-up "^4.0.0" + +gitconfiglocal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" + integrity sha1-QdBF84UaXqiPA/JMocYXgRRGS5s= + dependencies: + ini "^1.3.2" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" + integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= + +glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-dirs@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" + integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= + dependencies: + ini "^1.3.4" + +global-modules@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0, globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globby@8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.2.tgz#5697619ccd95c5275dbb2d6faa42087c1a941d8d" + integrity sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w== + dependencies: + array-union "^1.0.1" + dir-glob "2.0.0" + fast-glob "^2.0.2" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globby@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" + integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^1.0.2" + dir-glob "^2.2.2" + fast-glob "^2.2.6" + glob "^7.1.3" + ignore "^4.0.3" + pify "^4.0.1" + slash "^2.0.0" + +got@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" + integrity sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA= + dependencies: + create-error-class "^3.0.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-redirect "^1.0.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + lowercase-keys "^1.0.0" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + unzip-response "^2.0.1" + url-parse-lax "^1.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== + +graceful-fs@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" + integrity sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q== + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= + +gud@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" + integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== + +gzip-size@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.0.0.tgz#a55ecd99222f4c48fd8c01c625ce3b349d0a0e80" + integrity sha512-5iI7omclyqrnWw4XbXAmGhPsABkSIDQonv2K0h61lybgofWa6iZyvrI3r2zsJH4P8Nb64fFVzlvfhs0g7BBxAA== + dependencies: + duplexer "^0.1.1" + pify "^3.0.0" + +handle-thing@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" + integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== + +handlebars@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.2.tgz#b6b37c1ced0306b221e094fc7aca3ec23b131b67" + integrity sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw== + dependencies: + neo-async "^2.6.0" + optimist "^0.6.1" + source-map "^0.6.1" + optionalDependencies: + uglify-js "^3.1.4" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + +harmony-reflect@^1.4.6: + version "1.6.1" + resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.1.tgz#c108d4f2bb451efef7a37861fdbdae72c9bdefa9" + integrity sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA== + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= + +has-unicode@^2.0.0, has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.0, has@^1.0.1, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hast-util-from-parse5@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-5.0.0.tgz#a505a05766e0f96e389bfb0b1dd809eeefcef47b" + integrity sha512-A7ev5OseS/J15214cvDdcI62uwovJO2PB60Xhnq7kaxvvQRFDEccuqbkrFXU03GPBGopdPqlpQBRqIcDS/Fjbg== + dependencies: + ccount "^1.0.3" + hastscript "^5.0.0" + property-information "^5.0.0" + web-namespaces "^1.1.2" + xtend "^4.0.1" + +hast-util-parse-selector@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.1.tgz#4ddbae1ae12c124e3eb91b581d2556441766f0ab" + integrity sha512-Xyh0v+nHmQvrOqop2Jqd8gOdyQtE8sIP9IQf7mlVDqp924W4w/8Liuguk2L2qei9hARnQSG2m+wAOCxM7npJVw== + +hastscript@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-5.0.0.tgz#fee10382c1bc4ba3f1be311521d368c047d2c43a" + integrity sha512-xJtuJ8D42Xtq5yJrnDg/KAIxl2cXBXKoiIJwmWX9XMf8113qHTGl/Bf7jEsxmENJ4w6q4Tfl8s/Y6mEZo8x8qw== + dependencies: + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.2.0" + property-information "^5.0.1" + space-separated-tokens "^1.0.0" + +he@1.2.x: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hex-color-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" + integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== + +history@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/history/-/history-4.9.0.tgz#84587c2068039ead8af769e9d6a6860a14fa1bca" + integrity sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA== + dependencies: + "@babel/runtime" "^7.1.2" + loose-envify "^1.2.0" + resolve-pathname "^2.2.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + value-equal "^0.4.0" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.2.1, hoist-non-react-statics@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b" + integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA== + dependencies: + react-is "^16.7.0" + +hosted-git-info@^2.1.4: + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== + +hosted-git-info@^2.6.0: + version "2.8.4" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546" + integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +hsl-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" + integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= + +hsla-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" + integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= + +html-comment-regex@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" + integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== + +html-encoding-sniffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== + dependencies: + whatwg-encoding "^1.0.1" + +html-entities@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" + integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= + +html-minifier@^3.5.20: + version "3.5.21" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" + integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== + dependencies: + camel-case "3.0.x" + clean-css "4.2.x" + commander "2.17.x" + he "1.2.x" + param-case "2.1.x" + relateurl "0.2.x" + uglify-js "3.4.x" + +html-webpack-plugin@4.0.0-beta.5: + version "4.0.0-beta.5" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.5.tgz#2c53083c1151bfec20479b1f8aaf0039e77b5513" + integrity sha512-y5l4lGxOW3pz3xBTFdfB9rnnrWRPVxlAhX6nrBYIcW+2k2zC3mSp/3DxlWVCMBfnO6UAnoF8OcFn0IMy6kaKAQ== + dependencies: + html-minifier "^3.5.20" + loader-utils "^1.1.0" + lodash "^4.17.11" + pretty-error "^2.1.1" + tapable "^1.1.0" + util.promisify "1.0.0" + +htmlparser2@^3.3.0: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + +http-assert@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.4.1.tgz#c5f725d677aa7e873ef736199b89686cceb37878" + integrity sha512-rdw7q6GTlibqVVbXr0CKelfV5iY8G2HqEUkhSk297BMbSpSL8crXC+9rjKoMcZZEsksX30le6f/4ul4E28gegw== + dependencies: + deep-equal "~1.0.1" + http-errors "~1.7.2" + +http-cache-semantics@^3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" + integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= + +http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-errors@1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@^1.3.1, http-errors@^1.6.3, http-errors@~1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-parser-js@>=0.4.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.0.tgz#d65edbede84349d0dc30320815a15d39cc3cbbd8" + integrity sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w== + +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== + dependencies: + agent-base "4" + debug "3.1.0" + +http-proxy-middleware@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== + dependencies: + http-proxy "^1.17.0" + is-glob "^4.0.0" + lodash "^4.17.11" + micromatch "^3.1.10" + +http-proxy@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" + integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== + dependencies: + eventemitter3 "^3.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + +https-proxy-agent@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz#271ea8e90f836ac9f119daccd39c19ff7dfb0793" + integrity sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= + dependencies: + ms "^2.0.0" + +hyphenate-style-name@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" + integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ== + +iconv-lite@0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= + +icss-utils@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.0.tgz#339dbbffb9f8729a243b701e1c29d4cc58c52f0e" + integrity sha512-3DEun4VOeMvSczifM3F2cKQrDQ5Pj6WKhkOq6HD4QTnDUAq8MQRxy5TX6Sy1iY6WPBe4gQ3p5vTECjbIkglkkQ== + dependencies: + postcss "^7.0.14" + +identity-obj-proxy@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14" + integrity sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ= + dependencies: + harmony-reflect "^1.4.6" + +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= + +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + dependencies: + minimatch "^3.0.4" + +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + +ignore@^4.0.3, ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.2.tgz#e28e584d43ad7e92f96995019cc43b9e1ac49558" + integrity sha512-vdqWBp7MyzdmHkkRWV5nY+PfGRbYbahfuvsBCh277tq+w9zyNi7h5CYJCK0kmzti9kU+O/cB7sE8HvKv6aXAKQ== + +immer@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d" + integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg== + +import-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" + integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk= + dependencies: + import-from "^2.1.0" + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.0.0.tgz#a3d897f420cab0e671236897f75bc14b4885c390" + integrity sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-from@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" + integrity sha1-M1238qev/VOqpHHUuAId7ja387E= + dependencies: + resolve-from "^3.0.0" + +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= + +import-local@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" + integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== + dependencies: + pkg-dir "^3.0.0" + resolve-cwd "^2.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= + +infer-owner@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + +inflation@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/inflation/-/inflation-2.0.0.tgz#8b417e47c28f925a45133d914ca1fd389107f30f" + integrity sha1-i0F+R8KPklpFEz2RTKH9OJEH8w8= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +inherits@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@^1.3.2, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +init-package-json@^1.10.3: + version "1.10.3" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-1.10.3.tgz#45ffe2f610a8ca134f2bd1db5637b235070f6cbe" + integrity sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw== + dependencies: + glob "^7.1.1" + npm-package-arg "^4.0.0 || ^5.0.0 || ^6.0.0" + promzard "^0.3.0" + read "~1.0.1" + read-package-json "1 || 2" + semver "2.x || 3.x || 4 || 5" + validate-npm-package-license "^3.0.1" + validate-npm-package-name "^3.0.0" + +inquirer@6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.2.tgz#46941176f65c9eb20804627149b743a218f25406" + integrity sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.11" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.0.0" + through "^2.3.6" + +inquirer@^6.2.0: + version "6.5.1" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.1.tgz#8bfb7a5ac02dac6ff641ac4c5ff17da112fcdb42" + integrity sha512-uxNHBeQhRXIoHWTSNYUFhQVrHYFThIt6IVo2fFmSe8aBwdR3/w6b58hJpiL/fMukFkvGzjg+hSxFtwvVmKZmXw== + dependencies: + ansi-escapes "^4.2.1" + chalk "^2.4.2" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^4.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +inquirer@^6.2.2: + version "6.3.1" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.3.1.tgz#7a413b5e7950811013a3db491c61d1f3b776e8e7" + integrity sha512-MmL624rfkFt4TG9y/Jvmt8vdmOo836U7Y0Hxr2aFk3RelZEGX4Igk0KabWrcaaZaTv9uzglOqWh1Vly+FAWAXA== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.11" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +internal-ip@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== + dependencies: + default-gateway "^4.2.0" + ipaddr.js "^1.9.0" + +invariant@^2.2.2, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ip@^1.1.0, ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + +ipaddr.js@1.9.0, ipaddr.js@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" + integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.0.2, is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-buffer@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" + integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== + +is-callable@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== + +is-ci@^1.0.10: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" + integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== + dependencies: + ci-info "^1.5.0" + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-color-stop@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" + integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= + dependencies: + css-color-names "^0.0.4" + hex-color-regex "^1.1.0" + hsl-regex "^1.0.0" + hsla-regex "^1.0.0" + rgb-regex "^1.0.1" + rgba-regex "^1.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-generator-function@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" + integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-in-browser@^1.0.2, is-in-browser@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" + integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= + +is-installed-globally@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" + integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA= + dependencies: + global-dirs "^0.1.0" + is-path-inside "^1.0.0" + +is-npm@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" + integrity sha1-8vtjpl5JBbQGyGBydloaTceTufQ= + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-obj@^1.0.0, is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= + +is-path-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0= + +is-path-in-cwd@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" + integrity sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ== + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" + integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= + dependencies: + path-is-inside "^1.0.1" + +is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-plain-object@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.0.tgz#47bfc5da1b5d50d64110806c199359482e75a928" + integrity sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg== + dependencies: + isobject "^4.0.0" + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= + +is-redirect@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" + integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= + dependencies: + has "^1.0.1" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= + +is-resolvable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== + +is-retry-allowed@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" + integrity sha1-EaBgVotnM5REAz0BJaYaINVk+zQ= + +is-root@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.0.0.tgz#838d1e82318144e5a6f77819d90207645acc7019" + integrity sha512-F/pJIk8QD6OX5DNhRB7hWamLsUilmkDGho48KbgZ6xg/lmAZXHxzXQ91jzB3yRSw5kdQGGGc4yz8HYhTYIMWPg== + +is-ssh@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.1.tgz#f349a8cadd24e65298037a522cf7520f2e81a0f3" + integrity sha512-0eRIASHZt1E68/ixClI8bp2YK2wmBPVWEismTs6M+M099jKgrzl/3E976zIbImSIob48N2/XGe9y7ZiYdImSlg== + dependencies: + protocols "^1.1.0" + +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-svg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" + integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== + dependencies: + html-comment-regex "^1.1.0" + +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + dependencies: + has-symbols "^1.0.0" + +is-text-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-2.0.0.tgz#b2484e2b720a633feb2e85b67dc193ff72c75636" + integrity sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw== + dependencies: + text-extensions "^2.0.0" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-windows@^1.0.0, is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isobject@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" + integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== + +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk= + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" + integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== + +istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" + integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== + dependencies: + "@babel/generator" "^7.4.0" + "@babel/parser" "^7.4.3" + "@babel/template" "^7.4.0" + "@babel/traverse" "^7.4.3" + "@babel/types" "^7.4.0" + istanbul-lib-coverage "^2.0.5" + semver "^6.0.0" + +istanbul-lib-report@^2.0.4: + version "2.0.8" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" + integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== + dependencies: + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + supports-color "^6.1.0" + +istanbul-lib-source-maps@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" + integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + rimraf "^2.6.3" + source-map "^0.6.1" + +istanbul-reports@^2.1.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.4.tgz#4e0d0ddf0f0ad5b49a314069d31b4f06afe49ad3" + integrity sha512-QCHGyZEK0bfi9GR215QSm+NJwFKEShbtc7tfbUdLAEzn3kKhLDDZqvljn8rPZM9v8CEOhzL1nlYoO4r1ryl67w== + dependencies: + handlebars "^4.1.2" + +jest-changed-files@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.8.0.tgz#7e7eb21cf687587a85e50f3d249d1327e15b157b" + integrity sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug== + dependencies: + "@jest/types" "^24.8.0" + execa "^1.0.0" + throat "^4.0.0" + +jest-cli@^24.7.1: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.8.0.tgz#b075ac914492ed114fa338ade7362a301693e989" + integrity sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA== + dependencies: + "@jest/core" "^24.8.0" + "@jest/test-result" "^24.8.0" + "@jest/types" "^24.8.0" + chalk "^2.0.1" + exit "^0.1.2" + import-local "^2.0.0" + is-ci "^2.0.0" + jest-config "^24.8.0" + jest-util "^24.8.0" + jest-validate "^24.8.0" + prompts "^2.0.1" + realpath-native "^1.1.0" + yargs "^12.0.2" + +jest-config@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.8.0.tgz#77db3d265a6f726294687cbbccc36f8a76ee0f4f" + integrity sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^24.8.0" + "@jest/types" "^24.8.0" + babel-jest "^24.8.0" + chalk "^2.0.1" + glob "^7.1.1" + jest-environment-jsdom "^24.8.0" + jest-environment-node "^24.8.0" + jest-get-type "^24.8.0" + jest-jasmine2 "^24.8.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.8.0" + jest-util "^24.8.0" + jest-validate "^24.8.0" + micromatch "^3.1.10" + pretty-format "^24.8.0" + realpath-native "^1.1.0" + +jest-diff@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.8.0.tgz#146435e7d1e3ffdf293d53ff97e193f1d1546172" + integrity sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g== + dependencies: + chalk "^2.0.1" + diff-sequences "^24.3.0" + jest-get-type "^24.8.0" + pretty-format "^24.8.0" + +jest-docblock@^24.3.0: + version "24.3.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.3.0.tgz#b9c32dac70f72e4464520d2ba4aec02ab14db5dd" + integrity sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg== + dependencies: + detect-newline "^2.1.0" + +jest-each@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.8.0.tgz#a05fd2bf94ddc0b1da66c6d13ec2457f35e52775" + integrity sha512-NrwK9gaL5+XgrgoCsd9svsoWdVkK4gnvyhcpzd6m487tXHqIdYeykgq3MKI1u4I+5Zf0tofr70at9dWJDeb+BA== + dependencies: + "@jest/types" "^24.8.0" + chalk "^2.0.1" + jest-get-type "^24.8.0" + jest-util "^24.8.0" + pretty-format "^24.8.0" + +jest-environment-jsdom-fourteen@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom-fourteen/-/jest-environment-jsdom-fourteen-0.1.0.tgz#aad6393a9d4b565b69a609109bf469f62bf18ccc" + integrity sha512-4vtoRMg7jAstitRzL4nbw83VmGH8Rs13wrND3Ud2o1fczDhMUF32iIrNKwYGgeOPUdfvZU4oy8Bbv+ni1fgVCA== + dependencies: + jest-mock "^24.5.0" + jest-util "^24.5.0" + jsdom "^14.0.0" + +jest-environment-jsdom@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz#300f6949a146cabe1c9357ad9e9ecf9f43f38857" + integrity sha512-qbvgLmR7PpwjoFjM/sbuqHJt/NCkviuq9vus9NBn/76hhSidO+Z6Bn9tU8friecegbJL8gzZQEMZBQlFWDCwAQ== + dependencies: + "@jest/environment" "^24.8.0" + "@jest/fake-timers" "^24.8.0" + "@jest/types" "^24.8.0" + jest-mock "^24.8.0" + jest-util "^24.8.0" + jsdom "^11.5.1" + +jest-environment-node@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.8.0.tgz#d3f726ba8bc53087a60e7a84ca08883a4c892231" + integrity sha512-vIGUEScd1cdDgR6sqn2M08sJTRLQp6Dk/eIkCeO4PFHxZMOgy+uYLPMC4ix3PEfM5Au/x3uQ/5Tl0DpXXZsJ/Q== + dependencies: + "@jest/environment" "^24.8.0" + "@jest/fake-timers" "^24.8.0" + "@jest/types" "^24.8.0" + jest-mock "^24.8.0" + jest-util "^24.8.0" + +jest-get-type@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.8.0.tgz#a7440de30b651f5a70ea3ed7ff073a32dfe646fc" + integrity sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ== + +jest-haste-map@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.8.0.tgz#51794182d877b3ddfd6e6d23920e3fe72f305800" + integrity sha512-ZBPRGHdPt1rHajWelXdqygIDpJx8u3xOoLyUBWRW28r3tagrgoepPrzAozW7kW9HrQfhvmiv1tncsxqHJO1onQ== + dependencies: + "@jest/types" "^24.8.0" + anymatch "^2.0.0" + fb-watchman "^2.0.0" + graceful-fs "^4.1.15" + invariant "^2.2.4" + jest-serializer "^24.4.0" + jest-util "^24.8.0" + jest-worker "^24.6.0" + micromatch "^3.1.10" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^1.2.7" + +jest-jasmine2@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.8.0.tgz#a9c7e14c83dd77d8b15e820549ce8987cc8cd898" + integrity sha512-cEky88npEE5LKd5jPpTdDCLvKkdyklnaRycBXL6GNmpxe41F0WN44+i7lpQKa/hcbXaQ+rc9RMaM4dsebrYong== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^24.8.0" + "@jest/test-result" "^24.8.0" + "@jest/types" "^24.8.0" + chalk "^2.0.1" + co "^4.6.0" + expect "^24.8.0" + is-generator-fn "^2.0.0" + jest-each "^24.8.0" + jest-matcher-utils "^24.8.0" + jest-message-util "^24.8.0" + jest-runtime "^24.8.0" + jest-snapshot "^24.8.0" + jest-util "^24.8.0" + pretty-format "^24.8.0" + throat "^4.0.0" + +jest-leak-detector@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.8.0.tgz#c0086384e1f650c2d8348095df769f29b48e6980" + integrity sha512-cG0yRSK8A831LN8lIHxI3AblB40uhv0z+SsQdW3GoMMVcK+sJwrIIyax5tu3eHHNJ8Fu6IMDpnLda2jhn2pD/g== + dependencies: + pretty-format "^24.8.0" + +jest-matcher-utils@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz#2bce42204c9af12bde46f83dc839efe8be832495" + integrity sha512-lex1yASY51FvUuHgm0GOVj7DCYEouWSlIYmCW7APSqB9v8mXmKSn5+sWVF0MhuASG0bnYY106/49JU1FZNl5hw== + dependencies: + chalk "^2.0.1" + jest-diff "^24.8.0" + jest-get-type "^24.8.0" + pretty-format "^24.8.0" + +jest-message-util@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.8.0.tgz#0d6891e72a4beacc0292b638685df42e28d6218b" + integrity sha512-p2k71rf/b6ns8btdB0uVdljWo9h0ovpnEe05ZKWceQGfXYr4KkzgKo3PBi8wdnd9OtNh46VpNIJynUn/3MKm1g== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/test-result" "^24.8.0" + "@jest/types" "^24.8.0" + "@types/stack-utils" "^1.0.1" + chalk "^2.0.1" + micromatch "^3.1.10" + slash "^2.0.0" + stack-utils "^1.0.1" + +jest-mock@^24.5.0, jest-mock@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.8.0.tgz#2f9d14d37699e863f1febf4e4d5a33b7fdbbde56" + integrity sha512-6kWugwjGjJw+ZkK4mDa0Df3sDlUTsV47MSrT0nGQ0RBWJbpODDQ8MHDVtGtUYBne3IwZUhtB7elxHspU79WH3A== + dependencies: + "@jest/types" "^24.8.0" + +jest-pnp-resolver@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" + integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== + +jest-regex-util@^24.3.0: + version "24.3.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.3.0.tgz#d5a65f60be1ae3e310d5214a0307581995227b36" + integrity sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg== + +jest-resolve-dependencies@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.8.0.tgz#19eec3241f2045d3f990dba331d0d7526acff8e0" + integrity sha512-hyK1qfIf/krV+fSNyhyJeq3elVMhK9Eijlwy+j5jqmZ9QsxwKBiP6qukQxaHtK8k6zql/KYWwCTQ+fDGTIJauw== + dependencies: + "@jest/types" "^24.8.0" + jest-regex-util "^24.3.0" + jest-snapshot "^24.8.0" + +jest-resolve@24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.7.1.tgz#e4150198299298380a75a9fd55043fa3b9b17fde" + integrity sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw== + dependencies: + "@jest/types" "^24.7.0" + browser-resolve "^1.11.3" + chalk "^2.0.1" + jest-pnp-resolver "^1.2.1" + realpath-native "^1.1.0" + +jest-resolve@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.8.0.tgz#84b8e5408c1f6a11539793e2b5feb1b6e722439f" + integrity sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw== + dependencies: + "@jest/types" "^24.8.0" + browser-resolve "^1.11.3" + chalk "^2.0.1" + jest-pnp-resolver "^1.2.1" + realpath-native "^1.1.0" + +jest-runner@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.8.0.tgz#4f9ae07b767db27b740d7deffad0cf67ccb4c5bb" + integrity sha512-utFqC5BaA3JmznbissSs95X1ZF+d+4WuOWwpM9+Ak356YtMhHE/GXUondZdcyAAOTBEsRGAgH/0TwLzfI9h7ow== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.8.0" + "@jest/test-result" "^24.8.0" + "@jest/types" "^24.8.0" + chalk "^2.4.2" + exit "^0.1.2" + graceful-fs "^4.1.15" + jest-config "^24.8.0" + jest-docblock "^24.3.0" + jest-haste-map "^24.8.0" + jest-jasmine2 "^24.8.0" + jest-leak-detector "^24.8.0" + jest-message-util "^24.8.0" + jest-resolve "^24.8.0" + jest-runtime "^24.8.0" + jest-util "^24.8.0" + jest-worker "^24.6.0" + source-map-support "^0.5.6" + throat "^4.0.0" + +jest-runtime@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.8.0.tgz#05f94d5b05c21f6dc54e427cd2e4980923350620" + integrity sha512-Mq0aIXhvO/3bX44ccT+czU1/57IgOMyy80oM0XR/nyD5zgBcesF84BPabZi39pJVA6UXw+fY2Q1N+4BiVUBWOA== + dependencies: + "@jest/console" "^24.7.1" + "@jest/environment" "^24.8.0" + "@jest/source-map" "^24.3.0" + "@jest/transform" "^24.8.0" + "@jest/types" "^24.8.0" + "@types/yargs" "^12.0.2" + chalk "^2.0.1" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.1.15" + jest-config "^24.8.0" + jest-haste-map "^24.8.0" + jest-message-util "^24.8.0" + jest-mock "^24.8.0" + jest-regex-util "^24.3.0" + jest-resolve "^24.8.0" + jest-snapshot "^24.8.0" + jest-util "^24.8.0" + jest-validate "^24.8.0" + realpath-native "^1.1.0" + slash "^2.0.0" + strip-bom "^3.0.0" + yargs "^12.0.2" + +jest-serializer@^24.4.0: + version "24.4.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.4.0.tgz#f70c5918c8ea9235ccb1276d232e459080588db3" + integrity sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q== + +jest-snapshot@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.8.0.tgz#3bec6a59da2ff7bc7d097a853fb67f9d415cb7c6" + integrity sha512-5ehtWoc8oU9/cAPe6fez6QofVJLBKyqkY2+TlKTOf0VllBB/mqUNdARdcjlZrs9F1Cv+/HKoCS/BknT0+tmfPg== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^24.8.0" + chalk "^2.0.1" + expect "^24.8.0" + jest-diff "^24.8.0" + jest-matcher-utils "^24.8.0" + jest-message-util "^24.8.0" + jest-resolve "^24.8.0" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + pretty-format "^24.8.0" + semver "^5.5.0" + +jest-util@^24.5.0, jest-util@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.8.0.tgz#41f0e945da11df44cc76d64ffb915d0716f46cd1" + integrity sha512-DYZeE+XyAnbNt0BG1OQqKy/4GVLPtzwGx5tsnDrFcax36rVE3lTA5fbvgmbVPUZf9w77AJ8otqR4VBbfFJkUZA== + dependencies: + "@jest/console" "^24.7.1" + "@jest/fake-timers" "^24.8.0" + "@jest/source-map" "^24.3.0" + "@jest/test-result" "^24.8.0" + "@jest/types" "^24.8.0" + callsites "^3.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.15" + is-ci "^2.0.0" + mkdirp "^0.5.1" + slash "^2.0.0" + source-map "^0.6.0" + +jest-validate@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.8.0.tgz#624c41533e6dfe356ffadc6e2423a35c2d3b4849" + integrity sha512-+/N7VOEMW1Vzsrk3UWBDYTExTPwf68tavEPKDnJzrC6UlHtUDU/fuEdXqFoHzv9XnQ+zW6X3qMZhJ3YexfeLDA== + dependencies: + "@jest/types" "^24.8.0" + camelcase "^5.0.0" + chalk "^2.0.1" + jest-get-type "^24.8.0" + leven "^2.1.0" + pretty-format "^24.8.0" + +jest-watch-typeahead@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.3.0.tgz#f56d9ee17ea71ecbf8253fed213df3185a1584c9" + integrity sha512-+uOtlppt9ysST6k6ZTqsPI0WNz2HLa8bowiZylZoQCQaAVn7XsVmHhZREkz73FhKelrFrpne4hQQjdq42nFEmA== + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.4.1" + jest-watcher "^24.3.0" + slash "^2.0.0" + string-length "^2.0.0" + strip-ansi "^5.0.0" + +jest-watcher@^24.3.0, jest-watcher@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.8.0.tgz#58d49915ceddd2de85e238f6213cef1c93715de4" + integrity sha512-SBjwHt5NedQoVu54M5GEx7cl7IGEFFznvd/HNT8ier7cCAx/Qgu9ZMlaTQkvK22G1YOpcWBLQPFSImmxdn3DAw== + dependencies: + "@jest/test-result" "^24.8.0" + "@jest/types" "^24.8.0" + "@types/yargs" "^12.0.9" + ansi-escapes "^3.0.0" + chalk "^2.0.1" + jest-util "^24.8.0" + string-length "^2.0.0" + +jest-worker@^24.6.0: + version "24.6.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.6.0.tgz#7f81ceae34b7cde0c9827a6980c35b7cdc0161b3" + integrity sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ== + dependencies: + merge-stream "^1.0.1" + supports-color "^6.1.0" + +jest@24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-24.7.1.tgz#0d94331cf510c75893ee32f87d7321d5bf8f2501" + integrity sha512-AbvRar5r++izmqo5gdbAjTeA6uNRGoNRuj5vHB0OnDXo2DXWZJVuaObiGgtlvhKb+cWy2oYbQSfxv7Q7GjnAtA== + dependencies: + import-local "^2.0.0" + jest-cli "^24.7.1" + +js-levenshtein@^1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" + integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= + +js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.9.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsdom@^11.5.1: + version "11.12.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" + integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== + dependencies: + abab "^2.0.0" + acorn "^5.5.3" + acorn-globals "^4.1.0" + array-equal "^1.0.0" + cssom ">= 0.3.2 < 0.4.0" + cssstyle "^1.0.0" + data-urls "^1.0.0" + domexception "^1.0.1" + escodegen "^1.9.1" + html-encoding-sniffer "^1.0.2" + left-pad "^1.3.0" + nwsapi "^2.0.7" + parse5 "4.0.0" + pn "^1.1.0" + request "^2.87.0" + request-promise-native "^1.0.5" + sax "^1.2.4" + symbol-tree "^3.2.2" + tough-cookie "^2.3.4" + w3c-hr-time "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.3" + whatwg-mimetype "^2.1.0" + whatwg-url "^6.4.1" + ws "^5.2.0" + xml-name-validator "^3.0.0" + +jsdom@^14.0.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-14.1.0.tgz#916463b6094956b0a6c1782c94e380cd30e1981b" + integrity sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng== + dependencies: + abab "^2.0.0" + acorn "^6.0.4" + acorn-globals "^4.3.0" + array-equal "^1.0.0" + cssom "^0.3.4" + cssstyle "^1.1.1" + data-urls "^1.1.0" + domexception "^1.0.1" + escodegen "^1.11.0" + html-encoding-sniffer "^1.0.2" + nwsapi "^2.1.3" + parse5 "5.1.0" + pn "^1.1.0" + request "^2.88.0" + request-promise-native "^1.0.5" + saxes "^3.1.9" + symbol-tree "^3.2.2" + tough-cookie "^2.5.0" + w3c-hr-time "^1.0.1" + w3c-xmlserializer "^1.1.2" + webidl-conversions "^4.0.2" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^7.0.0" + ws "^6.1.2" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json3@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" + integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +json5@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" + integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== + dependencies: + minimist "^1.2.0" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= + +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +jss-plugin-camel-case@10.0.0-alpha.17: + version "10.0.0-alpha.17" + resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0-alpha.17.tgz#6f7c9d9742e349bb061e53cd9b1c3cb006169a67" + integrity sha512-aPY4kr6MwliH7KToLRzeSk1NxXUo9n7MQsAa0Hghwj01x9UnMkDkGAKENMKUtPjGkQZfiJpB9tTLFrSJ/6VrIQ== + dependencies: + "@babel/runtime" "^7.3.1" + hyphenate-style-name "^1.0.3" + jss "10.0.0-alpha.17" + +jss-plugin-default-unit@10.0.0-alpha.17: + version "10.0.0-alpha.17" + resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0-alpha.17.tgz#4e3bf6d8e9691a8e05d50b5abf300515eb0f67ee" + integrity sha512-KQgiXczvzJ9AlFdD8NS7FZLub0NSctSrCA9Yi/GqdsfJg4ZCriU4DzIybCZBHCi/INFGJmLIESYWSxnuhAzgSQ== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.0.0-alpha.17" + +jss-plugin-global@10.0.0-alpha.17: + version "10.0.0-alpha.17" + resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.0.0-alpha.17.tgz#13005f6b963aee3c1498fe2bad767967ad2eb838" + integrity sha512-WYxiwwI+CLk0ozW8loeceqXBAZXBMsLBEZeRwVf9WX+FljdJkGwVZpRCk6LBX4aXnqAGyKqCxIAIJ3KP2yBdEg== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.0.0-alpha.17" + +jss-plugin-nested@10.0.0-alpha.17: + version "10.0.0-alpha.17" + resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.0.0-alpha.17.tgz#cb1c20cdc81558c164eaa333bbb24c88bef12202" + integrity sha512-onpFqv904KCujryf2t6IIV1/QoB7cSF7ojrd4UujcN5TPvYOvXF5bchi7jnHG5U0SLlRSDGMLJ9fhtoCknhEbw== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.0.0-alpha.17" + tiny-warning "^1.0.2" + +jss-plugin-props-sort@10.0.0-alpha.17: + version "10.0.0-alpha.17" + resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0-alpha.17.tgz#a49be72b8dc8e2861f8136661c53d130abb07ccd" + integrity sha512-KnbyrxCbtQTqpDx2mSZU/r/E5QnDPIVfIxRi8K+W/q4gZpomBvqWC+xgvAk9hbpmA6QBoQaOilV8o12w2IZ6fg== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.0.0-alpha.17" + +jss-plugin-rule-value-function@10.0.0-alpha.17: + version "10.0.0-alpha.17" + resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0-alpha.17.tgz#45617ccc2d695d77287554e7dbe3b9c37f5f5af4" + integrity sha512-8AuJB44Q+ehfkWVRi2XlRbUf6SrLmrHTa5EXd6dgQRCCRuvGmqX8Dl4fZvNeKRFjTLPZgzg9+31rqeOMhKa2vA== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.0.0-alpha.17" + +jss-plugin-vendor-prefixer@10.0.0-alpha.17: + version "10.0.0-alpha.17" + resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0-alpha.17.tgz#7bb05076d1a14d20b567231c36e57ebf6cb6625f" + integrity sha512-wDq9EL0QaoMGSGifPEBb+/SA9LBcqPEW0jpL9ht+Z2t+lV7NNz0j7uCEOuE6FvNWqHzUKTsiATs1rTHPkzNBEQ== + dependencies: + "@babel/runtime" "^7.3.1" + css-vendor "^2.0.1" + jss "10.0.0-alpha.17" + +jss@10.0.0-alpha.17: + version "10.0.0-alpha.17" + resolved "https://registry.yarnpkg.com/jss/-/jss-10.0.0-alpha.17.tgz#3057c85a846c3bd207c04aafd91c8277955d9c57" + integrity sha512-egGIUg+YRu0+U+XXlD0gmVtU/gW5sn7+qmDv7opwK5s8emZBE/VoN55X6CaMrAa0kLeGMldnI43KOWea6M9/mA== + dependencies: + "@babel/runtime" "^7.3.1" + is-in-browser "^1.1.3" + tiny-warning "^1.0.2" + +jsx-ast-utils@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.1.0.tgz#0ee4e2c971fb9601c67b5641b71be80faecf0b36" + integrity sha512-yDGDG2DS4JcqhA6blsuYbtsT09xL8AoLuUR2Gb5exrw7UEM19sBcOTq+YBBhrNbl0PUC4R4LnFu+dHg2HKeVvA== + dependencies: + array-includes "^3.0.3" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +keycode@^2.1.7: + version "2.2.0" + resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04" + integrity sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ= + +keygrip@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.3.tgz#399d709f0aed2bab0a059e0cdd3a5023a053e1dc" + integrity sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g== + +killable@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" + integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== + +kind-of@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" + integrity sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU= + dependencies: + is-buffer "^1.0.2" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + +kleur@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +koa-bodyparser@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/koa-bodyparser/-/koa-bodyparser-4.2.1.tgz#4d7dacb5e6db1106649b595d9e5ccb158b6f3b29" + integrity sha512-UIjPAlMZfNYDDe+4zBaOAUKYqkwAGcIU6r2ARf1UOXPAlfennQys5IiShaVeNf7KkVBlf88f2LeLvBFvKylttw== + dependencies: + co-body "^6.0.0" + copy-to "^2.0.1" + +koa-compose@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7" + integrity sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec= + dependencies: + any-promise "^1.1.0" + +koa-compose@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" + integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== + +koa-convert@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0" + integrity sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA= + dependencies: + co "^4.6.0" + koa-compose "^3.0.0" + +koa-is-json@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/koa-is-json/-/koa-is-json-1.0.0.tgz#273c07edcdcb8df6a2c1ab7d59ee76491451ec14" + integrity sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ= + +koa-mount@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/koa-mount/-/koa-mount-4.0.0.tgz#e0265e58198e1a14ef889514c607254ff386329c" + integrity sha512-rm71jaA/P+6HeCpoRhmCv8KVBIi0tfGuO/dMKicbQnQW/YJntJ6MnnspkodoA4QstMVEZArsCphmd0bJEtoMjQ== + dependencies: + debug "^4.0.1" + koa-compose "^4.1.0" + +koa-pino-logger@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/koa-pino-logger/-/koa-pino-logger-2.1.3.tgz#bc2de057113969a189546897f7130083c65ed236" + integrity sha512-9MV5GX9GYSzEnUlHtSYveR4hGhBvWu2iQByWtdDssQ+iv2ZWaHEo2eksPNqJqQHw9H6kFYGRIoinnrIAvr1AXQ== + dependencies: + pino-http "^2.0.1" + +koa-router@^7.4.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/koa-router/-/koa-router-7.4.0.tgz#aee1f7adc02d5cb31d7d67465c9eacc825e8c5e0" + integrity sha512-IWhaDXeAnfDBEpWS6hkGdZ1ablgr6Q6pGdXCyK38RbzuH4LkUOpPqPw+3f8l8aTDrQmBQ7xJc0bs2yV4dzcO+g== + dependencies: + debug "^3.1.0" + http-errors "^1.3.1" + koa-compose "^3.0.0" + methods "^1.0.1" + path-to-regexp "^1.1.1" + urijs "^1.19.0" + +koa-send@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.0.tgz#5e8441e07ef55737734d7ced25b842e50646e7eb" + integrity sha512-90ZotV7t0p3uN9sRwW2D484rAaKIsD8tAVtypw/aBU+ryfV+fR2xrcAwhI8Wl6WRkojLUs/cB9SBSCuIb+IanQ== + dependencies: + debug "^3.1.0" + http-errors "^1.6.3" + mz "^2.7.0" + resolve-path "^1.4.0" + +koa-static@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/koa-static/-/koa-static-5.0.0.tgz#5e92fc96b537ad5219f425319c95b64772776943" + integrity sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ== + dependencies: + debug "^3.1.0" + koa-send "^5.0.0" + +koa@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/koa/-/koa-2.7.0.tgz#7e00843506942b9d82c6cc33749f657c6e5e7adf" + integrity sha512-7ojD05s2Q+hFudF8tDLZ1CpCdVZw8JQELWSkcfG9bdtoTDzMmkRF6BQBU7JzIzCCOY3xd3tftiy/loHBUYaY2Q== + dependencies: + accepts "^1.3.5" + cache-content-type "^1.0.0" + content-disposition "~0.5.2" + content-type "^1.0.4" + cookies "~0.7.1" + debug "~3.1.0" + delegates "^1.0.0" + depd "^1.1.2" + destroy "^1.0.4" + error-inject "^1.0.0" + escape-html "^1.0.3" + fresh "~0.5.2" + http-assert "^1.3.0" + http-errors "^1.6.3" + is-generator-function "^1.0.7" + koa-compose "^4.1.0" + koa-convert "^1.2.0" + koa-is-json "^1.0.0" + on-finished "^2.3.0" + only "~0.0.2" + parseurl "^1.3.2" + statuses "^1.5.0" + type-is "^1.6.16" + vary "^1.1.2" + +last-call-webpack-plugin@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555" + integrity sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w== + dependencies: + lodash "^4.17.5" + webpack-sources "^1.1.0" + +latest-version@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" + integrity sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU= + dependencies: + package-json "^4.0.0" + +lazy-cache@^0.2.3: + version "0.2.7" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" + integrity sha1-f+3fLctu23fRHvHRF6tf/fCrG2U= + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= + +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + +left-pad@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" + integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== + +lerna@^3.10.2: + version "3.16.4" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.16.4.tgz#158cb4f478b680f46f871d5891f531f3a2cb31ec" + integrity sha512-0HfwXIkqe72lBLZcNO9NMRfylh5Ng1l8tETgYQ260ZdHRbPuaLKE3Wqnd2YYRRkWfwPyEyZO8mZweBR+slVe1A== + dependencies: + "@lerna/add" "3.16.2" + "@lerna/bootstrap" "3.16.2" + "@lerna/changed" "3.16.4" + "@lerna/clean" "3.16.0" + "@lerna/cli" "3.13.0" + "@lerna/create" "3.16.0" + "@lerna/diff" "3.16.0" + "@lerna/exec" "3.16.0" + "@lerna/import" "3.16.0" + "@lerna/init" "3.16.0" + "@lerna/link" "3.16.2" + "@lerna/list" "3.16.0" + "@lerna/publish" "3.16.4" + "@lerna/run" "3.16.0" + "@lerna/version" "3.16.4" + import-local "^2.0.0" + npmlog "^4.1.2" + +leven@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" + integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA= + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +load-json-file@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-5.3.0.tgz#4d3c1e01fa1c03ea78a60ac7af932c9ce53403f3" + integrity sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw== + dependencies: + graceful-fs "^4.1.15" + parse-json "^4.0.0" + pify "^4.0.1" + strip-bom "^3.0.0" + type-fest "^0.3.0" + +loader-fs-cache@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz#54cedf6b727e1779fd8f01205f05f6e88706f086" + integrity sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw== + dependencies: + find-cache-dir "^0.1.1" + mkdirp "0.5.1" + +loader-runner@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== + +loader-utils@1.2.3, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash._reinterpolate@^3.0.0, lodash._reinterpolate@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.ismatch@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" + integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +lodash.set@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" + integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= + +lodash.tail@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" + integrity sha1-0jM6NtnncXyK0vfKyv7HwytERmQ= + +lodash.template@^4.0.2, lodash.template@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.template@^4.2.4, lodash.template@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" + integrity sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A= + dependencies: + lodash._reinterpolate "~3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz#2b4d4e95ba440d915ff08bc899e4553666713316" + integrity sha1-K01OlbpEDZFf8IvImeRVNmZxMxY= + dependencies: + lodash._reinterpolate "~3.0.0" + +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= + +lodash.unescape@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" + integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + +"lodash@>=3.5 <5", lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.5, lodash@~4.17.4: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== + +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.2.1: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +loglevel@^1.4.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa" + integrity sha1-4PyVEztu8nbNyIh82vJKpvFW+Po= + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lowercase-keys@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +luxon@^1.17.2: + version "1.17.2" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.17.2.tgz#95189c450341cfddf5f826ef8c32b5b022943fd5" + integrity sha512-qELKtIj3HD41N+MvgoxArk8DZGUb4Gpiijs91oi+ZmKJzRlxY6CoyTwNoUwnogCVs4p8HuxVJDik9JbnYgrCng== + +macos-release@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" + integrity sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA== + +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== + dependencies: + pify "^3.0.0" + +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-fetch-happen@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.0.tgz#a8e3fe41d3415dd656fe7b8e8172e1fb4458b38d" + integrity sha512-nFr/vpL1Jc60etMVKeaLOqfGjMMb3tAHFVJWxHOFCFS04Zmd7kGlMxo0l1tzfhoQje0/UPnd0X8OeGUiXXnfPA== + dependencies: + agentkeepalive "^3.4.1" + cacache "^12.0.0" + http-cache-semantics "^3.8.1" + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + lru-cache "^5.1.1" + mississippi "^3.0.0" + node-fetch-npm "^2.0.2" + promise-retry "^1.1.1" + socks-proxy-agent "^4.0.0" + ssri "^6.0.0" + +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= + dependencies: + tmpl "1.0.x" + +mamacro@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" + integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== + +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + +map-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" + integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +material-table@^1.49.0: + version "1.49.0" + resolved "https://registry.yarnpkg.com/material-table/-/material-table-1.49.0.tgz#86cfb794b501aaf763f35b28e712bbe54c69be54" + integrity sha512-nCuSS18GMNDGqzZO5GAc8C/YHq8rV5+YZ6xF+H1XMQ6CImXXxgM4unaOEzTN+pr+JVhsJniDELUivHH6+LXpXg== + dependencies: + "@date-io/date-fns" "^1.1.0" + "@material-ui/pickers" "^3.2.2" + classnames "^2.2.6" + date-fns "^2.0.0-alpha.27" + debounce "^1.2.0" + filefy "0.1.9" + prop-types "^15.6.2" + react-beautiful-dnd "11.0.3" + react-double-scrollbar "0.0.15" + +math-expression-evaluator@^1.2.14: + version "1.2.17" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" + integrity sha1-3oGf282E3M2PrlnGrreWFbnSZqw= + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mdn-data@~1.1.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-1.1.4.tgz#50b5d4ffc4575276573c4eedb8780812a8419f01" + integrity sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + +memoize-one@^5.0.4: + version "5.0.5" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.5.tgz#8cd3809555723a07684afafcd6f756072ac75d7e" + integrity sha512-ey6EpYv0tEaIbM/nTDOpHciXUvd+ackQrJgEzBwemhZZIWZjcyodqEcrmqDy2BKRTM3a65kKBV4WtLXJDt26SQ== + +memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^3.3.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +meow@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/meow/-/meow-4.0.1.tgz#d48598f6f4b1472f35bf6317a95945ace347f975" + integrity sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A== + dependencies: + camelcase-keys "^4.0.0" + decamelize-keys "^1.0.0" + loud-rejection "^1.0.0" + minimist "^1.1.3" + minimist-options "^3.0.1" + normalize-package-data "^2.3.4" + read-pkg-up "^3.0.0" + redent "^2.0.0" + trim-newlines "^2.0.0" + +merge-deep@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.2.tgz#f39fa100a4f1bd34ff29f7d2bf4508fbb8d83ad2" + integrity sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA== + dependencies: + arr-union "^3.1.0" + clone-deep "^0.2.4" + kind-of "^3.0.2" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merge-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" + integrity sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE= + dependencies: + readable-stream "^2.0.1" + +merge2@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.3.tgz#7ee99dbd69bb6481689253f018488a1b902b0ed5" + integrity sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA== + +methods@^1.0.1, methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +microevent.ts@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" + integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== + +micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.40.0, "mime-db@>= 1.40.0 < 2": + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== + +mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== + dependencies: + mime-db "1.40.0" + +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== + +mime@^2.0.3, mime@^2.3.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.2.tgz#ce5229a5e99ffc313abac806b482c10e7ba6ac78" + integrity sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg== + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-fn@^2.0.0, mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mini-create-react-context@^0.3.0: + version "0.3.2" + resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz#79fc598f283dd623da8e088b05db8cddab250189" + integrity sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw== + dependencies: + "@babel/runtime" "^7.4.0" + gud "^1.0.0" + tiny-warning "^1.0.2" + +mini-css-extract-plugin@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz#ac0059b02b9692515a637115b0cc9fed3a35c7b0" + integrity sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw== + dependencies: + loader-utils "^1.1.0" + schema-utils "^1.0.0" + webpack-sources "^1.1.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist-options@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" + integrity sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= + +minipass@^2.2.1, minipass@^2.3.4, minipass@^2.3.5: + version "2.3.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" + integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.1.1, minizlib@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" + integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== + dependencies: + minipass "^2.2.1" + +mississippi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^3.0.0" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mixin-object@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" + integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4= + dependencies: + for-in "^0.1.3" + is-extendable "^0.1.1" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= + dependencies: + mkdirp "*" + +mkdirp@*, mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +modify-values@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" + integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== + +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +multicast-dns-service-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= + +multicast-dns@^6.0.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== + dependencies: + dns-packet "^1.3.1" + thunky "^1.0.2" + +multimatch@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-3.0.0.tgz#0e2534cc6bc238d9ab67e1b9cd5fcd85a6dbf70b" + integrity sha512-22foS/gqQfANZ3o+W7ST2x25ueHDVNWl/b9OlGcLpy/iKxjCpvcNCM51YCenUi7Mt/jAjjqv8JwZRs8YP5sRjA== + dependencies: + array-differ "^2.0.3" + array-union "^1.0.2" + arrify "^1.0.1" + minimatch "^3.0.4" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + +mute-stream@0.0.8, mute-stream@~0.0.4: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +mz@^2.5.0, mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nan@^2.12.1: + version "2.13.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" + integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +needle@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.1.tgz#d272f2f4034afb9c4c9ab1379aabc17fc85c9388" + integrity sha512-CaLXV3W8Vnbps8ZANqDGz7j4x7Yj1LW4TWF/TQuDfj7Cfx4nAPTvw98qgTevtto1oHDrh3pQkaODbqupXlsWTg== + dependencies: + debug "^4.1.0" + iconv-lite "^0.4.4" + sax "^1.2.4" + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +neo-async@^2.5.0, neo-async@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835" + integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +node-fetch-npm@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7" + integrity sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw== + dependencies: + encoding "^0.1.11" + json-parse-better-errors "^1.0.0" + safe-buffer "^5.1.1" + +node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-fetch@^2.3.0, node-fetch@^2.5.0, node-fetch@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + +node-forge@0.7.5: + version "0.7.5" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df" + integrity sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ== + +node-gyp@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.0.3.tgz#80d64c23790244991b6d44532f0a351bedd3dd45" + integrity sha512-z/JdtkFGUm0QaQUusvloyYuGDub3nUbOo5de1Fz57cM++osBTvQatBUSTlF1k/w8vFHPxxXW6zxGvkxXSpaBkQ== + dependencies: + env-paths "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + request "^2.87.0" + rimraf "2" + semver "~5.3.0" + tar "^4.4.8" + which "1" + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-libs-browser@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.0.tgz#c72f60d9d46de08a940dedbb25f3ffa2f9bbaa77" + integrity sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA== + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^3.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.0" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.11.0" + vm-browserify "0.0.4" + +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + +node-notifier@^5.2.1: + version "5.4.0" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.0.tgz#7b455fdce9f7de0c63538297354f3db468426e6a" + integrity sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ== + dependencies: + growly "^1.3.0" + is-wsl "^1.1.0" + semver "^5.5.0" + shellwords "^0.1.1" + which "^1.3.0" + +node-pre-gyp@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" + integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +node-releases@^1.1.13, node-releases@^1.1.17: + version "1.1.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.18.tgz#cc98fd75598a324a77188ebddf6650e9cbd8b1d5" + integrity sha512-/mnVgm6u/8OwlIsoyRXtTI0RfQcxZoAZbdwyXap0EeWwcOpDDymyCHM2/aR9XKmHXrvizHoPAOs0pcbiJ6RUaA== + dependencies: + semver "^5.3.0" + +nodemon@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.19.1.tgz#576f0aad0f863aabf8c48517f6192ff987cd5071" + integrity sha512-/DXLzd/GhiaDXXbGId5BzxP1GlsqtMGM9zTmkWrgXtSqjKmGSbLicM/oAy4FR0YWm14jCHRwnR31AHS2dYFHrg== + dependencies: + chokidar "^2.1.5" + debug "^3.1.0" + ignore-by-default "^1.0.1" + minimatch "^3.0.4" + pstree.remy "^1.1.6" + semver "^5.5.0" + supports-color "^5.2.0" + touch "^3.1.0" + undefsafe "^2.0.2" + update-notifier "^2.5.0" + +"nopt@2 || 3": + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= + dependencies: + abbrev "1" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + dependencies: + abbrev "1" + osenv "^0.1.4" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + +normalize-package-data@^2.0.0, normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.3.5, normalize-package-data@^2.4.0, normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= + +normalize-scroll-left@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/normalize-scroll-left/-/normalize-scroll-left-0.2.0.tgz#9445d74275f303cc661e113329aefa492f58114c" + integrity sha512-t5oCENZJl8TGusJKoCJm7+asaSsPuNmK6+iEjrZ5TyBj2f02brCRsd4c83hwtu+e5d4LCSBZ0uoDlMjBo+A8yA== + +normalize-url@^3.0.0, normalize-url@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" + integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== + +npm-bundled@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== + +npm-lifecycle@^3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.3.tgz#09e9b0b6686e85fd53bab82364386222d97a3730" + integrity sha512-M0QmmqbEHBXxDrmc6X3+eKjW9+F7Edg1ENau92WkYw1sox6wojHzEZJIRm1ItljEiaigZlKL8mXni/4ylAy1Dg== + dependencies: + byline "^5.0.0" + graceful-fs "^4.1.15" + node-gyp "^5.0.2" + resolve-from "^4.0.0" + slide "^1.1.6" + uid-number "0.0.6" + umask "^1.1.0" + which "^1.3.1" + +"npm-package-arg@^4.0.0 || ^5.0.0 || ^6.0.0", npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.0.tgz#15ae1e2758a5027efb4c250554b85a737db7fcc1" + integrity sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA== + dependencies: + hosted-git-info "^2.6.0" + osenv "^0.1.5" + semver "^5.5.0" + validate-npm-package-name "^3.0.0" + +npm-packlist@^1.1.6: + version "1.4.1" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc" + integrity sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npm-packlist@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" + integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npm-pick-manifest@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz#32111d2a9562638bb2c8f2bf27f7f3092c8fae40" + integrity sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA== + dependencies: + figgy-pudding "^3.5.1" + npm-package-arg "^6.0.0" + semver "^5.4.1" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.2, npmlog@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +nth-check@^1.0.2, nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== + dependencies: + boolbase "~1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +nwsapi@^2.0.7, nwsapi@^2.1.3: + version "2.1.4" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.4.tgz#e006a878db23636f8e8a67d33ca0e4edf61a842f" + integrity sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-hash@^1.1.4: + version "1.3.1" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" + integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== + +object-keys@^1.0.11, object-keys@^1.0.12: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.fromentries@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab" + integrity sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA== + dependencies: + define-properties "^1.1.2" + es-abstract "^1.11.0" + function-bind "^1.1.1" + has "^1.0.1" + +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +object.values@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" + integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.12.0" + function-bind "^1.1.1" + has "^1.0.3" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +octokit-pagination-methods@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4" + integrity sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ== + +on-finished@^2.3.0, on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + dependencies: + mimic-fn "^2.1.0" + +only@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" + integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= + +opn@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.4.0.tgz#cb545e7aab78562beb11aa3bfabc7042e1761035" + integrity sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw== + dependencies: + is-wsl "^1.1.0" + +opn@^5.1.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + +optimist@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optimize-css-assets-webpack-plugin@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.1.tgz#9eb500711d35165b45e7fd60ba2df40cb3eb9159" + integrity sha512-Rqm6sSjWtx9FchdP0uzTQDc7GXDKnwVEGoSxjezPkzMewx7gEWE9IMUYKmigTRC4U3RaNSwYVnUDLuIdtTpm0A== + dependencies: + cssnano "^4.1.0" + last-call-webpack-plugin "^3.0.0" + +optionator@^0.8.1, optionator@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +original@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + +os-name@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-name/-/os-name-3.1.0.tgz#dec19d966296e1cd62d701a5a66ee1ddeae70801" + integrity sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg== + dependencies: + macos-release "^2.2.0" + windows-release "^3.1.0" + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4, osenv@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + +p-each-series@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" + integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E= + dependencies: + p-reduce "^1.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2" + integrity sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-map-series@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca" + integrity sha1-v5j+V1cFZYqeE1G++4WuTB8Hvco= + dependencies: + p-reduce "^1.0.0" + +p-map@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" + integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== + +p-map@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-pipe@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" + integrity sha1-SxoROZoRUgpneQ7loMHViB1r7+k= + +p-queue@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-4.0.0.tgz#ed0eee8798927ed6f2c2f5f5b77fdb2061a5d346" + integrity sha512-3cRXXn3/O0o3+eVmUroJPSj/esxoEFIm0ZOno/T+NzG/VZgPOqQ8WKmlNqubSEpZmCIngEy34unkHGg83ZIBmg== + dependencies: + eventemitter3 "^3.1.0" + +p-reduce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" + integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +p-waterfall@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-waterfall/-/p-waterfall-1.0.0.tgz#7ed94b3ceb3332782353af6aae11aa9fc235bb00" + integrity sha1-ftlLPOszMngjU69qrhGqn8I1uwA= + dependencies: + p-reduce "^1.0.0" + +package-json@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" + integrity sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0= + dependencies: + got "^6.7.1" + registry-auth-token "^3.0.1" + registry-url "^3.0.3" + semver "^5.1.0" + +pako@~1.0.5: + version "1.0.10" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" + integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== + +parallel-transform@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" + integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY= + dependencies: + cyclist "~0.2.2" + inherits "^2.0.3" + readable-stream "^2.1.5" + +param-case@2.1.x: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= + dependencies: + no-case "^2.2.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0: + version "5.1.4" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc" + integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw== + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-github-repo-url@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50" + integrity sha1-nn2LslKmy2ukJZUGC3v23z28H1A= + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-4.0.1.tgz#0ec769704949778cb3b8eda5e994c32073a1adff" + integrity sha512-d7yhga0Oc+PwNXDvQ0Jv1BuWkLVPXcAoQ/WREgd6vNNoKYaW52KI+RdOFjI63wjkmps9yUE8VS4veP+AgpQ/hA== + dependencies: + is-ssh "^1.3.0" + protocols "^1.4.0" + +parse-url@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-5.0.1.tgz#99c4084fc11be14141efa41b3d117a96fcb9527f" + integrity sha512-flNUPP27r3vJpROi0/R3/2efgKkyXqnXwyP1KQ2U0SfFRgdizOdWfvrrvJg1LuOoxs7GQhmxJlq23IpQ/BkByg== + dependencies: + is-ssh "^1.3.0" + normalize-url "^3.3.0" + parse-path "^4.0.0" + protocols "^1.4.0" + +parse5@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" + integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== + +parse5@5.1.0, parse5@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" + integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== + +parseurl@^1.3.2, parseurl@~1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + integrity sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo= + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-is-absolute@1.0.1, path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.1, path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-to-regexp@^1.1.1, path-to-regexp@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + integrity sha1-Wf3g9DW62suhA6hOnTvGTpa5k30= + dependencies: + isarray "0.0.1" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +pbkdf2@^3.0.3: + version "3.0.17" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" + integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +pify@^2.0.0, pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pino-http@^2.0.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/pino-http/-/pino-http-2.6.2.tgz#aedb71b08d15bc69d959870bafa6b7780103274f" + integrity sha512-89bJRjW5rdTHm7nTe5hou93V+5DHqqfzk53xFO+cQw1hi+XXHKLHTAKeLJ2LYK1ChLQlhIDrQMffE88KFta/HA== + dependencies: + pino "^4.0.2" + +pino-std-serializers@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-2.4.2.tgz#cb5e3e58c358b26f88969d7e619ae54bdfcc1ae1" + integrity sha512-WaL504dO8eGs+vrK+j4BuQQq6GLKeCCcHaMB2ItygzVURcL1CycwNEUHTD/lHFHs/NL5qAz2UKrjYWXKSf4aMQ== + +pino@^4.0.0, pino@^4.0.2: + version "4.17.6" + resolved "https://registry.yarnpkg.com/pino/-/pino-4.17.6.tgz#8c237f3a29f4104f89321c25037deab6a7998fb4" + integrity sha512-LFDwmhyWLBnmwO/2UFbWu1jEGVDzaPupaVdx0XcZ3tIAx1EDEBauzxXf2S0UcFK7oe+X9MApjH0hx9U1XMgfCA== + dependencies: + chalk "^2.4.1" + fast-json-parse "^1.0.3" + fast-safe-stringify "^1.2.3" + flatstr "^1.0.5" + pino-std-serializers "^2.0.0" + pump "^3.0.0" + quick-format-unescaped "^1.1.2" + split2 "^2.2.0" + +pirates@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q= + dependencies: + find-up "^1.0.0" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-up@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= + dependencies: + find-up "^2.1.0" + +pn@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" + integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== + +pnp-webpack-plugin@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.2.1.tgz#cd9d698df2a6fcf7255093c1c9511adf65b9421b" + integrity sha512-W6GctK7K2qQiVR+gYSv/Gyt6jwwIH4vwdviFqx+Y2jAtVf5eZyYIDf5Ac2NCDMBiX5yWscBLZElPTsyA1UtVVA== + dependencies: + ts-pnp "^1.0.0" + +popper.js@^1.14.1: + version "1.15.0" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.15.0.tgz#5560b99bbad7647e9faa475c6b8056621f5a4ff2" + integrity sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA== + +portfinder@^1.0.9: + version "1.0.20" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a" + integrity sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw== + dependencies: + async "^1.5.2" + debug "^2.2.0" + mkdirp "0.5.x" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +postcss-attribute-case-insensitive@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.1.tgz#b2a721a0d279c2f9103a36331c88981526428cc7" + integrity sha512-L2YKB3vF4PetdTIthQVeT+7YiSzMoNMLLYxPXXppOOP7NoazEAy45sh2LvJ8leCQjfBcfkYQs8TtCcQjeZTp8A== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0" + +postcss-browser-comments@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-browser-comments/-/postcss-browser-comments-2.0.0.tgz#dc48d6a8ddbff188a80a000b7393436cb18aed88" + integrity sha512-xGG0UvoxwBc4Yx4JX3gc0RuDl1kc4bVihCzzk6UC72YPfq5fu3c717Nu8Un3nvnq1BJ31gBnFXIG/OaUTnpHgA== + dependencies: + postcss "^7.0.2" + +postcss-calc@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.1.tgz#36d77bab023b0ecbb9789d84dcb23c4941145436" + integrity sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ== + dependencies: + css-unit-converter "^1.1.1" + postcss "^7.0.5" + postcss-selector-parser "^5.0.0-rc.4" + postcss-value-parser "^3.3.1" + +postcss-color-functional-notation@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz#5efd37a88fbabeb00a2966d1e53d98ced93f74e0" + integrity sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-color-gray@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz#532a31eb909f8da898ceffe296fdc1f864be8547" + integrity sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw== + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^7.0.5" + postcss-values-parser "^2.0.0" + +postcss-color-hex-alpha@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz#a8d9ca4c39d497c9661e374b9c51899ef0f87388" + integrity sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw== + dependencies: + postcss "^7.0.14" + postcss-values-parser "^2.0.1" + +postcss-color-mod-function@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz#816ba145ac11cc3cb6baa905a75a49f903e4d31d" + integrity sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ== + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-color-rebeccapurple@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz#c7a89be872bb74e45b1e3022bfe5748823e6de77" + integrity sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-colormin@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" + integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== + dependencies: + browserslist "^4.0.0" + color "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-convert-values@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" + integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-custom-media@^7.0.7: + version "7.0.8" + resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz#fffd13ffeffad73621be5f387076a28b00294e0c" + integrity sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg== + dependencies: + postcss "^7.0.14" + +postcss-custom-properties@^8.0.9: + version "8.0.10" + resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-8.0.10.tgz#e8dc969e1e15c555f0b836b7f278ef47e3cdeaff" + integrity sha512-GDL0dyd7++goDR4SSasYdRNNvp4Gqy1XMzcCnTijiph7VB27XXpJ8bW/AI0i2VSBZ55TpdGhMr37kMSpRfYD0Q== + dependencies: + postcss "^7.0.14" + postcss-values-parser "^2.0.1" + +postcss-custom-selectors@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz#64858c6eb2ecff2fb41d0b28c9dd7b3db4de7fba" + integrity sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0-rc.3" + +postcss-dir-pseudo-class@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz#6e3a4177d0edb3abcc85fdb6fbb1c26dabaeaba2" + integrity sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0-rc.3" + +postcss-discard-comments@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" + integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== + dependencies: + postcss "^7.0.0" + +postcss-discard-duplicates@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" + integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== + dependencies: + postcss "^7.0.0" + +postcss-discard-empty@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" + integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== + dependencies: + postcss "^7.0.0" + +postcss-discard-overridden@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" + integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== + dependencies: + postcss "^7.0.0" + +postcss-double-position-gradients@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz#fc927d52fddc896cb3a2812ebc5df147e110522e" + integrity sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA== + dependencies: + postcss "^7.0.5" + postcss-values-parser "^2.0.0" + +postcss-env-function@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-2.0.2.tgz#0f3e3d3c57f094a92c2baf4b6241f0b0da5365d7" + integrity sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-flexbugs-fixes@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.1.0.tgz#e094a9df1783e2200b7b19f875dcad3b3aff8b20" + integrity sha512-jr1LHxQvStNNAHlgco6PzY308zvLklh7SJVYuWUwyUQncofaAlD2l+P/gxKHOdqWKe7xJSkVLFF/2Tp+JqMSZA== + dependencies: + postcss "^7.0.0" + +postcss-focus-visible@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz#477d107113ade6024b14128317ade2bd1e17046e" + integrity sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g== + dependencies: + postcss "^7.0.2" + +postcss-focus-within@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz#763b8788596cee9b874c999201cdde80659ef680" + integrity sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w== + dependencies: + postcss "^7.0.2" + +postcss-font-variant@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-4.0.0.tgz#71dd3c6c10a0d846c5eda07803439617bbbabacc" + integrity sha512-M8BFYKOvCrI2aITzDad7kWuXXTm0YhGdP9Q8HanmN4EF1Hmcgs1KK5rSHylt/lUJe8yLxiSwWAHdScoEiIxztg== + dependencies: + postcss "^7.0.2" + +postcss-gap-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz#431c192ab3ed96a3c3d09f2ff615960f902c1715" + integrity sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg== + dependencies: + postcss "^7.0.2" + +postcss-image-set-function@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz#28920a2f29945bed4c3198d7df6496d410d3f288" + integrity sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-initial@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-3.0.0.tgz#1772512faf11421b791fb2ca6879df5f68aa0517" + integrity sha512-WzrqZ5nG9R9fUtrA+we92R4jhVvEB32IIRTzfIG/PLL8UV4CvbF1ugTEHEFX6vWxl41Xt5RTCJPEZkuWzrOM+Q== + dependencies: + lodash.template "^4.2.4" + postcss "^7.0.2" + +postcss-lab-function@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz#bb51a6856cd12289ab4ae20db1e3821ef13d7d2e" + integrity sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg== + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-load-config@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.0.0.tgz#f1312ddbf5912cd747177083c5ef7a19d62ee484" + integrity sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ== + dependencies: + cosmiconfig "^4.0.0" + import-cwd "^2.0.0" + +postcss-loader@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" + integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== + dependencies: + loader-utils "^1.1.0" + postcss "^7.0.0" + postcss-load-config "^2.0.0" + schema-utils "^1.0.0" + +postcss-logical@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-3.0.0.tgz#2495d0f8b82e9f262725f75f9401b34e7b45d5b5" + integrity sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA== + dependencies: + postcss "^7.0.2" + +postcss-media-minmax@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz#b75bb6cbc217c8ac49433e12f22048814a4f5ed5" + integrity sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw== + dependencies: + postcss "^7.0.2" + +postcss-merge-longhand@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" + integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== + dependencies: + css-color-names "0.0.4" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + stylehacks "^4.0.0" + +postcss-merge-rules@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" + integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + cssnano-util-same-parent "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + vendors "^1.0.0" + +postcss-minify-font-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" + integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-gradients@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" + integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + is-color-stop "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-minify-params@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" + integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== + dependencies: + alphanum-sort "^1.0.0" + browserslist "^4.0.0" + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + uniqs "^2.0.0" + +postcss-minify-selectors@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" + integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== + dependencies: + alphanum-sort "^1.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +postcss-modules-extract-imports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" + integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== + dependencies: + postcss "^7.0.5" + +postcss-modules-local-by-default@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz#dd9953f6dd476b5fd1ef2d8830c8929760b56e63" + integrity sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + postcss-value-parser "^3.3.1" + +postcss-modules-scope@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz#ad3f5bf7856114f6fcab901b0502e2a2bc39d4eb" + integrity sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + +postcss-modules-values@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz#479b46dc0c5ca3dc7fa5270851836b9ec7152f64" + integrity sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w== + dependencies: + icss-replace-symbols "^1.1.0" + postcss "^7.0.6" + +postcss-nesting@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-7.0.0.tgz#6e26a770a0c8fcba33782a6b6f350845e1a448f6" + integrity sha512-WSsbVd5Ampi3Y0nk/SKr5+K34n52PqMqEfswu6RtU4r7wA8vSD+gM8/D9qq4aJkHImwn1+9iEFTbjoWsQeqtaQ== + dependencies: + postcss "^7.0.2" + +postcss-normalize-charset@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" + integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== + dependencies: + postcss "^7.0.0" + +postcss-normalize-display-values@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" + integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-positions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" + integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== + dependencies: + cssnano-util-get-arguments "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-repeat-style@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" + integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== + dependencies: + cssnano-util-get-arguments "^4.0.0" + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-string@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" + integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== + dependencies: + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-timing-functions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" + integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== + dependencies: + cssnano-util-get-match "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-unicode@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" + integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-url@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" + integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize-whitespace@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" + integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== + dependencies: + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-normalize@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/postcss-normalize/-/postcss-normalize-7.0.1.tgz#eb51568d962b8aa61a8318383c8bb7e54332282e" + integrity sha512-NOp1fwrG+6kVXWo7P9SizCHX6QvioxFD/hZcI2MLxPmVnFJFC0j0DDpIuNw2tUDeCFMni59gCVgeJ1/hYhj2OQ== + dependencies: + "@csstools/normalize.css" "^9.0.1" + browserslist "^4.1.1" + postcss "^7.0.2" + postcss-browser-comments "^2.0.0" + +postcss-ordered-values@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" + integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== + dependencies: + cssnano-util-get-arguments "^4.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-overflow-shorthand@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz#31ecf350e9c6f6ddc250a78f0c3e111f32dd4c30" + integrity sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g== + dependencies: + postcss "^7.0.2" + +postcss-page-break@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-2.0.0.tgz#add52d0e0a528cabe6afee8b46e2abb277df46bf" + integrity sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ== + dependencies: + postcss "^7.0.2" + +postcss-place@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-4.0.1.tgz#e9f39d33d2dc584e46ee1db45adb77ca9d1dcc62" + integrity sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg== + dependencies: + postcss "^7.0.2" + postcss-values-parser "^2.0.0" + +postcss-preset-env@6.6.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-6.6.0.tgz#642e7d962e2bdc2e355db117c1eb63952690ed5b" + integrity sha512-I3zAiycfqXpPIFD6HXhLfWXIewAWO8emOKz+QSsxaUZb9Dp8HbF5kUf+4Wy/AxR33o+LRoO8blEWCHth0ZsCLA== + dependencies: + autoprefixer "^9.4.9" + browserslist "^4.4.2" + caniuse-lite "^1.0.30000939" + css-blank-pseudo "^0.1.4" + css-has-pseudo "^0.10.0" + css-prefers-color-scheme "^3.1.1" + cssdb "^4.3.0" + postcss "^7.0.14" + postcss-attribute-case-insensitive "^4.0.1" + postcss-color-functional-notation "^2.0.1" + postcss-color-gray "^5.0.0" + postcss-color-hex-alpha "^5.0.2" + postcss-color-mod-function "^3.0.3" + postcss-color-rebeccapurple "^4.0.1" + postcss-custom-media "^7.0.7" + postcss-custom-properties "^8.0.9" + postcss-custom-selectors "^5.1.2" + postcss-dir-pseudo-class "^5.0.0" + postcss-double-position-gradients "^1.0.0" + postcss-env-function "^2.0.2" + postcss-focus-visible "^4.0.0" + postcss-focus-within "^3.0.0" + postcss-font-variant "^4.0.0" + postcss-gap-properties "^2.0.0" + postcss-image-set-function "^3.0.1" + postcss-initial "^3.0.0" + postcss-lab-function "^2.0.1" + postcss-logical "^3.0.0" + postcss-media-minmax "^4.0.0" + postcss-nesting "^7.0.0" + postcss-overflow-shorthand "^2.0.0" + postcss-page-break "^2.0.0" + postcss-place "^4.0.1" + postcss-pseudo-class-any-link "^6.0.0" + postcss-replace-overflow-wrap "^3.0.0" + postcss-selector-matches "^4.0.0" + postcss-selector-not "^4.0.0" + +postcss-pseudo-class-any-link@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz#2ed3eed393b3702879dec4a87032b210daeb04d1" + integrity sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew== + dependencies: + postcss "^7.0.2" + postcss-selector-parser "^5.0.0-rc.3" + +postcss-reduce-initial@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" + integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== + dependencies: + browserslist "^4.0.0" + caniuse-api "^3.0.0" + has "^1.0.0" + postcss "^7.0.0" + +postcss-reduce-transforms@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" + integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== + dependencies: + cssnano-util-get-match "^4.0.0" + has "^1.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + +postcss-replace-overflow-wrap@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz#61b360ffdaedca84c7c918d2b0f0d0ea559ab01c" + integrity sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw== + dependencies: + postcss "^7.0.2" + +postcss-safe-parser@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz#8756d9e4c36fdce2c72b091bbc8ca176ab1fcdea" + integrity sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ== + dependencies: + postcss "^7.0.0" + +postcss-selector-matches@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz#71c8248f917ba2cc93037c9637ee09c64436fcff" + integrity sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww== + dependencies: + balanced-match "^1.0.0" + postcss "^7.0.2" + +postcss-selector-not@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-4.0.0.tgz#c68ff7ba96527499e832724a2674d65603b645c0" + integrity sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ== + dependencies: + balanced-match "^1.0.0" + postcss "^7.0.2" + +postcss-selector-parser@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865" + integrity sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU= + dependencies: + dot-prop "^4.1.1" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^5.0.0, postcss-selector-parser@^5.0.0-rc.3, postcss-selector-parser@^5.0.0-rc.4: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c" + integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ== + dependencies: + cssesc "^2.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" + integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" + integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== + dependencies: + is-svg "^3.0.0" + postcss "^7.0.0" + postcss-value-parser "^3.0.0" + svgo "^1.0.0" + +postcss-unique-selectors@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" + integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== + dependencies: + alphanum-sort "^1.0.0" + postcss "^7.0.0" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== + +postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz#da8b472d901da1e205b47bdc98637b9e9e550e5f" + integrity sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg== + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.5, postcss@^7.0.6: + version "7.0.16" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.16.tgz#48f64f1b4b558cb8b52c88987724359acb010da2" + integrity sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +pretty-bytes@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.2.0.tgz#96c92c6e95a0b35059253fb33c03e260d40f5a1f" + integrity sha512-ujANBhiUsl9AhREUDUEY1GPOharMGm8x8juS7qOHybcLi7XsKfrYQ88hSly1l2i0klXHTDYrlL8ihMCG55Dc3w== + +pretty-error@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" + integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM= + dependencies: + renderkid "^2.0.1" + utila "~0.4" + +pretty-format@^24.8.0: + version "24.8.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2" + integrity sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw== + dependencies: + "@jest/types" "^24.8.0" + ansi-regex "^4.0.0" + ansi-styles "^3.2.0" + react-is "^16.8.4" + +private@^0.1.6: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + +promise-retry@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-1.1.1.tgz#6739e968e3051da20ce6497fb2b50f6911df3d6d" + integrity sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0= + dependencies: + err-code "^1.0.0" + retry "^0.10.0" + +promise@8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.0.2.tgz#9dcd0672192c589477d56891271bdc27547ae9f0" + integrity sha512-EIyzM39FpVOMbqgzEHhxdrEhtOSDOtjMZQ0M6iVfCE+kWNgCkAyOdnuCWqfmflylftfadU6FkiMgHZA2kUzwRw== + dependencies: + asap "~2.0.6" + +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + +prompts@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.0.4.tgz#179f9d4db3128b9933aa35f93a800d8fce76a682" + integrity sha512-HTzM3UWp/99A0gk51gAegwo1QRYA7xjcZufMNe33rCclFszUYAuHe1fIN/3ZmiHeGPkUsNaRyQm1hHOfM0PKxA== + dependencies: + kleur "^3.0.2" + sisteransi "^1.0.0" + +promzard@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" + integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4= + dependencies: + read "1" + +prop-types@^15.5.4, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + +property-information@^5.0.0, property-information@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.0.1.tgz#c3b09f4f5750b1634c0b24205adbf78f18bdf94f" + integrity sha512-nAtBDVeSwFM3Ot/YxT7s4NqZmqXI7lLzf46BThvotEtYf2uk2yH0ACYuWQkJ7gxKs49PPtKVY0UlDGkyN9aJlw== + dependencies: + xtend "^4.0.1" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= + +protocols@^1.1.0, protocols@^1.4.0: + version "1.4.7" + resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.7.tgz#95f788a4f0e979b291ffefcf5636ad113d037d32" + integrity sha512-Fx65lf9/YDn3hUX08XUc0J8rSux36rEsyiv21ZGUC1mOyeM3lTRpZLcrm8aAolzS4itwVfm7TAPyxC2E5zd6xg== + +protoduck@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/protoduck/-/protoduck-5.0.1.tgz#03c3659ca18007b69a50fd82a7ebcc516261151f" + integrity sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg== + dependencies: + genfun "^5.0.0" + +proxy-addr@~2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" + integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.0" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.24, psl@^1.1.28: + version "1.1.31" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" + integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== + +pstree.remy@^1.1.6: + version "1.1.7" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.7.tgz#c76963a28047ed61542dc361aa26ee55a7fa15f3" + integrity sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +q@^1.1.2, q@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + +qs@6.5.2, qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +qs@^6.5.2: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +query-string@^6.8.2: + version "6.8.2" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.8.2.tgz#36cb7e452ae11a4b5e9efee83375e0954407b2f6" + integrity sha512-J3Qi8XZJXh93t2FiKyd/7Ec6GNifsjKXUsVFkSBj/kjLsDylWhnCz4NT1bkPcKotttPW+QbKGqqPH8OoI2pdqw== + dependencies: + decode-uri-component "^0.2.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + +quick-format-unescaped@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-1.1.2.tgz#0ca581de3174becef25ac3c2e8956342381db698" + integrity sha1-DKWB3jF0vs7yWsPC6JVjQjgdtpg= + dependencies: + fast-safe-stringify "^1.0.8" + +quick-lru@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" + integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= + +raf-schd@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0" + integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ== + +raf@3.4.1, raf@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" + integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== + dependencies: + performance-now "^2.1.0" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@^1.0.3, range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= + +raw-body@2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" + integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw== + dependencies: + bytes "3.0.0" + http-errors "1.6.3" + iconv-lite "0.4.23" + unpipe "1.0.0" + +raw-body@^2.3.3: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-app-polyfill@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-1.0.1.tgz#809a858e44f9564c7f4205e173076f90048274f1" + integrity sha512-LbVpT1NdzTdDDs7xEZdebjDrqsvKi5UyVKUQqtTYYNyC1JJYVAwNQWe4ybWvoT2V2WW9PGVO2u5Y6aVj4ER/Ow== + dependencies: + core-js "3.0.1" + object-assign "4.1.1" + promise "8.0.2" + raf "3.4.1" + regenerator-runtime "0.13.2" + whatwg-fetch "3.0.0" + +react-beautiful-dnd@11.0.3: + version "11.0.3" + resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-11.0.3.tgz#5678bb3e725d8b56cb7cf57f56e952105fc4f2af" + integrity sha512-2FX2SnOlKMmfn90xUHCav7cxRWXwY7FeRa6TzdxWeX7DdP5JTvVQcsWgiOkdbJSj+J+1q1nA9QO4/HQ52D0DAA== + dependencies: + "@babel/runtime-corejs2" "^7.4.4" + css-box-model "^1.1.2" + memoize-one "^5.0.4" + raf-schd "^4.0.0" + react-redux "^7.0.3" + redux "^4.0.1" + tiny-invariant "^1.0.4" + use-memo-one "^1.1.0" + +react-dev-utils@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-9.0.1.tgz#5c03d85a0b2537d0c46af7165c24a7dfb274bef2" + integrity sha512-pnaeMo/Pxel8aZpxk1WwxT3uXxM3tEwYvsjCYn5R7gNxjhN1auowdcLDzFB8kr7rafAj2rxmvfic/fbac5CzwQ== + dependencies: + "@babel/code-frame" "7.0.0" + address "1.0.3" + browserslist "4.5.4" + chalk "2.4.2" + cross-spawn "6.0.5" + detect-port-alt "1.1.6" + escape-string-regexp "1.0.5" + filesize "3.6.1" + find-up "3.0.0" + fork-ts-checker-webpack-plugin "1.1.1" + global-modules "2.0.0" + globby "8.0.2" + gzip-size "5.0.0" + immer "1.10.0" + inquirer "6.2.2" + is-root "2.0.0" + loader-utils "1.2.3" + opn "5.4.0" + pkg-up "2.0.0" + react-error-overlay "^5.1.6" + recursive-readdir "2.2.2" + shell-quote "1.6.1" + sockjs-client "1.3.0" + strip-ansi "5.2.0" + text-table "0.2.0" + +react-dom@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f" + integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.6" + +react-double-scrollbar@0.0.15: + version "0.0.15" + resolved "https://registry.yarnpkg.com/react-double-scrollbar/-/react-double-scrollbar-0.0.15.tgz#e915ab8cb3b959877075f49436debfdb04288fe4" + integrity sha1-6RWrjLO5WYdwdfSUNt6/2wQoj+Q= + +react-draggable@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-3.3.2.tgz#966ef1d90f2387af3c2d8bd3516f601ea42ca359" + integrity sha512-oaz8a6enjbPtx5qb0oDWxtDNuybOylvto1QLydsXgKmwT7e3GXC2eMVDwEMIUYJIFqVG72XpOv673UuuAq6LhA== + dependencies: + classnames "^2.2.5" + prop-types "^15.6.0" + +react-error-overlay@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.6.tgz#0cd73407c5d141f9638ae1e0c63e7b2bf7e9929d" + integrity sha512-X1Y+0jR47ImDVr54Ab6V9eGk0Hnu7fVWGeHQSOXHf/C2pF9c6uy3gef8QUeuUiWlNb0i08InPSE5a/KJzNzw1Q== + +react-event-listener@^0.6.0: + version "0.6.6" + resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.6.tgz#758f7b991cad9086dd39fd29fad72127e1d8962a" + integrity sha512-+hCNqfy7o9wvO6UgjqFmBzARJS7qrNoda0VqzvOuioEpoEXKutiKuv92dSz6kP7rYLmyHPyYNLesi5t/aH1gfw== + dependencies: + "@babel/runtime" "^7.2.0" + prop-types "^15.6.0" + warning "^4.0.1" + +react-iframe@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/react-iframe/-/react-iframe-1.8.0.tgz#8c78f2c59b894ca5605fa7e45e61e27e57e96091" + integrity sha512-NYi89+rEqREwQxW9sDf+akh6/dtwWd3bOjByoVEIQ7SicOxVawRMer3pLdWjFaHXpuxTB9NqobPf/Ohj2iAKkg== + dependencies: + object-assign "^4.1.1" + +react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" + integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== + +react-is@^16.8.6: + version "16.9.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" + integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-redux@^7.0.3: + version "7.1.0" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.0.tgz#72af7cf490a74acdc516ea9c1dd80e25af9ea0b2" + integrity sha512-hyu/PoFK3vZgdLTg9ozbt7WF3GgX5+Yn3pZm5/96/o4UueXA+zj08aiSC9Mfj2WtD1bvpIb3C5yvskzZySzzaw== + dependencies: + "@babel/runtime" "^7.4.5" + hoist-non-react-statics "^3.3.0" + invariant "^2.2.4" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.8.6" + +react-resize-detector@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-2.3.0.tgz#57bad1ae26a28a62a2ddb678ba6ffdf8fa2b599c" + integrity sha512-oCAddEWWeFWYH5FAcHdBYcZjAw9fMzRUK9sWSx6WvSSOPVRxcHd5zTIGy/mOus+AhN/u6T4TMiWxvq79PywnJQ== + dependencies: + lodash.debounce "^4.0.8" + lodash.throttle "^4.1.1" + prop-types "^15.6.0" + resize-observer-polyfill "^1.5.0" + +react-router-dom@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.0.1.tgz#ee66f4a5d18b6089c361958e443489d6bab714be" + integrity sha512-zaVHSy7NN0G91/Bz9GD4owex5+eop+KvgbxXsP/O+iW1/Ln+BrJ8QiIR5a6xNPtrdTvLkxqlDClx13QO1uB8CA== + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + loose-envify "^1.3.1" + prop-types "^15.6.2" + react-router "5.0.1" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-router@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.0.1.tgz#04ee77df1d1ab6cb8939f9f01ad5702dbadb8b0f" + integrity sha512-EM7suCPNKb1NxcTZ2LEOWFtQBQRQXecLxVpdsP4DW4PbbqYWeRiLyV/Tt1SdCrvT2jcyXAXmVTmzvSzrPR63Bg== + dependencies: + "@babel/runtime" "^7.1.2" + history "^4.9.0" + hoist-non-react-statics "^3.1.0" + loose-envify "^1.3.1" + mini-create-react-context "^0.3.0" + path-to-regexp "^1.7.0" + prop-types "^15.6.2" + react-is "^16.6.0" + tiny-invariant "^1.0.2" + tiny-warning "^1.0.0" + +react-scripts@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.0.1.tgz#e5565350d8069cc9966b5998d3fe3befe3d243ac" + integrity sha512-LKEjBhVpEB+c312NeJhzF+NATxF7JkHNr5GhtwMeRS1cMeLElMeIu8Ye7WGHtDP7iz7ra4ryy48Zpo6G/cwWUw== + dependencies: + "@babel/core" "7.4.3" + "@svgr/webpack" "4.1.0" + "@typescript-eslint/eslint-plugin" "1.6.0" + "@typescript-eslint/parser" "1.6.0" + babel-eslint "10.0.1" + babel-jest "^24.8.0" + babel-loader "8.0.5" + babel-plugin-named-asset-import "^0.3.2" + babel-preset-react-app "^9.0.0" + camelcase "^5.2.0" + case-sensitive-paths-webpack-plugin "2.2.0" + css-loader "2.1.1" + dotenv "6.2.0" + dotenv-expand "4.2.0" + eslint "^5.16.0" + eslint-config-react-app "^4.0.1" + eslint-loader "2.1.2" + eslint-plugin-flowtype "2.50.1" + eslint-plugin-import "2.16.0" + eslint-plugin-jsx-a11y "6.2.1" + eslint-plugin-react "7.12.4" + eslint-plugin-react-hooks "^1.5.0" + file-loader "3.0.1" + fs-extra "7.0.1" + html-webpack-plugin "4.0.0-beta.5" + identity-obj-proxy "3.0.0" + is-wsl "^1.1.0" + jest "24.7.1" + jest-environment-jsdom-fourteen "0.1.0" + jest-resolve "24.7.1" + jest-watch-typeahead "0.3.0" + mini-css-extract-plugin "0.5.0" + optimize-css-assets-webpack-plugin "5.0.1" + pnp-webpack-plugin "1.2.1" + postcss-flexbugs-fixes "4.1.0" + postcss-loader "3.0.0" + postcss-normalize "7.0.1" + postcss-preset-env "6.6.0" + postcss-safe-parser "4.0.1" + react-app-polyfill "^1.0.1" + react-dev-utils "^9.0.1" + resolve "1.10.0" + sass-loader "7.1.0" + semver "6.0.0" + style-loader "0.23.1" + terser-webpack-plugin "1.2.3" + ts-pnp "1.1.2" + url-loader "1.1.2" + webpack "4.29.6" + webpack-dev-server "3.2.1" + webpack-manifest-plugin "2.0.4" + workbox-webpack-plugin "4.2.0" + optionalDependencies: + fsevents "2.0.6" + +react-smooth@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-1.0.2.tgz#f7a2d932ece8db898646078c3c97f3e9533e0486" + integrity sha512-pIGzL1g9VGAsRsdZQokIK0vrCkcdKtnOnS1gyB2rrowdLy69lNSWoIjCTWAfgbiYvria8tm5hEZqj+jwXMkV4A== + dependencies: + lodash "~4.17.4" + prop-types "^15.6.0" + raf "^3.4.0" + react-transition-group "^2.5.0" + +react-swipeable-views-core@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/react-swipeable-views-core/-/react-swipeable-views-core-0.13.1.tgz#8829a922462a8bdd701709cd1b385393d38f1527" + integrity sha512-EP8sCvvD7VDiZLglPt9icMuMNu8qLRLk0ab/fB1HXv7lX8ClnwF3UMCM0ZrN3sguSY7CsX3LevducGGsT1VcDg== + dependencies: + "@babel/runtime" "7.0.0" + warning "^4.0.1" + +react-swipeable-views-utils@^0.13.3: + version "0.13.3" + resolved "https://registry.yarnpkg.com/react-swipeable-views-utils/-/react-swipeable-views-utils-0.13.3.tgz#c234d8d836bb085803631a9fef0adb2f9597221f" + integrity sha512-CZkJwiNQPISkyTsPMUPiJgwJBrUVd7NC3WSUvx30uwvPb0Sy2w2+tpU51qeYc6YwIhex0s5Eu5YPjK3PDBh+gA== + dependencies: + "@babel/runtime" "7.0.0" + fbjs "^0.8.4" + keycode "^2.1.7" + prop-types "^15.6.0" + react-event-listener "^0.6.0" + react-swipeable-views-core "^0.13.1" + +react-swipeable-views@^0.13.3: + version "0.13.3" + resolved "https://registry.yarnpkg.com/react-swipeable-views/-/react-swipeable-views-0.13.3.tgz#2ad886767c6b2de88000606a14bedde12156e6d0" + integrity sha512-LBHRA5ZouipmoLLwi0cqB8qc7NHLskbXmT1I+ZztC9JfmgKrfichw5R+7q4igQ+5VbaP6jL1vn8BtHW96WYNFQ== + dependencies: + "@babel/runtime" "7.0.0" + dom-helpers "^3.2.1" + prop-types "^15.5.4" + react-swipeable-views-core "^0.13.1" + react-swipeable-views-utils "^0.13.3" + warning "^4.0.1" + +react-transition-group@^2.5.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" + integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== + dependencies: + dom-helpers "^3.4.0" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react-lifecycles-compat "^3.0.4" + +react-transition-group@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.2.1.tgz#61fc9e36568bff9a1fe4e60fae323c8a6dbc0680" + integrity sha512-IXrPr93VzCPupwm2O6n6C2kJIofJ/Rp5Ltihhm9UfE8lkuVX2ng/SUUl/oWjblybK9Fq2Io7LGa6maVqPB762Q== + dependencies: + "@babel/runtime" "^7.4.5" + dom-helpers "^3.4.0" + loose-envify "^1.4.0" + prop-types "^15.6.2" + +react@^16.8.6: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" + integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.13.6" + +read-cmd-shim@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.3.tgz#b246608c8e76e332a99be7811c096a4baf60015a" + integrity sha512-HUHb2imlZ8xBJjiZZRx0Ag9JfZ3jxQRfORMQXWCDeHE6PCCnpQrMq6LhyNqEPnMXhMDDIyq/BK7pBbhNy9zDDA== + dependencies: + graceful-fs "^4.1.2" + +"read-package-json@1 || 2", read-package-json@^2.0.0, read-package-json@^2.0.13: + version "2.1.0" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.0.tgz#e3d42e6c35ea5ae820d9a03ab0c7291217fc51d5" + integrity sha512-KLhu8M1ZZNkMcrq1+0UJbR8Dii8KZUqB0Sha4mOx/bknfKI/fyrQVrG/YIt2UOtG667sD8+ee4EXMM91W9dC+A== + dependencies: + glob "^7.1.1" + json-parse-better-errors "^1.0.1" + normalize-package-data "^2.0.0" + slash "^1.0.0" + optionalDependencies: + graceful-fs "^4.1.2" + +read-package-tree@^5.1.6: + version "5.3.1" + resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.3.1.tgz#a32cb64c7f31eb8a6f31ef06f9cedf74068fe636" + integrity sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw== + dependencies: + read-package-json "^2.0.0" + readdir-scoped-modules "^1.0.0" + util-promisify "^2.1.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= + dependencies: + find-up "^2.0.0" + read-pkg "^3.0.0" + +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +read@1, read@~1.0.1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= + dependencies: + mute-stream "~0.0.4" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +"readable-stream@2 || 3", readable-stream@^3.0.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" + integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^3.0.6, readable-stream@^3.1.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.3.0.tgz#cb8011aad002eb717bf040291feba8569c986fb9" + integrity sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdir-scoped-modules@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" + integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== + dependencies: + debuglog "^1.0.1" + dezalgo "^1.0.0" + graceful-fs "^4.1.2" + once "^1.3.0" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +realpath-native@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== + dependencies: + util.promisify "^1.0.0" + +recharts-scale@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.4.2.tgz#b66315d985cd9b80d5f7d977a5aab9a305abc354" + integrity sha512-p/cKt7j17D1CImLgX2f5+6IXLbRHGUQkogIp06VUoci/XkhOQiGSzUrsD1uRmiI7jha4u8XNFOjkHkzzBPivMg== + dependencies: + decimal.js-light "^2.4.1" + +recharts@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/recharts/-/recharts-1.6.2.tgz#4ced884f04b680e8dac5d3e109f99b0e7cfb9b0f" + integrity sha512-NqVN8Hq5wrrBthTxQB+iCnZjup1dc+AYRIB6Q9ck9UjdSJTt4PbLepGpudQEYJEN5iIpP/I2vThC4uiTJa7xUQ== + dependencies: + classnames "^2.2.5" + core-js "^2.5.1" + d3-interpolate "^1.3.0" + d3-scale "^2.1.0" + d3-shape "^1.2.0" + lodash "^4.17.5" + prop-types "^15.6.0" + react-resize-detector "^2.3.0" + react-smooth "^1.0.0" + recharts-scale "^0.4.2" + reduce-css-calc "^1.3.0" + +recursive-readdir@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" + integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== + dependencies: + minimatch "3.0.4" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +redent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" + integrity sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo= + dependencies: + indent-string "^3.0.0" + strip-indent "^2.0.0" + +reduce-css-calc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + integrity sha1-dHyRTgSWFKTJz7umKYca0dKSdxY= + dependencies: + balanced-match "^0.4.2" + math-expression-evaluator "^1.2.14" + reduce-function-call "^1.0.1" + +reduce-function-call@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" + integrity sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk= + dependencies: + balanced-match "^0.4.2" + +redux@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.4.tgz#4ee1aeb164b63d6a1bcc57ae4aa0b6e6fa7a3796" + integrity sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q== + dependencies: + loose-envify "^1.4.0" + symbol-observable "^1.2.0" + +regenerate-unicode-properties@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.2.tgz#7b38faa296252376d363558cfbda90c9ce709662" + integrity sha512-SbA/iNrBUf6Pv2zU8Ekv1Qbhv92yxL4hiDa2siuxs4KKn4oOoMDHXjAf7+Nz9qinUQ46B1LcWEi/PhJfPWpZWQ== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + +regenerator-runtime@0.13.2, regenerator-runtime@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" + integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== + +regenerator-runtime@^0.12.0: + version "0.12.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" + integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== + +regenerator-transform@^0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.4.tgz#18f6763cf1382c69c36df76c6ce122cc694284fb" + integrity sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A== + dependencies: + private "^0.1.6" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp-tree@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.6.tgz#84900fa12fdf428a2ac25f04300382a7c0148479" + integrity sha512-LFrA98Dw/heXqDojz7qKFdygZmFoiVlvE1Zp7Cq2cvF+ZA+03Gmhy0k0PQlsC1jvHPiTUSs+pDHEuSWv6+6D7w== + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +regexpu-core@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.4.tgz#080d9d02289aa87fe1667a4f5136bc98a6aebaae" + integrity sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.0.2" + regjsgen "^0.5.0" + regjsparser "^0.6.0" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.1.0" + +registry-auth-token@^3.0.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e" + integrity sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A== + dependencies: + rc "^1.1.6" + safe-buffer "^5.0.1" + +registry-url@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" + integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI= + dependencies: + rc "^1.0.1" + +regjsgen@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" + integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== + +regjsparser@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" + integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ== + dependencies: + jsesc "~0.5.0" + +rehype-parse@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-6.0.0.tgz#f681555f2598165bee2c778b39f9073d17b16bca" + integrity sha512-V2OjMD0xcSt39G4uRdMTqDXXm6HwkUbLMDayYKA/d037j8/OtVSQ+tqKwYWOuyBeoCs/3clXRe30VUjeMDTBSA== + dependencies: + hast-util-from-parse5 "^5.0.0" + parse5 "^5.0.0" + xtend "^4.0.1" + +relateurl@0.2.x: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +renderkid@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149" + integrity sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA== + dependencies: + css-select "^1.1.0" + dom-converter "^0.2" + htmlparser2 "^3.3.0" + strip-ansi "^3.0.0" + utila "^0.4.0" + +repeat-element@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +replace-ext@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" + integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= + +request-promise-core@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346" + integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== + dependencies: + lodash "^4.17.11" + +request-promise-native@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59" + integrity sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== + dependencies: + request-promise-core "1.1.2" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + +request@^2.87.0, request@^2.88.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-from-string@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requireindex@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef" + integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +resize-observer-polyfill@^1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= + dependencies: + resolve-from "^3.0.0" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-path@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/resolve-path/-/resolve-path-1.4.0.tgz#c4bda9f5efb2fce65247873ab36bb4d834fe16f7" + integrity sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc= + dependencies: + http-errors "~1.6.2" + path-is-absolute "1.0.1" + +resolve-pathname@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879" + integrity sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= + +resolve@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" + integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg== + dependencies: + path-parse "^1.0.6" + +resolve@^1.10.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1, resolve@^1.9.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.1.tgz#664842ac960795bbe758221cdccda61fb64b5f18" + integrity sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA== + dependencies: + path-parse "^1.0.6" + +resolve@^1.10.1, resolve@^1.11.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" + integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw== + dependencies: + path-parse "^1.0.6" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" + integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q= + +rgb-regex@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" + integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= + +rgba-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" + integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= + +rifm@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/rifm/-/rifm-0.7.0.tgz#debe951a9c83549ca6b33e5919f716044c2230be" + integrity sha512-DSOJTWHD67860I5ojetXdEQRIBvF6YcpNe53j0vn1vp9EUb9N80EiZTxgP+FkDKorWC8PZw052kTF4C1GOivCQ== + dependencies: + "@babel/runtime" "^7.3.1" + +rimraf@2: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@2.6.3, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rsvp@^4.8.4: + version "4.8.4" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.4.tgz#b50e6b34583f3dd89329a2f23a8a2be072845911" + integrity sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA== + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= + dependencies: + is-promise "^2.1.0" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= + dependencies: + aproba "^1.1.1" + +rxjs@^6.4.0: + version "6.5.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.1.tgz#f7a005a9386361921b8524f38f54cbf80e5d08f4" + integrity sha512-y0j31WJc83wPu31vS1VlAFW5JGrnGC+j+TtGAa1fRQphy48+fDYiDmX8tjGloToEsMkxnouOg/1IzXGKkJnZMg== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +sass-loader@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.1.0.tgz#16fd5138cb8b424bf8a759528a1972d72aad069d" + integrity sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w== + dependencies: + clone-deep "^2.0.1" + loader-utils "^1.0.1" + lodash.tail "^4.1.1" + neo-async "^2.5.0" + pify "^3.0.0" + semver "^5.5.0" + +sax@^1.2.4, sax@~1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +saxes@^3.1.9: + version "3.1.9" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.9.tgz#c1c197cd54956d88c09f960254b999e192d7058b" + integrity sha512-FZeKhJglhJHk7eWG5YM0z46VHmI3KJpMBAQm3xa9meDvd+wevB5GuBB0wc0exPInZiBBHqi00DbS8AcvCGCFMw== + dependencies: + xmlchars "^1.3.1" + +scheduler@^0.13.6: + version "0.13.6" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889" + integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= + +selfsigned@^1.9.1: + version "1.10.4" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.4.tgz#cdd7eccfca4ed7635d47a08bf2d5d3074092e2cd" + integrity sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw== + dependencies: + node-forge "0.7.5" + +semver-diff@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" + integrity sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY= + dependencies: + semver "^5.0.3" + +"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" + integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== + +"semver@2.x || 3.x || 4 || 5", semver@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== + +semver@6.0.0, semver@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.0.0.tgz#05e359ee571e5ad7ed641a6eec1e547ba52dea65" + integrity sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ== + +semver@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.2.0.tgz#4d813d9590aaf8a9192693d6c85b9344de5901db" + integrity sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A== + +semver@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= + +send@0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.6.2" + mime "1.4.1" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.4.0" + +serialize-javascript@^1.4.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65" + integrity sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA== + +serve-index@^1.7.2: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" + integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.2" + send "0.16.2" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE= + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4, setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shallow-clone@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" + integrity sha1-WQnodLp3EG1zrEFM/sH/yofZcGA= + dependencies: + is-extendable "^0.1.1" + kind-of "^2.0.1" + lazy-cache "^0.2.3" + mixin-object "^2.0.1" + +shallow-clone@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-1.0.0.tgz#4480cd06e882ef68b2ad88a3ea54832e2c48b571" + integrity sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA== + dependencies: + is-extendable "^0.1.1" + kind-of "^5.0.0" + mixin-object "^2.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shell-quote@1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c= + dependencies: + array-filter "~0.0.0" + array-map "~0.0.0" + array-reduce "~0.0.0" + jsonify "~0.0.0" + +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +sisteransi@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c" + integrity sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ== + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +slide@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= + +smart-buffer@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.2.tgz#5207858c3815cc69110703c6b94e46c15634395d" + integrity sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sockjs-client@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.3.0.tgz#12fc9d6cb663da5739d3dc5fb6e8687da95cb177" + integrity sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg== + dependencies: + debug "^3.2.5" + eventsource "^1.0.7" + faye-websocket "~0.11.1" + inherits "^2.0.3" + json3 "^3.3.2" + url-parse "^1.4.3" + +sockjs@0.3.19: + version "0.3.19" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" + integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw== + dependencies: + faye-websocket "^0.10.0" + uuid "^3.0.1" + +socks-proxy-agent@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" + integrity sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg== + dependencies: + agent-base "~4.2.1" + socks "~2.3.2" + +socks@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.2.tgz#ade388e9e6d87fdb11649c15746c578922a5883e" + integrity sha512-pCpjxQgOByDHLlNqlnh/mNSAxIUkyBBuwwhTcV+enZGbDaClPvHdvm6uvOwZfFJkam7cGhBNbb4JxiP8UZkRvQ== + dependencies: + ip "^1.1.5" + smart-buffer "4.0.2" + +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= + dependencies: + is-plain-obj "^1.0.0" + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.6, source-map-support@~0.5.10: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +space-separated-tokens@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.3.tgz#bc6500e116d13285a94b59b58c44c7f045fe6124" + integrity sha512-/M5RAdBuQlSDPNfA5ube+fkHbHyY08pMuADLmsAQURzo56w90r681oiOoz3o3ZQyWdSeNucpTFjL+Ggd5qui3w== + +spdx-correct@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz#75ecd1a88de8c184ef015eafb51b5b48bfd11bb1" + integrity sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.0.tgz#81f222b5a743a329aa12cea6a390e60e9b613c52" + integrity sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +split2@^2.0.0, split2@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-2.2.0.tgz#186b2575bcf83e85b7d18465756238ee4ee42493" + integrity sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw== + dependencies: + through2 "^2.0.2" + +split@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== + dependencies: + through "2" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +ssri@^6.0.0, ssri@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" + integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== + dependencies: + figgy-pudding "^3.5.1" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +stack-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" + integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== + +stealthy-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= + +stream-browserify@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" + integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= + +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= + +string-length@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" + integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0= + dependencies: + astral-regex "^1.0.0" + strip-ansi "^4.0.0" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.1.0.tgz#ba846d1daa97c3c596155308063e075ed1c99aff" + integrity sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^5.2.0" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" + integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w== + dependencies: + safe-buffer "~5.1.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringify-object@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +strip-ansi@5.2.0, strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-comments@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-1.0.2.tgz#82b9c45e7f05873bee53f37168af930aa368679d" + integrity sha512-kL97alc47hoyIQSV165tTt9rG5dn4w1dNnBhOQ3bOU1Nc1hel09jnXANaHJ7vzHLd4Ju8kseDGzlev96pghLFw== + dependencies: + babel-extract-comments "^1.0.0" + babel-plugin-transform-object-rest-spread "^6.26.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= + +strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +strong-log-transformer@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" + integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== + dependencies: + duplexer "^0.1.1" + minimist "^1.2.0" + through "^2.3.4" + +style-loader@0.23.1: + version "0.23.1" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.23.1.tgz#cb9154606f3e771ab6c4ab637026a1049174d925" + integrity sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg== + dependencies: + loader-utils "^1.1.0" + schema-utils "^1.0.0" + +stylehacks@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" + integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== + dependencies: + browserslist "^4.0.0" + postcss "^7.0.0" + postcss-selector-parser "^3.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.2.0, supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +svgo@^1.0.0, svgo@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.2.2.tgz#0253d34eccf2aed4ad4f283e11ee75198f9d7316" + integrity sha512-rAfulcwp2D9jjdGu+0CuqlrAUin6bBWrpoqXWwKDZZZJfXcUXQSxLJOFJCQCSA0x0pP2U0TxSlJu2ROq5Bq6qA== + dependencies: + chalk "^2.4.1" + coa "^2.0.2" + css-select "^2.0.0" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.28" + css-url-regex "^1.1.0" + csso "^3.5.1" + js-yaml "^3.13.1" + mkdirp "~0.5.1" + object.values "^1.1.0" + sax "~1.2.4" + stable "^0.1.8" + unquote "~1.1.1" + util.promisify "~1.0.0" + +symbol-observable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + +symbol-tree@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" + integrity sha1-rifbOPZgp64uHDt9G8KQgZuFGeY= + +table@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/table/-/table-5.2.3.tgz#cde0cc6eb06751c009efab27e8c820ca5b67b7f2" + integrity sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ== + dependencies: + ajv "^6.9.1" + lodash "^4.17.11" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tapable@^1.0.0, tapable@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tar@^4: + version "4.4.8" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" + integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.3.4" + minizlib "^1.1.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" + +tar@^4.4.10, tar@^4.4.8: + version "4.4.10" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" + integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.3.5" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +temp-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" + integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= + +temp-write@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-3.4.0.tgz#8cff630fb7e9da05f047c74ce4ce4d685457d492" + integrity sha1-jP9jD7fp2gXwR8dM5M5NaFRX1JI= + dependencies: + graceful-fs "^4.1.2" + is-stream "^1.1.0" + make-dir "^1.0.0" + pify "^3.0.0" + temp-dir "^1.0.0" + uuid "^3.0.1" + +term-size@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" + integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= + dependencies: + execa "^0.7.0" + +terser-webpack-plugin@1.2.3, terser-webpack-plugin@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz#3f98bc902fac3e5d0de730869f50668561262ec8" + integrity sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA== + dependencies: + cacache "^11.0.2" + find-cache-dir "^2.0.0" + schema-utils "^1.0.0" + serialize-javascript "^1.4.0" + source-map "^0.6.1" + terser "^3.16.1" + webpack-sources "^1.1.0" + worker-farm "^1.5.2" + +terser@^3.16.1: + version "3.17.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2" + integrity sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ== + dependencies: + commander "^2.19.0" + source-map "~0.6.1" + source-map-support "~0.5.10" + +test-exclude@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" + integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + read-pkg-up "^4.0.0" + require-main-filename "^2.0.0" + +text-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-2.0.0.tgz#43eabd1b495482fae4a2bf65e5f56c29f69220f6" + integrity sha512-F91ZqLgvi1E0PdvmxMgp+gcf6q8fMH7mhdwWfzXnl1k+GbpQDmi8l7DzLC5JTASKbwpY3TfxajAUzAXcv2NmsQ== + +text-table@0.2.0, text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.0" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.0.tgz#e69e38a1babe969b0108207978b9f62b88604839" + integrity sha1-5p44obq+lpsBCCB5eLn2K4hgSDk= + dependencies: + any-promise "^1.0.0" + +throat@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" + integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= + +through2@^2.0.0, through2@^2.0.2: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through2@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.1.tgz#39276e713c3302edf9e388dd9c812dd3b825bd5a" + integrity sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww== + dependencies: + readable-stream "2 || 3" + +through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +thunky@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.3.tgz#f5df732453407b09191dae73e2a8cc73f381a826" + integrity sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow== + +timed-out@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + +timers-browserify@^2.0.4: + version "2.0.10" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" + integrity sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg== + dependencies: + setimmediate "^1.0.4" + +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + +tiny-invariant@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.4.tgz#346b5415fd93cb696b0c4e8a96697ff590f92463" + integrity sha512-lMhRd/djQJ3MoaHEBrw8e2/uM4rs9YMNk0iOr8rHQ0QdbM7D4l0gFl3szKdeixrlyfm9Zqi4dxHCM2qVG8ND5g== + +tiny-invariant@^1.0.4, tiny-invariant@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.6.tgz#b3f9b38835e36a41c843a3b0907a5a7b3755de73" + integrity sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA== + +tiny-warning@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.2.tgz#1dfae771ee1a04396bdfde27a3adcebc6b648b28" + integrity sha512-rru86D9CpQRLvsFG5XFdy0KdLAvjdQDyZCsRcuu60WtzFylDM3eAWSxEVz5kzL2Gp544XiUvPbVKtOA/txLi9Q== + +tiny-warning@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== + dependencies: + nopt "~1.0.10" + +tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== + dependencies: + psl "^1.1.24" + punycode "^1.4.1" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= + dependencies: + punycode "^2.1.0" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= + +trim-newlines@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" + integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= + +trim-off-newlines@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" + integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= + +trough@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.3.tgz#e29bd1614c6458d44869fc28b255ab7857ef7c24" + integrity sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw== + +ts-pnp@1.1.2, ts-pnp@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.2.tgz#be8e4bfce5d00f0f58e0666a82260c34a57af552" + integrity sha512-f5Knjh7XCyRIzoC/z1Su1yLLRrPrFCgtUAh/9fCSP6NKbATwpOL1+idQVXQokK9GRFURn/jYPGPfegIctwunoA== + +tslib@^1.8.1, tslib@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== + +tslib@^1.9.3: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + +tsutils@^3.7.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.10.0.tgz#6f1c95c94606e098592b0dff06590cf9659227d6" + integrity sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q== + dependencies: + tslib "^1.8.1" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" + integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ== + +type-fest@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.5.2.tgz#d6ef42a0356c6cd45f49485c3b6281fc148e48a2" + integrity sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw== + +type-is@^1.6.16, type-is@~1.6.16: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +typeface-roboto-mono@^0.0.54: + version "0.0.54" + resolved "https://registry.yarnpkg.com/typeface-roboto-mono/-/typeface-roboto-mono-0.0.54.tgz#78e706e0a4158b2b670ed3cdf2269e7a54e671cd" + integrity sha512-UjE3Y4Bk7DRA3m9aCN4pp8OOGFEYbJH2wK1Qj3Xv2TjEqEJo+oEmQcXwnt1/DKSYAWdKxSL3D/4ida7OoBFkwQ== + +typeface-roboto@^0.0.54: + version "0.0.54" + resolved "https://registry.yarnpkg.com/typeface-roboto/-/typeface-roboto-0.0.54.tgz#8f02c9a18d1cfa7f49381a6ff0d21ff061f38ad2" + integrity sha512-sOFA1FXgP0gOgBYlS6irwq6hHYA370KE3dPlgYEJHL3PJd5X8gQE0RmL79ONif6fL5JZuGDj+rtOrFeOqz5IZQ== + +typescript@^3.2.2: + version "3.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" + integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== + +typescript@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.2.tgz#a09e1dc69bc9551cadf17dba10ee42cf55e5d56c" + integrity sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA== + +ua-parser-js@^0.7.18: + version "0.7.20" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.20.tgz#7527178b82f6a62a0f243d1f94fd30e3e3c21098" + integrity sha512-8OaIKfzL5cpx8eCMAhhvTlft8GYF8b2eQr6JkCyVdrgjcytyOmPCXrqXFcUnhonRpLlh5yxEZVohm6mzaowUOw== + +uglify-js@3.4.x: + version "3.4.10" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" + integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== + dependencies: + commander "~2.19.0" + source-map "~0.6.1" + +uglify-js@^3.1.4: + version "3.5.11" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.5.11.tgz#833442c0aa29b3a7d34344c7c63adaa3f3504f6a" + integrity sha512-izPJg8RsSyqxbdnqX36ExpbH3K7tDBsAU/VfNv89VkMFy3z39zFjunQGsSHOlGlyIfGLGprGeosgQno3bo2/Kg== + dependencies: + commander "~2.20.0" + source-map "~0.6.1" + +uid-number@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= + +umask@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" + integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= + +undefsafe@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.2.tgz#225f6b9e0337663e0d8e7cfd686fc2836ccace76" + integrity sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY= + dependencies: + debug "^2.2.0" + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" + integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" + integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== + +unified@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-7.1.0.tgz#5032f1c1ee3364bd09da12e27fdd4a7553c7be13" + integrity sha512-lbk82UOIGuCEsZhPj8rNAkXSDXd6p0QLzIuSsCdxrqnqU56St4eyOB+AlXsVgVeRmetPTYydIuvFfpDIed8mqw== + dependencies: + "@types/unist" "^2.0.0" + "@types/vfile" "^3.0.0" + bail "^1.0.0" + extend "^3.0.0" + is-plain-obj "^1.1.0" + trough "^1.0.0" + vfile "^3.0.0" + x-is-string "^0.1.0" + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ= + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= + +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.1.tgz#5e9edc6d1ce8fb264db18a507ef9bd8544451ca6" + integrity sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg== + dependencies: + imurmurhash "^0.1.4" + +unique-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" + integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo= + dependencies: + crypto-random-string "^1.0.0" + +unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6" + integrity sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ== + +unist-util-stringify-position@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.0.tgz#4c452c0dbcbc509f7bcd366e9a8afd646f9d51ae" + integrity sha512-Uz5negUTrf9zm2ZT2Z9kdOL7Mr7FJLyq3ByqagUi7QZRVK1HnspVazvSqwHt73jj7APHtpuJ4K110Jm8O6/elw== + dependencies: + "@types/unist" "^2.0.2" + +universal-user-agent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-3.0.0.tgz#4cc88d68097bffd7ac42e3b7c903e7481424b4b9" + integrity sha512-T3siHThqoj5X0benA5H0qcDnrKGXzU8TKoX15x/tQHw1hQBvIEBHjxQ2klizYsqBOO/Q+WuxoQUihadeeqDnoA== + dependencies: + os-name "^3.0.0" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +unzip-response@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" + integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= + +upath@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" + integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== + +update-notifier@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" + integrity sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw== + dependencies: + boxen "^1.2.1" + chalk "^2.0.1" + configstore "^3.0.0" + import-lazy "^2.1.0" + is-ci "^1.0.10" + is-installed-globally "^0.1.0" + is-npm "^1.0.0" + latest-version "^3.0.0" + semver-diff "^2.0.0" + xdg-basedir "^3.0.0" + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +urijs@^1.19.0: + version "1.19.1" + resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.1.tgz#5b0ff530c0cbde8386f6342235ba5ca6e995d25a" + integrity sha512-xVrGVi94ueCJNrBSTjWqjvtgvl3cyOTThp2zaMaFNGp3F542TR6sM3f2o8RqZl+AwteClSVmoCyt0ka4RjQOQg== + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-loader@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.2.tgz#b971d191b83af693c5e3fea4064be9e1f2d7f8d8" + integrity sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg== + dependencies: + loader-utils "^1.1.0" + mime "^2.0.3" + schema-utils "^1.0.0" + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-parse@^1.4.3: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url-template@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21" + integrity sha1-/FZaPMy/93MMd19WQflVV5FDnyE= + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use-http@^0.1.79: + version "0.1.79" + resolved "https://registry.yarnpkg.com/use-http/-/use-http-0.1.79.tgz#071867f301c8554a0294ecdbea98f1d928093651" + integrity sha512-/BmLS2pAYamIIlQWAerLIbe0K+kjZUX6YD6sCfHuXGnRjCZTMeKCjX+/8kz5JbSIXmgjgi8fthRVr/FhpFe35A== + dependencies: + use-ssr "^1.0.18" + +use-memo-one@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.1.tgz#39e6f08fe27e422a7d7b234b5f9056af313bd22c" + integrity sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ== + +use-ssr@^1.0.18: + version "1.0.18" + resolved "https://registry.yarnpkg.com/use-ssr/-/use-ssr-1.0.18.tgz#a7bb7d96b128bc0940b77862cfaf69c40e74da1d" + integrity sha512-i+1J4Gbh9Vd2k/CEUoBjVDJfpNvMfq0+YulG+woBQwqbxvBAB89OScemseEULMQDIICiDbbpEBjUnqAiprVJyw== + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util-promisify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/util-promisify/-/util-promisify-2.1.0.tgz#3c2236476c4d32c5ff3c47002add7c13b9a82a53" + integrity sha1-PCI2R2xNMsX/PEcAKt18E7moKlM= + dependencies: + object.getownpropertydescriptors "^2.0.3" + +util.promisify@1.0.0, util.promisify@^1.0.0, util.promisify@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== + dependencies: + inherits "2.0.3" + +utila@^0.4.0, utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.0.1, uuid@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +validate-npm-package-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" + integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= + dependencies: + builtins "^1.0.3" + +value-equal@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7" + integrity sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw== + +vary@^1.1.2, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +vendors@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.2.tgz#7fcb5eef9f5623b156bcea89ec37d63676f21801" + integrity sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vfile-message@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-1.1.1.tgz#5833ae078a1dfa2d96e9647886cd32993ab313e1" + integrity sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA== + dependencies: + unist-util-stringify-position "^1.1.1" + +vfile-message@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.0.tgz#750bbb86fe545988a67e899b329bbcabb73edef6" + integrity sha512-YS6qg6UpBfIeiO+6XlhPOuJaoLvt1Y9g2cmlwqhBOOU0XRV8j5RLeoz72t6PWLvNXq3EBG1fQ05wNPrUoz0deQ== + dependencies: + "@types/unist" "^2.0.2" + unist-util-stringify-position "^1.1.1" + +vfile@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-3.0.1.tgz#47331d2abe3282424f4a4bb6acd20a44c4121803" + integrity sha512-y7Y3gH9BsUSdD4KzHsuMaCzRjglXN0W2EcMf0gpvu6+SbsGhMje7xDc8AEoeXy6mIwCKMI6BkjMsRjzQbhMEjQ== + dependencies: + is-buffer "^2.0.0" + replace-ext "1.0.0" + unist-util-stringify-position "^1.0.0" + vfile-message "^1.0.0" + +vfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.0.0.tgz#ebf3b48af9fcde524d5e08d5f75812058a5f78ad" + integrity sha512-WMNeHy5djSl895BqE86D7WqA0Ie5fAIeGCa7V1EqiXyJg5LaGch2SUaZueok5abYQGH6mXEAsZ45jkoILIOlyA== + dependencies: + "@types/unist" "^2.0.2" + is-buffer "^2.0.0" + replace-ext "1.0.0" + unist-util-stringify-position "^2.0.0" + vfile-message "^2.0.0" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + integrity sha1-XX6kW7755Kb/ZflUOOCofDV9WnM= + dependencies: + indexof "0.0.1" + +w3c-hr-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" + integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= + dependencies: + browser-process-hrtime "^0.1.2" + +w3c-xmlserializer@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" + integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== + dependencies: + domexception "^1.0.1" + webidl-conversions "^4.0.2" + xml-name-validator "^3.0.0" + +walker@^1.0.7, walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= + dependencies: + makeerror "1.0.x" + +warning@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + +watchpack@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" + integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== + dependencies: + chokidar "^2.0.2" + graceful-fs "^4.1.2" + neo-async "^2.5.0" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +wcwidth@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + dependencies: + defaults "^1.0.3" + +web-namespaces@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.3.tgz#9bbf5c99ff0908d2da031f1d732492a96571a83f" + integrity sha512-r8sAtNmgR0WKOKOxzuSgk09JsHlpKlB+uHi937qypOu3PZ17UxPrierFKDye/uNHjNTTEshu5PId8rojIPj/tA== + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +webpack-dev-middleware@^3.5.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.6.2.tgz#f37a27ad7c09cd7dc67cd97655413abaa1f55942" + integrity sha512-A47I5SX60IkHrMmZUlB0ZKSWi29TZTcPz7cha1Z75yYOsgWh/1AcPmQEbC8ZIbU3A1ytSv1PMU0PyPz2Lmz2jg== + dependencies: + memory-fs "^0.4.1" + mime "^2.3.1" + range-parser "^1.0.3" + webpack-log "^2.0.0" + +webpack-dev-server@3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.2.1.tgz#1b45ce3ecfc55b6ebe5e36dab2777c02bc508c4e" + integrity sha512-sjuE4mnmx6JOh9kvSbPYw3u/6uxCLHNWfhWaIPwcXWsvWOPN+nc5baq4i9jui3oOBRXGonK9+OI0jVkaz6/rCw== + dependencies: + ansi-html "0.0.7" + bonjour "^3.5.0" + chokidar "^2.0.0" + compression "^1.5.2" + connect-history-api-fallback "^1.3.0" + debug "^4.1.1" + del "^3.0.0" + express "^4.16.2" + html-entities "^1.2.0" + http-proxy-middleware "^0.19.1" + import-local "^2.0.0" + internal-ip "^4.2.0" + ip "^1.1.5" + killable "^1.0.0" + loglevel "^1.4.1" + opn "^5.1.0" + portfinder "^1.0.9" + schema-utils "^1.0.0" + selfsigned "^1.9.1" + semver "^5.6.0" + serve-index "^1.7.2" + sockjs "0.3.19" + sockjs-client "1.3.0" + spdy "^4.0.0" + strip-ansi "^3.0.0" + supports-color "^6.1.0" + url "^0.11.0" + webpack-dev-middleware "^3.5.1" + webpack-log "^2.0.0" + yargs "12.0.2" + +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-manifest-plugin@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-2.0.4.tgz#e4ca2999b09557716b8ba4475fb79fab5986f0cd" + integrity sha512-nejhOHexXDBKQOj/5v5IZSfCeTO3x1Dt1RZEcGfBSul891X/eLIcIVH31gwxPDdsi2Z8LKKFGpM4w9+oTBOSCg== + dependencies: + fs-extra "^7.0.0" + lodash ">=3.5 <5" + tapable "^1.0.0" + +webpack-sources@^1.1.0, webpack-sources@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" + integrity sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@4.29.6: + version "4.29.6" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.29.6.tgz#66bf0ec8beee4d469f8b598d3988ff9d8d90e955" + integrity sha512-MwBwpiE1BQpMDkbnUUaW6K8RFZjljJHArC6tWQJoFm0oQtfoSebtg4Y7/QHnJ/SddtjYLHaKGX64CFjG5rehJw== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/wasm-edit" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + acorn "^6.0.5" + acorn-dynamic-import "^4.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" + chrome-trace-event "^1.0.0" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.0" + json-parse-better-errors "^1.0.2" + loader-runner "^2.3.0" + loader-utils "^1.1.0" + memory-fs "~0.4.1" + micromatch "^3.1.8" + mkdirp "~0.5.0" + neo-async "^2.5.0" + node-libs-browser "^2.0.0" + schema-utils "^1.0.0" + tapable "^1.1.0" + terser-webpack-plugin "^1.1.0" + watchpack "^1.5.0" + webpack-sources "^1.3.0" + +websocket-driver@>=0.5.1: + version "0.7.0" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb" + integrity sha1-DK+dLXVdk67gSdS90NP+LMoqJOs= + dependencies: + http-parser-js ">=0.4.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" + integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== + +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-fetch@3.0.0, whatwg-fetch@>=0.10.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" + integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== + +whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^6.4.1: + version "6.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" + integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +whatwg-url@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd" + integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@1, which@^1.2.9, which@^1.3.0, which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +widest-line@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" + integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== + dependencies: + string-width "^2.1.1" + +windows-release@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f" + integrity sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA== + dependencies: + execa "^1.0.0" + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + +workbox-background-sync@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-4.3.1.tgz#26821b9bf16e9e37fd1d640289edddc08afd1950" + integrity sha512-1uFkvU8JXi7L7fCHVBEEnc3asPpiAL33kO495UMcD5+arew9IbKW2rV5lpzhoWcm/qhGB89YfO4PmB/0hQwPRg== + dependencies: + workbox-core "^4.3.1" + +workbox-broadcast-update@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-4.3.1.tgz#e2c0280b149e3a504983b757606ad041f332c35b" + integrity sha512-MTSfgzIljpKLTBPROo4IpKjESD86pPFlZwlvVG32Kb70hW+aob4Jxpblud8EhNb1/L5m43DUM4q7C+W6eQMMbA== + dependencies: + workbox-core "^4.3.1" + +workbox-build@^4.2.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-4.3.1.tgz#414f70fb4d6de47f6538608b80ec52412d233e64" + integrity sha512-UHdwrN3FrDvicM3AqJS/J07X0KXj67R8Cg0waq1MKEOqzo89ap6zh6LmaLnRAjpB+bDIz+7OlPye9iii9KBnxw== + dependencies: + "@babel/runtime" "^7.3.4" + "@hapi/joi" "^15.0.0" + common-tags "^1.8.0" + fs-extra "^4.0.2" + glob "^7.1.3" + lodash.template "^4.4.0" + pretty-bytes "^5.1.0" + stringify-object "^3.3.0" + strip-comments "^1.0.2" + workbox-background-sync "^4.3.1" + workbox-broadcast-update "^4.3.1" + workbox-cacheable-response "^4.3.1" + workbox-core "^4.3.1" + workbox-expiration "^4.3.1" + workbox-google-analytics "^4.3.1" + workbox-navigation-preload "^4.3.1" + workbox-precaching "^4.3.1" + workbox-range-requests "^4.3.1" + workbox-routing "^4.3.1" + workbox-strategies "^4.3.1" + workbox-streams "^4.3.1" + workbox-sw "^4.3.1" + workbox-window "^4.3.1" + +workbox-cacheable-response@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-4.3.1.tgz#f53e079179c095a3f19e5313b284975c91428c91" + integrity sha512-Rp5qlzm6z8IOvnQNkCdO9qrDgDpoPNguovs0H8C+wswLuPgSzSp9p2afb5maUt9R1uTIwOXrVQMmPfPypv+npw== + dependencies: + workbox-core "^4.3.1" + +workbox-core@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-4.3.1.tgz#005d2c6a06a171437afd6ca2904a5727ecd73be6" + integrity sha512-I3C9jlLmMKPxAC1t0ExCq+QoAMd0vAAHULEgRZ7kieCdUd919n53WC0AfvokHNwqRhGn+tIIj7vcb5duCjs2Kg== + +workbox-expiration@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-4.3.1.tgz#d790433562029e56837f341d7f553c4a78ebe921" + integrity sha512-vsJLhgQsQouv9m0rpbXubT5jw0jMQdjpkum0uT+d9tTwhXcEZks7qLfQ9dGSaufTD2eimxbUOJfWLbNQpIDMPw== + dependencies: + workbox-core "^4.3.1" + +workbox-google-analytics@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-4.3.1.tgz#9eda0183b103890b5c256e6f4ea15a1f1548519a" + integrity sha512-xzCjAoKuOb55CBSwQrbyWBKqp35yg1vw9ohIlU2wTy06ZrYfJ8rKochb1MSGlnoBfXGWss3UPzxR5QL5guIFdg== + dependencies: + workbox-background-sync "^4.3.1" + workbox-core "^4.3.1" + workbox-routing "^4.3.1" + workbox-strategies "^4.3.1" + +workbox-navigation-preload@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-4.3.1.tgz#29c8e4db5843803b34cd96dc155f9ebd9afa453d" + integrity sha512-K076n3oFHYp16/C+F8CwrRqD25GitA6Rkd6+qAmLmMv1QHPI2jfDwYqrytOfKfYq42bYtW8Pr21ejZX7GvALOw== + dependencies: + workbox-core "^4.3.1" + +workbox-precaching@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-4.3.1.tgz#9fc45ed122d94bbe1f0ea9584ff5940960771cba" + integrity sha512-piSg/2csPoIi/vPpp48t1q5JLYjMkmg5gsXBQkh/QYapCdVwwmKlU9mHdmy52KsDGIjVaqEUMFvEzn2LRaigqQ== + dependencies: + workbox-core "^4.3.1" + +workbox-range-requests@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-4.3.1.tgz#f8a470188922145cbf0c09a9a2d5e35645244e74" + integrity sha512-S+HhL9+iTFypJZ/yQSl/x2Bf5pWnbXdd3j57xnb0V60FW1LVn9LRZkPtneODklzYuFZv7qK6riZ5BNyc0R0jZA== + dependencies: + workbox-core "^4.3.1" + +workbox-routing@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-4.3.1.tgz#a675841af623e0bb0c67ce4ed8e724ac0bed0cda" + integrity sha512-FkbtrODA4Imsi0p7TW9u9MXuQ5P4pVs1sWHK4dJMMChVROsbEltuE79fBoIk/BCztvOJ7yUpErMKa4z3uQLX+g== + dependencies: + workbox-core "^4.3.1" + +workbox-strategies@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-4.3.1.tgz#d2be03c4ef214c115e1ab29c9c759c9fe3e9e646" + integrity sha512-F/+E57BmVG8dX6dCCopBlkDvvhg/zj6VDs0PigYwSN23L8hseSRwljrceU2WzTvk/+BSYICsWmRq5qHS2UYzhw== + dependencies: + workbox-core "^4.3.1" + +workbox-streams@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-4.3.1.tgz#0b57da70e982572de09c8742dd0cb40a6b7c2cc3" + integrity sha512-4Kisis1f/y0ihf4l3u/+ndMkJkIT4/6UOacU3A4BwZSAC9pQ9vSvJpIi/WFGQRH/uPXvuVjF5c2RfIPQFSS2uA== + dependencies: + workbox-core "^4.3.1" + +workbox-sw@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-4.3.1.tgz#df69e395c479ef4d14499372bcd84c0f5e246164" + integrity sha512-0jXdusCL2uC5gM3yYFT6QMBzKfBr2XTk0g5TPAV4y8IZDyVNDyj1a8uSXy3/XrvkVTmQvLN4O5k3JawGReXr9w== + +workbox-webpack-plugin@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-4.2.0.tgz#c94c3f69ff39c8a5b0c7e6bebc382cb53410a63d" + integrity sha512-YZsiA+y/ns/GdWRaBsfYv8dln1ebWtGnJcTOg1ppO0pO1tScAHX0yGtHIjndxz3L/UUhE8b0NQE9KeLNwJwA5A== + dependencies: + "@babel/runtime" "^7.0.0" + json-stable-stringify "^1.0.1" + workbox-build "^4.2.0" + +workbox-window@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-4.3.1.tgz#ee6051bf10f06afa5483c9b8dfa0531994ede0f3" + integrity sha512-C5gWKh6I58w3GeSc0wp2Ne+rqVw8qwcmZnQGpjiek8A2wpbxSJb1FdCoQVO+jDJs35bFgo/WETgl1fqgsxN0Hg== + dependencies: + workbox-core "^4.3.1" + +worker-farm@^1.5.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== + dependencies: + errno "~0.1.7" + +worker-rpc@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5" + integrity sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg== + dependencies: + microevent.ts "~0.1.1" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" + integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write-file-atomic@^2.0.0, write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: + version "2.4.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write-json-file@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-2.3.0.tgz#2b64c8a33004d54b8698c76d585a77ceb61da32f" + integrity sha1-K2TIozAE1UuGmMdtWFp3zrYdoy8= + dependencies: + detect-indent "^5.0.0" + graceful-fs "^4.1.2" + make-dir "^1.0.0" + pify "^3.0.0" + sort-keys "^2.0.0" + write-file-atomic "^2.0.0" + +write-json-file@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" + integrity sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ== + dependencies: + detect-indent "^5.0.0" + graceful-fs "^4.1.15" + make-dir "^2.1.0" + pify "^4.0.1" + sort-keys "^2.0.0" + write-file-atomic "^2.4.2" + +write-pkg@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-3.2.0.tgz#0e178fe97820d389a8928bc79535dbe68c2cff21" + integrity sha512-tX2ifZ0YqEFOF1wjRW2Pk93NLsj02+n1UP5RvO6rCs0K6R2g1padvf006cY74PQJKMGS2r42NK7FD0dG6Y6paw== + dependencies: + sort-keys "^2.0.0" + write-json-file "^2.2.0" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@^5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + +ws@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + dependencies: + async-limiter "~1.0.0" + +x-is-string@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" + integrity sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI= + +xdg-basedir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" + integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +xmlchars@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-1.3.1.tgz#1dda035f833dbb4f86a0c28eaa6ca769214793cf" + integrity sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw== + +xregexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020" + integrity sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg== + +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= + +"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" + integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== + +yargs-parser@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" + integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== + dependencies: + camelcase "^4.1.0" + +yargs-parser@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" + integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@12.0.2: + version "12.0.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.2.tgz#fe58234369392af33ecbef53819171eff0f5aadc" + integrity sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ== + dependencies: + cliui "^4.0.0" + decamelize "^2.0.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^3.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^10.1.0" + +yargs@^12.0.1, yargs@^12.0.2: + version "12.0.5" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" + integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== + dependencies: + cliui "^4.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^3.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^11.1.1" + +ylru@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f" + integrity sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ== From cc48c4334bd5b4971b6ea6c08f476dccaa30c568 Mon Sep 17 00:00:00 2001 From: Hao Yuan <hao1939@gmail.com> Date: Wed, 18 Sep 2019 05:22:35 +0000 Subject: [PATCH 595/595] add azure-pipeline --- azure-pipelines.yml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 000000000..f87ed15ca --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,40 @@ +# Starter pipeline +# Start with a minimal pipeline that you can customize to build and deploy your code. +# Add steps that build, run tests, deploy, and more: +# https://aka.ms/yaml + +trigger: +- dltsdev + +pool: + name: 'DLTS-Platform' + +# container: ubuntu:18.04 + +variables: { SUBSCRIPTION_NAME: "'Bing DLTS'" } + +steps: +- script: | + cd src/ClusterBootstrap/ + sudo ./install_prerequisites.sh + az account set --subscription $(SUBSCRIPTION_NAME) + az account list | grep -A5 -B5 '"isDefault": true' + displayName: 'Install prerequisites' + +- script: | + cd src/ClusterBootstrap/ + cp /mnt/_work/dlts_ci_config.yaml config.yaml + ./bash_step_by_step_deploy.sh + displayName: 'Deploy DLWorkspace' + +- script: | + echo TODO: verify the cluster is ready! + displayName: 'Verify deployment' + +- script: | + echo TODO: RUN functional tests! + displayName: 'Functional tests' + +- script: | + echo TODO: cleanup the deployment! + displayName: 'Cleanup'