text: #!/usr/bin/env python
  """
  Cloudman CLI Launcher v0.0.1
  
  Usage: cm-launcher [options]
  
  Options:
      --access_key=key
      --secret_key=key
      --cluster_name=name         Set the cluster name
      --cluster_type=type         Specify cluster type (Test/Data/Galaxy/Shared_cluster)
      --default_bucket_url=url    Set default_bucket_url (priority over bucket_default)
      --image_id=image_id         Set image id to use
      --instance_type=type        Set instance type
      --password=passwd           Set password [default: random]
      --key_name=key_name         SSH Key to use
      --zone=zone                 Specify an availability zone
  """
  
  DEFAULT_CLUSTER_NAME = "cls-"
  DEFAULT_INSTANCE_TYPE = 'm1.medium'
  
  import string, os, sys
  from time import sleep
  from collections import defaultdict
  import webbrowser as wb
  from docopt import docopt
  
  from boto import config as boto_config
  from bioblend.cloudman import CloudManConfig, CloudManInstance, CloudManLauncher, VMLaunchException
  from bioblend.util import Bunch
  
  
  def process_args(config, cli_args):
      if cli_args.get('--password') == 'random':
          del cli_args['--password']
      config.update({k.lstrip('--'): v for k, v in cli_args.iteritems()
          if v is not None})
      return config
  # make keys consistent, then update, starting at lowest priority to highest
  
  def process_env(config):
      access_key = os.environ.get('AWS_ACCESS_KEY_ID', None) or os.environ.get('EC2_ACCESS_KEY', None)
      if access_key:
          config['access_key'] = access_key
      secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY', None) or os.environ.get('EC2_SECRET_KEY', None)
      if secret_key:
          config['secret_key'] = secret_key
      password = os.environ.get('CM_PASSWORD', None)
      if password:
          config['password'] = password
      return config
  
  def process_cfg_file():
      pass
  
  def process_configuration(cli_args):
      config = defaultdict(lambda: None)
      process_env(config)
      process_args(config, cli_args)
      if not config['access_key'] or not config['secret_key']:
          raise RuntimeError("No credentials")
      return config
  
  
  def create_cloud_config():
      cloud_config = Bunch(
          name='nectar',
          cloud_type='openstack',
          bucket_default='cloudman-os',
          region_name='melbourne',
          region_endpoint='nova.rc.nectar.org.au',
          ec2_port=8773,
          ec2_conn_path='/services/Cloud',
          cidr_range='',      # ips that can access (sec groups)
          is_secure=True,
          s3_host='swift.rc.nectar.org.au',
          s3_port=8888,
          s3_conn_path='/')
      return cloud_config
  
  def create_cloudman_config(cloud_config, config):
      cfg = CloudManConfig(access_key=config['access_key'],
                           secret_key=config['secret_key'],
                           cluster_name=config['cluster_name'] or DEFAULT_CLUSTER_NAME + mkpasswd(5),
                           image_id=config['image_id'],
                           instance_type=config['instance_type'] or DEFAULT_INSTANCE_TYPE,
                           password=config['password'] or mkpasswd(),
                           placement=config['zone'],
                           key_name=config['key_name'],
                           cloud_metadata=cloud_config,
                           block_until_ready=True)
      return cfg
  
  def mkpasswd(length=20):
  # https://stackoverflow.com/questions/7479442/high-quality-simple-random-password-generator
      chars = string.ascii_uppercase + string.digits + string.ascii_lowercase
      password = ''
      for i in range(length):
          password += chars[ord(os.urandom(1)) % len(chars)]
      return password
  
  def launch_master(cm_cfg, **kwargs):
      launcher = CloudManLauncher(cm_cfg.access_key, cm_cfg.secret_key, cm_cfg.cloud_metadata)
      result = launcher.launch(cm_cfg.cluster_name, cm_cfg.image_id, cm_cfg.instance_type,
          cm_cfg.password, cm_cfg.kernel_id, cm_cfg.ramdisk_id, cm_cfg.key_name,
          cm_cfg.security_groups, cm_cfg.placement, **kwargs)
      if (result['error'] is not None):
          raise VMLaunchException("Error launching cloudman instance: {0}".format(result['error']))
      return CloudManInstance(None, None, launcher=launcher, launch_result=result,
              cloudman_config=cm_cfg)
  
  def cm_launch(cloud, config):
      # build cloudman config
      cm_cfg = create_cloudman_config(cloud, config)
      # launch instance
      #instance = CloudManInstance.launch_instance(cm_cfg)
      instance = launch_master(cm_cfg, default_bucket_url=config['default_bucket_url'])
      print("Starting cluster: {0}. Please wait.".format(cm_cfg.cluster_name))
      state = instance.get_machine_status()
      while state['instance_state'] not in {'running', 'error'}:
          sleep(10)
          state = instance.get_machine_status()
          sys.stderr.write("\r{0}".format(state['instance_state']))
          sys.stderr.flush()
      print()
      if state['instance_state'] == 'running':
          print("IP: {0}, Password: {1}".format(state['public_ip'], cm_cfg.password))
          #print(instance.get_static_state()) # nginx wants a user, bioblend doesn't provide
          wb.open_new_tab("http://{0}/cloud".format(state['public_ip']))
  
  def cm_launch_from_cli():
      if len(sys.argv[1:]) == 0:
          print("No arguments provided. Quitting.")
          sys.exit(1)
      args = docopt(__doc__, version='Galaxy Docker Launcher 0.0.1a1')
      # process args, combine with boto/bioblend config
      config = process_configuration(args)
      # build cloud config
      cloud = create_cloud_config()
      cm_launch(cloud, config)
  