|
|
@ -1,55 +1,73 @@ |
|
|
|
# import getpass |
|
|
|
# import os |
|
|
|
# import libcloud.security |
|
|
|
"""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 multiple instances splitting up the faafo monolithic application into |
|
|
|
# a microservice architecture with scalable api (controller) and worker instances using a |
|
|
|
# message queue and a database |
|
|
|
|
|
|
|
# Needed if the password should be prompted for: |
|
|
|
# import getpass |
|
|
|
import os |
|
|
|
import sys |
|
|
|
import time |
|
|
|
|
|
|
|
from libcloud.compute.providers import get_driver |
|
|
|
from libcloud.compute.types import Provider |
|
|
|
|
|
|
|
# reqs: |
|
|
|
# services: nova, glance, neutron |
|
|
|
# resources: 2 instances (m1.small), 2 floating ips (1 keypair, 2 security groups) |
|
|
|
# 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) |
|
|
|
# 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 = 2 |
|
|
|
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://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) |
|
|
|
AUTH_USERNAME = 'CloudComp' + str(group_number) |
|
|
|
print(f'Using username: {AUTH_USERNAME}\n') |
|
|
|
# your project in OpenStack |
|
|
|
project_name = 'CloudComp' + str(group_number) |
|
|
|
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' |
|
|
|
PROJECT_NETWORK = 'CloudComp' + str(group_number) + '-net' |
|
|
|
|
|
|
|
# The image to look for and use for the started instance |
|
|
|
#ubuntu_image_name = "Ubuntu 20.04 - Focal Fossa - 64-bit - Cloud Based Image" |
|
|
|
# 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" |
|
|
|
UBUNTU_IMAGE_NAME = "ubuntu-22.04-jammy-x86_64" |
|
|
|
|
|
|
|
|
|
|
|
# The public key to be used for SSH connection, please make sure, that you have the corresponding private key |
|
|
|
# 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' |
|
|
|
|
|
|
|
#keypair_name = 'CloudComp30-keypair' |
|
|
|
keypair_name = "srieger-pub" |
|
|
|
pub_key_file = '~/.ssh/id_rsa.pub' |
|
|
|
|
|
|
|
flavor_name = 'm1.small' |
|
|
|
FLAVOR_NAME = 'm1.small' |
|
|
|
|
|
|
|
|
|
|
|
# default region |
|
|
|
region_name = 'RegionOne' |
|
|
|
# domain to use, "default" for local accounts, "hsfulda" for RZ LDAP, e.g., using fdaiXXXX as auth_username |
|
|
|
domain_name = "default" |
|
|
|
REGION_NAME = 'RegionOne' |
|
|
|
# domain to use, "default" for local accounts, formerly "hsfulda" for LDAP accounts etc. |
|
|
|
# domain_name = "default" |
|
|
|
|
|
|
|
|
|
|
|
def main(): |
|
|
|
def main(): # noqa: C901 pylint: disable=too-many-branches,too-many-statements,too-many-locals,missing-function-docstring |
|
|
|
########################################################################### |
|
|
|
# |
|
|
|
# get credentials |
|
|
@ -68,16 +86,14 @@ def main(): |
|
|
|
# |
|
|
|
########################################################################### |
|
|
|
|
|
|
|
# libcloud.security.VERIFY_SSL_CERT = False |
|
|
|
|
|
|
|
provider = get_driver(Provider.OPENSTACK) |
|
|
|
conn = provider(auth_username, |
|
|
|
conn = provider(AUTH_USERNAME, |
|
|
|
auth_password, |
|
|
|
ex_force_auth_url=auth_url, |
|
|
|
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) |
|
|
|
ex_tenant_name=PROJECT_NAME, |
|
|
|
ex_force_service_region=REGION_NAME) |
|
|
|
# ex_domain_name=domain_name) |
|
|
|
|
|
|
|
########################################################################### |
|
|
|
# |
|
|
@ -88,19 +104,19 @@ def main(): |
|
|
|
images = conn.list_images() |
|
|
|
image = '' |
|
|
|
for img in images: |
|
|
|
if img.name == ubuntu_image_name: |
|
|
|
if img.name == UBUNTU_IMAGE_NAME: |
|
|
|
image = img |
|
|
|
|
|
|
|
flavors = conn.list_sizes() |
|
|
|
flavor = '' |
|
|
|
for flav in flavors: |
|
|
|
if flav.name == flavor_name: |
|
|
|
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: |
|
|
|
if net.name == PROJECT_NETWORK: |
|
|
|
network = net |
|
|
|
|
|
|
|
########################################################################### |
|
|
@ -112,14 +128,14 @@ def main(): |
|
|
|
print('Checking for existing SSH key pair...') |
|
|
|
keypair_exists = False |
|
|
|
for keypair in conn.list_key_pairs(): |
|
|
|
if keypair.name == keypair_name: |
|
|
|
if keypair.name == KEYPAIR_NAME: |
|
|
|
keypair_exists = True |
|
|
|
|
|
|
|
if keypair_exists: |
|
|
|
print(('Keypair ' + keypair_name + ' already exists. Skipping import.')) |
|
|
|
print(('Keypair ' + KEYPAIR_NAME + ' already exists. Skipping import.')) |
|
|
|
else: |
|
|
|
print('adding keypair...') |
|
|
|
conn.import_key_pair_from_file(keypair_name, pub_key_file) |
|
|
|
conn.import_key_pair_from_file(KEYPAIR_NAME, PUB_KEY_FILE) |
|
|
|
|
|
|
|
for keypair in conn.list_key_pairs(): |
|
|
|
print(keypair) |
|
|
@ -132,9 +148,10 @@ def main(): |
|
|
|
|
|
|
|
# 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', |
|
|
|
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(('Destroying Instance: %s' % instance.name)) |
|
|
|
print(f'Destroying Instance: ${instance.name}') |
|
|
|
conn.destroy_node(instance) |
|
|
|
|
|
|
|
# wait until all nodes are destroyed to be able to remove depended security groups |
|
|
@ -152,7 +169,7 @@ def main(): |
|
|
|
# delete security groups |
|
|
|
for group in conn.ex_list_security_groups(): |
|
|
|
if group.name in ['control', 'worker', 'api', 'services']: |
|
|
|
print(('Deleting security group: %s' % group.name)) |
|
|
|
print(f'Deleting security group: ${group.name}') |
|
|
|
conn.ex_delete_security_group(group) |
|
|
|
|
|
|
|
########################################################################### |
|
|
@ -166,7 +183,8 @@ def main(): |
|
|
|
print(('Checking for existing ' + security_group_name + ' security group...')) |
|
|
|
for security_grp in connection.ex_list_security_groups(): |
|
|
|
if security_grp.name == security_group_name: |
|
|
|
print(('Security Group ' + security_group_name + ' already exists. Skipping creation.')) |
|
|
|
print(('Security Group ' + security_group_name + |
|
|
|
' already exists. Skipping creation.')) |
|
|
|
return worker_security_group |
|
|
|
return False |
|
|
|
|
|
|
@ -178,20 +196,23 @@ def main(): |
|
|
|
api_security_group = get_security_group(conn, "api") |
|
|
|
|
|
|
|
if not get_security_group(conn, "worker"): |
|
|
|
worker_security_group = conn.ex_create_security_group('worker', 'for services that run on a worker node') |
|
|
|
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) |
|
|
|
else: |
|
|
|
worker_security_group = get_security_group(conn, "worker") |
|
|
|
|
|
|
|
if not get_security_group(conn, "control"): |
|
|
|
controller_security_group = conn.ex_create_security_group('control', 'for services that run on a control node') |
|
|
|
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) |
|
|
|
|
|
|
|
if not get_security_group(conn, "services"): |
|
|
|
services_security_group = conn.ex_create_security_group('services', 'for DB and AMQP services only') |
|
|
|
services_security_group = conn.ex_create_security_group( |
|
|
|
'services', 'for DB and AMQP services only') |
|
|
|
conn.ex_create_security_group_rule(services_security_group, 'TCP', 22, 22) |
|
|
|
conn.ex_create_security_group_rule(services_security_group, 'TCP', 3306, 3306, |
|
|
|
source_security_group=api_security_group) |
|
|
@ -229,24 +250,18 @@ def main(): |
|
|
|
# |
|
|
|
########################################################################### |
|
|
|
|
|
|
|
#hsfd_faafo_cloud_init_script = 'https://gogs.informatik.hs-fulda.de/srieger/cloud-computing-msc-ai-examples/raw/master/faafo/contrib/install.sh' |
|
|
|
# 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' |
|
|
|
|
|
|
|
userdata_service = '''#!/usr/bin/env bash |
|
|
|
curl -L -s ''' + hsfd_faafo_cloud_init_script + ''' | bash -s -- \ |
|
|
|
-i database -i messaging |
|
|
|
rabbitmqctl add_user faafo guest |
|
|
|
rabbitmqctl set_user_tags faafo administrator |
|
|
|
rabbitmqctl set_permissions -p / faafo ".*" ".*" ".*" |
|
|
|
''' |
|
|
|
userdata_service = '#!/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 database -i messaging\n' |
|
|
|
print('\nUsing cloud-init userdata for service:\n"' + userdata_service + '"\n') |
|
|
|
|
|
|
|
print('Starting new app-services instance and wait until it is running...') |
|
|
|
instance_services = conn.create_node(name='app-services', |
|
|
|
image=image, |
|
|
|
size=flavor, |
|
|
|
networks=[network], |
|
|
|
ex_keyname=keypair_name, |
|
|
|
ex_keyname=KEYPAIR_NAME, |
|
|
|
ex_userdata=userdata_service, |
|
|
|
ex_security_groups=[services_security_group]) |
|
|
|
instance_services = conn.wait_until_running(nodes=[instance_services], timeout=120, |
|
|
@ -259,18 +274,20 @@ def main(): |
|
|
|
# |
|
|
|
########################################################################### |
|
|
|
|
|
|
|
userdata_api = '''#!/usr/bin/env bash |
|
|
|
curl -L -s ''' + hsfd_faafo_cloud_init_script + ''' | bash -s -- \ |
|
|
|
-i faafo -r api -m 'amqp://faafo:guest@%(services_ip)s:5672/' \ |
|
|
|
-d 'mysql+pymysql://faafo:password@%(services_ip)s:3306/faafo' |
|
|
|
''' % {'services_ip': services_ip} |
|
|
|
userdata_api = '#!/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 api -m "amqp://faafo:guest@{services_ip}:5672/" ' \ |
|
|
|
f'-d "mysql+pymysql://faafo:password@{services_ip}:3306/faafo"' |
|
|
|
print('\nUsing cloud-init userdata for api:\n"' + userdata_api + '"\n') |
|
|
|
|
|
|
|
print('Starting new app-api-1 instance and wait until it is running...') |
|
|
|
instance_api_1 = conn.create_node(name='app-api-1', |
|
|
|
image=image, |
|
|
|
size=flavor, |
|
|
|
networks=[network], |
|
|
|
ex_keyname=keypair_name, |
|
|
|
ex_keyname=KEYPAIR_NAME, |
|
|
|
ex_userdata=userdata_api, |
|
|
|
ex_security_groups=[api_security_group]) |
|
|
|
|
|
|
@ -279,7 +296,7 @@ def main(): |
|
|
|
image=image, |
|
|
|
size=flavor, |
|
|
|
networks=[network], |
|
|
|
ex_keyname=keypair_name, |
|
|
|
ex_keyname=KEYPAIR_NAME, |
|
|
|
ex_userdata=userdata_api, |
|
|
|
ex_security_groups=[api_security_group]) |
|
|
|
|
|
|
@ -293,7 +310,8 @@ def main(): |
|
|
|
for instance in [instance_api_1, instance_api_2]: |
|
|
|
floating_ip = get_floating_ip(conn) |
|
|
|
conn.ex_attach_floating_ip_to_node(instance, floating_ip) |
|
|
|
print(('allocated %(ip)s to %(host)s' % {'ip': floating_ip.ip_address, 'host': instance.name})) |
|
|
|
print(('allocated %(ip)s to %(host)s' % {'ip': floating_ip.ip_address, |
|
|
|
'host': instance.name})) |
|
|
|
|
|
|
|
########################################################################### |
|
|
|
# |
|
|
@ -301,10 +319,14 @@ def main(): |
|
|
|
# |
|
|
|
########################################################################### |
|
|
|
|
|
|
|
userdata_worker = '''#!/usr/bin/env bash |
|
|
|
curl -L -s ''' + hsfd_faafo_cloud_init_script + ''' | bash -s -- \ |
|
|
|
-i faafo -r worker -e 'http://%(api_1_ip)s' -m 'amqp://faafo:guest@%(services_ip)s:5672/' |
|
|
|
''' % {'api_1_ip': api_1_ip, 'services_ip': services_ip} |
|
|
|
userdata_worker = '#!/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://{api_1_ip}" '\ |
|
|
|
f'-m "amqp://faafo:guest@{services_ip}:5672/"' |
|
|
|
print('\nUsing cloud-init userdata for worker:\n"' + userdata_worker + '"\n') |
|
|
|
|
|
|
|
|
|
|
|
# userdata_api-api-2 = '''#!/usr/bin/env bash |
|
|
|
# curl -L -s ''' + hsfd_faafo_cloud_init_script + ''' | bash -s -- \ |
|
|
@ -315,7 +337,7 @@ def main(): |
|
|
|
instance_worker_1 = conn.create_node(name='app-worker-1', |
|
|
|
image=image, size=flavor, |
|
|
|
networks=[network], |
|
|
|
ex_keyname=keypair_name, |
|
|
|
ex_keyname=KEYPAIR_NAME, |
|
|
|
ex_userdata=userdata_worker, |
|
|
|
ex_security_groups=[worker_security_group]) |
|
|
|
|
|
|
@ -323,12 +345,12 @@ def main(): |
|
|
|
instance_worker_2 = conn.create_node(name='app-worker-2', |
|
|
|
image=image, size=flavor, |
|
|
|
networks=[network], |
|
|
|
ex_keyname=keypair_name, |
|
|
|
ex_keyname=KEYPAIR_NAME, |
|
|
|
ex_userdata=userdata_worker, |
|
|
|
ex_security_groups=[worker_security_group]) |
|
|
|
|
|
|
|
# do not start worker 3 initially, can be started using scale-out-add-worker.py demo |
|
|
|
|
|
|
|
|
|
|
|
#print('Starting new app-worker-3 instance and wait until it is running...') |
|
|
|
#instance_worker_3 = conn.create_node(name='app-worker-3', |
|
|
|
# image=image, size=flavor, |