cluster 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #!/usr/bin/env python2
  2. # vim: expandtab:tabstop=4:shiftwidth=4
  3. import argparse
  4. import ConfigParser
  5. import sys
  6. import os
  7. class Cluster(object):
  8. """
  9. Control and Configuration Interface for OpenShift Clusters
  10. """
  11. def __init__(self):
  12. # setup ansible ssh environment
  13. if 'ANSIBLE_SSH_ARGS' not in os.environ:
  14. os.environ['ANSIBLE_SSH_ARGS'] = (
  15. '-o ForwardAgent=yes '
  16. '-o StrictHostKeyChecking=no '
  17. '-o UserKnownHostsFile=/dev/null '
  18. '-o ControlMaster=auto '
  19. '-o ControlPersist=600s '
  20. )
  21. def get_deployment_type(self, args):
  22. """
  23. Get the deployment_type based on the environment variables and the
  24. command line arguments
  25. :param args: command line arguments provided by the user
  26. :return: string representing the deployment type
  27. """
  28. deployment_type = 'origin'
  29. if args.deployment_type:
  30. deployment_type = args.deployment_type
  31. elif 'OS_DEPLOYMENT_TYPE' in os.environ:
  32. deployment_type = os.environ['OS_DEPLOYMENT_TYPE']
  33. return deployment_type
  34. def create(self, args):
  35. """
  36. Create an OpenShift cluster for given provider
  37. :param args: command line arguments provided by user
  38. :return: exit status from run command
  39. """
  40. env = {'cluster_id': args.cluster_id,
  41. 'deployment_type': self.get_deployment_type(args)}
  42. playbook = "playbooks/{}/openshift-cluster/launch.yml".format(args.provider)
  43. inventory = self.setup_provider(args.provider)
  44. env['num_masters'] = args.masters
  45. env['num_nodes'] = args.nodes
  46. return self.action(args, inventory, env, playbook)
  47. def terminate(self, args):
  48. """
  49. Destroy OpenShift cluster
  50. :param args: command line arguments provided by user
  51. :return: exit status from run command
  52. """
  53. env = {'cluster_id': args.cluster_id,
  54. 'deployment_type': self.get_deployment_type(args)}
  55. playbook = "playbooks/{}/openshift-cluster/terminate.yml".format(args.provider)
  56. inventory = self.setup_provider(args.provider)
  57. return self.action(args, inventory, env, playbook)
  58. def list(self, args):
  59. """
  60. List VMs in cluster
  61. :param args: command line arguments provided by user
  62. :return: exit status from run command
  63. """
  64. env = {'cluster_id': args.cluster_id,
  65. 'deployment_type': self.get_deployment_type(args)}
  66. playbook = "playbooks/{}/openshift-cluster/list.yml".format(args.provider)
  67. inventory = self.setup_provider(args.provider)
  68. return self.action(args, inventory, env, playbook)
  69. def config(self, args):
  70. """
  71. Configure or reconfigure OpenShift across clustered VMs
  72. :param args: command line arguments provided by user
  73. :return: exit status from run command
  74. """
  75. env = {'cluster_id': args.cluster_id,
  76. 'deployment_type': self.get_deployment_type(args)}
  77. playbook = "playbooks/{}/openshift-cluster/config.yml".format(args.provider)
  78. inventory = self.setup_provider(args.provider)
  79. return self.action(args, inventory, env, playbook)
  80. def update(self, args):
  81. """
  82. Update to latest OpenShift across clustered VMs
  83. :param args: command line arguments provided by user
  84. :return: exit status from run command
  85. """
  86. env = {'cluster_id': args.cluster_id,
  87. 'deployment_type': self.get_deployment_type(args)}
  88. playbook = "playbooks/{}/openshift-cluster/update.yml".format(args.provider)
  89. inventory = self.setup_provider(args.provider)
  90. return self.action(args, inventory, env, playbook)
  91. def setup_provider(self, provider):
  92. """
  93. Setup ansible playbook environment
  94. :param provider: command line arguments provided by user
  95. :return: path to inventory for given provider
  96. """
  97. config = ConfigParser.ConfigParser()
  98. if 'gce' == provider:
  99. config.readfp(open('inventory/gce/hosts/gce.ini'))
  100. for key in config.options('gce'):
  101. os.environ[key] = config.get('gce', key)
  102. inventory = '-i inventory/gce/hosts'
  103. elif 'aws' == provider:
  104. config.readfp(open('inventory/aws/hosts/ec2.ini'))
  105. for key in config.options('ec2'):
  106. os.environ[key] = config.get('ec2', key)
  107. inventory = '-i inventory/aws/hosts'
  108. elif 'libvirt' == provider:
  109. inventory = '-i inventory/libvirt/hosts'
  110. else:
  111. # this code should never be reached
  112. raise ValueError("invalid PROVIDER {}".format(provider))
  113. return inventory
  114. def action(self, args, inventory, env, playbook):
  115. """
  116. Build ansible-playbook command line and execute
  117. :param args: command line arguments provided by user
  118. :param inventory: derived provider library
  119. :param env: environment variables for kubernetes
  120. :param playbook: ansible playbook to execute
  121. :return: exit status from ansible-playbook command
  122. """
  123. verbose = ''
  124. if args.verbose > 0:
  125. verbose = '-{}'.format('v' * args.verbose)
  126. ansible_env = '-e \'{}\''.format(
  127. ' '.join(['%s=%s' % (key, value) for (key, value) in env.items()])
  128. )
  129. command = 'ansible-playbook {} {} {} {}'.format(
  130. verbose, inventory, ansible_env, playbook
  131. )
  132. if args.verbose > 1:
  133. command = 'time {}'.format(command)
  134. if args.verbose > 0:
  135. sys.stderr.write('RUN [{}]\n'.format(command))
  136. sys.stderr.flush()
  137. return os.system(command)
  138. if __name__ == '__main__':
  139. """
  140. Implemented to support writing unit tests
  141. """
  142. cluster = Cluster()
  143. providers = ['gce', 'aws', 'libvirt']
  144. parser = argparse.ArgumentParser(
  145. description='Python wrapper to ensure proper environment for OpenShift ansible playbooks',
  146. )
  147. parser.add_argument('-v', '--verbose', action='count',
  148. help='Multiple -v options increase the verbosity')
  149. parser.add_argument('--version', action='version', version='%(prog)s 0.2')
  150. meta_parser = argparse.ArgumentParser(add_help=False)
  151. meta_parser.add_argument('provider', choices=providers, help='provider')
  152. meta_parser.add_argument('cluster_id', help='prefix for cluster VM names')
  153. meta_parser.add_argument('-t', '--deployment-type',
  154. choices=['origin', 'online', 'enterprise'],
  155. help='Deployment type. (default: origin)')
  156. action_parser = parser.add_subparsers(dest='action', title='actions',
  157. description='Choose from valid actions')
  158. create_parser = action_parser.add_parser('create', help='Create a cluster',
  159. parents=[meta_parser])
  160. create_parser.add_argument('-m', '--masters', default=1, type=int,
  161. help='number of masters to create in cluster')
  162. create_parser.add_argument('-n', '--nodes', default=2, type=int,
  163. help='number of nodes to create in cluster')
  164. create_parser.set_defaults(func=cluster.create)
  165. config_parser = action_parser.add_parser('config',
  166. help='Configure or reconfigure a cluster',
  167. parents=[meta_parser])
  168. config_parser.set_defaults(func=cluster.config)
  169. terminate_parser = action_parser.add_parser('terminate',
  170. help='Destroy a cluster',
  171. parents=[meta_parser])
  172. terminate_parser.add_argument('-f', '--force', action='store_true',
  173. help='Destroy cluster without confirmation')
  174. terminate_parser.set_defaults(func=cluster.terminate)
  175. update_parser = action_parser.add_parser('update',
  176. help='Update OpenShift across cluster',
  177. parents=[meta_parser])
  178. update_parser.add_argument('-f', '--force', action='store_true',
  179. help='Update cluster without confirmation')
  180. update_parser.set_defaults(func=cluster.update)
  181. list_parser = action_parser.add_parser('list', help='List VMs in cluster',
  182. parents=[meta_parser])
  183. list_parser.set_defaults(func=cluster.list)
  184. args = parser.parse_args()
  185. if 'terminate' == args.action and not args.force:
  186. answer = raw_input("This will destroy the ENTIRE {} environment. Are you sure? [y/N] ".format(args.cluster_id))
  187. if answer not in ['y', 'Y']:
  188. sys.stderr.write('\nACTION [terminate] aborted by user!\n')
  189. exit(1)
  190. if 'update' == args.action and not args.force:
  191. answer = raw_input("This is destructive and could corrupt {} environment. Continue? [y/N] ".format(args.cluster_id))
  192. if answer not in ['y', 'Y']:
  193. sys.stderr.write('\nACTION [update] aborted by user!\n')
  194. exit(1)
  195. status = args.func(args)
  196. if status != 0:
  197. sys.stderr.write("ACTION [{}] failed with exit status {}\n".format(args.action, status))
  198. exit(status)