You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 lines
11 KiB

  1. """Example for Cloud Computing Course Master AI / GSD"""
  2. # This script demonstrates how to use libcloud to start an instance in an OpenStack environment.
  3. # The script will create and install a new SSH key pair, create a security group, start an instance
  4. # and deploy a demo app (faafo) using cloud-init and assign a floating IP to the instance.
  5. #
  6. # cloud-init is a multi-distribution package that handles early initialization of a cloud instance.
  7. # It is supported by many major cloud providers, including OpenStack.
  8. # cloud-init documentation: https://cloudinit.readthedocs.io/en/latest/
  9. # Needed if the password should be prompted for:
  10. # import getpass
  11. import os
  12. import sys
  13. from libcloud.compute.providers import get_driver
  14. from libcloud.compute.types import Provider
  15. # For our new Charmed OpenStack private cloud, we need to specify the path to the root
  16. # CA certificate
  17. import libcloud.security
  18. libcloud.security.CA_CERTS_PATH = ['./root-ca.crt']
  19. # Disable SSL certificate verification (not recommended for production)
  20. # libcloud.security.VERIFY_SSL_CERT = False
  21. # Please use 1-29 as environment variable GROUP_NUMBER to specify your group number.
  22. # (will be used for the username, project etc., as coordinated in the lab sessions)
  23. group_number = os.environ.get('GROUP_NUMBER')
  24. if group_number is None:
  25. sys.exit('Please set the GROUP_NUMBER environment variable to your group number,\n'
  26. 'e.g., on Windows:\n'
  27. ' "$env:GROUP_NUMBER=0" or "set GROUP_NUMBER=0"\n'
  28. 'or on Linux/MacOS:\n'
  29. ' "export GROUP_NUMBER=0" or "set GROUP_NUMBER=0"')
  30. # web service endpoint of the private cloud infrastructure
  31. # auth_url = 'https://private-cloud.informatik.hs-fulda.de:5000'
  32. AUTH_URL = 'https://10.32.4.182:5000'
  33. # auth_url = 'https://private-cloud2.informatik.hs-fulda.de:5000'
  34. # your username in OpenStack
  35. AUTH_USERNAME = 'CloudComp' + str(group_number)
  36. print(f'Using username: {AUTH_USERNAME}\n')
  37. # your project in OpenStack
  38. PROJECT_NAME = 'CloudComp' + str(group_number)
  39. # A network in the project the started instance will be attached to
  40. PROJECT_NETWORK = 'CloudComp' + str(group_number) + '-net'
  41. # The image to look for and use for the started instance
  42. # ubuntu_image_name = "Ubuntu 18.04 - Bionic Beaver - 64-bit - Cloud Based Image"
  43. #UBUNTU_IMAGE_NAME = "auto-sync/ubuntu-jammy-22.04-amd64-server-20240319-disk1.img"
  44. UBUNTU_IMAGE_NAME = "ubuntu-22.04-jammy-x86_64"
  45. # The public key to be used for SSH connection, please make sure, that you have the
  46. # corresponding private key
  47. #
  48. # id_rsa.pub should look like this (standard sshd pubkey format):
  49. # ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAw+J...F3w2mleybgT1w== user@HOSTNAME
  50. KEYPAIR_NAME = 'srieger-pub'
  51. PUB_KEY_FILE = '~/.ssh/id_rsa.pub'
  52. FLAVOR_NAME = 'm1.small'
  53. # default region
  54. REGION_NAME = 'RegionOne'
  55. # domain to use, "default" for local accounts, formerly "hsfulda" for LDAP accounts etc.
  56. # domain_name = "default"
  57. def main(): # noqa: C901 pylint: disable=too-many-branches,too-many-statements,too-many-locals,missing-function-docstring
  58. ###########################################################################
  59. #
  60. # get credentials
  61. #
  62. ###########################################################################
  63. # if "OS_PASSWORD" in os.environ:
  64. # auth_password = os.environ["OS_PASSWORD"]
  65. # else:
  66. # auth_password = getpass.getpass("Enter your OpenStack password:")
  67. auth_password = "demo"
  68. ###########################################################################
  69. #
  70. # create connection
  71. #
  72. ###########################################################################
  73. provider = get_driver(Provider.OPENSTACK)
  74. conn = provider(AUTH_USERNAME,
  75. auth_password,
  76. ex_force_auth_url=AUTH_URL,
  77. ex_force_auth_version='3.x_password',
  78. ex_tenant_name=PROJECT_NAME,
  79. ex_force_service_region=REGION_NAME)
  80. # ex_domain_name=domain_name)
  81. ###########################################################################
  82. #
  83. # get image, flavor, network for instance creation
  84. #
  85. ###########################################################################
  86. images = conn.list_images()
  87. image = ''
  88. for img in images:
  89. if img.name == UBUNTU_IMAGE_NAME:
  90. image = img
  91. flavors = conn.list_sizes()
  92. flavor = ''
  93. for flav in flavors:
  94. if flav.name == FLAVOR_NAME:
  95. flavor = conn.ex_get_size(flav.id)
  96. networks = conn.ex_list_networks()
  97. network = ''
  98. for net in networks:
  99. if net.name == PROJECT_NETWORK:
  100. network = net
  101. ###########################################################################
  102. #
  103. # create keypair dependency
  104. #
  105. ###########################################################################
  106. print('Checking for existing SSH key pair...')
  107. keypair_exists = False
  108. for keypair in conn.list_key_pairs():
  109. if keypair.name == KEYPAIR_NAME:
  110. keypair_exists = True
  111. if keypair_exists:
  112. print('Keypair ' + KEYPAIR_NAME + ' already exists. Skipping import.')
  113. else:
  114. print('adding keypair...')
  115. conn.import_key_pair_from_file(KEYPAIR_NAME, PUB_KEY_FILE)
  116. for keypair in conn.list_key_pairs():
  117. print(keypair)
  118. ###########################################################################
  119. #
  120. # create security group dependency
  121. #
  122. ###########################################################################
  123. print('Checking for existing security group...')
  124. security_group_name = 'all-in-one'
  125. security_group_exists = False
  126. all_in_one_security_group = ''
  127. for security_group in conn.ex_list_security_groups():
  128. if security_group.name == security_group_name:
  129. all_in_one_security_group = security_group
  130. security_group_exists = True
  131. if security_group_exists:
  132. print('Security Group ' + all_in_one_security_group.name + ' already exists. '
  133. 'Skipping creation.')
  134. else:
  135. all_in_one_security_group = conn.ex_create_security_group(security_group_name,
  136. 'network access for '
  137. 'all-in-one application.')
  138. conn.ex_create_security_group_rule(all_in_one_security_group, 'TCP', 80, 80)
  139. conn.ex_create_security_group_rule(all_in_one_security_group, 'TCP', 22, 22)
  140. for security_group in conn.ex_list_security_groups():
  141. print(security_group)
  142. ###########################################################################
  143. #
  144. # create all-in-one instance
  145. #
  146. ###########################################################################
  147. 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
  148. # testing / faafo dev branch:
  149. # 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
  150. userdata = '#!/usr/bin/env bash\n' \
  151. f'curl -L -s {hsfd_faafo_cloud_init_script} | bash -s -- ' \
  152. '-i faafo -i messaging -r api -r worker -r demo\n'
  153. print('\nUsing cloud-init userdata:\n"' + userdata + '"\n')
  154. print('Checking for existing instance...')
  155. instance_name = 'all-in-one'
  156. instance_exists = False
  157. testing_instance = ''
  158. for instance in conn.list_nodes():
  159. if instance.name == instance_name:
  160. testing_instance = instance
  161. instance_exists = True
  162. if instance_exists:
  163. print('Instance ' + testing_instance.name + ' already exists. Skipping creation.')
  164. exit()
  165. else:
  166. print('Starting new all-in-one instance and wait until it is running...')
  167. testing_instance = conn.create_node(name=instance_name,
  168. image=image,
  169. size=flavor,
  170. networks=[network],
  171. ex_keyname=KEYPAIR_NAME,
  172. ex_userdata=userdata,
  173. ex_security_groups=[all_in_one_security_group])
  174. conn.wait_until_running(nodes=[testing_instance], timeout=120, ssh_interface='private_ips')
  175. ###########################################################################
  176. #
  177. # assign all-in-one instance floating ip
  178. #
  179. ###########################################################################
  180. private_ip = None
  181. if len(testing_instance.private_ips):
  182. private_ip = testing_instance.private_ips[0]
  183. print(f'Private IP found: {private_ip}')
  184. public_ip = None
  185. if len(testing_instance.public_ips):
  186. public_ip = testing_instance.public_ips[0]
  187. print(f'Public IP found: {public_ip}')
  188. print('Checking for unused Floating IP...')
  189. unused_floating_ip = None
  190. for floating_ip in conn.ex_list_floating_ips():
  191. if not floating_ip.node_id:
  192. unused_floating_ip = floating_ip
  193. break
  194. if not unused_floating_ip and len(conn.ex_list_floating_ip_pools()):
  195. pool = conn.ex_list_floating_ip_pools()[0]
  196. print(f'Allocating new Floating IP from pool: {pool}')
  197. unused_floating_ip = pool.create_floating_ip()
  198. if public_ip:
  199. print('Instance ' + testing_instance.name + ' already has a public ip. Skipping attachment.')
  200. elif unused_floating_ip:
  201. conn.ex_attach_floating_ip_to_node(testing_instance, unused_floating_ip)
  202. actual_ip_address = None
  203. if public_ip:
  204. actual_ip_address = public_ip
  205. elif unused_floating_ip:
  206. actual_ip_address = unused_floating_ip.ip_address
  207. elif private_ip:
  208. actual_ip_address = private_ip
  209. print('\n\n#### Deployment finished\n\n')
  210. print('After some minutes, as soon as cloud-init installed required packages and the\n'
  211. 'faafo app, (First App Application For OpenStack) fractals demo will be available\n'
  212. f'at http://{actual_ip_address}\n')
  213. print('You can use ssh to login to the instance using your private key. Default user name for official Ubuntu\n'
  214. f'Cloud Images is: ubuntu, so you can use, e.g.: "ssh -i ~/.ssh/id_rsa ubuntu@{actual_ip_address}" if your\n'
  215. 'private key is in the default location.\n\n'
  216. 'After login, you can list available fractals using "faafo list". \n'
  217. 'To request the generation of new fractals, you can use "faafo create".\n\n'
  218. 'You can also see other options to use the faafo example cloud service using "faafo -h".\n\n'
  219. 'If you cannot start faafo command and/or do not see the webpage, you can check the Instance Console Log of\n'
  220. 'the instance, e.g., in OpenStack web interface or look at "tail -f /var/log/cloud-init*.log" for the\n'
  221. 'cloud-init log files.\n')
  222. if __name__ == '__main__':
  223. main()