# MIT License
#
# Copyright (c) 2020 Genesis Cloud Ltd. <opensource@genesiscloud.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Authors:
# Oz Tiram <otiram@genesiscloud.com>
"""
An example script to show how to start a Genesis Cloud GPU instance
with custom user data to install the NVIDIA GPU driver.
Grab your API key from the UI and save it in a safe place.
on the shell before running this script
$ export GENESISCLOUD_API_KEY=secretkey
"""
import os
import textwrap
import time
import subprocess as sp
from genesiscloud.client import Client, INSTANCE_TYPES
def simple_startup_script():
"""see the documentation of cloud init"""
return textwrap.dedent("""
#cloud-config
hostname: mytestubuntu
runcmd:
- [ "apt", "install", "-y", "vim" ]
""")
def get_startup_script():
return """#!/bin/bash
set -eux
IS_INSTALLED=false
NVIDIA_SHORT_VERSION=430
manual_fetch_install() {
__nvidia_full_version="430_430.50-0ubuntu2"
for i in $(seq 1 5)
do
echo "Connecting to http://archive.ubuntu.com site for $i time"
if curl -s --head --request GET http://archive.ubuntu.com/ubuntu/pool/restricted/n/nvidia-graphics-drivers-"${NVIDIA_SHORT_VERSION}" | grep "HTTP/1.1" > /dev/null ;
then
echo "Connected to http://archive.ubuntu.com. Start downloading and installing the NVIDIA driver..."
__tempdir="$(mktemp -d)"
apt-get install -y --no-install-recommends "linux-headers-$(uname -r)" dkms
wget -P "${__tempdir}" http://archive.ubuntu.com/ubuntu/pool/restricted/n/nvidia-graphics-drivers-${NVIDIA_SHORT_VERSION}/nvidia-kernel-common-${__nvidia_full_version}_amd64.deb
wget -P "${__tempdir}" http://archive.ubuntu.com/ubuntu/pool/restricted/n/nvidia-graphics-drivers-${NVIDIA_SHORT_VERSION}/nvidia-kernel-source-${__nvidia_full_version}_amd64.deb
wget -P "${__tempdir}" http://archive.ubuntu.com/ubuntu/pool/restricted/n/nvidia-graphics-drivers-${NVIDIA_SHORT_VERSION}/nvidia-dkms-${__nvidia_full_version}_amd64.deb
dpkg -i "${__tempdir}"/nvidia-kernel-common-${__nvidia_full_version}_amd64.deb "${__tempdir}"/nvidia-kernel-source-${__nvidia_full_version}_amd64.deb "${__tempdir}"/nvidia-dkms-${__nvidia_full_version}_amd64.deb
wget -P "${__tempdir}" http://archive.ubuntu.com/ubuntu/pool/restricted/n/nvidia-graphics-drivers-${NVIDIA_SHORT_VERSION}/nvidia-utils-${__nvidia_full_version}_amd64.deb
wget -P "${__tempdir}" http://archive.ubuntu.com/ubuntu/pool/restricted/n/nvidia-graphics-drivers-${NVIDIA_SHORT_VERSION}/libnvidia-compute-${__nvidia_full_version}_amd64.deb
dpkg -i "${__tempdir}"/nvidia-utils-${__nvidia_full_version}_amd64.deb "${__tempdir}"/libnvidia-compute-${__nvidia_full_version}_amd64.deb
IS_INSTALLED=true
rm -r "${__tempdir}"
break
fi
sleep 2
done
}
apt_fetch_install() {
add-apt-repository -s -u -y restricted
# Ubuntu has only a single version in the repository marked as "latest" of
# this series.
for _ in $(seq 1 5)
do
if apt-get install -y --no-install-recommends nvidia-utils-${NVIDIA_SHORT_VERSION} libnvidia-compute-${NVIDIA_SHORT_VERSION} \
nvidia-kernel-common-${NVIDIA_SHORT_VERSION} \
nvidia-kernel-source-${NVIDIA_SHORT_VERSION} \
nvidia-dkms-${NVIDIA_SHORT_VERSION} \
"linux-headers-$(uname -r)" dkms; then
IS_INSTALLED=true
break
fi
sleep 2
done
}
main() {
apt-get update
if grep xenial /etc/os-release; then
manual_fetch_install
else
apt_fetch_install
fi
# remove the module if it is inserted, blacklist it
rmmod nouveau || echo "nouveau kernel module not loaded ..."
echo "blacklist nouveau" > /etc/modprobe.d/nouveau.conf
# log insertion of the nvidia module
# this should always succeed on customer instances
if modprobe -vi nvidia; then
nvidia-smi
modinfo nvidia
gpu_found=true
else
gpu_found=false
fi
if [ "${IS_INSTALLED}" = true ]; then
echo "NVIDIA driver has been successfully installed."
else
echo "NVIDIA driver has NOT been installed."
fi
if [ "${gpu_found}" ]; then
echo "NVIDIA GPU device is found and ready"
else
echo "WARNING: NVIDIA GPU device is not found or is failed"
fi
}
main
"""
def create_instance():
client = Client(os.getenv("GENESISCLOUD_API_KEY"))
# before we continue to create objects, we check that we can communicate
# with the API, if the connect method does not succeed it will throw an
# error and the script will terminate
if client.connect():
pass
# To create an instance you will need an SSH public key.
# Upload it via the Web UI, you can now find it with.
# replace this to match your key
SSHKEYNAME = 'YourKeyName'
# genesiscloud.client.Resource.find methods returns generators - that is,
# they are lazy per-default.
sshkey_gen = client.SSHKeys.find({"name": SSHKEYNAME})
sshkey = list(sshkey_gen)[0]
# You need to tell the client which OS should be used for your instance
# One can use a snapshot or a base-os to create a new instance
ubuntu_18 = [image for image in client.Images.find({"name": 'Ubuntu 18.04'})][0]
# choose the most simple instance type
# to see the instance properties, use
# list(INSTANCE_TYPES.items())[0]
#
# ('vcpu-4_memory-12g_disk-80g_nvidia1080ti-1',
# {'vCPUs': 4, 'RAM': 12, 'Disk': 80, 'GPU': 1})
instace_type = list(INSTANCE_TYPES.keys())[0]
# To create an instace use Instances.create
# You must pass a ssh key to SSH into the machine. Currently, only one
# SSH key is supported. If you need more use the command
# `ssh-import-id-gh oz123`
# it can fetch public key from github.com/oz123.keys
# *Obviously* __replace__ my user name with YOURS or anyone you TRUST.
# You should put this in the user_data script. You can add this in the
# text block that the function `get_startup_script` returns.
# NOTE:
# you can also create an instance with SSH password enabled, but you should
# prefer SSH key authentication. If you choose to use password, you should
# not pass ssh_keys
my_instance = client.Instances.create(
name="demo",
hostname="demo",
ssh_keys=[sshkey.id], # comment this to enable password
image=ubuntu_18.id,
type=instace_type,
metadata={"startup_script":
simple_startup_script()},
#password="yourSekretPassword#12!"
)
# my_instance is a dictionary containing information about the instance
# that was just created.
print(my_instance)
while my_instance['status'] != 'active':
time.sleep(1)
my_instance = client.Instances.get(my_instance.id)
print(f"{my_instance['status']}\r", end="")
print("")
# yay! the instance is active
# let's ssh to the public IP of the instance
public_ip = my_instance.public_ip
print(f"The ssh address of the Instance is: {public_ip}")
# wait for ssh to become available, this returns exit code other
# than 0 as long the ssh connection isn't available
while sp.run(
("ssh -l ubuntu -o StrictHostKeyChecking=accept-new "
"-o ConnectTimeout=50 "
f"{public_ip} hostname"), shell=True).returncode:
time.sleep(1)
print("Congratulations! You genesiscloud instance has been created!")
print("You can ssh to it with:")
print(f"ssh -l ubuntu {public_ip}")
print("Some interesting commands to try at first:")
print("cloud-init stats # if this is still running, NVIDIA driver is still"
" installing")
print("use the following to see cloud-init output in real time:")
print("sudo tail -f /var/log/cloud-init-output.log")
return my_instance
def destroy(instance_id):
# finally destory this instance, when you no longer need it
client = Client(os.getenv("GENESISCLOUD_API_KEY"))
client.Instances.delete(id=instance_id)
if __name__ == "__main__":
instance = create_instance()
instance_id = instance['id']
# destroy(instance_id)