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

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