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.

252 lines
10 KiB

4 years ago
  1. #!/home/alpcentaur/ProjektA/PrototypeWebApp/venv/bin/python3.5
  2. # Copyright (c) 2009 Chris Moyer http://coredumped.org/
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining a
  5. # copy of this software and associated documentation files (the
  6. # "Software"), to deal in the Software without restriction, including
  7. # without limitation the rights to use, copy, modify, merge, publish, dis-
  8. # tribute, sublicense, and/or sell copies of the Software, and to permit
  9. # persons to whom the Software is furnished to do so, subject to the fol-
  10. # lowing conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included
  13. # in all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  16. # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
  17. # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
  18. # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  19. # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21. #
  22. # Utility to launch an EC2 Instance
  23. #
  24. VERSION="0.2"
  25. CLOUD_INIT_SCRIPT = """#!/usr/bin/env python
  26. f = open("/etc/boto.cfg", "w")
  27. f.write(\"\"\"%s\"\"\")
  28. f.close()
  29. """
  30. import boto.pyami.config
  31. import boto.utils
  32. import re, os
  33. from boto.compat import ConfigParser
  34. class Config(boto.pyami.config.Config):
  35. """A special config class that also adds import abilities
  36. Directly in the config file. To have a config file import
  37. another config file, simply use "#import <path>" where <path>
  38. is either a relative path or a full URL to another config
  39. """
  40. def __init__(self):
  41. ConfigParser.__init__(self, {'working_dir' : '/mnt/pyami', 'debug' : '0'})
  42. def add_config(self, file_url):
  43. """Add a config file to this configuration
  44. :param file_url: URL for the file to add, or a local path
  45. :type file_url: str
  46. """
  47. if not re.match("^([a-zA-Z0-9]*:\/\/)(.*)", file_url):
  48. if not file_url.startswith("/"):
  49. file_url = os.path.join(os.getcwd(), file_url)
  50. file_url = "file://%s" % file_url
  51. (base_url, file_name) = file_url.rsplit("/", 1)
  52. base_config = boto.utils.fetch_file(file_url)
  53. base_config.seek(0)
  54. for line in base_config.readlines():
  55. match = re.match("^#import[\s\t]*([^\s^\t]*)[\s\t]*$", line)
  56. if match:
  57. self.add_config("%s/%s" % (base_url, match.group(1)))
  58. base_config.seek(0)
  59. self.readfp(base_config)
  60. def add_creds(self, ec2):
  61. """Add the credentials to this config if they don't already exist"""
  62. if not self.has_section('Credentials'):
  63. self.add_section('Credentials')
  64. self.set('Credentials', 'aws_access_key_id', ec2.aws_access_key_id)
  65. self.set('Credentials', 'aws_secret_access_key', ec2.aws_secret_access_key)
  66. def __str__(self):
  67. """Get config as string"""
  68. from StringIO import StringIO
  69. s = StringIO()
  70. self.write(s)
  71. return s.getvalue()
  72. SCRIPTS = []
  73. def scripts_callback(option, opt, value, parser):
  74. arg = value.split(',')
  75. if len(arg) == 1:
  76. SCRIPTS.append(arg[0])
  77. else:
  78. SCRIPTS.extend(arg)
  79. setattr(parser.values, option.dest, SCRIPTS)
  80. def add_script(scr_url):
  81. """Read a script and any scripts that are added using #import"""
  82. base_url = '/'.join(scr_url.split('/')[:-1]) + '/'
  83. script_raw = boto.utils.fetch_file(scr_url)
  84. script_content = ''
  85. for line in script_raw.readlines():
  86. match = re.match("^#import[\s\t]*([^\s^\t]*)[\s\t]*$", line)
  87. #if there is an import
  88. if match:
  89. #Read the other script and put it in that spot
  90. script_content += add_script("%s/%s" % (base_url, match.group(1)))
  91. else:
  92. #Otherwise, add the line and move on
  93. script_content += line
  94. return script_content
  95. if __name__ == "__main__":
  96. try:
  97. import readline
  98. except ImportError:
  99. pass
  100. import sys
  101. import time
  102. import boto
  103. from boto.ec2 import regions
  104. from optparse import OptionParser
  105. from boto.mashups.iobject import IObject
  106. parser = OptionParser(version=VERSION, usage="%prog [options] config_url")
  107. parser.add_option("-c", "--max-count", help="Maximum number of this type of instance to launch", dest="max_count", default="1")
  108. parser.add_option("--min-count", help="Minimum number of this type of instance to launch", dest="min_count", default="1")
  109. parser.add_option("--cloud-init", help="Indicates that this is an instance that uses 'CloudInit', Ubuntu's cloud bootstrap process. This wraps the config in a shell script command instead of just passing it in directly", dest="cloud_init", default=False, action="store_true")
  110. parser.add_option("-g", "--groups", help="Security Groups to add this instance to", action="append", dest="groups")
  111. parser.add_option("-a", "--ami", help="AMI to launch", dest="ami_id")
  112. parser.add_option("-t", "--type", help="Type of Instance (default m1.small)", dest="type", default="m1.small")
  113. parser.add_option("-k", "--key", help="Keypair", dest="key_name")
  114. parser.add_option("-z", "--zone", help="Zone (default us-east-1a)", dest="zone", default="us-east-1a")
  115. parser.add_option("-r", "--region", help="Region (default us-east-1)", dest="region", default="us-east-1")
  116. parser.add_option("-i", "--ip", help="Elastic IP", dest="elastic_ip")
  117. parser.add_option("-n", "--no-add-cred", help="Don't add a credentials section", default=False, action="store_true", dest="nocred")
  118. parser.add_option("--save-ebs", help="Save the EBS volume on shutdown, instead of deleting it", default=False, action="store_true", dest="save_ebs")
  119. parser.add_option("-w", "--wait", help="Wait until instance is running", default=False, action="store_true", dest="wait")
  120. parser.add_option("-d", "--dns", help="Returns public and private DNS (implicates --wait)", default=False, action="store_true", dest="dns")
  121. parser.add_option("-T", "--tag", help="Set tag", default=None, action="append", dest="tags", metavar="key:value")
  122. parser.add_option("-s", "--scripts", help="Pass in a script or a folder containing scripts to be run when the instance starts up, assumes cloud-init. Specify scripts in a list specified by commas. If multiple scripts are specified, they are run lexically (A good way to ensure they run in the order is to prefix filenames with numbers)", type='string', action="callback", callback=scripts_callback)
  123. parser.add_option("--role", help="IAM Role to use, this implies --no-add-cred", dest="role")
  124. (options, args) = parser.parse_args()
  125. if len(args) < 1:
  126. parser.print_help()
  127. sys.exit(1)
  128. file_url = os.path.expanduser(args[0])
  129. cfg = Config()
  130. cfg.add_config(file_url)
  131. for r in regions():
  132. if r.name == options.region:
  133. region = r
  134. break
  135. else:
  136. print("Region %s not found." % options.region)
  137. sys.exit(1)
  138. ec2 = boto.connect_ec2(region=region)
  139. if not options.nocred and not options.role:
  140. cfg.add_creds(ec2)
  141. iobj = IObject()
  142. if options.ami_id:
  143. ami = ec2.get_image(options.ami_id)
  144. else:
  145. ami_id = options.ami_id
  146. l = [(a, a.id, a.location) for a in ec2.get_all_images()]
  147. ami = iobj.choose_from_list(l, prompt='Choose AMI')
  148. if options.key_name:
  149. key_name = options.key_name
  150. else:
  151. l = [(k, k.name, '') for k in ec2.get_all_key_pairs()]
  152. key_name = iobj.choose_from_list(l, prompt='Choose Keypair').name
  153. if options.groups:
  154. groups = options.groups
  155. else:
  156. groups = []
  157. l = [(g, g.name, g.description) for g in ec2.get_all_security_groups()]
  158. g = iobj.choose_from_list(l, prompt='Choose Primary Security Group')
  159. while g != None:
  160. groups.append(g)
  161. l.remove((g, g.name, g.description))
  162. g = iobj.choose_from_list(l, prompt='Choose Additional Security Group (0 to quit)')
  163. user_data = str(cfg)
  164. # If it's a cloud init AMI,
  165. # then we need to wrap the config in our
  166. # little wrapper shell script
  167. if options.cloud_init:
  168. user_data = CLOUD_INIT_SCRIPT % user_data
  169. scriptuples = []
  170. if options.scripts:
  171. scripts = options.scripts
  172. scriptuples.append(('user_data', user_data))
  173. for scr in scripts:
  174. scr_url = scr
  175. if not re.match("^([a-zA-Z0-9]*:\/\/)(.*)", scr_url):
  176. if not scr_url.startswith("/"):
  177. scr_url = os.path.join(os.getcwd(), scr_url)
  178. try:
  179. newfiles = os.listdir(scr_url)
  180. for f in newfiles:
  181. #put the scripts in the folder in the array such that they run in the correct order
  182. scripts.insert(scripts.index(scr) + 1, scr.split("/")[-1] + "/" + f)
  183. except OSError:
  184. scr_url = "file://%s" % scr_url
  185. try:
  186. scriptuples.append((scr, add_script(scr_url)))
  187. except Exception as e:
  188. pass
  189. user_data = boto.utils.write_mime_multipart(scriptuples, compress=True)
  190. shutdown_proc = "terminate"
  191. if options.save_ebs:
  192. shutdown_proc = "save"
  193. instance_profile_name = None
  194. if options.role:
  195. instance_profile_name = options.role
  196. r = ami.run(min_count=int(options.min_count), max_count=int(options.max_count),
  197. key_name=key_name, user_data=user_data,
  198. security_groups=groups, instance_type=options.type,
  199. placement=options.zone, instance_initiated_shutdown_behavior=shutdown_proc,
  200. instance_profile_name=instance_profile_name)
  201. instance = r.instances[0]
  202. if options.tags:
  203. for tag_pair in options.tags:
  204. name = tag_pair
  205. value = ''
  206. if ':' in tag_pair:
  207. name, value = tag_pair.split(':', 1)
  208. instance.add_tag(name, value)
  209. if options.dns:
  210. options.wait = True
  211. if not options.wait:
  212. sys.exit(0)
  213. while True:
  214. instance.update()
  215. if instance.state == 'running':
  216. break
  217. time.sleep(3)
  218. if options.dns:
  219. print("Public DNS name: %s" % instance.public_dns_name)
  220. print("Private DNS name: %s" % instance.private_dns_name)