docs: def add_cli_options(cli_options, action):
      # type: (dict, str) -> None
      """Adds CLI options to the configuration object
      :param dict cli_options: CLI options dict
      :param TransferAction action: action
      """
      cli_options['_action'] = action.name.lower()
      # if url is present, convert to constituent options
      if blobxfer.util.is_not_empty(cli_options.get('storage_url')):
          if (blobxfer.util.is_not_empty(cli_options.get('storage_account')) or
                  blobxfer.util.is_not_empty(cli_options.get('mode')) or
                  blobxfer.util.is_not_empty(cli_options.get('endpoint')) or
                  blobxfer.util.is_not_empty(cli_options.get('remote_path'))):
              raise ValueError(
                  'Specified both --storage-url and --storage-account, '
                  '--mode, --endpoint, or --remote-path')
          cli_options['storage_account'], mode, \
              cli_options['endpoint'], \
              cli_options['remote_path'], \
              sas = blobxfer.util.explode_azure_storage_url(
                  cli_options['storage_url'])
          if blobxfer.util.is_not_empty(sas):
              if blobxfer.util.is_not_empty(cli_options['sas']):
                  raise ValueError(
                      'Specified both --storage-url with a SAS token and --sas')
              cli_options['sas'] = sas
          if mode != 'blob' and mode != 'file':
              raise ValueError(
                  'Invalid derived mode from --storage-url: {}'.format(mode))
          if mode == 'file':
              cli_options['mode'] = mode
          del mode
          del sas
      storage_account = cli_options.get('storage_account')
      azstorage = {
          'endpoint': cli_options.get('endpoint')
      }
      if blobxfer.util.is_not_empty(storage_account):
          azstorage['accounts'] = {
              storage_account: (
                  cli_options.get('access_key') or cli_options.get('sas')
              )
          }
      sa_rp = {
          storage_account: cli_options.get('remote_path')
      }
      local_resource = cli_options.get('local_resource')
      # construct "argument" from cli options
      if action == TransferAction.Download:
          arg = {
              'source': [sa_rp] if sa_rp[storage_account] is not None else None,
              'destination': local_resource if local_resource is not None else
              None,
              'include': cli_options.get('include'),
              'exclude': cli_options.get('exclude'),
              'options': {
                  'check_file_md5': cli_options.get('file_md5'),
                  'chunk_size_bytes': cli_options.get('chunk_size_bytes'),
                  'delete_extraneous_destination': cli_options.get('delete'),
                  'max_single_object_concurrency': cli_options.get(
                      'max_single_object_concurrency'),
                  'mode': cli_options.get('mode'),
                  'overwrite': cli_options.get('overwrite'),
                  'recursive': cli_options.get('recursive'),
                  'rename': cli_options.get('rename'),
                  'rsa_private_key': cli_options.get('rsa_private_key'),
                  'rsa_private_key_passphrase': cli_options.get(
                      'rsa_private_key_passphrase'),
                  'restore_file_properties': {
                      'attributes': cli_options.get('file_attributes'),
                      'lmt': cli_options.get('restore_file_lmt'),
                      'md5': None,
                  },
                  'strip_components': cli_options.get('strip_components'),
                  'skip_on': {
                      'filesize_match': cli_options.get(
                          'skip_on_filesize_match'),
                      'lmt_ge': cli_options.get('skip_on_lmt_ge'),
                      'md5_match': cli_options.get('skip_on_md5_match'),
                  },
              },
          }
      elif action == TransferAction.Synccopy:
          # if url is present, convert to constituent options
          if blobxfer.util.is_not_empty(
                  cli_options.get('sync_copy_dest_storage_url')):
              if (blobxfer.util.is_not_empty(
                      cli_options.get('sync_copy_dest_storage_account')) or
                      blobxfer.util.is_not_empty(
                          cli_options.get('sync_copy_dest_mode')) or
                      blobxfer.util.is_not_empty(
                          cli_options.get('sync_copy_dest_remote_path'))):
                  raise ValueError(
                      'Specified both --sync-copy-dest-storage-url and '
                      '--sync-copy-dest-storage-account, '
                      '--sync-copy-dest-mode, or'
                      '--sync-copy-dest-remote-path')
              cli_options['sync_copy_dest_storage_account'], mode, _, \
                  cli_options['sync_copy_dest_remote_path'], sas = \
                  blobxfer.util.explode_azure_storage_url(
                      cli_options['sync_copy_dest_storage_url'])
              if blobxfer.util.is_not_empty(sas):
                  if blobxfer.util.is_not_empty(
                          cli_options['sync_copy_dest_sas']):
                      raise ValueError(
                          'Specified both --sync-copy-dest-storage-url with '
                          'a SAS token and --sync-copy-dest-sas')
                  cli_options['sync_copy_dest_sas'] = sas
              if mode != 'blob' and mode != 'file':
                  raise ValueError(
                      'Invalid derived destination mode from '
                      '--sync-copy-dest-storage-url: {}'.format(mode))
              if mode == 'file':
                  cli_options['dest_mode'] = mode
              del mode
              del sas
          sync_copy_dest_storage_account = cli_options.get(
              'sync_copy_dest_storage_account')
          sync_copy_dest_remote_path = cli_options.get(
              'sync_copy_dest_remote_path')
          if (sync_copy_dest_storage_account is not None and
                  sync_copy_dest_remote_path is not None):
              sync_copy_dest = [
                  {
                      sync_copy_dest_storage_account:
                      sync_copy_dest_remote_path
                  }
              ]
              azstorage['accounts'][sync_copy_dest_storage_account] = (
                  cli_options.get('sync_copy_dest_access_key') or
                  cli_options.get('sync_copy_dest_sas')
              )
          else:
              sync_copy_dest = None
          arg = {
              'source': [sa_rp] if sa_rp[storage_account] is not None else None,
              'destination': sync_copy_dest,
              'include': cli_options.get('include'),
              'exclude': cli_options.get('exclude'),
              'options': {
                  'access_tier': cli_options.get('access_tier'),
                  'chunk_size_bytes': cli_options.get('chunk_size_bytes'),
                  'dest_mode': cli_options.get('sync_copy_dest_mode'),
                  'mode': cli_options.get('mode'),
                  'overwrite': cli_options.get('overwrite'),
                  'rename': cli_options.get('rename'),
                  'skip_on': {
                      'filesize_match': cli_options.get(
                          'skip_on_filesize_match'),
                      'lmt_ge': cli_options.get('skip_on_lmt_ge'),
                      'md5_match': cli_options.get('skip_on_md5_match'),
                  },
              },
          }
      elif action == TransferAction.Upload:
          arg = {
              'source': [local_resource] if local_resource is not None else None,
              'destination': [sa_rp] if sa_rp[storage_account] is not None else
              None,
              'include': cli_options.get('include'),
              'exclude': cli_options.get('exclude'),
              'options': {
                  'access_tier': cli_options.get('access_tier'),
                  'chunk_size_bytes': cli_options.get('chunk_size_bytes'),
                  'delete_extraneous_destination': cli_options.get('delete'),
                  'mode': cli_options.get('mode'),
                  'one_shot_bytes': cli_options.get('one_shot_bytes'),
                  'overwrite': cli_options.get('overwrite'),
                  'recursive': cli_options.get('recursive'),
                  'rename': cli_options.get('rename'),
                  'rsa_private_key': cli_options.get('rsa_private_key'),
                  'rsa_private_key_passphrase': cli_options.get(
                      'rsa_private_key_passphrase'),
                  'rsa_public_key': cli_options.get('rsa_public_key'),
                  'skip_on': {
                      'filesize_match': cli_options.get(
                          'skip_on_filesize_match'),
                      'lmt_ge': cli_options.get('skip_on_lmt_ge'),
                      'md5_match': cli_options.get('skip_on_md5_match'),
                  },
                  'stdin_as_page_blob_size': cli_options.get(
                      'stdin_as_page_blob_size'),
                  'store_file_properties': {
                      'attributes': cli_options.get('file_attributes'),
                      'cache_control': cli_options.get('file_cache_control'),
                      'lmt': None,
                      'md5': cli_options.get('file_md5'),
                  },
                  'strip_components': cli_options.get('strip_components'),
                  'vectored_io': {
                      'stripe_chunk_size_bytes': cli_options.get(
                          'stripe_chunk_size_bytes'),
                      'distribution_mode': cli_options.get('distribution_mode'),
                  },
              },
          }
      count = 0
      if arg['source'] is None:
          arg.pop('source')
          count += 1
      if arg['destination'] is None:
          arg.pop('destination')
          count += 1
      if count == 1:
          if action == TransferAction.Synccopy:
              raise ValueError(
                  '--remote-path and --sync-copy-dest-remote-path must be '
                  'specified together through the commandline')
          else:
              raise ValueError(
                  '--local-path and --remote-path must be specified together '
                  'through the commandline')
      if 'accounts' in azstorage:
          cli_options['azure_storage'] = azstorage
      cli_options[action.name.lower()] = arg