docs: def scan_vpns(directory, auth_file, crt_file, tls_auth, key_direction,
                exclude_list, shuffle_lists, vm_num, vm_index, reduce_vp):
      """
      For each VPN, check if there are experiments and scan with it if
      necessary
  
      Note: the expected directory structure is
      args.directory
      -----vpns (contains the OpenVPN config files
      -----configs (contains the Centinel config files)
      -----exps (contains the experiments directories)
  
      :param directory: root directory that contains vpn configs and
                        centinel client configs
      :param auth_file: a text file with username at first line and
                        password at second line
      :param crt_file: optional root certificate file
      :param tls_auth: additional key
      :param key_direction: must specify if tls_auth is used
      :param exclude_list: optional list of exluded countries
      :param shuffle_lists: shuffle vpn list if set true
      :param vm_num: number of VMs that are running currently
      :param vm_index: index of current VM
      :param reduce_vp: reduce number of vantage points
      :return:
      """
  
      logging.info("Starting to run the experiments for each VPN")
      logging.warn("Excluding vantage points from: %s" % exclude_list)
  
      # iterate over each VPN
      vpn_dir = return_abs_path(directory, "vpns")
      conf_dir = return_abs_path(directory, "configs")
      home_dir = return_abs_path(directory, "home")
      if auth_file is not None:
          auth_file = return_abs_path(directory, auth_file)
      if crt_file is not None:
          crt_file = return_abs_path(directory, crt_file)
      if tls_auth is not None:
          tls_auth = return_abs_path(directory, tls_auth)
      conf_list = sorted(os.listdir(conf_dir))
  
      # determine VPN provider
      vpn_provider = None
      if "hma" in directory:
          vpn_provider = "hma"
      elif "ipvanish" in directory:
          vpn_provider = "ipvanish"
      elif "purevpn" in directory:
          vpn_provider = "purevpn"
      elif "vpngate" in directory:
          vpn_provider = "vpngate"
      if vpn_provider:
          logging.info("Detected VPN provider is %s" % vpn_provider)
      else:
          logging.warning("Cannot determine VPN provider!")
  
      # reduce size of list if reduce_vp is true
      if reduce_vp:
          logging.info("Reducing list size. Original size: %d" % len(conf_list))
          country_asn_set = set()
          reduced_conf_set = set()
          for filename in conf_list:
              centinel_config = os.path.join(conf_dir, filename)
              config = centinel.config.Configuration()
              config.parse_config(centinel_config)
  	    # filename is the hostname, need to resolve it to get vp_ip
              hostname = os.path.splitext(filename)[0]
              vp_ip = "unknown"
              try:
   		vp_ip = socket.gethostbyname(hostname)
   	    except Exception as exp:
   		logging.exception("Failed to resolve %s : %s" %(hostname,str(exp)))
   		continue
  	    # before
  #            vp_ip = os.path.splitext(filename)[0]
  
              try:
                  meta = centinel.backend.get_meta(config.params, vp_ip)
                  if 'country' in meta and 'as_number' in meta \
                          and meta['country'] and meta['as_number']:
                      country_asn = '_'.join([meta['country'], meta['as_number']])
                      if country_asn not in country_asn_set:
                          country_asn_set.add(country_asn)
                          reduced_conf_set.add(filename)
                  else:
                      # run this endpoint if missing info
                      reduced_conf_set.add(filename)
              except:
                  logging.warning("Failed to geolocate %s" % vp_ip)
                  reduced_conf_set.add(filename)
  
          conf_list = list(reduced_conf_set)
          logging.info("List size reduced. New size: %d" % len(conf_list))
  
      # sort file list to ensure the same filename sequence in each VM
      conf_list = sorted(conf_list)
  
      # only select its own portion according to vm_num and vm_index
      chunk_size = len(conf_list) / vm_num
      last_chunk_additional = len(conf_list) % vm_num
      start_pointer = 0 + (vm_index - 1) * chunk_size
      end_pointer = start_pointer + chunk_size
      if vm_index == vm_num:
          end_pointer += last_chunk_additional
      conf_list = conf_list[start_pointer:end_pointer]
  
      if shuffle_lists:
          shuffle(conf_list)
  
      number = 1
      total = len(conf_list)
  
      external_ip = get_external_ip()
      if external_ip is None:
          logging.error("No network connection, exiting...")
          return
      logging.info("Current external IP: %s" % (external_ip))
  
      # getting namesevers that should be excluded
      local_nameservers = dns.resolver.Resolver().nameservers
  
      for filename in conf_list:
          # Check network connection first
          time.sleep(5)
          logging.info("Checking network connectivity...")
          current_ip = get_external_ip()
          if current_ip is None:
              logging.error("Network connection lost!")
              break
          elif current_ip != external_ip:
              logging.error("VPN still connected! IP: %s" % current_ip)
              if len(openvpn.OpenVPN.connected_instances) == 0:
                  logging.error("No active OpenVPN instance found! Exiting...")
                  break
              else:
                  logging.warn("Trying to disconnect VPN")
                  for instance in openvpn.OpenVPN.connected_instances:
                      instance.stop()
                      time.sleep(5)
  
                  current_ip = get_external_ip()
                  if current_ip is None or current_ip != external_ip:
                      logging.error("Stopping VPN failed! Exiting...")
                      break
  
              logging.info("Disconnecting VPN successfully")
  
          # start centinel for this endpoint
          logging.info("Moving onto (%d/%d) %s" % (number, total, filename))
  
          number += 1
          vpn_config = os.path.join(vpn_dir, filename)
          centinel_config = os.path.join(conf_dir, filename)
  
          # before starting the VPN, check if there are any experiments
          # to run
          config = centinel.config.Configuration()
          config.parse_config(centinel_config)
  
          # assuming that each VPN config file has a name like:
          # [ip-address].ovpn, we can extract IP address from filename
          # and use it to geolocate and fetch experiments before connecting
          # to VPN.
          vpn_address, extension = os.path.splitext(filename)
  	
  	hostname = os.path.splitext(filename)[0]
          vp_ip = "unknown"
          try:
   		vp_ip = socket.gethostbyname(hostname)
   	except Exception as exp:
  		logging.exception("Failed to resolve %s : %s" %(hostname,str(exp)))
  		
          country = None
          try:
  #            meta = centinel.backend.get_meta(config.params,vpn_address)
  #	    vpn_address contains the hostname
              meta = centinel.backend.get_meta(config.params,vp_ip)
  
  
              if 'country' in meta:
                  country = meta['country']
          except:
  #           logging.exception("%s: Failed to geolocate %s" % (filename, vpn_address))
  #	    vpn_address contains the hostname
      	    logging.exception("%s: Failed to geolocate %s" % (filename, vp_ip))
          if country and exclude_list and country in exclude_list:
              logging.info("%s: Skipping this server (%s)" % (filename, country))
              continue
  
          # try setting the VPN info (IP and country) to get appropriate
          # experiemnts and input data.
          try:
              #centinel.backend.set_vpn_info(config.params, vpn_address, country)
  	    # vpn_address is the filename which has the hostname
  	    centinel.backend.set_vpn_info(config.params, vp_ip, country)
          except Exception as exp:
              logging.exception("%s: Failed to set VPN info: %s" % (filename, exp))
  
          logging.info("%s: Synchronizing." % filename)
          try:
              centinel.backend.sync(config.params)
          except Exception as exp:
              logging.exception("%s: Failed to sync: %s" % (filename, exp))
  
          if not experiments_available(config.params):
              logging.info("%s: No experiments available." % filename)
              try:
  #                centinel.backend.set_vpn_info(config.params, vpn_address, country)
  #		vpn_address contains the hostname
                  centinel.backend.set_vpn_info(config.params, vp_ip, country)
  
  
              except Exception as exp:
                  logging.exception("Failed to set VPN info: %s" % exp)
              continue
  
          # add exclude_nameservers to scheduler
          sched_path = os.path.join(home_dir, filename, "experiments", "scheduler.info")
          if os.path.exists(sched_path):
              with open(sched_path, 'r+') as f:
                  sched_info = json.load(f)
                  for task in sched_info:
                      if "python_exps" in sched_info[task] and "baseline" in sched_info[task]["python_exps"]:
                          if "params" in sched_info[task]["python_exps"]["baseline"]:
                              sched_info[task]["python_exps"]["baseline"]["params"]["exclude_nameservers"] = \
                                  local_nameservers
                          else:
                              sched_info[task]["python_exps"]["baseline"]["params"] = \
                                  {"exclude_nameservers": local_nameservers}
  
                  # write back to same file
                  f.seek(0)
                  json.dump(sched_info, f, indent=2)
                  f.truncate()
  
          logging.info("%s: Starting VPN." % filename)
  
          vpn = openvpn.OpenVPN(timeout=60, auth_file=auth_file, config_file=vpn_config,
                                crt_file=crt_file, tls_auth=tls_auth, key_direction=key_direction)
  
          vpn.start()
          if not vpn.started:
              logging.error("%s: Failed to start VPN!" % filename)
              vpn.stop()
              time.sleep(5)
              continue
  
          logging.info("%s: Running Centinel." % filename)
          try:
              client = centinel.client.Client(config.params, vpn_provider)
              centinel.conf = config.params
              # do not use client logging config
              # client.setup_logging()
              client.run()
          except Exception as exp:
              logging.exception("%s: Error running Centinel: %s" % (filename, exp))
  
          logging.info("%s: Stopping VPN." % filename)
          vpn.stop()
          time.sleep(5)
  
          logging.info("%s: Synchronizing." % filename)
          try:
              centinel.backend.sync(config.params)
          except Exception as exp:
              logging.exception("%s: Failed to sync: %s" % (filename, exp))
  
          # try setting the VPN info (IP and country) to the correct address
          # after sync is over.
          try:
  #            centinel.backend.set_vpn_info(config.params, vpn_address, country)
  #	    vpn_address contains the hostname
              centinel.backend.set_vpn_info(config.params, vp_ip, country)
  
  
          except Exception as exp:
              logging.exception("Failed to set VPN info: %s" % exp)