cluster 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. #!/usr/bin/env python
  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 create(self, args):
  22. """
  23. Create an OpenShift cluster for given provider
  24. :param args: command line arguments provided by user
  25. :return: exit status from run command
  26. """
  27. env = {'cluster_id': args.cluster_id}
  28. playbook = "playbooks/{}/openshift-cluster/launch.yml".format(args.provider)
  29. inventory = self.setup_provider(args.provider)
  30. env['num_masters'] = args.masters
  31. env['num_nodes'] = args.nodes
  32. return self.action(args, inventory, env, playbook)
  33. def terminate(self, args):
  34. """
  35. Destroy OpenShift cluster
  36. :param args: command line arguments provided by user
  37. :return: exit status from run command
  38. """
  39. env = {'cluster_id': args.cluster_id}
  40. playbook = "playbooks/{}/openshift-cluster/terminate.yml".format(args.provider)
  41. inventory = self.setup_provider(args.provider)
  42. return self.action(args, inventory, env, playbook)
  43. def list(self, args):
  44. """
  45. List VMs in cluster
  46. :param args: command line arguments provided by user
  47. :return: exit status from run command
  48. """
  49. env = {'cluster_id': args.cluster_id}
  50. playbook = "playbooks/{}/openshift-cluster/list.yml".format(args.provider)
  51. inventory = self.setup_provider(args.provider)
  52. return self.action(args, inventory, env, playbook)
  53. def update(self, args):
  54. """
  55. Update to latest OpenShift across clustered VMs
  56. :param args: command line arguments provided by user
  57. :return: exit status from run command
  58. """
  59. env = {'cluster_id': args.cluster_id}
  60. playbook = "playbooks/{}/openshift-cluster/update.yml".format(args.provider)
  61. inventory = self.setup_provider(args.provider)
  62. return self.action(args, inventory, env, playbook)
  63. def setup_provider(self, provider):
  64. """
  65. Setup ansible playbook environment
  66. :param provider: command line arguments provided by user
  67. :return: path to inventory for given provider
  68. """
  69. config = ConfigParser.ConfigParser()
  70. if 'gce' == provider:
  71. config.readfp(open('inventory/gce/gce.ini'))
  72. for key in config.options('gce'):
  73. os.environ[key] = config.get('gce', key)
  74. inventory = '-i inventory/gce/gce.py'
  75. elif 'aws' == provider:
  76. config.readfp(open('inventory/aws/ec2.ini'))
  77. for key in config.options('ec2'):
  78. os.environ[key] = config.get('ec2', key)
  79. inventory = '-i inventory/aws/ec2.py'
  80. else:
  81. # this code should never be reached
  82. raise ValueError("invalid PROVIDER {}".format(provider))
  83. return inventory
  84. def action(self, args, inventory, env, playbook):
  85. """
  86. Build ansible-playbook command line and execute
  87. :param args: command line arguments provided by user
  88. :param inventory: derived provider library
  89. :param env: environment variables for kubernetes
  90. :param playbook: ansible playbook to execute
  91. :return: exit status from ansible-playbook command
  92. """
  93. verbose = ''
  94. if args.verbose > 0:
  95. verbose = '-{}'.format('v' * args.verbose)
  96. ansible_env = '-e \'{}\''.format(
  97. ' '.join(['%s=%s' % (key, value) for (key, value) in env.items()])
  98. )
  99. command = 'ansible-playbook {} {} {} {}'.format(
  100. verbose, inventory, ansible_env, playbook
  101. )
  102. if args.verbose > 1:
  103. command = 'time {}'.format(command)
  104. if args.verbose > 0:
  105. sys.stderr.write('RUN [{}]\n'.format(command))
  106. sys.stderr.flush()
  107. return os.system(command)
  108. if __name__ == '__main__':
  109. """
  110. Implemented to support writing unit tests
  111. """
  112. cluster = Cluster()
  113. providers = ['gce', 'aws']
  114. parser = argparse.ArgumentParser(
  115. description='Python wrapper to ensure proper environment for OpenShift ansible playbooks',
  116. )
  117. parser.add_argument('-v', '--verbose', action='count', help='Multiple -v options increase the verbosity')
  118. parser.add_argument('--version', action='version', version='%(prog)s 0.2')
  119. meta_parser = argparse.ArgumentParser(add_help=False)
  120. meta_parser.add_argument('provider', choices=providers, help='provider')
  121. meta_parser.add_argument('cluster_id', help='prefix for cluster VM names')
  122. action_parser = parser.add_subparsers(dest='action', title='actions', description='Choose from valid actions')
  123. create_parser = action_parser.add_parser('create', help='Create a cluster', parents=[meta_parser])
  124. create_parser.add_argument('-m', '--masters', default=1, type=int, help='number of masters to create in cluster')
  125. create_parser.add_argument('-n', '--nodes', default=2, type=int, help='number of nodes to create in cluster')
  126. create_parser.set_defaults(func=cluster.create)
  127. terminate_parser = action_parser.add_parser('terminate', help='Destroy a cluster', parents=[meta_parser])
  128. terminate_parser.add_argument('-f', '--force', action='store_true', help='Destroy cluster without confirmation')
  129. terminate_parser.set_defaults(func=cluster.terminate)
  130. update_parser = action_parser.add_parser('update', help='Update OpenShift across cluster', parents=[meta_parser])
  131. update_parser.add_argument('-f', '--force', action='store_true', help='Update cluster without confirmation')
  132. update_parser.set_defaults(func=cluster.update)
  133. list_parser = action_parser.add_parser('list', help='List VMs in cluster', parents=[meta_parser])
  134. list_parser.set_defaults(func=cluster.list)
  135. args = parser.parse_args()
  136. if 'terminate' == args.action and not args.force:
  137. answer = raw_input("This will destroy the ENTIRE {} environment. Are you sure? [y/N] ".format(args.cluster_id))
  138. if answer not in ['y', 'Y']:
  139. sys.stderr.write('\nACTION [terminate] aborted by user!\n')
  140. exit(1)
  141. if 'update' == args.action and not args.force:
  142. answer = raw_input("This is destructive and could corrupt {} environment. Continue? [y/N] ".format(args.cluster_id))
  143. if answer not in ['y', 'Y']:
  144. sys.stderr.write('\nACTION [update] aborted by user!\n')
  145. exit(1)
  146. status = args.func(args)
  147. if status != 0:
  148. sys.stderr.write("ACTION [{}] failed with exit status {}\n".format(args.action, status))
  149. exit(status)