From f63e5ebd8d38382943df736b5dced2807635fc90 Mon Sep 17 00:00:00 2001 From: Sebastian Rieger Date: Fri, 12 Apr 2024 13:53:47 +0200 Subject: [PATCH] adapted demos for new Charmed OpenStack environment --- CloudComp-openrc | 47 +++++ demo1_getting_started.py | 144 +++++++++++++ demo2_instance_with_init_script.py | 268 ++++++++++++++++++++++++ demo3_microservice.py | 317 +++++++++++++++++++++++++++++ destroy_all_demo_instances.py | 126 ++++++++++++ root-ca.crt | 21 ++ 6 files changed, 923 insertions(+) create mode 100644 CloudComp-openrc create mode 100644 demo1_getting_started.py create mode 100644 demo2_instance_with_init_script.py create mode 100644 demo3_microservice.py create mode 100644 destroy_all_demo_instances.py create mode 100644 root-ca.crt diff --git a/CloudComp-openrc b/CloudComp-openrc new file mode 100644 index 0000000..402cbdf --- /dev/null +++ b/CloudComp-openrc @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# To use an OpenStack cloud you need to authenticate against the Identity +# service named keystone, which returns a **Token** and **Service Catalog**. +# The catalog contains the endpoints for all services the user/tenant has +# access to - such as Compute, Image Service, Identity, Object Storage, Block +# Storage, and Networking (code-named nova, glance, keystone, swift, +# cinder, and neutron). + +# unset all OPENSTACK ENV VARS +unset $(env | grep OS_ | cut -d "=" -f 1) + +export OS_CACERT=./root-ca.crt +export GROUP_NUMBER=0 + +# +# *NOTE*: Using the 3 *Identity API* does not necessarily mean any other +# OpenStack API is version 3. For example, your cloud provider may implement +# Image API v1.1, Block Storage API v2, and Compute API v2.0. OS_AUTH_URL is +# only for the Identity API served through keystone. +export OS_AUTH_URL=https://10.32.4.182:5000/v3 +# With the addition of Keystone we have standardized on the term **project** +# as the entity that owns the resources. +#export OS_PROJECT_ID=bba62cf6bf0b447491829d207e1b05f9 +export OS_PROJECT_NAME="CloudComp$GROUP_NUMBER" +unset OS_DOMAIN_NAME +export OS_USER_DOMAIN_NAME="Default" +if [ -z "$OS_USER_DOMAIN_NAME" ]; then unset OS_USER_DOMAIN_NAME; fi +export OS_PROJECT_DOMAIN_ID="default" +if [ -z "$OS_PROJECT_DOMAIN_ID" ]; then unset OS_PROJECT_DOMAIN_ID; fi +# unset v2.0 items in case set +unset OS_TENANT_ID +unset OS_TENANT_NAME +# In addition to the owning entity (tenant), OpenStack stores the entity +# performing the action as the **user**. +export OS_USERNAME="CloudComp$GROUP_NUMBER" +# With Keystone you pass the keystone password. +#echo "Please enter your OpenStack Password for project $OS_PROJECT_NAME as user $OS_USERNAME: " +#read -sr OS_PASSWORD_INPUT +#export OS_PASSWORD=$OS_PASSWORD_INPUT +export OS_PASSWORD="demo" +# If your configuration has multiple regions, we set that information here. +# OS_REGION_NAME is optional and only valid in certain environments. +export OS_REGION_NAME="RegionOne" +# Don't leave a blank variable, unset it if it was empty +if [ -z "$OS_REGION_NAME" ]; then unset OS_REGION_NAME; fi +export OS_INTERFACE=public +export OS_IDENTITY_API_VERSION=3 \ No newline at end of file diff --git a/demo1_getting_started.py b/demo1_getting_started.py new file mode 100644 index 0000000..d8dd3ab --- /dev/null +++ b/demo1_getting_started.py @@ -0,0 +1,144 @@ +"""Example for Cloud Computing Course Master AI / GSD""" + +# This script demonstrates how to use libcloud to start an instance in an OpenStack environment. +# The script will start an instance, list all instances, and then destroy the instance again. +# +# The script uses the libcloud library to interact with the OpenStack API. +# Need to install libcloud first: pip install apache-libcloud +# +# libCloud: https://libcloud.apache.org/ +# libCloud API documentation: https://libcloud.readthedocs.io/en/latest/ +# OpenStack API documentation: https://developer.openstack.org/ +# this code was initially based on the former tutorial: +# https://developer.openstack.org/firstapp-libcloud/ + +# Only needed for the password prompt: +# import getpass + +from libcloud.compute.providers import get_driver +from libcloud.compute.types import Provider + +# For our new Charmed OpenStack private cloud, we need to specify the path to the +# root CA certificate +import libcloud.security +libcloud.security.CA_CERTS_PATH = ['./root-ca.crt'] +# Disable SSL certificate verification (not recommended for production) +# libcloud.security.VERIFY_SSL_CERT = False + +# Please use 1-29 for 0 in the following variable to specify your group number. +# (will be used for the username, project etc., as coordinated in the lab sessions) + +GROUP_NUMBER = 0 + + +############################################################################################### +# +# no changes necessary below this line in this example +# +############################################################################################### + +# web service endpoint of the private cloud infrastructure +# auth_url = 'https://private-cloud.informatik.hs-fulda.de:5000' +AUTH_URL = 'https://10.32.4.182:5000' +# auth_url = 'https://private-cloud2.informatik.hs-fulda.de:5000' +# your username in OpenStack +AUTH_USERNAME = 'CloudComp' + str(GROUP_NUMBER) +# your project in OpenStack +PROJECT_NAME = 'CloudComp' + str(GROUP_NUMBER) +# A network in the project the started instance will be attached to +PROJECT_NETWORK = 'CloudComp' + str(GROUP_NUMBER) + '-net' + +# The image to look for and use for the started instance +# ubuntu_image_name = "Ubuntu 18.04 - Bionic Beaver - 64-bit - Cloud Based Image" +UBUNTU_IMAGE_NAME = "auto-sync/ubuntu-jammy-22.04-amd64-server-20240319-disk1.img" + +# default region +REGION_NAME = 'RegionOne' +# domain to use, "default" for local accounts, formerly "hsfulda" for LDAP accounts etc. +# domain_name = "default" + + +def main(): # noqa: C901 pylint: disable=too-many-branches,too-many-statements,too-many-locals,missing-function-docstring + # get the password from user + # auth_password = getpass.getpass("Enter your OpenStack password:") + auth_password = "demo" + + # instantiate a connection to the OpenStack private cloud + # make sure to include ex_force_auth_version='3.x_password', as needed in our environment + provider = get_driver(Provider.OPENSTACK) + + print(f"Opening connection to {AUTH_URL} as {AUTH_USERNAME}...") + + conn = provider(AUTH_USERNAME, + auth_password, + ex_force_auth_url=AUTH_URL, + ex_force_auth_version='3.x_password', + ex_tenant_name=PROJECT_NAME, + ex_force_service_region=REGION_NAME) + # ex_domain_name=domain_name) + + print("Getting images and selecting desired one...") + print("=========================================================================") + + # get a list of images offered in the cloud context (e.g. Ubuntu 20.04, cirros, ...) + images = conn.list_images() + image = '' + for img in images: + if img.name == UBUNTU_IMAGE_NAME: + image = img + print(img) + + print("Getting flavors...") + print("=========================================================================") + + # get a list of flavors offered in the cloud context (e.g. m1.small, m1.medium, ...) + flavors = conn.list_sizes() + for flavor in flavors: + print(flavor) + + print("Selecting desired flavor...") + print("=========================================================================") + + # get the flavor with id 2 + flavor_id = '2' + flavor = conn.ex_get_size(flavor_id) + print(flavor) + + print("Selecting desired network...") + print("=========================================================================") + + # get a list of networks in the cloud context + networks = conn.ex_list_networks() + network = '' + for net in networks: + if net.name == PROJECT_NETWORK: + network = net + + print("Create instance 'testing'...") + print("=========================================================================") + + # create a new instance with the name "testing" + # make sure to provide networks (networks={network}) the instance should be attached to + instance_name = 'testing' + testing_instance = conn.create_node(name=instance_name, image=image, size=flavor, + networks={network}) + print(testing_instance) + + print("Showing all running instances...") + print("=========================================================================") + + # show all instances (running nodes) in the cloud context + instances = conn.list_nodes() + for instance in instances: + print(instance) + + print("Destroying instance...") + print("=========================================================================") + + # destroy the instance we have just created + conn.destroy_node(testing_instance) + + +# method that is called when the script is started from the command line +if __name__ == '__main__': + main() diff --git a/demo2_instance_with_init_script.py b/demo2_instance_with_init_script.py new file mode 100644 index 0000000..881ee9c --- /dev/null +++ b/demo2_instance_with_init_script.py @@ -0,0 +1,268 @@ +"""Example for Cloud Computing Course Master AI / GSD""" + +# This script demonstrates how to use libcloud to start an instance in an OpenStack environment. +# The script will create and install a new SSH key pair, create a security group, start an instance +# and deploy a demo app (faafo) using cloud-init and assign a floating IP to the instance. +# +# cloud-init is a multi-distribution package that handles early initialization of a cloud instance. +# It is supported by many major cloud providers, including OpenStack. +# cloud-init documentation: https://cloudinit.readthedocs.io/en/latest/ + +# Needed if the password should be prompted for: +# import getpass +import os +import sys + +from libcloud.compute.providers import get_driver +from libcloud.compute.types import Provider + +# For our new Charmed OpenStack private cloud, we need to specify the path to the root +# CA certificate +import libcloud.security +libcloud.security.CA_CERTS_PATH = ['./root-ca.crt'] +# Disable SSL certificate verification (not recommended for production) +# libcloud.security.VERIFY_SSL_CERT = False + +# Please use 1-29 as environment variable GROUP_NUMBER to specify your group number. +# (will be used for the username, project etc., as coordinated in the lab sessions) + +group_number = os.environ.get('GROUP_NUMBER') +if group_number is None: + sys.exit('Please set the GROUP_NUMBER environment variable to your group number,\n' + 'e.g., on Windows:\n' + ' "$env:GROUP_NUMBER=0" or "set GROUP_NUMBER=0"\n' + 'or on Linux/MacOS:\n' + ' "export GROUP_NUMBER=0" or "set GROUP_NUMBER=0"') + + +# web service endpoint of the private cloud infrastructure +# auth_url = 'https://private-cloud.informatik.hs-fulda.de:5000' +AUTH_URL = 'https://10.32.4.182:5000' +# auth_url = 'https://private-cloud2.informatik.hs-fulda.de:5000' +# your username in OpenStack +AUTH_USERNAME = 'CloudComp' + str(group_number) +print(f'Using username: {AUTH_USERNAME}\n') +# your project in OpenStack +PROJECT_NAME = 'CloudComp' + str(group_number) +# A network in the project the started instance will be attached to +PROJECT_NETWORK = 'CloudComp' + str(group_number) + '-net' + +# The image to look for and use for the started instance +# ubuntu_image_name = "Ubuntu 18.04 - Bionic Beaver - 64-bit - Cloud Based Image" +UBUNTU_IMAGE_NAME = "auto-sync/ubuntu-jammy-22.04-amd64-server-20240319-disk1.img" + +# The public key to be used for SSH connection, please make sure, that you have the +# corresponding private key +# +# id_rsa.pub should look like this (standard sshd pubkey format): +# ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAw+J...F3w2mleybgT1w== user@HOSTNAME + +KEYPAIR_NAME = 'srieger-pub' +PUB_KEY_FILE = '~/.ssh/id_rsa.pub' + +FLAVOR_NAME = 'm1.small' + + +# default region +REGION_NAME = 'RegionOne' +# domain to use, "default" for local accounts, formerly "hsfulda" for LDAP accounts etc. +# domain_name = "default" + + +def main(): # noqa: C901 pylint: disable=too-many-branches,too-many-statements,too-many-locals,missing-function-docstring + ########################################################################### + # + # get credentials + # + ########################################################################### + + # if "OS_PASSWORD" in os.environ: + # auth_password = os.environ["OS_PASSWORD"] + # else: + # auth_password = getpass.getpass("Enter your OpenStack password:") + auth_password = "demo" + + ########################################################################### + # + # create connection + # + ########################################################################### + + provider = get_driver(Provider.OPENSTACK) + conn = provider(AUTH_USERNAME, + auth_password, + ex_force_auth_url=AUTH_URL, + ex_force_auth_version='3.x_password', + ex_tenant_name=PROJECT_NAME, + ex_force_service_region=REGION_NAME) + # ex_domain_name=domain_name) + + ########################################################################### + # + # get image, flavor, network for instance creation + # + ########################################################################### + images = conn.list_images() + image = '' + for img in images: + if img.name == UBUNTU_IMAGE_NAME: + image = img + + flavors = conn.list_sizes() + flavor = '' + for flav in flavors: + if flav.name == FLAVOR_NAME: + flavor = conn.ex_get_size(flav.id) + + networks = conn.ex_list_networks() + network = '' + for net in networks: + if net.name == PROJECT_NETWORK: + network = net + + ########################################################################### + # + # create keypair dependency + # + ########################################################################### + + print('Checking for existing SSH key pair...') + keypair_exists = False + for keypair in conn.list_key_pairs(): + if keypair.name == KEYPAIR_NAME: + keypair_exists = True + + if keypair_exists: + print('Keypair ' + KEYPAIR_NAME + ' already exists. Skipping import.') + else: + print('adding keypair...') + conn.import_key_pair_from_file(KEYPAIR_NAME, PUB_KEY_FILE) + + for keypair in conn.list_key_pairs(): + print(keypair) + + ########################################################################### + # + # create security group dependency + # + ########################################################################### + + print('Checking for existing security group...') + security_group_name = 'all-in-one' + security_group_exists = False + all_in_one_security_group = '' + for security_group in conn.ex_list_security_groups(): + if security_group.name == security_group_name: + all_in_one_security_group = security_group + security_group_exists = True + + if security_group_exists: + print('Security Group ' + all_in_one_security_group.name + ' already exists. ' + 'Skipping creation.') + else: + all_in_one_security_group = conn.ex_create_security_group(security_group_name, + 'network access for ' + 'all-in-one application.') + conn.ex_create_security_group_rule(all_in_one_security_group, 'TCP', 80, 80) + conn.ex_create_security_group_rule(all_in_one_security_group, 'TCP', 22, 22) + + for security_group in conn.ex_list_security_groups(): + print(security_group) + + ########################################################################### + # + # create all-in-one instance + # + ########################################################################### + + hsfd_faafo_cloud_init_script = 'https://gogs.informatik.hs-fulda.de/srieger/cloud-computing-msc-ai-examples/raw/master/faafo/contrib/install.sh' # noqa: E501 pylint: disable=line-too-long + # testing / faafo dev branch: + # hsfd_faafo_cloud_init_script = 'https://gogs.informatik.hs-fulda.de/srieger/cloud-computing-msc-ai-examples/raw/branch/dev_faafo/faafo/contrib/install.sh' # noqa: E501 pylint: disable=line-too-long + + userdata = '#!/usr/bin/env bash\n' \ + f'curl -L -s {hsfd_faafo_cloud_init_script} | bash -s -- ' \ + '-i faafo -i messaging -r api -r worker -r demo\n' + print('\nUsing cloud-init userdata:\n"' + userdata + '"\n') + + print('Checking for existing instance...') + instance_name = 'all-in-one' + instance_exists = False + testing_instance = '' + for instance in conn.list_nodes(): + if instance.name == instance_name: + testing_instance = instance + instance_exists = True + + if instance_exists: + print('Instance ' + testing_instance.name + ' already exists. Skipping creation.') + exit() + else: + print('Starting new all-in-one instance and wait until it is running...') + testing_instance = conn.create_node(name=instance_name, + image=image, + size=flavor, + networks=[network], + ex_keyname=KEYPAIR_NAME, + ex_userdata=userdata, + ex_security_groups=[all_in_one_security_group]) + conn.wait_until_running(nodes=[testing_instance], timeout=120, ssh_interface='private_ips') + + ########################################################################### + # + # assign all-in-one instance floating ip + # + ########################################################################### + + private_ip = None + if len(testing_instance.private_ips): + private_ip = testing_instance.private_ips[0] + print(f'Private IP found: {private_ip}') + + public_ip = None + if len(testing_instance.public_ips): + public_ip = testing_instance.public_ips[0] + print(f'Public IP found: {public_ip}') + + print('Checking for unused Floating IP...') + unused_floating_ip = None + for floating_ip in conn.ex_list_floating_ips(): + if not floating_ip.node_id: + unused_floating_ip = floating_ip + break + + if not unused_floating_ip and len(conn.ex_list_floating_ip_pools()): + pool = conn.ex_list_floating_ip_pools()[0] + print(f'Allocating new Floating IP from pool: {pool}') + unused_floating_ip = pool.create_floating_ip() + + if public_ip: + print('Instance ' + testing_instance.name + ' already has a public ip. Skipping attachment.') + elif unused_floating_ip: + conn.ex_attach_floating_ip_to_node(testing_instance, unused_floating_ip) + + actual_ip_address = None + if public_ip: + actual_ip_address = public_ip + elif unused_floating_ip: + actual_ip_address = unused_floating_ip.ip_address + elif private_ip: + actual_ip_address = private_ip + + print('\n\n#### Deployment finished\n\n') + print('After some minutes, as soon as cloud-init installed required packages and the\n' + 'faafo app, (First App Application For OpenStack) fractals demo will be available\n' + f'at http://{actual_ip_address}\n') + + print('You can use ssh to login to the instance using your private key. Default user name for official Ubuntu\n' + f'Cloud Images is: ubuntu, so you can use, e.g.: "ssh -i ~/.ssh/id_rsa ubuntu@{actual_ip_address}" if your\n' + 'private key is in the default location.\n\n' + 'After login, you can list available fractals using "faafo list". \n' + 'To request the generation of new fractals, you can use "faafo create".\n\n' + 'You can also see other options to use the faafo example cloud service using "faafo -h".\n\n' + 'If you cannot start faafo command and/or do not see the webpage, you can check the Instance Console Log of\n' + 'the instance, e.g., in OpenStack web interface or look at "tail -f /var/log/cloud-init*.log" for the\n' + 'cloud-init log files.\n') + + +if __name__ == '__main__': + main() diff --git a/demo3_microservice.py b/demo3_microservice.py new file mode 100644 index 0000000..bef866f --- /dev/null +++ b/demo3_microservice.py @@ -0,0 +1,317 @@ +"""Example for Cloud Computing Course Master AI / GSD""" + +# This script demonstrates how to use libcloud to start an instance in an OpenStack environment. +# The script will create start multiple instances splitting up the faafo monolithic application into +# a (minimalistic but already scalable) microservice architecture. +# Also introduces the concept of different security groups and corresponding frontend/backend +# separation. + +# Needed if the password should be prompted for: +# import getpass +import os +import sys + +from libcloud.compute.providers import get_driver +from libcloud.compute.types import Provider + +# For our new Charmed OpenStack private cloud, we need to specify the path to the root +# CA certificate +import libcloud.security +libcloud.security.CA_CERTS_PATH = ['./root-ca.crt'] +# Disable SSL certificate verification (not recommended for production) +# libcloud.security.VERIFY_SSL_CERT = False + +# Please use 1-29 as environment variable GROUP_NUMBER to specify your group number. +# (will be used for the username, project etc., as coordinated in the lab sessions) + +group_number = os.environ.get('GROUP_NUMBER') +if group_number is None: + sys.exit('Please set the GROUP_NUMBER environment variable to your group number,\n' + 'e.g., on Windows:\n' + ' "$env:GROUP_NUMBER=0" or "set GROUP_NUMBER=0"\n' + 'or on Linux/MacOS:\n' + ' "export GROUP_NUMBER=0" or "set GROUP_NUMBER=0"') + + +# web service endpoint of the private cloud infrastructure +# auth_url = 'https://private-cloud.informatik.hs-fulda.de:5000' +AUTH_URL = 'https://10.32.4.182:5000' +# auth_url = 'https://private-cloud2.informatik.hs-fulda.de:5000' +# your username in OpenStack +AUTH_USERNAME = 'CloudComp' + str(group_number) +print(f'Using username: {AUTH_USERNAME}\n') +# your project in OpenStack +PROJECT_NAME = 'CloudComp' + str(group_number) +# A network in the project the started instance will be attached to +PROJECT_NETWORK = 'CloudComp' + str(group_number) + '-net' + +# The image to look for and use for the started instance +# ubuntu_image_name = "Ubuntu 18.04 - Bionic Beaver - 64-bit - Cloud Based Image" +UBUNTU_IMAGE_NAME = "auto-sync/ubuntu-jammy-22.04-amd64-server-20240319-disk1.img" + +# The public key to be used for SSH connection, please make sure, that you have the +# corresponding private key +# +# id_rsa.pub should look like this (standard sshd pubkey format): +# ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAw+J...F3w2mleybgT1w== user@HOSTNAME + +KEYPAIR_NAME = 'srieger-pub' +PUB_KEY_FILE = '~/.ssh/id_rsa.pub' + +FLAVOR_NAME = 'm1.small' + + +# default region +REGION_NAME = 'RegionOne' +# domain to use, "default" for local accounts, formerly "hsfulda" for LDAP accounts etc. +# domain_name = "default" + + +def main(): # noqa: C901 pylint: disable=too-many-branches,too-many-statements,too-many-locals,missing-function-docstring + ########################################################################### + # + # get credentials + # + ########################################################################### + + # if "OS_PASSWORD" in os.environ: + # auth_password = os.environ["OS_PASSWORD"] + # else: + # auth_password = getpass.getpass("Enter your OpenStack password:") + auth_password = "demo" + + ########################################################################### + # + # create connection + # + ########################################################################### + + provider = get_driver(Provider.OPENSTACK) + conn = provider(AUTH_USERNAME, + auth_password, + ex_force_auth_url=AUTH_URL, + ex_force_auth_version='3.x_password', + ex_tenant_name=PROJECT_NAME, + ex_force_service_region=REGION_NAME) + # ex_domain_name=domain_name) + + ########################################################################### + # + # get image, flavor, network for instance creation + # + ########################################################################### + + images = conn.list_images() + image = '' + for img in images: + if img.name == UBUNTU_IMAGE_NAME: + image = img + + flavors = conn.list_sizes() + flavor = '' + for flav in flavors: + if flav.name == FLAVOR_NAME: + flavor = conn.ex_get_size(flav.id) + + networks = conn.ex_list_networks() + network = '' + for net in networks: + if net.name == PROJECT_NETWORK: + network = net + + ########################################################################### + # + # create keypair dependency + # + ########################################################################### + + print('Checking for existing SSH key pair...') + keypair_exists = False + for keypair in conn.list_key_pairs(): + if keypair.name == KEYPAIR_NAME: + keypair_exists = True + + if keypair_exists: + print('Keypair ' + KEYPAIR_NAME + ' already exists. Skipping import.') + else: + print('adding keypair...') + conn.import_key_pair_from_file(KEYPAIR_NAME, PUB_KEY_FILE) + + for keypair in conn.list_key_pairs(): + print(keypair) + + ########################################################################### + # + # create security group dependency + # + ########################################################################### + + print('Checking for existing worker security group...') + security_group_name = 'worker' + security_group_exists = False + worker_security_group = '' + for security_group in conn.ex_list_security_groups(): + if security_group.name == security_group_name: + worker_security_group = security_group + security_group_exists = True + + if security_group_exists: + print('Worker Security Group ' + worker_security_group.name + ' already exists. ' + 'Skipping creation.') + else: + worker_security_group = conn.ex_create_security_group('worker', 'for services ' + 'that run on a worker node') + conn.ex_create_security_group_rule(worker_security_group, 'TCP', 22, 22) + + print('Checking for existing controller security group...') + security_group_name = 'control' + security_group_exists = False + controller_security_group = '' + for security_group in conn.ex_list_security_groups(): + if security_group.name == security_group_name: + controller_security_group = security_group + security_group_exists = True + + if security_group_exists: + print('Controller Security Group ' + controller_security_group.name + ' already exists. ' + 'Skipping creation.') + else: + controller_security_group = conn.ex_create_security_group('control', 'for services that ' + 'run on a control node') + conn.ex_create_security_group_rule(controller_security_group, 'TCP', 22, 22) + conn.ex_create_security_group_rule(controller_security_group, 'TCP', 80, 80) + conn.ex_create_security_group_rule(controller_security_group, 'TCP', 5672, 5672, + source_security_group=worker_security_group) + + for security_group in conn.ex_list_security_groups(): + print(security_group) + + ########################################################################### + # + # create app-controller + # + ########################################################################### + + # https://git.openstack.org/cgit/openstack/faafo/plain/contrib/install.sh + # is currently broken, hence the "rabbitctl" lines were added in the example + # below, see also https://bugs.launchpad.net/faafo/+bug/1679710 + # + # Thanks to Stefan Friedmann for finding this fix ;) + # TODO: still needed for new version of faafo and Ubuntu 22.04? + + userdata = '#!/usr/bin/env bash\n' \ + 'curl -L -s https://gogs.informatik.hs-fulda.de/srieger/cloud-computing-msc-ai-' \ + 'examples/raw/master/faafo/contrib/install.sh | bash -s -- ' \ + '-i messaging -i faafo -r api\n' \ + 'rabbitmqctl add_user faafo guest\n' \ + 'rabbitmqctl set_user_tags faafo administrator\n' \ + 'rabbitmqctl set_permissions -p / faafo ".*" ".*" ".*"\n' + print('\nUsing cloud-init userdata:\n"' + userdata + '"\n') + + print('Starting new app-controller instance and wait until it is running...') + instance_controller_1 = conn.create_node(name='app-controller', + image=image, + size=flavor, + networks=[network], + ex_keyname=KEYPAIR_NAME, + ex_userdata=userdata, + ex_security_groups=[controller_security_group]) + + conn.wait_until_running(nodes=[instance_controller_1], timeout=120, ssh_interface='private_ips') + + ########################################################################### + # + # assign app-controller floating ip + # + ########################################################################### + + print('Checking for unused Floating IP...') + unused_floating_ip = None + for floating_ip in conn.ex_list_floating_ips(): + if not floating_ip.node_id: + unused_floating_ip = floating_ip + break + + if not unused_floating_ip: + pool = conn.ex_list_floating_ip_pools()[0] + print(f'Allocating new Floating IP from pool: {pool}') + unused_floating_ip = pool.create_floating_ip() + + conn.ex_attach_floating_ip_to_node(instance_controller_1, unused_floating_ip) + print(f'Controller Application will be deployed to http://{unused_floating_ip.ip_address}') + actual_ip_address = unused_floating_ip.ip_address + + ########################################################################### + # + # getting id and ip address of app-controller instance + # + ########################################################################### + + # instance should not have a public ip? floating ips are assigned later + instance_controller_1 = conn.ex_get_node_details(instance_controller_1.id) + ip_controller = '' + if instance_controller_1.public_ips: + ip_controller = instance_controller_1.public_ips[0] + else: + ip_controller = instance_controller_1.private_ips[0] + + ########################################################################### + # + # create app-worker-1 + # + ########################################################################### + + userdata = '#!/usr/bin/env bash\n' \ + 'curl -L -s https://gogs.informatik.hs-fulda.de/srieger/cloud-computing-msc-ai-' \ + 'examples/raw/master/faafo/contrib/install.sh | bash -s -- ' \ + f'-i faafo -r worker -e "http://{ip_controller}" -m "amqp://faafo:guest@' \ + f'{ip_controller}:5672/\n' + print('\nUsing cloud-init userdata:\n"' + userdata + '"\n') + + print('Starting new app-worker-1 instance and wait until it is running...') + instance_worker_1 = conn.create_node(name='app-worker-1', + image=image, + size=flavor, + networks=[network], + ex_keyname=KEYPAIR_NAME, + ex_userdata=userdata, + ex_security_groups=[worker_security_group]) + + conn.wait_until_running(nodes=[instance_worker_1], timeout=120, ssh_interface='private_ips') + + ########################################################################### + # + # assign app-worker floating ip + # + ########################################################################### + + print('Checking for unused Floating IP...') + unused_floating_ip = None + for floating_ip in conn.ex_list_floating_ips(): + if not floating_ip.node_id: + unused_floating_ip = floating_ip + break + + if not unused_floating_ip: + pool = conn.ex_list_floating_ip_pools()[0] + print(f'Allocating new Floating IP from pool: {pool}') + unused_floating_ip = pool.create_floating_ip() + + conn.ex_attach_floating_ip_to_node(instance_worker_1, unused_floating_ip) + print(f'The worker will be available for SSH at {unused_floating_ip.ip_address}') + + print('\n\n#### Deployment finished\n\n') + print('After some minutes, as soon as cloud-init installed required packages and the\n' + 'faafo app, (First App Application For OpenStack) fractals demo will be available\n' + f'at http://{actual_ip_address}\n') + + print('You can use ssh to login to the controller using your private key.\n' + f'E.g., "ssh -i ~/.ssh/id_rsa ubuntu@{actual_ip_address}". After login,\n' + 'you can list available fractals using "faafo list". To request the generation of\n' + 'new fractals, you can use "faafo create". \n' + 'You can also see other options to use the faafo example cloud service using ' + '"faafo -h".') + + +if __name__ == '__main__': + main() diff --git a/destroy_all_demo_instances.py b/destroy_all_demo_instances.py new file mode 100644 index 0000000..1173ea8 --- /dev/null +++ b/destroy_all_demo_instances.py @@ -0,0 +1,126 @@ +"""Example for Cloud Computing Course Master AI / GSD""" + + +# This script demonstrates how to use libcloud to cleanup all instances used in the demos +# for our OpenStack private cloud environment. + +# import getpass +import os +import sys + +import time +from libcloud.compute.providers import get_driver +from libcloud.compute.types import Provider + +# For our new Charmed OpenStack private cloud, we need to specify the path to the +# root CA certificate +import libcloud.security +libcloud.security.CA_CERTS_PATH = ['./root-ca.crt'] +# Disable SSL certificate verification (not recommended for production) +# libcloud.security.VERIFY_SSL_CERT = False + +# Please use 1-29 for X in the following variable to specify your group number. +# (will be used for the username, project etc., as coordinated in the lab sessions) + +group_number = os.environ.get('GROUP_NUMBER') +if group_number is None: + sys.exit('Please set the GROUP_NUMBER environment variable to your group number,\n' + 'e.g., on Windows:\n' + ' "$env:GROUP_NUMBER=0" or "set GROUP_NUMBER=0"\n' + 'or on Linux/MacOS:\n' + ' "export GROUP_NUMBER=0" or "set GROUP_NUMBER=0"') + + +############################################################################################### +# +# no changes necessary below this line in this example +# +############################################################################################### + +# web service endpoint of the private cloud infrastructure +# auth_url = 'https://private-cloud.informatik.hs-fulda.de:5000' +AUTH_URL = 'https://10.32.4.182:5000' +# auth_url = 'https://private-cloud2.informatik.hs-fulda.de:5000' +# your username in OpenStack +AUTH_USERNAME = 'CloudComp' + str(group_number) +# your project in OpenStack +PROJECT_NAME = 'CloudComp' + str(group_number) +# A network in the project the started instance will be attached to +PROJET_NETWORK = 'CloudComp' + str(group_number) + '-net' + +# The image to look for and use for the started instance +# ubuntu_image_name = "Ubuntu 18.04 - Bionic Beaver - 64-bit - Cloud Based Image" +UBUNTU_IMAGE_NAME = "auto-sync/ubuntu-jammy-22.04-amd64-server-20240319-disk1.img" + +# default region +REGION_NAME = 'RegionOne' +# domain to use, "default" for local accounts, formerly "hsfulda" for LDAP accounts etc. +# domain_name = "default" + + +def main(): # noqa: C901 pylint: disable=too-many-branches,too-many-statements,too-many-locals,missing-function-docstring + ########################################################################### + # + # get credentials + # + ########################################################################### + + # if "OS_PASSWORD" in os.environ: + # auth_password = os.environ["OS_PASSWORD"] + # else: + # auth_password = getpass.getpass("Enter your OpenStack password:") + auth_password = "demo" + + ########################################################################### + # + # create connection + # + ########################################################################### + + # libcloud.security.VERIFY_SSL_CERT = False + + provider = get_driver(Provider.OPENSTACK) + conn = provider(AUTH_USERNAME, + auth_password, + ex_force_auth_url=AUTH_URL, + ex_force_auth_version='3.x_password', + ex_tenant_name=PROJECT_NAME, + ex_force_service_region=REGION_NAME) +# ex_domain_name=domain_name) + + ########################################################################### + # + # clean up resources from previous demos + # + ########################################################################### + + # destroy running demo instances + for instance in conn.list_nodes(): + if instance.name in ['all-in-one', 'app-worker-1', 'app-worker-2', 'app-worker-3', + 'app-controller', + 'app-services', 'app-api-1', 'app-api-2']: + print(f'Destroying Instance: {instance.name}') + conn.destroy_node(instance) + + # wait until all nodes are destroyed to be able to remove depended security groups + nodes_still_running = True + while nodes_still_running: + nodes_still_running = False + time.sleep(3) + instances = conn.list_nodes() + for instance in instances: + # if we see any demo instances still running continue to wait for them to stop + if instance.name in ['all-in-one', 'app-worker-1', 'app-worker-2', 'app-worker-3', + 'app-controller', 'app-services', 'app-api-1', 'app-api-2']: + nodes_still_running = True + print('There are still instances running, waiting for them to be destroyed...') + + # delete security groups + for group in conn.ex_list_security_groups(): + if group.name in ['control', 'worker', 'api', 'services']: + print(f'Deleting security group: {group.name}') + conn.ex_delete_security_group(group) + + +if __name__ == '__main__': + main() diff --git a/root-ca.crt b/root-ca.crt new file mode 100644 index 0000000..1fcd3e7 --- /dev/null +++ b/root-ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUEQjSqiZ86fhawQU09G8hn3i9dIwwDQYJKoZIhvcNAQEL +BQAwPTE7MDkGA1UEAxMyVmF1bHQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkg +KGNoYXJtLXBraS1sb2NhbCkwHhcNMjQwMzI1MTMxNDU0WhcNMzQwMzIzMTIxNTIz +WjA9MTswOQYDVQQDEzJWYXVsdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAo +Y2hhcm0tcGtpLWxvY2FsKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALcax5l2zU1ELCtF/k6yq3HUv7gFq6f/sl8rX0VRhzZyEy4hcMgRSZDAM14viTVZ +8d7+ptY3+GwuLSpEY2UUlX5kJSDb4pUNRXDhxzRatbByG8pr5FQE8pX9W7y4C0TU +3PQA4uIjAsPFKayFxXjJjOQN0HX3K6MCQz/BTV81U3fmdFrKma3x/PXyUYndjQH6 +zlIiQSdYh7FMTbS2FlpvwWbT9zKOpp+2M0odI8Y8fjCSUdSdKDFhVu02zQTq6/h0 +Q1/sNHz2IP9F83sNW+ro0bvv5CJ2iCyAk/RiFoB+RoSO6HncOtYHxa/guwTy4eHh +VQVJXkEI2PutCw6S3lWqLEcCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFDKQHfpVHJgge6RRC5uDwMByLbV3MB8GA1Ud +IwQYMBaAFDKQHfpVHJgge6RRC5uDwMByLbV3MA0GCSqGSIb3DQEBCwUAA4IBAQCa +ajIRi/+7Yy7l46yFURLyELMWayRpdx2KCxIuAiSkTlNSVOPCmwZvgnYLPVffXWpt +IXJGQk//9+5q18LiZat5MbvUU3ffLc/ZCxIeQiWNuYKziLYNFHmpfMvxNxzAJ6Pi +2fj5ZP/cA4Vie3M1iHfdSXmYPvyw76i9/sA2+F7Wy8fzK53S1OaMaeADNGljHTaW +ovRxreLKJZybqe/YWlcEiP4dC4VMHLl+H5RmZ5ojrRiy1c3uUssNnIJU+ilkY8TP +0VV17+wQBaJbbp4jh8acwvOJbN8Y1EHQWhxkEf3PfjJRv+b1NI/Iai27DfYto7Dm +rZvaFnAMCcyFXyJv3WdJ +-----END CERTIFICATE----- \ No newline at end of file