docs: def start_instance(
              self,
              key_name,
              public_key_path,
              private_key_path,
              security_group,
              flavor,
              image,
              image_userdata,
              location=None,
              base_name=None,
              username=None,
              node_name=None,
              host_name=None,
              use_public_ips=None,
              wait_timeout=None,
              use_short_vm_names=None,
              n_cloud_services=None,
              n_storage_accounts=None,
              **kwargs):
          """Starts a new instance on the cloud using the given properties.
          Multiple instances might be started in different threads at the same
          time. The implementation should handle any problems regarding this
          itself.
          :return: str - instance id of the started instance
          """
  
          if self._start_failed:
              raise Exception('start_instance for node %s: failing due to'
                              ' previous errors.' % node_name)
  
          index = None
          with self._resource_lock:
              # it'd be nice if elasticluster called something like
              # init_cluster() with all the args that will be the
              # same for every node created. But since it doesn't, handle that on
              # first start_instance call.
              if not self._cluster_prep_done:
                  self._times['CLUSTER_START'] = time.time()
                  self._config.setup(
                      key_name,
                      public_key_path,
                      private_key_path,
                      security_group,
                      location,
                      base_name=base_name,
                      username=username,
                      use_public_ips=use_public_ips,
                      wait_timeout=wait_timeout,
                      use_short_vm_names=use_short_vm_names,
                      n_cloud_services=n_cloud_services,
                      n_storage_accounts=n_storage_accounts,
                      **kwargs)
                  # we know we're starting the first node, so create global
                  # requirements now
                  self._create_global_reqs()
                  if self._start_failed:
                      return None
                  # this will allow vms to be created
                  self._times['SETUP_DONE'] = time.time()
                  self._cluster_prep_done = True
  
              # absolute node index in cluster (0..n-1) determines what
              # subscription, cloud service, storage
              # account, etc. this VM will use. Create the vm and add it to
              # its cloud service, then try to start it.
              index = self._n_instances
  
              v_m = AzureVM(
                  self._config,
                  index,
                  flavor=flavor,
                  image=image,
                  node_name=node_name,
                  host_name=host_name,
                  image_userdata=image_userdata)
              v_m._cloud_service._instances[v_m._qualified_name] = v_m
  
          try:
              v_m._cloud_service._start_vm(v_m)
          except Exception:
              log.error(traceback.format_exc())
              log.error("setting start_failed flag. Will not "
                        "try to start further nodes.")
              self._start_failed = True
              return None
          log.debug('started instance %s', v_m._qualified_name)
          if index == self._config._n_vms_requested - 1:
              # all nodes started
              self._times['NODES_STARTED'] = time.time()
              self._times['SETUP_ELAPSED'] = self._times['SETUP_DONE'] - \
                  self._times['CLUSTER_START']
              self._times['NODE_START_ELAPSED'] = self._times['NODES_STARTED']\
                  - self._times['SETUP_DONE']
              self._times['CLUSTER_START_ELAPSED'] = \
                  self._times['SETUP_ELAPSED'] + \
                  self._times['NODE_START_ELAPSED']
  
              log.debug("setup time: %.1f sec", self._times['SETUP_ELAPSED'])
              log.debug("node start time: %.1f sec (%.1f sec per vm)",
                        self._times['NODE_START_ELAPSED'],
                        self._times['NODE_START_ELAPSED'] /
                        self._config._n_vms_requested)
              log.debug("total cluster start time: %.1f sec (%.1f sec per vm)",
                        self._times['CLUSTER_START_ELAPSED'],
                        self._times['CLUSTER_START_ELAPSED'] /
                        self._config._n_vms_requested)
              # pause here to try to address the fact that Ansible setup fails
              # more often on the first try than subsequent tries
              time.sleep(_retry_sleep())
          self._save_or_update()  # store our state
          return v_m._qualified_name