Black (#948)
This commit is contained in:
+78
-68
@@ -16,34 +16,34 @@ import sys, os
|
|||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
#sys.path.insert(0, os.path.abspath('.'))
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
#needs_sphinx = '1.0'
|
# needs_sphinx = '1.0'
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest']
|
extensions = ["sphinx.ext.autodoc", "sphinx.ext.doctest"]
|
||||||
|
|
||||||
autoclass_content = "both"
|
autoclass_content = "both"
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ["_templates"]
|
||||||
|
|
||||||
# The suffix of source filenames.
|
# The suffix of source filenames.
|
||||||
source_suffix = '.rst'
|
source_suffix = ".rst"
|
||||||
|
|
||||||
# The encoding of source files.
|
# The encoding of source files.
|
||||||
#source_encoding = 'utf-8-sig'
|
# source_encoding = 'utf-8-sig'
|
||||||
|
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = 'index'
|
master_doc = "index"
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'Elasticsearch'
|
project = u"Elasticsearch"
|
||||||
copyright = u'2013, Honza Král'
|
copyright = u"2013, Honza Král"
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
@@ -51,6 +51,7 @@ copyright = u'2013, Honza Král'
|
|||||||
#
|
#
|
||||||
|
|
||||||
import elasticsearch
|
import elasticsearch
|
||||||
|
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = elasticsearch.__versionstr__
|
version = elasticsearch.__versionstr__
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
@@ -58,40 +59,40 @@ release = version
|
|||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
#language = None
|
# language = None
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
# non-false value, then it is used:
|
# non-false value, then it is used:
|
||||||
#today = ''
|
# today = ''
|
||||||
# Else, today_fmt is used as the format for a strftime call.
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
#today_fmt = '%B %d, %Y'
|
# today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
exclude_patterns = ['_build']
|
exclude_patterns = ["_build"]
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
#default_role = None
|
# default_role = None
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
#add_function_parentheses = True
|
# add_function_parentheses = True
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
# If true, the current module name will be prepended to all description
|
||||||
# unit titles (such as .. function::).
|
# unit titles (such as .. function::).
|
||||||
#add_module_names = True
|
# add_module_names = True
|
||||||
|
|
||||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
# output. They are ignored by default.
|
# output. They are ignored by default.
|
||||||
#show_authors = False
|
# show_authors = False
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = 'sphinx'
|
pygments_style = "sphinx"
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
# A list of ignored prefixes for module index sorting.
|
||||||
#modindex_common_prefix = []
|
# modindex_common_prefix = []
|
||||||
|
|
||||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||||
#keep_warnings = False
|
# keep_warnings = False
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ---------------------------------------------------
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
@@ -99,11 +100,12 @@ pygments_style = 'sphinx'
|
|||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
# a list of builtin themes.
|
# a list of builtin themes.
|
||||||
|
|
||||||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
on_rtd = os.environ.get("READTHEDOCS", None) == "True"
|
||||||
|
|
||||||
if not on_rtd: # only import and set the theme if we're building docs locally
|
if not on_rtd: # only import and set the theme if we're building docs locally
|
||||||
import sphinx_rtd_theme
|
import sphinx_rtd_theme
|
||||||
html_theme = 'sphinx_rtd_theme'
|
|
||||||
|
html_theme = "sphinx_rtd_theme"
|
||||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
@@ -113,116 +115,119 @@ if not on_rtd: # only import and set the theme if we're building docs locally
|
|||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
# further. For a list of options available for each theme, see the
|
# further. For a list of options available for each theme, see the
|
||||||
# documentation.
|
# documentation.
|
||||||
#html_theme_options = {}
|
# html_theme_options = {}
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
#html_theme_path = []
|
# html_theme_path = []
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
# "<project> v<release> documentation".
|
# "<project> v<release> documentation".
|
||||||
#html_title = None
|
# html_title = None
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
#html_short_title = None
|
# html_short_title = None
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
# of the sidebar.
|
# of the sidebar.
|
||||||
#html_logo = None
|
# html_logo = None
|
||||||
|
|
||||||
# The name of an image file (within the static path) to use as favicon of the
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
# pixels large.
|
# pixels large.
|
||||||
#html_favicon = None
|
# html_favicon = None
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ['_static']
|
html_static_path = ["_static"]
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
# using the given strftime format.
|
# using the given strftime format.
|
||||||
#html_last_updated_fmt = '%b %d, %Y'
|
# html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
# typographically correct entities.
|
# typographically correct entities.
|
||||||
#html_use_smartypants = True
|
# html_use_smartypants = True
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
# Custom sidebar templates, maps document names to template names.
|
||||||
#html_sidebars = {}
|
# html_sidebars = {}
|
||||||
|
|
||||||
# Additional templates that should be rendered to pages, maps page names to
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
# template names.
|
# template names.
|
||||||
#html_additional_pages = {}
|
# html_additional_pages = {}
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#html_domain_indices = True
|
# html_domain_indices = True
|
||||||
|
|
||||||
# If false, no index is generated.
|
# If false, no index is generated.
|
||||||
#html_use_index = True
|
# html_use_index = True
|
||||||
|
|
||||||
# If true, the index is split into individual pages for each letter.
|
# If true, the index is split into individual pages for each letter.
|
||||||
#html_split_index = False
|
# html_split_index = False
|
||||||
|
|
||||||
# If true, links to the reST sources are added to the pages.
|
# If true, links to the reST sources are added to the pages.
|
||||||
#html_show_sourcelink = True
|
# html_show_sourcelink = True
|
||||||
|
|
||||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||||
#html_show_sphinx = True
|
# html_show_sphinx = True
|
||||||
|
|
||||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||||
#html_show_copyright = True
|
# html_show_copyright = True
|
||||||
|
|
||||||
# If true, an OpenSearch description file will be output, and all pages will
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
# contain a <link> tag referring to it. The value of this option must be the
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
# base URL from which the finished HTML is served.
|
# base URL from which the finished HTML is served.
|
||||||
#html_use_opensearch = ''
|
# html_use_opensearch = ''
|
||||||
|
|
||||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
#html_file_suffix = None
|
# html_file_suffix = None
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = 'Elasticsearchdoc'
|
htmlhelp_basename = "Elasticsearchdoc"
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
latex_elements = {
|
latex_elements = {
|
||||||
# The paper size ('letterpaper' or 'a4paper').
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
#'papersize': 'letterpaper',
|
#'papersize': 'letterpaper',
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
#'pointsize': '10pt',
|
||||||
#'pointsize': '10pt',
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#'preamble': '',
|
||||||
# Additional stuff for the LaTeX preamble.
|
|
||||||
#'preamble': '',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
('index', 'Elasticsearch.tex', u'Elasticsearch Documentation',
|
(
|
||||||
u'Honza Král', 'manual'),
|
"index",
|
||||||
|
"Elasticsearch.tex",
|
||||||
|
u"Elasticsearch Documentation",
|
||||||
|
u"Honza Král",
|
||||||
|
"manual",
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
# the title page.
|
# the title page.
|
||||||
#latex_logo = None
|
# latex_logo = None
|
||||||
|
|
||||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
# not chapters.
|
# not chapters.
|
||||||
#latex_use_parts = False
|
# latex_use_parts = False
|
||||||
|
|
||||||
# If true, show page references after internal links.
|
# If true, show page references after internal links.
|
||||||
#latex_show_pagerefs = False
|
# latex_show_pagerefs = False
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
# If true, show URL addresses after external links.
|
||||||
#latex_show_urls = False
|
# latex_show_urls = False
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
# Documents to append as an appendix to all manuals.
|
||||||
#latex_appendices = []
|
# latex_appendices = []
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#latex_domain_indices = True
|
# latex_domain_indices = True
|
||||||
|
|
||||||
|
|
||||||
# -- Options for manual page output --------------------------------------------
|
# -- Options for manual page output --------------------------------------------
|
||||||
@@ -230,12 +235,11 @@ latex_documents = [
|
|||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [
|
man_pages = [
|
||||||
('index', 'elasticsearch-py', u'Elasticsearch Documentation',
|
("index", "elasticsearch-py", u"Elasticsearch Documentation", [u"Honza Král"], 1)
|
||||||
[u'Honza Král'], 1)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
# If true, show URL addresses after external links.
|
||||||
#man_show_urls = False
|
# man_show_urls = False
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output ------------------------------------------------
|
# -- Options for Texinfo output ------------------------------------------------
|
||||||
@@ -244,19 +248,25 @@ man_pages = [
|
|||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
('index', 'Elasticsearch', u'Elasticsearch Documentation',
|
(
|
||||||
u'Honza Král', 'Elasticsearch', 'One line description of project.',
|
"index",
|
||||||
'Miscellaneous'),
|
"Elasticsearch",
|
||||||
|
u"Elasticsearch Documentation",
|
||||||
|
u"Honza Král",
|
||||||
|
"Elasticsearch",
|
||||||
|
"One line description of project.",
|
||||||
|
"Miscellaneous",
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
# Documents to append as an appendix to all manuals.
|
||||||
#texinfo_appendices = []
|
# texinfo_appendices = []
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#texinfo_domain_indices = True
|
# texinfo_domain_indices = True
|
||||||
|
|
||||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||||
#texinfo_show_urls = 'footnote'
|
# texinfo_show_urls = 'footnote'
|
||||||
|
|
||||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||||
#texinfo_no_detailmenu = False
|
# texinfo_no_detailmenu = False
|
||||||
|
|||||||
+109
-68
@@ -1,7 +1,8 @@
|
|||||||
from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH
|
from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH
|
||||||
|
|
||||||
|
|
||||||
class CatClient(NamespacedClient):
|
class CatClient(NamespacedClient):
|
||||||
@query_params('format', 'h', 'help', 'local', 'master_timeout', 's', 'v')
|
@query_params("format", "h", "help", "local", "master_timeout", "s", "v")
|
||||||
def aliases(self, name=None, params=None):
|
def aliases(self, name=None, params=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -19,11 +20,13 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_cat',
|
return self.transport.perform_request(
|
||||||
'aliases', name), params=params)
|
"GET", _make_path("_cat", "aliases", name), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('bytes', 'size', 'format', 'h', 'help', 'local', 'master_timeout',
|
@query_params(
|
||||||
's', 'v')
|
"bytes", "size", "format", "h", "help", "local", "master_timeout", "s", "v"
|
||||||
|
)
|
||||||
def allocation(self, node_id=None, params=None):
|
def allocation(self, node_id=None, params=None):
|
||||||
"""
|
"""
|
||||||
Allocation provides a snapshot of how shards have located around the
|
Allocation provides a snapshot of how shards have located around the
|
||||||
@@ -45,10 +48,11 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_cat',
|
return self.transport.perform_request(
|
||||||
'allocation', node_id), params=params)
|
"GET", _make_path("_cat", "allocation", node_id), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('size', 'format', 'h', 'help', 'local', 'master_timeout', 's', 'v')
|
@query_params("size", "format", "h", "help", "local", "master_timeout", "s", "v")
|
||||||
def count(self, index=None, params=None):
|
def count(self, index=None, params=None):
|
||||||
"""
|
"""
|
||||||
Count provides quick access to the document count of the entire cluster,
|
Count provides quick access to the document count of the entire cluster,
|
||||||
@@ -68,11 +72,11 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_cat', 'count',
|
return self.transport.perform_request(
|
||||||
index), params=params)
|
"GET", _make_path("_cat", "count", index), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('format', 'h', 'help', 'local', 'master_timeout', 's', 'ts',
|
@query_params("format", "h", "help", "local", "master_timeout", "s", "ts", "v")
|
||||||
'v')
|
|
||||||
def health(self, params=None):
|
def health(self, params=None):
|
||||||
"""
|
"""
|
||||||
health is a terse, one-line representation of the same information from
|
health is a terse, one-line representation of the same information from
|
||||||
@@ -91,10 +95,9 @@ class CatClient(NamespacedClient):
|
|||||||
:arg ts: Set to false to disable timestamping, default True
|
:arg ts: Set to false to disable timestamping, default True
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', '/_cat/health',
|
return self.transport.perform_request("GET", "/_cat/health", params=params)
|
||||||
params=params)
|
|
||||||
|
|
||||||
@query_params('help', 's')
|
@query_params("help", "s")
|
||||||
def help(self, params=None):
|
def help(self, params=None):
|
||||||
"""
|
"""
|
||||||
A simple help for the cat api.
|
A simple help for the cat api.
|
||||||
@@ -104,10 +107,22 @@ class CatClient(NamespacedClient):
|
|||||||
:arg s: Comma-separated list of column names or column aliases to sort
|
:arg s: Comma-separated list of column names or column aliases to sort
|
||||||
by
|
by
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', '/_cat', params=params)
|
return self.transport.perform_request("GET", "/_cat", params=params)
|
||||||
|
|
||||||
@query_params('bytes', 'time', 'size', 'format', 'h', 'health', 'help', 'local',
|
@query_params(
|
||||||
'master_timeout', 'pri', 's', 'v')
|
"bytes",
|
||||||
|
"time",
|
||||||
|
"size",
|
||||||
|
"format",
|
||||||
|
"h",
|
||||||
|
"health",
|
||||||
|
"help",
|
||||||
|
"local",
|
||||||
|
"master_timeout",
|
||||||
|
"pri",
|
||||||
|
"s",
|
||||||
|
"v",
|
||||||
|
)
|
||||||
def indices(self, index=None, params=None):
|
def indices(self, index=None, params=None):
|
||||||
"""
|
"""
|
||||||
The indices command provides a cross-section of each index.
|
The indices command provides a cross-section of each index.
|
||||||
@@ -133,10 +148,11 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_cat',
|
return self.transport.perform_request(
|
||||||
'indices', index), params=params)
|
"GET", _make_path("_cat", "indices", index), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('format', 'h', 'help', 'local', 'master_timeout', 's', 'v')
|
@query_params("format", "h", "help", "local", "master_timeout", "s", "v")
|
||||||
def master(self, params=None):
|
def master(self, params=None):
|
||||||
"""
|
"""
|
||||||
Displays the master's node ID, bound IP address, and node name.
|
Displays the master's node ID, bound IP address, and node name.
|
||||||
@@ -153,11 +169,9 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', '/_cat/master',
|
return self.transport.perform_request("GET", "/_cat/master", params=params)
|
||||||
params=params)
|
|
||||||
|
|
||||||
@query_params('format', 'full_id', 'h', 'help', 'local', 'master_timeout',
|
@query_params("format", "full_id", "h", "help", "local", "master_timeout", "s", "v")
|
||||||
's', 'v')
|
|
||||||
def nodes(self, params=None):
|
def nodes(self, params=None):
|
||||||
"""
|
"""
|
||||||
The nodes command shows the cluster topology.
|
The nodes command shows the cluster topology.
|
||||||
@@ -176,10 +190,11 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', '/_cat/nodes',
|
return self.transport.perform_request("GET", "/_cat/nodes", params=params)
|
||||||
params=params)
|
|
||||||
|
|
||||||
@query_params('bytes', 'time', 'size', 'format', 'h', 'help', 'master_timeout', 's', 'v')
|
@query_params(
|
||||||
|
"bytes", "time", "size", "format", "h", "help", "master_timeout", "s", "v"
|
||||||
|
)
|
||||||
def recovery(self, index=None, params=None):
|
def recovery(self, index=None, params=None):
|
||||||
"""
|
"""
|
||||||
recovery is a view of shard replication.
|
recovery is a view of shard replication.
|
||||||
@@ -198,10 +213,22 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_cat',
|
return self.transport.perform_request(
|
||||||
'recovery', index), params=params)
|
"GET", _make_path("_cat", "recovery", index), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('bytes', 'time', 'size', 'format', 'h', 'help', 'local', 'master_timeout', 's', 'v')
|
@query_params(
|
||||||
|
"bytes",
|
||||||
|
"time",
|
||||||
|
"size",
|
||||||
|
"format",
|
||||||
|
"h",
|
||||||
|
"help",
|
||||||
|
"local",
|
||||||
|
"master_timeout",
|
||||||
|
"s",
|
||||||
|
"v",
|
||||||
|
)
|
||||||
def shards(self, index=None, params=None):
|
def shards(self, index=None, params=None):
|
||||||
"""
|
"""
|
||||||
The shards command is the detailed view of what nodes contain which shards.
|
The shards command is the detailed view of what nodes contain which shards.
|
||||||
@@ -222,10 +249,11 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_cat',
|
return self.transport.perform_request(
|
||||||
'shards', index), params=params)
|
"GET", _make_path("_cat", "shards", index), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('bytes', 'size', 'format', 'h', 'help', 's', 'v')
|
@query_params("bytes", "size", "format", "h", "help", "s", "v")
|
||||||
def segments(self, index=None, params=None):
|
def segments(self, index=None, params=None):
|
||||||
"""
|
"""
|
||||||
The segments command is the detailed view of Lucene segments per index.
|
The segments command is the detailed view of Lucene segments per index.
|
||||||
@@ -242,10 +270,11 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_cat',
|
return self.transport.perform_request(
|
||||||
'segments', index), params=params)
|
"GET", _make_path("_cat", "segments", index), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('format', 'h', 'help', 'local', 'master_timeout', 's', 'v')
|
@query_params("format", "h", "help", "local", "master_timeout", "s", "v")
|
||||||
def pending_tasks(self, params=None):
|
def pending_tasks(self, params=None):
|
||||||
"""
|
"""
|
||||||
pending_tasks provides the same information as the
|
pending_tasks provides the same information as the
|
||||||
@@ -264,11 +293,11 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', '/_cat/pending_tasks',
|
return self.transport.perform_request(
|
||||||
params=params)
|
"GET", "/_cat/pending_tasks", params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('format', 'h', 'help', 'local', 'master_timeout', 's', 'size',
|
@query_params("format", "h", "help", "local", "master_timeout", "s", "size", "v")
|
||||||
'v')
|
|
||||||
def thread_pool(self, thread_pool_patterns=None, params=None):
|
def thread_pool(self, thread_pool_patterns=None, params=None):
|
||||||
"""
|
"""
|
||||||
Get information about thread pools.
|
Get information about thread pools.
|
||||||
@@ -289,11 +318,13 @@ class CatClient(NamespacedClient):
|
|||||||
'', 'k', 'm', 'g', 't', 'p'
|
'', 'k', 'm', 'g', 't', 'p'
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_cat',
|
return self.transport.perform_request(
|
||||||
'thread_pool', thread_pool_patterns), params=params)
|
"GET",
|
||||||
|
_make_path("_cat", "thread_pool", thread_pool_patterns),
|
||||||
|
params=params,
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('bytes', 'format', 'h', 'help', 'local', 'master_timeout',
|
@query_params("bytes", "format", "h", "help", "local", "master_timeout", "s", "v")
|
||||||
's', 'v')
|
|
||||||
def fielddata(self, fields=None, params=None):
|
def fielddata(self, fields=None, params=None):
|
||||||
"""
|
"""
|
||||||
Shows information about currently loaded fielddata on a per-node basis.
|
Shows information about currently loaded fielddata on a per-node basis.
|
||||||
@@ -314,10 +345,11 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_cat',
|
return self.transport.perform_request(
|
||||||
'fielddata', fields), params=params)
|
"GET", _make_path("_cat", "fielddata", fields), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('format', 'h', 'help', 'local', 'master_timeout', 's', 'v')
|
@query_params("format", "h", "help", "local", "master_timeout", "s", "v")
|
||||||
def plugins(self, params=None):
|
def plugins(self, params=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -334,10 +366,9 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', '/_cat/plugins',
|
return self.transport.perform_request("GET", "/_cat/plugins", params=params)
|
||||||
params=params)
|
|
||||||
|
|
||||||
@query_params('format', 'h', 'help', 'local', 'master_timeout', 's', 'v')
|
@query_params("format", "h", "help", "local", "master_timeout", "s", "v")
|
||||||
def nodeattrs(self, params=None):
|
def nodeattrs(self, params=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -354,10 +385,9 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', '/_cat/nodeattrs',
|
return self.transport.perform_request("GET", "/_cat/nodeattrs", params=params)
|
||||||
params=params)
|
|
||||||
|
|
||||||
@query_params('format', 'h', 'help', 'local', 'master_timeout', 's', 'v')
|
@query_params("format", "h", "help", "local", "master_timeout", "s", "v")
|
||||||
def repositories(self, params=None):
|
def repositories(self, params=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -374,11 +404,13 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', '/_cat/repositories',
|
return self.transport.perform_request(
|
||||||
params=params)
|
"GET", "/_cat/repositories", params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('format', 'h', 'help', 'ignore_unavailable', 'master_timeout',
|
@query_params(
|
||||||
's', 'v')
|
"format", "h", "help", "ignore_unavailable", "master_timeout", "s", "v"
|
||||||
|
)
|
||||||
def snapshots(self, repository, params=None):
|
def snapshots(self, repository, params=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -399,11 +431,21 @@ class CatClient(NamespacedClient):
|
|||||||
"""
|
"""
|
||||||
if repository in SKIP_IN_PATH:
|
if repository in SKIP_IN_PATH:
|
||||||
raise ValueError("Empty value passed for a required argument 'repository'.")
|
raise ValueError("Empty value passed for a required argument 'repository'.")
|
||||||
return self.transport.perform_request('GET', _make_path('_cat',
|
return self.transport.perform_request(
|
||||||
'snapshots', repository), params=params)
|
"GET", _make_path("_cat", "snapshots", repository), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('actions', 'detailed', 'format', 'h', 'help', 'nodes',
|
@query_params(
|
||||||
'parent_task_id', 's', 'v')
|
"actions",
|
||||||
|
"detailed",
|
||||||
|
"format",
|
||||||
|
"h",
|
||||||
|
"help",
|
||||||
|
"nodes",
|
||||||
|
"parent_task_id",
|
||||||
|
"s",
|
||||||
|
"v",
|
||||||
|
)
|
||||||
def tasks(self, params=None):
|
def tasks(self, params=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -425,10 +467,9 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', '/_cat/tasks',
|
return self.transport.perform_request("GET", "/_cat/tasks", params=params)
|
||||||
params=params)
|
|
||||||
|
|
||||||
@query_params('format', 'h', 'help', 'local', 'master_timeout', 's', 'v')
|
@query_params("format", "h", "help", "local", "master_timeout", "s", "v")
|
||||||
def templates(self, name=None, params=None):
|
def templates(self, name=None, params=None):
|
||||||
"""
|
"""
|
||||||
`<https://www.elastic.co/guide/en/elasticsearch/reference/current/cat-templates.html>`_
|
`<https://www.elastic.co/guide/en/elasticsearch/reference/current/cat-templates.html>`_
|
||||||
@@ -445,6 +486,6 @@ class CatClient(NamespacedClient):
|
|||||||
by
|
by
|
||||||
:arg v: Verbose mode. Display column headers, default False
|
:arg v: Verbose mode. Display column headers, default False
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_cat',
|
return self.transport.perform_request(
|
||||||
'templates', name), params=params)
|
"GET", _make_path("_cat", "templates", name), params=params
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
from .utils import NamespacedClient, query_params, _make_path
|
from .utils import NamespacedClient, query_params, _make_path
|
||||||
|
|
||||||
|
|
||||||
class ClusterClient(NamespacedClient):
|
class ClusterClient(NamespacedClient):
|
||||||
@query_params('level', 'local', 'master_timeout', 'timeout',
|
@query_params(
|
||||||
'wait_for_active_shards', 'wait_for_events',
|
"level",
|
||||||
'wait_for_no_relocating_shards', 'wait_for_nodes',
|
"local",
|
||||||
'wait_for_status', 'wait_for_no_initializing_shards')
|
"master_timeout",
|
||||||
|
"timeout",
|
||||||
|
"wait_for_active_shards",
|
||||||
|
"wait_for_events",
|
||||||
|
"wait_for_no_relocating_shards",
|
||||||
|
"wait_for_nodes",
|
||||||
|
"wait_for_status",
|
||||||
|
"wait_for_no_initializing_shards",
|
||||||
|
)
|
||||||
def health(self, index=None, params=None):
|
def health(self, index=None, params=None):
|
||||||
"""
|
"""
|
||||||
Get a very simple status on the health of the cluster.
|
Get a very simple status on the health of the cluster.
|
||||||
@@ -30,10 +39,11 @@ class ClusterClient(NamespacedClient):
|
|||||||
:arg wait_for_status: Wait until cluster is in a specific state, default
|
:arg wait_for_status: Wait until cluster is in a specific state, default
|
||||||
None, valid choices are: 'green', 'yellow', 'red'
|
None, valid choices are: 'green', 'yellow', 'red'
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_cluster',
|
return self.transport.perform_request(
|
||||||
'health', index), params=params)
|
"GET", _make_path("_cluster", "health", index), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('local', 'master_timeout')
|
@query_params("local", "master_timeout")
|
||||||
def pending_tasks(self, params=None):
|
def pending_tasks(self, params=None):
|
||||||
"""
|
"""
|
||||||
The pending cluster tasks API returns a list of any cluster-level
|
The pending cluster tasks API returns a list of any cluster-level
|
||||||
@@ -45,11 +55,18 @@ class ClusterClient(NamespacedClient):
|
|||||||
master node (default: false)
|
master node (default: false)
|
||||||
:arg master_timeout: Specify timeout for connection to master
|
:arg master_timeout: Specify timeout for connection to master
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET',
|
return self.transport.perform_request(
|
||||||
'/_cluster/pending_tasks', params=params)
|
"GET", "/_cluster/pending_tasks", params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('allow_no_indices', 'expand_wildcards', 'flat_settings',
|
@query_params(
|
||||||
'ignore_unavailable', 'local', 'master_timeout')
|
"allow_no_indices",
|
||||||
|
"expand_wildcards",
|
||||||
|
"flat_settings",
|
||||||
|
"ignore_unavailable",
|
||||||
|
"local",
|
||||||
|
"master_timeout",
|
||||||
|
)
|
||||||
def state(self, metric=None, index=None, params=None):
|
def state(self, metric=None, index=None, params=None):
|
||||||
"""
|
"""
|
||||||
Get a comprehensive state information of the whole cluster.
|
Get a comprehensive state information of the whole cluster.
|
||||||
@@ -72,11 +89,12 @@ class ClusterClient(NamespacedClient):
|
|||||||
:arg master_timeout: Specify timeout for connection to master
|
:arg master_timeout: Specify timeout for connection to master
|
||||||
"""
|
"""
|
||||||
if index and not metric:
|
if index and not metric:
|
||||||
metric = '_all'
|
metric = "_all"
|
||||||
return self.transport.perform_request('GET', _make_path('_cluster',
|
return self.transport.perform_request(
|
||||||
'state', metric, index), params=params)
|
"GET", _make_path("_cluster", "state", metric, index), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('flat_settings', 'timeout')
|
@query_params("flat_settings", "timeout")
|
||||||
def stats(self, node_id=None, params=None):
|
def stats(self, node_id=None, params=None):
|
||||||
"""
|
"""
|
||||||
The Cluster Stats API allows to retrieve statistics from a cluster wide
|
The Cluster Stats API allows to retrieve statistics from a cluster wide
|
||||||
@@ -91,13 +109,14 @@ class ClusterClient(NamespacedClient):
|
|||||||
:arg flat_settings: Return settings in flat format (default: false)
|
:arg flat_settings: Return settings in flat format (default: false)
|
||||||
:arg timeout: Explicit operation timeout
|
:arg timeout: Explicit operation timeout
|
||||||
"""
|
"""
|
||||||
url = '/_cluster/stats'
|
url = "/_cluster/stats"
|
||||||
if node_id:
|
if node_id:
|
||||||
url = _make_path('_cluster/stats/nodes', node_id)
|
url = _make_path("_cluster/stats/nodes", node_id)
|
||||||
return self.transport.perform_request('GET', url, params=params)
|
return self.transport.perform_request("GET", url, params=params)
|
||||||
|
|
||||||
@query_params('dry_run', 'explain', 'master_timeout', 'metric',
|
@query_params(
|
||||||
'retry_failed', 'timeout')
|
"dry_run", "explain", "master_timeout", "metric", "retry_failed", "timeout"
|
||||||
|
)
|
||||||
def reroute(self, body=None, params=None):
|
def reroute(self, body=None, params=None):
|
||||||
"""
|
"""
|
||||||
Explicitly execute a cluster reroute allocation command including specific commands.
|
Explicitly execute a cluster reroute allocation command including specific commands.
|
||||||
@@ -117,11 +136,11 @@ class ClusterClient(NamespacedClient):
|
|||||||
too many subsequent allocation failures
|
too many subsequent allocation failures
|
||||||
:arg timeout: Explicit operation timeout
|
:arg timeout: Explicit operation timeout
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('POST', '/_cluster/reroute',
|
return self.transport.perform_request(
|
||||||
params=params, body=body)
|
"POST", "/_cluster/reroute", params=params, body=body
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('flat_settings', 'include_defaults', 'master_timeout',
|
@query_params("flat_settings", "include_defaults", "master_timeout", "timeout")
|
||||||
'timeout')
|
|
||||||
def get_settings(self, params=None):
|
def get_settings(self, params=None):
|
||||||
"""
|
"""
|
||||||
Get cluster settings.
|
Get cluster settings.
|
||||||
@@ -134,10 +153,11 @@ class ClusterClient(NamespacedClient):
|
|||||||
node
|
node
|
||||||
:arg timeout: Explicit operation timeout
|
:arg timeout: Explicit operation timeout
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', '/_cluster/settings',
|
return self.transport.perform_request(
|
||||||
params=params)
|
"GET", "/_cluster/settings", params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('flat_settings', 'master_timeout', 'timeout')
|
@query_params("flat_settings", "master_timeout", "timeout")
|
||||||
def put_settings(self, body=None, params=None):
|
def put_settings(self, body=None, params=None):
|
||||||
"""
|
"""
|
||||||
Update cluster wide specific settings.
|
Update cluster wide specific settings.
|
||||||
@@ -150,10 +170,11 @@ class ClusterClient(NamespacedClient):
|
|||||||
node
|
node
|
||||||
:arg timeout: Explicit operation timeout
|
:arg timeout: Explicit operation timeout
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('PUT', '/_cluster/settings',
|
return self.transport.perform_request(
|
||||||
params=params, body=body)
|
"PUT", "/_cluster/settings", params=params, body=body
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('include_disk_info', 'include_yes_decisions')
|
@query_params("include_disk_info", "include_yes_decisions")
|
||||||
def allocation_explain(self, body=None, params=None):
|
def allocation_explain(self, body=None, params=None):
|
||||||
"""
|
"""
|
||||||
`<http://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-allocation-explain.html>`_
|
`<http://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-allocation-explain.html>`_
|
||||||
@@ -165,6 +186,6 @@ class ClusterClient(NamespacedClient):
|
|||||||
:arg include_yes_decisions: Return 'YES' decisions in explanation
|
:arg include_yes_decisions: Return 'YES' decisions in explanation
|
||||||
(default: false)
|
(default: false)
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET',
|
return self.transport.perform_request(
|
||||||
'/_cluster/allocation/explain', params=params, body=body)
|
"GET", "/_cluster/allocation/explain", params=params, body=body
|
||||||
|
)
|
||||||
|
|||||||
@@ -79,7 +79,10 @@ class IndicesClient(NamespacedClient):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@query_params(
|
@query_params(
|
||||||
"master_timeout", "request_timeout", "wait_for_active_shards", "include_type_name"
|
"master_timeout",
|
||||||
|
"request_timeout",
|
||||||
|
"wait_for_active_shards",
|
||||||
|
"include_type_name",
|
||||||
)
|
)
|
||||||
def create(self, index, body=None, params=None):
|
def create(self, index, body=None, params=None):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH
|
from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH
|
||||||
|
|
||||||
|
|
||||||
class IngestClient(NamespacedClient):
|
class IngestClient(NamespacedClient):
|
||||||
@query_params('master_timeout')
|
@query_params("master_timeout")
|
||||||
def get_pipeline(self, id=None, params=None):
|
def get_pipeline(self, id=None, params=None):
|
||||||
"""
|
"""
|
||||||
`<https://www.elastic.co/guide/en/elasticsearch/plugins/current/ingest.html>`_
|
`<https://www.elastic.co/guide/en/elasticsearch/plugins/current/ingest.html>`_
|
||||||
@@ -10,10 +11,11 @@ class IngestClient(NamespacedClient):
|
|||||||
:arg master_timeout: Explicit operation timeout for connection to master
|
:arg master_timeout: Explicit operation timeout for connection to master
|
||||||
node
|
node
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_ingest',
|
return self.transport.perform_request(
|
||||||
'pipeline', id), params=params)
|
"GET", _make_path("_ingest", "pipeline", id), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('master_timeout', 'timeout')
|
@query_params("master_timeout", "timeout")
|
||||||
def put_pipeline(self, id, body, params=None):
|
def put_pipeline(self, id, body, params=None):
|
||||||
"""
|
"""
|
||||||
`<https://www.elastic.co/guide/en/elasticsearch/plugins/current/ingest.html>`_
|
`<https://www.elastic.co/guide/en/elasticsearch/plugins/current/ingest.html>`_
|
||||||
@@ -27,10 +29,11 @@ class IngestClient(NamespacedClient):
|
|||||||
for param in (id, body):
|
for param in (id, body):
|
||||||
if param in SKIP_IN_PATH:
|
if param in SKIP_IN_PATH:
|
||||||
raise ValueError("Empty value passed for a required argument.")
|
raise ValueError("Empty value passed for a required argument.")
|
||||||
return self.transport.perform_request('PUT', _make_path('_ingest',
|
return self.transport.perform_request(
|
||||||
'pipeline', id), params=params, body=body)
|
"PUT", _make_path("_ingest", "pipeline", id), params=params, body=body
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('master_timeout', 'timeout')
|
@query_params("master_timeout", "timeout")
|
||||||
def delete_pipeline(self, id, params=None):
|
def delete_pipeline(self, id, params=None):
|
||||||
"""
|
"""
|
||||||
`<https://www.elastic.co/guide/en/elasticsearch/plugins/current/ingest.html>`_
|
`<https://www.elastic.co/guide/en/elasticsearch/plugins/current/ingest.html>`_
|
||||||
@@ -42,10 +45,11 @@ class IngestClient(NamespacedClient):
|
|||||||
"""
|
"""
|
||||||
if id in SKIP_IN_PATH:
|
if id in SKIP_IN_PATH:
|
||||||
raise ValueError("Empty value passed for a required argument 'id'.")
|
raise ValueError("Empty value passed for a required argument 'id'.")
|
||||||
return self.transport.perform_request('DELETE', _make_path('_ingest',
|
return self.transport.perform_request(
|
||||||
'pipeline', id), params=params)
|
"DELETE", _make_path("_ingest", "pipeline", id), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('verbose')
|
@query_params("verbose")
|
||||||
def simulate(self, body, id=None, params=None):
|
def simulate(self, body, id=None, params=None):
|
||||||
"""
|
"""
|
||||||
`<https://www.elastic.co/guide/en/elasticsearch/plugins/current/ingest.html>`_
|
`<https://www.elastic.co/guide/en/elasticsearch/plugins/current/ingest.html>`_
|
||||||
@@ -57,5 +61,9 @@ class IngestClient(NamespacedClient):
|
|||||||
"""
|
"""
|
||||||
if body in SKIP_IN_PATH:
|
if body in SKIP_IN_PATH:
|
||||||
raise ValueError("Empty value passed for a required argument 'body'.")
|
raise ValueError("Empty value passed for a required argument 'body'.")
|
||||||
return self.transport.perform_request('GET', _make_path('_ingest',
|
return self.transport.perform_request(
|
||||||
'pipeline', id, '_simulate'), params=params, body=body)
|
"GET",
|
||||||
|
_make_path("_ingest", "pipeline", id, "_simulate"),
|
||||||
|
params=params,
|
||||||
|
body=body,
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH
|
from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH
|
||||||
|
|
||||||
|
|
||||||
class RemoteClient(NamespacedClient):
|
class RemoteClient(NamespacedClient):
|
||||||
@query_params()
|
@query_params()
|
||||||
def info(self, params=None):
|
def info(self, params=None):
|
||||||
"""
|
"""
|
||||||
`<http://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-remote-info.html>`_
|
`<http://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-remote-info.html>`_
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', '/_remote/info',
|
return self.transport.perform_request("GET", "/_remote/info", params=params)
|
||||||
params=params)
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH
|
from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH
|
||||||
|
|
||||||
|
|
||||||
class SnapshotClient(NamespacedClient):
|
class SnapshotClient(NamespacedClient):
|
||||||
@query_params('master_timeout', 'wait_for_completion')
|
@query_params("master_timeout", "wait_for_completion")
|
||||||
def create(self, repository, snapshot, body=None, params=None):
|
def create(self, repository, snapshot, body=None, params=None):
|
||||||
"""
|
"""
|
||||||
Create a snapshot in repository
|
Create a snapshot in repository
|
||||||
@@ -18,10 +19,14 @@ class SnapshotClient(NamespacedClient):
|
|||||||
for param in (repository, snapshot):
|
for param in (repository, snapshot):
|
||||||
if param in SKIP_IN_PATH:
|
if param in SKIP_IN_PATH:
|
||||||
raise ValueError("Empty value passed for a required argument.")
|
raise ValueError("Empty value passed for a required argument.")
|
||||||
return self.transport.perform_request('PUT', _make_path('_snapshot',
|
return self.transport.perform_request(
|
||||||
repository, snapshot), params=params, body=body)
|
"PUT",
|
||||||
|
_make_path("_snapshot", repository, snapshot),
|
||||||
|
params=params,
|
||||||
|
body=body,
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('master_timeout')
|
@query_params("master_timeout")
|
||||||
def delete(self, repository, snapshot, params=None):
|
def delete(self, repository, snapshot, params=None):
|
||||||
"""
|
"""
|
||||||
Deletes a snapshot from a repository.
|
Deletes a snapshot from a repository.
|
||||||
@@ -35,10 +40,11 @@ class SnapshotClient(NamespacedClient):
|
|||||||
for param in (repository, snapshot):
|
for param in (repository, snapshot):
|
||||||
if param in SKIP_IN_PATH:
|
if param in SKIP_IN_PATH:
|
||||||
raise ValueError("Empty value passed for a required argument.")
|
raise ValueError("Empty value passed for a required argument.")
|
||||||
return self.transport.perform_request('DELETE',
|
return self.transport.perform_request(
|
||||||
_make_path('_snapshot', repository, snapshot), params=params)
|
"DELETE", _make_path("_snapshot", repository, snapshot), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('ignore_unavailable', 'master_timeout', 'verbose')
|
@query_params("ignore_unavailable", "master_timeout", "verbose")
|
||||||
def get(self, repository, snapshot, params=None):
|
def get(self, repository, snapshot, params=None):
|
||||||
"""
|
"""
|
||||||
Retrieve information about a snapshot.
|
Retrieve information about a snapshot.
|
||||||
@@ -56,10 +62,11 @@ class SnapshotClient(NamespacedClient):
|
|||||||
for param in (repository, snapshot):
|
for param in (repository, snapshot):
|
||||||
if param in SKIP_IN_PATH:
|
if param in SKIP_IN_PATH:
|
||||||
raise ValueError("Empty value passed for a required argument.")
|
raise ValueError("Empty value passed for a required argument.")
|
||||||
return self.transport.perform_request('GET', _make_path('_snapshot',
|
return self.transport.perform_request(
|
||||||
repository, snapshot), params=params)
|
"GET", _make_path("_snapshot", repository, snapshot), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('master_timeout', 'timeout')
|
@query_params("master_timeout", "timeout")
|
||||||
def delete_repository(self, repository, params=None):
|
def delete_repository(self, repository, params=None):
|
||||||
"""
|
"""
|
||||||
Removes a shared file system repository.
|
Removes a shared file system repository.
|
||||||
@@ -72,10 +79,11 @@ class SnapshotClient(NamespacedClient):
|
|||||||
"""
|
"""
|
||||||
if repository in SKIP_IN_PATH:
|
if repository in SKIP_IN_PATH:
|
||||||
raise ValueError("Empty value passed for a required argument 'repository'.")
|
raise ValueError("Empty value passed for a required argument 'repository'.")
|
||||||
return self.transport.perform_request('DELETE',
|
return self.transport.perform_request(
|
||||||
_make_path('_snapshot', repository), params=params)
|
"DELETE", _make_path("_snapshot", repository), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('local', 'master_timeout')
|
@query_params("local", "master_timeout")
|
||||||
def get_repository(self, repository=None, params=None):
|
def get_repository(self, repository=None, params=None):
|
||||||
"""
|
"""
|
||||||
Return information about registered repositories.
|
Return information about registered repositories.
|
||||||
@@ -87,10 +95,11 @@ class SnapshotClient(NamespacedClient):
|
|||||||
:arg master_timeout: Explicit operation timeout for connection to master
|
:arg master_timeout: Explicit operation timeout for connection to master
|
||||||
node
|
node
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_snapshot',
|
return self.transport.perform_request(
|
||||||
repository), params=params)
|
"GET", _make_path("_snapshot", repository), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('master_timeout', 'timeout', 'verify')
|
@query_params("master_timeout", "timeout", "verify")
|
||||||
def create_repository(self, repository, body, params=None):
|
def create_repository(self, repository, body, params=None):
|
||||||
"""
|
"""
|
||||||
Registers a shared file system repository.
|
Registers a shared file system repository.
|
||||||
@@ -106,10 +115,11 @@ class SnapshotClient(NamespacedClient):
|
|||||||
for param in (repository, body):
|
for param in (repository, body):
|
||||||
if param in SKIP_IN_PATH:
|
if param in SKIP_IN_PATH:
|
||||||
raise ValueError("Empty value passed for a required argument.")
|
raise ValueError("Empty value passed for a required argument.")
|
||||||
return self.transport.perform_request('PUT', _make_path('_snapshot',
|
return self.transport.perform_request(
|
||||||
repository), params=params, body=body)
|
"PUT", _make_path("_snapshot", repository), params=params, body=body
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('master_timeout', 'wait_for_completion')
|
@query_params("master_timeout", "wait_for_completion")
|
||||||
def restore(self, repository, snapshot, body=None, params=None):
|
def restore(self, repository, snapshot, body=None, params=None):
|
||||||
"""
|
"""
|
||||||
Restore a snapshot.
|
Restore a snapshot.
|
||||||
@@ -126,10 +136,14 @@ class SnapshotClient(NamespacedClient):
|
|||||||
for param in (repository, snapshot):
|
for param in (repository, snapshot):
|
||||||
if param in SKIP_IN_PATH:
|
if param in SKIP_IN_PATH:
|
||||||
raise ValueError("Empty value passed for a required argument.")
|
raise ValueError("Empty value passed for a required argument.")
|
||||||
return self.transport.perform_request('POST', _make_path('_snapshot',
|
return self.transport.perform_request(
|
||||||
repository, snapshot, '_restore'), params=params, body=body)
|
"POST",
|
||||||
|
_make_path("_snapshot", repository, snapshot, "_restore"),
|
||||||
|
params=params,
|
||||||
|
body=body,
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('ignore_unavailable', 'master_timeout')
|
@query_params("ignore_unavailable", "master_timeout")
|
||||||
def status(self, repository=None, snapshot=None, params=None):
|
def status(self, repository=None, snapshot=None, params=None):
|
||||||
"""
|
"""
|
||||||
Return information about all currently running snapshots. By specifying
|
Return information about all currently running snapshots. By specifying
|
||||||
@@ -144,10 +158,13 @@ class SnapshotClient(NamespacedClient):
|
|||||||
:arg master_timeout: Explicit operation timeout for connection to master
|
:arg master_timeout: Explicit operation timeout for connection to master
|
||||||
node
|
node
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_snapshot',
|
return self.transport.perform_request(
|
||||||
repository, snapshot, '_status'), params=params)
|
"GET",
|
||||||
|
_make_path("_snapshot", repository, snapshot, "_status"),
|
||||||
|
params=params,
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('master_timeout', 'timeout')
|
@query_params("master_timeout", "timeout")
|
||||||
def verify_repository(self, repository, params=None):
|
def verify_repository(self, repository, params=None):
|
||||||
"""
|
"""
|
||||||
Returns a list of nodes where repository was successfully verified or
|
Returns a list of nodes where repository was successfully verified or
|
||||||
@@ -161,5 +178,6 @@ class SnapshotClient(NamespacedClient):
|
|||||||
"""
|
"""
|
||||||
if repository in SKIP_IN_PATH:
|
if repository in SKIP_IN_PATH:
|
||||||
raise ValueError("Empty value passed for a required argument 'repository'.")
|
raise ValueError("Empty value passed for a required argument 'repository'.")
|
||||||
return self.transport.perform_request('POST', _make_path('_snapshot',
|
return self.transport.perform_request(
|
||||||
repository, '_verify'), params=params)
|
"POST", _make_path("_snapshot", repository, "_verify"), params=params
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH
|
from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH
|
||||||
|
|
||||||
|
|
||||||
class TasksClient(NamespacedClient):
|
class TasksClient(NamespacedClient):
|
||||||
@query_params('actions', 'detailed', 'group_by', 'nodes',
|
@query_params(
|
||||||
'parent_task_id', 'wait_for_completion', 'timeout')
|
"actions",
|
||||||
|
"detailed",
|
||||||
|
"group_by",
|
||||||
|
"nodes",
|
||||||
|
"parent_task_id",
|
||||||
|
"wait_for_completion",
|
||||||
|
"timeout",
|
||||||
|
)
|
||||||
def list(self, params=None):
|
def list(self, params=None):
|
||||||
"""
|
"""
|
||||||
`<http://www.elastic.co/guide/en/elasticsearch/reference/current/tasks.html>`_
|
`<http://www.elastic.co/guide/en/elasticsearch/reference/current/tasks.html>`_
|
||||||
@@ -22,9 +30,9 @@ class TasksClient(NamespacedClient):
|
|||||||
(default: false)
|
(default: false)
|
||||||
:arg timeout: Maximum waiting time for `wait_for_completion`
|
:arg timeout: Maximum waiting time for `wait_for_completion`
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', '/_tasks', params=params)
|
return self.transport.perform_request("GET", "/_tasks", params=params)
|
||||||
|
|
||||||
@query_params('actions', 'nodes', 'parent_task_id')
|
@query_params("actions", "nodes", "parent_task_id")
|
||||||
def cancel(self, task_id=None, params=None):
|
def cancel(self, task_id=None, params=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -41,10 +49,11 @@ class TasksClient(NamespacedClient):
|
|||||||
:arg parent_task_id: Cancel tasks with specified parent task id
|
:arg parent_task_id: Cancel tasks with specified parent task id
|
||||||
(node_id:task_number). Set to -1 to cancel all.
|
(node_id:task_number). Set to -1 to cancel all.
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('POST', _make_path('_tasks',
|
return self.transport.perform_request(
|
||||||
task_id, '_cancel'), params=params)
|
"POST", _make_path("_tasks", task_id, "_cancel"), params=params
|
||||||
|
)
|
||||||
|
|
||||||
@query_params('wait_for_completion', 'timeout')
|
@query_params("wait_for_completion", "timeout")
|
||||||
def get(self, task_id=None, params=None):
|
def get(self, task_id=None, params=None):
|
||||||
"""
|
"""
|
||||||
Retrieve information for a particular task.
|
Retrieve information for a particular task.
|
||||||
@@ -55,5 +64,6 @@ class TasksClient(NamespacedClient):
|
|||||||
(default: false)
|
(default: false)
|
||||||
:arg timeout: Maximum waiting time for `wait_for_completion`
|
:arg timeout: Maximum waiting time for `wait_for_completion`
|
||||||
"""
|
"""
|
||||||
return self.transport.perform_request('GET', _make_path('_tasks',
|
return self.transport.perform_request(
|
||||||
task_id), params=params)
|
"GET", _make_path("_tasks", task_id), params=params
|
||||||
|
)
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ import sys
|
|||||||
PY2 = sys.version_info[0] == 2
|
PY2 = sys.version_info[0] == 2
|
||||||
|
|
||||||
if PY2:
|
if PY2:
|
||||||
string_types = basestring,
|
string_types = (basestring,)
|
||||||
from urllib import quote_plus, urlencode, unquote
|
from urllib import quote_plus, urlencode, unquote
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
from itertools import imap as map
|
from itertools import imap as map
|
||||||
from Queue import Queue
|
from Queue import Queue
|
||||||
else:
|
else:
|
||||||
string_types = str, bytes
|
string_types = str, bytes
|
||||||
from urllib.parse import quote_plus, urlencode, urlparse, unquote
|
from urllib.parse import quote_plus, urlencode, urlparse, unquote
|
||||||
|
|
||||||
map = map
|
map = map
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
from .base import Connection
|
from .base import Connection
|
||||||
from .http_requests import RequestsHttpConnection
|
from .http_requests import RequestsHttpConnection
|
||||||
from .http_urllib3 import Urllib3HttpConnection, create_ssl_context
|
from .http_urllib3 import Urllib3HttpConnection, create_ssl_context
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -6,12 +7,12 @@ except ImportError:
|
|||||||
|
|
||||||
from ..exceptions import TransportError, HTTP_EXCEPTIONS
|
from ..exceptions import TransportError, HTTP_EXCEPTIONS
|
||||||
|
|
||||||
logger = logging.getLogger('elasticsearch')
|
logger = logging.getLogger("elasticsearch")
|
||||||
|
|
||||||
# create the elasticsearch.trace logger, but only set propagate to False if the
|
# create the elasticsearch.trace logger, but only set propagate to False if the
|
||||||
# logger hasn't already been configured
|
# logger hasn't already been configured
|
||||||
_tracer_already_configured = 'elasticsearch.trace' in logging.Logger.manager.loggerDict
|
_tracer_already_configured = "elasticsearch.trace" in logging.Logger.manager.loggerDict
|
||||||
tracer = logging.getLogger('elasticsearch.trace')
|
tracer = logging.getLogger("elasticsearch.trace")
|
||||||
if not _tracer_already_configured:
|
if not _tracer_already_configured:
|
||||||
tracer.propagate = False
|
tracer.propagate = False
|
||||||
|
|
||||||
@@ -24,32 +25,43 @@ class Connection(object):
|
|||||||
|
|
||||||
Also responsible for logging.
|
Also responsible for logging.
|
||||||
"""
|
"""
|
||||||
def __init__(self, host='localhost', port=9200, use_ssl=False, url_prefix='', timeout=10, **kwargs):
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
host="localhost",
|
||||||
|
port=9200,
|
||||||
|
use_ssl=False,
|
||||||
|
url_prefix="",
|
||||||
|
timeout=10,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
:arg host: hostname of the node (default: localhost)
|
:arg host: hostname of the node (default: localhost)
|
||||||
:arg port: port to use (integer, default: 9200)
|
:arg port: port to use (integer, default: 9200)
|
||||||
:arg url_prefix: optional url prefix for elasticsearch
|
:arg url_prefix: optional url prefix for elasticsearch
|
||||||
:arg timeout: default timeout in seconds (float, default: 10)
|
:arg timeout: default timeout in seconds (float, default: 10)
|
||||||
"""
|
"""
|
||||||
scheme = kwargs.get('scheme', 'http')
|
scheme = kwargs.get("scheme", "http")
|
||||||
if use_ssl or scheme == 'https':
|
if use_ssl or scheme == "https":
|
||||||
scheme = 'https'
|
scheme = "https"
|
||||||
use_ssl = True
|
use_ssl = True
|
||||||
self.use_ssl = use_ssl
|
self.use_ssl = use_ssl
|
||||||
|
|
||||||
self.host = '%s://%s:%s' % (scheme, host, port)
|
self.host = "%s://%s:%s" % (scheme, host, port)
|
||||||
if url_prefix:
|
if url_prefix:
|
||||||
url_prefix = '/' + url_prefix.strip('/')
|
url_prefix = "/" + url_prefix.strip("/")
|
||||||
self.url_prefix = url_prefix
|
self.url_prefix = url_prefix
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<%s: %s>' % (self.__class__.__name__, self.host)
|
return "<%s: %s>" % (self.__class__.__name__, self.host)
|
||||||
|
|
||||||
def _pretty_json(self, data):
|
def _pretty_json(self, data):
|
||||||
# pretty JSON in tracer curl logs
|
# pretty JSON in tracer curl logs
|
||||||
try:
|
try:
|
||||||
return json.dumps(json.loads(data), sort_keys=True, indent=2, separators=(',', ': ')).replace("'", r'\u0027')
|
return json.dumps(
|
||||||
|
json.loads(data), sort_keys=True, indent=2, separators=(",", ": ")
|
||||||
|
).replace("'", r"\u0027")
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
# non-json data or a bulk request
|
# non-json data or a bulk request
|
||||||
return data
|
return data
|
||||||
@@ -59,17 +71,28 @@ class Connection(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# include pretty in trace curls
|
# include pretty in trace curls
|
||||||
path = path.replace('?', '?pretty&', 1) if '?' in path else path + '?pretty'
|
path = path.replace("?", "?pretty&", 1) if "?" in path else path + "?pretty"
|
||||||
if self.url_prefix:
|
if self.url_prefix:
|
||||||
path = path.replace(self.url_prefix, '', 1)
|
path = path.replace(self.url_prefix, "", 1)
|
||||||
tracer.info("curl %s-X%s 'http://localhost:9200%s' -d '%s'",
|
tracer.info(
|
||||||
"-H 'Content-Type: application/json' " if body else '',
|
"curl %s-X%s 'http://localhost:9200%s' -d '%s'",
|
||||||
method, path, self._pretty_json(body) if body else '')
|
"-H 'Content-Type: application/json' " if body else "",
|
||||||
|
method,
|
||||||
|
path,
|
||||||
|
self._pretty_json(body) if body else "",
|
||||||
|
)
|
||||||
|
|
||||||
if tracer.isEnabledFor(logging.DEBUG):
|
if tracer.isEnabledFor(logging.DEBUG):
|
||||||
tracer.debug('#[%s] (%.3fs)\n#%s', status_code, duration, self._pretty_json(response).replace('\n', '\n#') if response else '')
|
tracer.debug(
|
||||||
|
"#[%s] (%.3fs)\n#%s",
|
||||||
|
status_code,
|
||||||
|
duration,
|
||||||
|
self._pretty_json(response).replace("\n", "\n#") if response else "",
|
||||||
|
)
|
||||||
|
|
||||||
def log_request_success(self, method, full_url, path, body, status_code, response, duration):
|
def log_request_success(
|
||||||
|
self, method, full_url, path, body, status_code, response, duration
|
||||||
|
):
|
||||||
""" Log a successful API call. """
|
""" Log a successful API call. """
|
||||||
# TODO: optionally pass in params instead of full_url and do urlencode only when needed
|
# TODO: optionally pass in params instead of full_url and do urlencode only when needed
|
||||||
|
|
||||||
@@ -77,43 +100,56 @@ class Connection(object):
|
|||||||
# TODO: find a better way to avoid (de)encoding the body back and forth
|
# TODO: find a better way to avoid (de)encoding the body back and forth
|
||||||
if body:
|
if body:
|
||||||
try:
|
try:
|
||||||
body = body.decode('utf-8', 'ignore')
|
body = body.decode("utf-8", "ignore")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
'%s %s [status:%s request:%.3fs]', method, full_url,
|
"%s %s [status:%s request:%.3fs]", method, full_url, status_code, duration
|
||||||
status_code, duration
|
|
||||||
)
|
)
|
||||||
logger.debug('> %s', body)
|
logger.debug("> %s", body)
|
||||||
logger.debug('< %s', response)
|
logger.debug("< %s", response)
|
||||||
|
|
||||||
self._log_trace(method, path, body, status_code, response, duration)
|
self._log_trace(method, path, body, status_code, response, duration)
|
||||||
|
|
||||||
def log_request_fail(self, method, full_url, path, body, duration, status_code=None, response=None, exception=None):
|
def log_request_fail(
|
||||||
|
self,
|
||||||
|
method,
|
||||||
|
full_url,
|
||||||
|
path,
|
||||||
|
body,
|
||||||
|
duration,
|
||||||
|
status_code=None,
|
||||||
|
response=None,
|
||||||
|
exception=None,
|
||||||
|
):
|
||||||
""" Log an unsuccessful API call. """
|
""" Log an unsuccessful API call. """
|
||||||
# do not log 404s on HEAD requests
|
# do not log 404s on HEAD requests
|
||||||
if method == 'HEAD' and status_code == 404:
|
if method == "HEAD" and status_code == 404:
|
||||||
return
|
return
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'%s %s [status:%s request:%.3fs]', method, full_url,
|
"%s %s [status:%s request:%.3fs]",
|
||||||
status_code or 'N/A', duration, exc_info=exception is not None
|
method,
|
||||||
|
full_url,
|
||||||
|
status_code or "N/A",
|
||||||
|
duration,
|
||||||
|
exc_info=exception is not None,
|
||||||
)
|
)
|
||||||
|
|
||||||
# body has already been serialized to utf-8, deserialize it for logging
|
# body has already been serialized to utf-8, deserialize it for logging
|
||||||
# TODO: find a better way to avoid (de)encoding the body back and forth
|
# TODO: find a better way to avoid (de)encoding the body back and forth
|
||||||
if body:
|
if body:
|
||||||
try:
|
try:
|
||||||
body = body.decode('utf-8', 'ignore')
|
body = body.decode("utf-8", "ignore")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
logger.debug('> %s', body)
|
logger.debug("> %s", body)
|
||||||
|
|
||||||
self._log_trace(method, path, body, status_code, response, duration)
|
self._log_trace(method, path, body, status_code, response, duration)
|
||||||
|
|
||||||
if response is not None:
|
if response is not None:
|
||||||
logger.debug('< %s', response)
|
logger.debug("< %s", response)
|
||||||
|
|
||||||
def _raise_error(self, status_code, raw_data):
|
def _raise_error(self, status_code, raw_data):
|
||||||
""" Locate appropriate exception and raise it. """
|
""" Locate appropriate exception and raise it. """
|
||||||
@@ -122,12 +158,12 @@ class Connection(object):
|
|||||||
try:
|
try:
|
||||||
if raw_data:
|
if raw_data:
|
||||||
additional_info = json.loads(raw_data)
|
additional_info = json.loads(raw_data)
|
||||||
error_message = additional_info.get('error', error_message)
|
error_message = additional_info.get("error", error_message)
|
||||||
if isinstance(error_message, dict) and 'type' in error_message:
|
if isinstance(error_message, dict) and "type" in error_message:
|
||||||
error_message = error_message['type']
|
error_message = error_message["type"]
|
||||||
except (ValueError, TypeError) as err:
|
except (ValueError, TypeError) as err:
|
||||||
logger.warning('Undecodable raw error response from server: %s', err)
|
logger.warning("Undecodable raw error response from server: %s", err)
|
||||||
|
|
||||||
raise HTTP_EXCEPTIONS.get(status_code, TransportError)(status_code, error_message, additional_info)
|
|
||||||
|
|
||||||
|
|
||||||
|
raise HTTP_EXCEPTIONS.get(status_code, TransportError)(
|
||||||
|
status_code, error_message, additional_info
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
import time
|
import time
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
REQUESTS_AVAILABLE = True
|
REQUESTS_AVAILABLE = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
REQUESTS_AVAILABLE = False
|
REQUESTS_AVAILABLE = False
|
||||||
|
|
||||||
from .base import Connection
|
from .base import Connection
|
||||||
from ..exceptions import ConnectionError, ImproperlyConfigured, ConnectionTimeout, SSLError
|
from ..exceptions import (
|
||||||
|
ConnectionError,
|
||||||
|
ImproperlyConfigured,
|
||||||
|
ConnectionTimeout,
|
||||||
|
SSLError,
|
||||||
|
)
|
||||||
from ..compat import urlencode, string_types
|
from ..compat import urlencode, string_types
|
||||||
|
|
||||||
|
|
||||||
class RequestsHttpConnection(Connection):
|
class RequestsHttpConnection(Connection):
|
||||||
"""
|
"""
|
||||||
Connection using the `requests` library.
|
Connection using the `requests` library.
|
||||||
@@ -27,25 +35,43 @@ class RequestsHttpConnection(Connection):
|
|||||||
separate cert and key files (client_cert will contain only the cert)
|
separate cert and key files (client_cert will contain only the cert)
|
||||||
:arg headers: any custom http headers to be add to requests
|
:arg headers: any custom http headers to be add to requests
|
||||||
"""
|
"""
|
||||||
def __init__(self, host='localhost', port=9200, http_auth=None,
|
|
||||||
use_ssl=False, verify_certs=True, ssl_show_warn=True, ca_certs=None, client_cert=None,
|
|
||||||
client_key=None, headers=None, **kwargs):
|
|
||||||
if not REQUESTS_AVAILABLE:
|
|
||||||
raise ImproperlyConfigured("Please install requests to use RequestsHttpConnection.")
|
|
||||||
|
|
||||||
super(RequestsHttpConnection, self).__init__(host=host, port=port, use_ssl=use_ssl, **kwargs)
|
def __init__(
|
||||||
|
self,
|
||||||
|
host="localhost",
|
||||||
|
port=9200,
|
||||||
|
http_auth=None,
|
||||||
|
use_ssl=False,
|
||||||
|
verify_certs=True,
|
||||||
|
ssl_show_warn=True,
|
||||||
|
ca_certs=None,
|
||||||
|
client_cert=None,
|
||||||
|
client_key=None,
|
||||||
|
headers=None,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
|
if not REQUESTS_AVAILABLE:
|
||||||
|
raise ImproperlyConfigured(
|
||||||
|
"Please install requests to use RequestsHttpConnection."
|
||||||
|
)
|
||||||
|
|
||||||
|
super(RequestsHttpConnection, self).__init__(
|
||||||
|
host=host, port=port, use_ssl=use_ssl, **kwargs
|
||||||
|
)
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self.session.headers = headers or {}
|
self.session.headers = headers or {}
|
||||||
self.session.headers.setdefault('content-type', 'application/json')
|
self.session.headers.setdefault("content-type", "application/json")
|
||||||
if http_auth is not None:
|
if http_auth is not None:
|
||||||
if isinstance(http_auth, (tuple, list)):
|
if isinstance(http_auth, (tuple, list)):
|
||||||
http_auth = tuple(http_auth)
|
http_auth = tuple(http_auth)
|
||||||
elif isinstance(http_auth, string_types):
|
elif isinstance(http_auth, string_types):
|
||||||
http_auth = tuple(http_auth.split(':', 1))
|
http_auth = tuple(http_auth.split(":", 1))
|
||||||
self.session.auth = http_auth
|
self.session.auth = http_auth
|
||||||
self.base_url = 'http%s://%s:%d%s' % (
|
self.base_url = "http%s://%s:%d%s" % (
|
||||||
's' if self.use_ssl else '',
|
"s" if self.use_ssl else "",
|
||||||
host, port, self.url_prefix
|
host,
|
||||||
|
port,
|
||||||
|
self.url_prefix,
|
||||||
)
|
)
|
||||||
self.session.verify = verify_certs
|
self.session.verify = verify_certs
|
||||||
if not client_key:
|
if not client_key:
|
||||||
@@ -55,42 +81,76 @@ class RequestsHttpConnection(Connection):
|
|||||||
self.session.cert = (client_cert, client_key)
|
self.session.cert = (client_cert, client_key)
|
||||||
if ca_certs:
|
if ca_certs:
|
||||||
if not verify_certs:
|
if not verify_certs:
|
||||||
raise ImproperlyConfigured("You cannot pass CA certificates when verify SSL is off.")
|
raise ImproperlyConfigured(
|
||||||
|
"You cannot pass CA certificates when verify SSL is off."
|
||||||
|
)
|
||||||
self.session.verify = ca_certs
|
self.session.verify = ca_certs
|
||||||
|
|
||||||
if self.use_ssl and not verify_certs and ssl_show_warn:
|
if self.use_ssl and not verify_certs and ssl_show_warn:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
'Connecting to %s using SSL with verify_certs=False is insecure.' % self.base_url)
|
"Connecting to %s using SSL with verify_certs=False is insecure."
|
||||||
|
% self.base_url
|
||||||
|
)
|
||||||
|
|
||||||
def perform_request(self, method, url, params=None, body=None, timeout=None, ignore=(), headers=None):
|
def perform_request(
|
||||||
|
self, method, url, params=None, body=None, timeout=None, ignore=(), headers=None
|
||||||
|
):
|
||||||
url = self.base_url + url
|
url = self.base_url + url
|
||||||
if params:
|
if params:
|
||||||
url = '%s?%s' % (url, urlencode(params or {}))
|
url = "%s?%s" % (url, urlencode(params or {}))
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
request = requests.Request(method=method, headers=headers, url=url, data=body)
|
request = requests.Request(method=method, headers=headers, url=url, data=body)
|
||||||
prepared_request = self.session.prepare_request(request)
|
prepared_request = self.session.prepare_request(request)
|
||||||
settings = self.session.merge_environment_settings(prepared_request.url, {}, None, None, None)
|
settings = self.session.merge_environment_settings(
|
||||||
send_kwargs = {'timeout': timeout or self.timeout}
|
prepared_request.url, {}, None, None, None
|
||||||
|
)
|
||||||
|
send_kwargs = {"timeout": timeout or self.timeout}
|
||||||
send_kwargs.update(settings)
|
send_kwargs.update(settings)
|
||||||
try:
|
try:
|
||||||
response = self.session.send(prepared_request, **send_kwargs)
|
response = self.session.send(prepared_request, **send_kwargs)
|
||||||
duration = time.time() - start
|
duration = time.time() - start
|
||||||
raw_data = response.text
|
raw_data = response.text
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log_request_fail(method, url, prepared_request.path_url, body, time.time() - start, exception=e)
|
self.log_request_fail(
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
prepared_request.path_url,
|
||||||
|
body,
|
||||||
|
time.time() - start,
|
||||||
|
exception=e,
|
||||||
|
)
|
||||||
if isinstance(e, requests.exceptions.SSLError):
|
if isinstance(e, requests.exceptions.SSLError):
|
||||||
raise SSLError('N/A', str(e), e)
|
raise SSLError("N/A", str(e), e)
|
||||||
if isinstance(e, requests.Timeout):
|
if isinstance(e, requests.Timeout):
|
||||||
raise ConnectionTimeout('TIMEOUT', str(e), e)
|
raise ConnectionTimeout("TIMEOUT", str(e), e)
|
||||||
raise ConnectionError('N/A', str(e), e)
|
raise ConnectionError("N/A", str(e), e)
|
||||||
|
|
||||||
# raise errors based on http status codes, let the client handle those if needed
|
# raise errors based on http status codes, let the client handle those if needed
|
||||||
if not (200 <= response.status_code < 300) and response.status_code not in ignore:
|
if (
|
||||||
self.log_request_fail(method, url, response.request.path_url, body, duration, response.status_code, raw_data)
|
not (200 <= response.status_code < 300)
|
||||||
|
and response.status_code not in ignore
|
||||||
|
):
|
||||||
|
self.log_request_fail(
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
response.request.path_url,
|
||||||
|
body,
|
||||||
|
duration,
|
||||||
|
response.status_code,
|
||||||
|
raw_data,
|
||||||
|
)
|
||||||
self._raise_error(response.status_code, raw_data)
|
self._raise_error(response.status_code, raw_data)
|
||||||
|
|
||||||
self.log_request_success(method, url, response.request.path_url, body, response.status_code, raw_data, duration)
|
self.log_request_success(
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
response.request.path_url,
|
||||||
|
body,
|
||||||
|
response.status_code,
|
||||||
|
raw_data,
|
||||||
|
duration,
|
||||||
|
)
|
||||||
|
|
||||||
return response.status_code, response.headers, raw_data
|
return response.status_code, response.headers, raw_data
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class PoolingConnection(Connection):
|
|||||||
``_make_connection`` method that constructs a new connection and returns
|
``_make_connection`` method that constructs a new connection and returns
|
||||||
it.
|
it.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self._free_connections = queue.Queue()
|
self._free_connections = queue.Queue()
|
||||||
super(PoolingConnection, self).__init__(*args, **kwargs)
|
super(PoolingConnection, self).__init__(*args, **kwargs)
|
||||||
@@ -30,4 +31,3 @@ class PoolingConnection(Connection):
|
|||||||
Explicitly close connection
|
Explicitly close connection
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ except ImportError:
|
|||||||
|
|
||||||
from .exceptions import ImproperlyConfigured
|
from .exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
logger = logging.getLogger('elasticsearch')
|
logger = logging.getLogger("elasticsearch")
|
||||||
|
|
||||||
|
|
||||||
class ConnectionSelector(object):
|
class ConnectionSelector(object):
|
||||||
"""
|
"""
|
||||||
@@ -30,6 +31,7 @@ class ConnectionSelector(object):
|
|||||||
only select connections from it's own zones and only fall back to other
|
only select connections from it's own zones and only fall back to other
|
||||||
connections where there would be none in it's zones.
|
connections where there would be none in it's zones.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, opts):
|
def __init__(self, opts):
|
||||||
"""
|
"""
|
||||||
:arg opts: dictionary of connection instances and their options
|
:arg opts: dictionary of connection instances and their options
|
||||||
@@ -49,6 +51,7 @@ class RandomSelector(ConnectionSelector):
|
|||||||
"""
|
"""
|
||||||
Select a connection at random
|
Select a connection at random
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def select(self, connections):
|
def select(self, connections):
|
||||||
return random.choice(connections)
|
return random.choice(connections)
|
||||||
|
|
||||||
@@ -57,15 +60,17 @@ class RoundRobinSelector(ConnectionSelector):
|
|||||||
"""
|
"""
|
||||||
Selector using round-robin.
|
Selector using round-robin.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, opts):
|
def __init__(self, opts):
|
||||||
super(RoundRobinSelector, self).__init__(opts)
|
super(RoundRobinSelector, self).__init__(opts)
|
||||||
self.data = threading.local()
|
self.data = threading.local()
|
||||||
|
|
||||||
def select(self, connections):
|
def select(self, connections):
|
||||||
self.data.rr = getattr(self.data, 'rr', -1) + 1
|
self.data.rr = getattr(self.data, "rr", -1) + 1
|
||||||
self.data.rr %= len(connections)
|
self.data.rr %= len(connections)
|
||||||
return connections[self.data.rr]
|
return connections[self.data.rr]
|
||||||
|
|
||||||
|
|
||||||
class ConnectionPool(object):
|
class ConnectionPool(object):
|
||||||
"""
|
"""
|
||||||
Container holding the :class:`~elasticsearch.Connection` instances,
|
Container holding the :class:`~elasticsearch.Connection` instances,
|
||||||
@@ -88,8 +93,16 @@ class ConnectionPool(object):
|
|||||||
live pool. A connection that has been previously marked as dead and
|
live pool. A connection that has been previously marked as dead and
|
||||||
succeeds will be marked as live (its fail count will be deleted).
|
succeeds will be marked as live (its fail count will be deleted).
|
||||||
"""
|
"""
|
||||||
def __init__(self, connections, dead_timeout=60, timeout_cutoff=5,
|
|
||||||
selector_class=RoundRobinSelector, randomize_hosts=True, **kwargs):
|
def __init__(
|
||||||
|
self,
|
||||||
|
connections,
|
||||||
|
dead_timeout=60,
|
||||||
|
timeout_cutoff=5,
|
||||||
|
selector_class=RoundRobinSelector,
|
||||||
|
randomize_hosts=True,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
:arg connections: list of tuples containing the
|
:arg connections: list of tuples containing the
|
||||||
:class:`~elasticsearch.Connection` instance and it's options
|
:class:`~elasticsearch.Connection` instance and it's options
|
||||||
@@ -103,8 +116,9 @@ class ConnectionPool(object):
|
|||||||
avoid dog piling effect across processes
|
avoid dog piling effect across processes
|
||||||
"""
|
"""
|
||||||
if not connections:
|
if not connections:
|
||||||
raise ImproperlyConfigured("No defined connections, you need to "
|
raise ImproperlyConfigured(
|
||||||
"specify at least one host.")
|
"No defined connections, you need to " "specify at least one host."
|
||||||
|
)
|
||||||
self.connection_opts = connections
|
self.connection_opts = connections
|
||||||
self.connections = [c for (c, opts) in connections]
|
self.connections = [c for (c, opts) in connections]
|
||||||
# remember original connection list for resurrect(force=True)
|
# remember original connection list for resurrect(force=True)
|
||||||
@@ -144,8 +158,10 @@ class ConnectionPool(object):
|
|||||||
timeout = self.dead_timeout * 2 ** min(dead_count - 1, self.timeout_cutoff)
|
timeout = self.dead_timeout * 2 ** min(dead_count - 1, self.timeout_cutoff)
|
||||||
self.dead.put((now + timeout, connection))
|
self.dead.put((now + timeout, connection))
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'Connection %r has failed for %i times in a row, putting on %i second timeout.',
|
"Connection %r has failed for %i times in a row, putting on %i second timeout.",
|
||||||
connection, dead_count, timeout
|
connection,
|
||||||
|
dead_count,
|
||||||
|
timeout,
|
||||||
)
|
)
|
||||||
|
|
||||||
def mark_live(self, connection):
|
def mark_live(self, connection):
|
||||||
@@ -200,7 +216,7 @@ class ConnectionPool(object):
|
|||||||
|
|
||||||
# either we were forced or the connection is elligible to be retried
|
# either we were forced or the connection is elligible to be retried
|
||||||
self.connections.append(connection)
|
self.connections.append(connection)
|
||||||
logger.info('Resurrecting connection %r (force=%s).', connection, force)
|
logger.info("Resurrecting connection %r (force=%s).", connection, force)
|
||||||
return connection
|
return connection
|
||||||
|
|
||||||
def get_connection(self):
|
def get_connection(self):
|
||||||
@@ -235,15 +251,17 @@ class ConnectionPool(object):
|
|||||||
for conn in self.orig_connections:
|
for conn in self.orig_connections:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
class DummyConnectionPool(ConnectionPool):
|
class DummyConnectionPool(ConnectionPool):
|
||||||
def __init__(self, connections, **kwargs):
|
def __init__(self, connections, **kwargs):
|
||||||
if len(connections) != 1:
|
if len(connections) != 1:
|
||||||
raise ImproperlyConfigured("DummyConnectionPool needs exactly one "
|
raise ImproperlyConfigured(
|
||||||
"connection defined.")
|
"DummyConnectionPool needs exactly one " "connection defined."
|
||||||
|
)
|
||||||
# we need connection opts for sniffing logic
|
# we need connection opts for sniffing logic
|
||||||
self.connection_opts = connections
|
self.connection_opts = connections
|
||||||
self.connection = connections[0][0]
|
self.connection = connections[0][0]
|
||||||
self.connections = (self.connection, )
|
self.connections = (self.connection,)
|
||||||
|
|
||||||
def get_connection(self):
|
def get_connection(self):
|
||||||
return self.connection
|
return self.connection
|
||||||
@@ -256,6 +274,5 @@ class DummyConnectionPool(ConnectionPool):
|
|||||||
|
|
||||||
def _noop(self, *args, **kwargs):
|
def _noop(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
mark_dead = mark_live = resurrect = _noop
|
mark_dead = mark_live = resurrect = _noop
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+39
-16
@@ -1,7 +1,16 @@
|
|||||||
__all__ = [
|
__all__ = [
|
||||||
'ImproperlyConfigured', 'ElasticsearchException', 'SerializationError',
|
"ImproperlyConfigured",
|
||||||
'TransportError', 'NotFoundError', 'ConflictError', 'RequestError', 'ConnectionError',
|
"ElasticsearchException",
|
||||||
'SSLError', 'ConnectionTimeout', 'AuthenticationException', 'AuthorizationException'
|
"SerializationError",
|
||||||
|
"TransportError",
|
||||||
|
"NotFoundError",
|
||||||
|
"ConflictError",
|
||||||
|
"RequestError",
|
||||||
|
"ConnectionError",
|
||||||
|
"SSLError",
|
||||||
|
"ConnectionTimeout",
|
||||||
|
"AuthenticationException",
|
||||||
|
"AuthorizationException",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -31,6 +40,7 @@ class TransportError(ElasticsearchException):
|
|||||||
an actual connection error happens; in that case the ``status_code`` will
|
an actual connection error happens; in that case the ``status_code`` will
|
||||||
be set to ``'N/A'``.
|
be set to ``'N/A'``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status_code(self):
|
def status_code(self):
|
||||||
"""
|
"""
|
||||||
@@ -53,20 +63,28 @@ class TransportError(ElasticsearchException):
|
|||||||
return self.args[2]
|
return self.args[2]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
cause = ''
|
cause = ""
|
||||||
try:
|
try:
|
||||||
if self.info and 'error' in self.info:
|
if self.info and "error" in self.info:
|
||||||
if isinstance(self.info['error'], dict):
|
if isinstance(self.info["error"], dict):
|
||||||
root_cause = self.info['error']['root_cause'][0]
|
root_cause = self.info["error"]["root_cause"][0]
|
||||||
cause = ', '.join(filter(None, [repr(root_cause['reason']), root_cause.get('resource.id'),
|
cause = ", ".join(
|
||||||
root_cause.get('resource.type')]))
|
filter(
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
repr(root_cause["reason"]),
|
||||||
|
root_cause.get("resource.id"),
|
||||||
|
root_cause.get("resource.type"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
cause = repr(self.info['error'])
|
cause = repr(self.info["error"])
|
||||||
except LookupError:
|
except LookupError:
|
||||||
pass
|
pass
|
||||||
msg = ', '.join(filter(None, [str(self.status_code), repr(self.error), cause]))
|
msg = ", ".join(filter(None, [str(self.status_code), repr(self.error), cause]))
|
||||||
return '%s(%s)' % (self.__class__.__name__, msg)
|
return "%s(%s)" % (self.__class__.__name__, msg)
|
||||||
|
|
||||||
|
|
||||||
class ConnectionError(TransportError):
|
class ConnectionError(TransportError):
|
||||||
@@ -77,8 +95,11 @@ class ConnectionError(TransportError):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'ConnectionError(%s) caused by: %s(%s)' % (
|
return "ConnectionError(%s) caused by: %s(%s)" % (
|
||||||
self.error, self.info.__class__.__name__, self.info)
|
self.error,
|
||||||
|
self.info.__class__.__name__,
|
||||||
|
self.info,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SSLError(ConnectionError):
|
class SSLError(ConnectionError):
|
||||||
@@ -89,8 +110,10 @@ class ConnectionTimeout(ConnectionError):
|
|||||||
""" A network timeout. Doesn't cause a node retry by default. """
|
""" A network timeout. Doesn't cause a node retry by default. """
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'ConnectionTimeout caused by - %s(%s)' % (
|
return "ConnectionTimeout caused by - %s(%s)" % (
|
||||||
self.info.__class__.__name__, self.info)
|
self.info.__class__.__name__,
|
||||||
|
self.info,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class NotFoundError(TransportError):
|
class NotFoundError(TransportError):
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
|
|
||||||
|
|
||||||
from .errors import BulkIndexError, ScanError
|
from .errors import BulkIndexError, ScanError
|
||||||
from .actions import expand_action, streaming_bulk, bulk, parallel_bulk
|
from .actions import expand_action, streaming_bulk, bulk, parallel_bulk
|
||||||
from .actions import scan, reindex
|
from .actions import scan, reindex
|
||||||
from .actions import _chunk_actions, _process_bulk_chunk
|
from .actions import _chunk_actions, _process_bulk_chunk
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -437,7 +437,7 @@ def scan(
|
|||||||
scroll_id = resp.get("_scroll_id")
|
scroll_id = resp.get("_scroll_id")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while scroll_id and resp['hits']['hits']:
|
while scroll_id and resp["hits"]["hits"]:
|
||||||
for hit in resp["hits"]["hits"]:
|
for hit in resp["hits"]["hits"]:
|
||||||
yield hit
|
yield hit
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
from ..exceptions import ElasticsearchException
|
from ..exceptions import ElasticsearchException
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# python 2.6
|
# python 2.6
|
||||||
from unittest2 import TestCase, SkipTest
|
from unittest2 import TestCase, SkipTest
|
||||||
@@ -9,33 +10,37 @@ except ImportError:
|
|||||||
from elasticsearch import Elasticsearch
|
from elasticsearch import Elasticsearch
|
||||||
from elasticsearch.exceptions import ConnectionError
|
from elasticsearch.exceptions import ConnectionError
|
||||||
|
|
||||||
|
|
||||||
def get_test_client(nowait=False, **kwargs):
|
def get_test_client(nowait=False, **kwargs):
|
||||||
# construct kwargs from the environment
|
# construct kwargs from the environment
|
||||||
kw = {'timeout': 30}
|
kw = {"timeout": 30}
|
||||||
if 'TEST_ES_CONNECTION' in os.environ:
|
if "TEST_ES_CONNECTION" in os.environ:
|
||||||
from elasticsearch import connection
|
from elasticsearch import connection
|
||||||
kw['connection_class'] = getattr(connection, os.environ['TEST_ES_CONNECTION'])
|
|
||||||
|
kw["connection_class"] = getattr(connection, os.environ["TEST_ES_CONNECTION"])
|
||||||
|
|
||||||
kw.update(kwargs)
|
kw.update(kwargs)
|
||||||
client = Elasticsearch([os.environ.get('TEST_ES_SERVER', {})], **kw)
|
client = Elasticsearch([os.environ.get("TEST_ES_SERVER", {})], **kw)
|
||||||
|
|
||||||
# wait for yellow status
|
# wait for yellow status
|
||||||
for _ in range(1 if nowait else 100):
|
for _ in range(1 if nowait else 100):
|
||||||
try:
|
try:
|
||||||
client.cluster.health(wait_for_status='yellow')
|
client.cluster.health(wait_for_status="yellow")
|
||||||
return client
|
return client
|
||||||
except ConnectionError:
|
except ConnectionError:
|
||||||
time.sleep(.1)
|
time.sleep(0.1)
|
||||||
else:
|
else:
|
||||||
# timeout
|
# timeout
|
||||||
raise SkipTest("Elasticsearch failed to start.")
|
raise SkipTest("Elasticsearch failed to start.")
|
||||||
|
|
||||||
|
|
||||||
def _get_version(version_string):
|
def _get_version(version_string):
|
||||||
if '.' not in version_string:
|
if "." not in version_string:
|
||||||
return ()
|
return ()
|
||||||
version = version_string.strip().split('.')
|
version = version_string.strip().split(".")
|
||||||
return tuple(int(v) if v.isdigit() else 999 for v in version)
|
return tuple(int(v) if v.isdigit() else 999 for v in version)
|
||||||
|
|
||||||
|
|
||||||
class ElasticsearchTestCase(TestCase):
|
class ElasticsearchTestCase(TestCase):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_client():
|
def _get_client():
|
||||||
@@ -48,13 +53,12 @@ class ElasticsearchTestCase(TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super(ElasticsearchTestCase, self).tearDown()
|
super(ElasticsearchTestCase, self).tearDown()
|
||||||
self.client.indices.delete(index='*', ignore=404)
|
self.client.indices.delete(index="*", ignore=404)
|
||||||
self.client.indices.delete_template(name='*', ignore=404)
|
self.client.indices.delete_template(name="*", ignore=404)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def es_version(self):
|
def es_version(self):
|
||||||
if not hasattr(self, '_es_version'):
|
if not hasattr(self, "_es_version"):
|
||||||
version_string = self.client.info()['version']['number']
|
version_string = self.client.info()["version"]["number"]
|
||||||
self._es_version = _get_version(version_string)
|
self._es_version = _get_version(version_string)
|
||||||
return self._es_version
|
return self._es_version
|
||||||
|
|
||||||
|
|||||||
+16
-12
@@ -9,8 +9,9 @@ from decimal import Decimal
|
|||||||
from .exceptions import SerializationError, ImproperlyConfigured
|
from .exceptions import SerializationError, ImproperlyConfigured
|
||||||
from .compat import string_types
|
from .compat import string_types
|
||||||
|
|
||||||
|
|
||||||
class TextSerializer(object):
|
class TextSerializer(object):
|
||||||
mimetype = 'text/plain'
|
mimetype = "text/plain"
|
||||||
|
|
||||||
def loads(self, s):
|
def loads(self, s):
|
||||||
return s
|
return s
|
||||||
@@ -19,10 +20,11 @@ class TextSerializer(object):
|
|||||||
if isinstance(data, string_types):
|
if isinstance(data, string_types):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
raise SerializationError('Cannot serialize %r into text.' % data)
|
raise SerializationError("Cannot serialize %r into text." % data)
|
||||||
|
|
||||||
|
|
||||||
class JSONSerializer(object):
|
class JSONSerializer(object):
|
||||||
mimetype = 'application/json'
|
mimetype = "application/json"
|
||||||
|
|
||||||
def default(self, data):
|
def default(self, data):
|
||||||
if isinstance(data, (date, datetime)):
|
if isinstance(data, (date, datetime)):
|
||||||
@@ -46,25 +48,26 @@ class JSONSerializer(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return json.dumps(
|
return json.dumps(
|
||||||
data,
|
data, default=self.default, ensure_ascii=False, separators=(",", ":")
|
||||||
default=self.default,
|
|
||||||
ensure_ascii=False,
|
|
||||||
separators=(',', ':'),
|
|
||||||
)
|
)
|
||||||
except (ValueError, TypeError) as e:
|
except (ValueError, TypeError) as e:
|
||||||
raise SerializationError(data, e)
|
raise SerializationError(data, e)
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_SERIALIZERS = {
|
DEFAULT_SERIALIZERS = {
|
||||||
JSONSerializer.mimetype: JSONSerializer(),
|
JSONSerializer.mimetype: JSONSerializer(),
|
||||||
TextSerializer.mimetype: TextSerializer(),
|
TextSerializer.mimetype: TextSerializer(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Deserializer(object):
|
class Deserializer(object):
|
||||||
def __init__(self, serializers, default_mimetype='application/json'):
|
def __init__(self, serializers, default_mimetype="application/json"):
|
||||||
try:
|
try:
|
||||||
self.default = serializers[default_mimetype]
|
self.default = serializers[default_mimetype]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ImproperlyConfigured('Cannot find default serializer (%s)' % default_mimetype)
|
raise ImproperlyConfigured(
|
||||||
|
"Cannot find default serializer (%s)" % default_mimetype
|
||||||
|
)
|
||||||
self.serializers = serializers
|
self.serializers = serializers
|
||||||
|
|
||||||
def loads(self, s, mimetype=None):
|
def loads(self, s, mimetype=None):
|
||||||
@@ -72,11 +75,12 @@ class Deserializer(object):
|
|||||||
deserializer = self.default
|
deserializer = self.default
|
||||||
else:
|
else:
|
||||||
# split out charset
|
# split out charset
|
||||||
mimetype, _, _ = mimetype.partition(';')
|
mimetype, _, _ = mimetype.partition(";")
|
||||||
try:
|
try:
|
||||||
deserializer = self.serializers[mimetype]
|
deserializer = self.serializers[mimetype]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise SerializationError('Unknown mimetype, unable to deserialize: %s' % mimetype)
|
raise SerializationError(
|
||||||
|
"Unknown mimetype, unable to deserialize: %s" % mimetype
|
||||||
|
)
|
||||||
|
|
||||||
return deserializer.loads(s)
|
return deserializer.loads(s)
|
||||||
|
|
||||||
|
|||||||
+72
-34
@@ -4,8 +4,12 @@ from itertools import chain
|
|||||||
from .connection import Urllib3HttpConnection
|
from .connection import Urllib3HttpConnection
|
||||||
from .connection_pool import ConnectionPool, DummyConnectionPool
|
from .connection_pool import ConnectionPool, DummyConnectionPool
|
||||||
from .serializer import JSONSerializer, Deserializer, DEFAULT_SERIALIZERS
|
from .serializer import JSONSerializer, Deserializer, DEFAULT_SERIALIZERS
|
||||||
from .exceptions import ConnectionError, TransportError, SerializationError, \
|
from .exceptions import (
|
||||||
ConnectionTimeout
|
ConnectionError,
|
||||||
|
TransportError,
|
||||||
|
SerializationError,
|
||||||
|
ConnectionTimeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_host_info(node_info, host):
|
def get_host_info(node_info, host):
|
||||||
@@ -23,10 +27,11 @@ def get_host_info(node_info, host):
|
|||||||
:arg host: connection information (host, port) extracted from the node info
|
:arg host: connection information (host, port) extracted from the node info
|
||||||
"""
|
"""
|
||||||
# ignore master only nodes
|
# ignore master only nodes
|
||||||
if node_info.get('roles', []) == ['master']:
|
if node_info.get("roles", []) == ["master"]:
|
||||||
return None
|
return None
|
||||||
return host
|
return host
|
||||||
|
|
||||||
|
|
||||||
class Transport(object):
|
class Transport(object):
|
||||||
"""
|
"""
|
||||||
Encapsulation of transport-related to logic. Handles instantiation of the
|
Encapsulation of transport-related to logic. Handles instantiation of the
|
||||||
@@ -34,12 +39,26 @@ class Transport(object):
|
|||||||
|
|
||||||
Main interface is the `perform_request` method.
|
Main interface is the `perform_request` method.
|
||||||
"""
|
"""
|
||||||
def __init__(self, hosts, connection_class=Urllib3HttpConnection,
|
|
||||||
connection_pool_class=ConnectionPool, host_info_callback=get_host_info,
|
def __init__(
|
||||||
sniff_on_start=False, sniffer_timeout=None, sniff_timeout=.1,
|
self,
|
||||||
sniff_on_connection_fail=False, serializer=JSONSerializer(), serializers=None,
|
hosts,
|
||||||
default_mimetype='application/json', max_retries=3, retry_on_status=(502, 503, 504, ),
|
connection_class=Urllib3HttpConnection,
|
||||||
retry_on_timeout=False, send_get_body_as='GET', **kwargs):
|
connection_pool_class=ConnectionPool,
|
||||||
|
host_info_callback=get_host_info,
|
||||||
|
sniff_on_start=False,
|
||||||
|
sniffer_timeout=None,
|
||||||
|
sniff_timeout=0.1,
|
||||||
|
sniff_on_connection_fail=False,
|
||||||
|
serializer=JSONSerializer(),
|
||||||
|
serializers=None,
|
||||||
|
default_mimetype="application/json",
|
||||||
|
max_retries=3,
|
||||||
|
retry_on_status=(502, 503, 504),
|
||||||
|
retry_on_timeout=False,
|
||||||
|
send_get_body_as="GET",
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
:arg hosts: list of dictionaries, each containing keyword arguments to
|
:arg hosts: list of dictionaries, each containing keyword arguments to
|
||||||
create a `connection_class` instance
|
create a `connection_class` instance
|
||||||
@@ -143,7 +162,7 @@ class Transport(object):
|
|||||||
# if this is not the initial setup look at the existing connection
|
# if this is not the initial setup look at the existing connection
|
||||||
# options and identify connections that haven't changed and can be
|
# options and identify connections that haven't changed and can be
|
||||||
# kept around.
|
# kept around.
|
||||||
if hasattr(self, 'connection_pool'):
|
if hasattr(self, "connection_pool"):
|
||||||
for (connection, old_host) in self.connection_pool.connection_opts:
|
for (connection, old_host) in self.connection_pool.connection_opts:
|
||||||
if old_host == host:
|
if old_host == host:
|
||||||
return connection
|
return connection
|
||||||
@@ -152,6 +171,7 @@ class Transport(object):
|
|||||||
kwargs = self.kwargs.copy()
|
kwargs = self.kwargs.copy()
|
||||||
kwargs.update(host)
|
kwargs.update(host)
|
||||||
return self.connection_class(**kwargs)
|
return self.connection_class(**kwargs)
|
||||||
|
|
||||||
connections = map(_create_connection, hosts)
|
connections = map(_create_connection, hosts)
|
||||||
|
|
||||||
connections = list(zip(connections, hosts))
|
connections = list(zip(connections, hosts))
|
||||||
@@ -159,7 +179,9 @@ class Transport(object):
|
|||||||
self.connection_pool = DummyConnectionPool(connections)
|
self.connection_pool = DummyConnectionPool(connections)
|
||||||
else:
|
else:
|
||||||
# pass the hosts dicts to the connection pool to optionally extract parameters from
|
# pass the hosts dicts to the connection pool to optionally extract parameters from
|
||||||
self.connection_pool = self.connection_pool_class(connections, **self.kwargs)
|
self.connection_pool = self.connection_pool_class(
|
||||||
|
connections, **self.kwargs
|
||||||
|
)
|
||||||
|
|
||||||
def get_connection(self):
|
def get_connection(self):
|
||||||
"""
|
"""
|
||||||
@@ -194,9 +216,13 @@ class Transport(object):
|
|||||||
try:
|
try:
|
||||||
# use small timeout for the sniffing request, should be a fast api call
|
# use small timeout for the sniffing request, should be a fast api call
|
||||||
_, headers, node_info = c.perform_request(
|
_, headers, node_info = c.perform_request(
|
||||||
'GET', '/_nodes/_all/http',
|
"GET",
|
||||||
timeout=self.sniff_timeout if not initial else None)
|
"/_nodes/_all/http",
|
||||||
node_info = self.deserializer.loads(node_info, headers.get('content-type'))
|
timeout=self.sniff_timeout if not initial else None,
|
||||||
|
)
|
||||||
|
node_info = self.deserializer.loads(
|
||||||
|
node_info, headers.get("content-type")
|
||||||
|
)
|
||||||
break
|
break
|
||||||
except (ConnectionError, SerializationError):
|
except (ConnectionError, SerializationError):
|
||||||
pass
|
pass
|
||||||
@@ -207,18 +233,18 @@ class Transport(object):
|
|||||||
self.last_sniff = previous_sniff
|
self.last_sniff = previous_sniff
|
||||||
raise
|
raise
|
||||||
|
|
||||||
return list(node_info['nodes'].values())
|
return list(node_info["nodes"].values())
|
||||||
|
|
||||||
def _get_host_info(self, host_info):
|
def _get_host_info(self, host_info):
|
||||||
host = {}
|
host = {}
|
||||||
address = host_info.get('http', {}).get('publish_address')
|
address = host_info.get("http", {}).get("publish_address")
|
||||||
|
|
||||||
# malformed or no address given
|
# malformed or no address given
|
||||||
if not address or ':' not in address:
|
if not address or ":" not in address:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
host['host'], host['port'] = address.rsplit(':', 1)
|
host["host"], host["port"] = address.rsplit(":", 1)
|
||||||
host['port'] = int(host['port'])
|
host["port"] = int(host["port"])
|
||||||
|
|
||||||
return self.host_info_callback(host_info, host)
|
return self.host_info_callback(host_info, host)
|
||||||
|
|
||||||
@@ -239,7 +265,9 @@ class Transport(object):
|
|||||||
# we weren't able to get any nodes or host_info_callback blocked all -
|
# we weren't able to get any nodes or host_info_callback blocked all -
|
||||||
# raise error.
|
# raise error.
|
||||||
if not hosts:
|
if not hosts:
|
||||||
raise TransportError("N/A", "Unable to sniff hosts - no viable hosts found.")
|
raise TransportError(
|
||||||
|
"N/A", "Unable to sniff hosts - no viable hosts found."
|
||||||
|
)
|
||||||
|
|
||||||
self.set_connections(hosts)
|
self.set_connections(hosts)
|
||||||
|
|
||||||
@@ -280,21 +308,21 @@ class Transport(object):
|
|||||||
body = self.serializer.dumps(body)
|
body = self.serializer.dumps(body)
|
||||||
|
|
||||||
# some clients or environments don't support sending GET with body
|
# some clients or environments don't support sending GET with body
|
||||||
if method in ('HEAD', 'GET') and self.send_get_body_as != 'GET':
|
if method in ("HEAD", "GET") and self.send_get_body_as != "GET":
|
||||||
# send it as post instead
|
# send it as post instead
|
||||||
if self.send_get_body_as == 'POST':
|
if self.send_get_body_as == "POST":
|
||||||
method = 'POST'
|
method = "POST"
|
||||||
|
|
||||||
# or as source parameter
|
# or as source parameter
|
||||||
elif self.send_get_body_as == 'source':
|
elif self.send_get_body_as == "source":
|
||||||
if params is None:
|
if params is None:
|
||||||
params = {}
|
params = {}
|
||||||
params['source'] = body
|
params["source"] = body
|
||||||
body = None
|
body = None
|
||||||
|
|
||||||
if body is not None:
|
if body is not None:
|
||||||
try:
|
try:
|
||||||
body = body.encode('utf-8', 'surrogatepass')
|
body = body.encode("utf-8", "surrogatepass")
|
||||||
except (UnicodeDecodeError, AttributeError):
|
except (UnicodeDecodeError, AttributeError):
|
||||||
# bytes/str - no need to re-encode
|
# bytes/str - no need to re-encode
|
||||||
pass
|
pass
|
||||||
@@ -302,10 +330,10 @@ class Transport(object):
|
|||||||
ignore = ()
|
ignore = ()
|
||||||
timeout = None
|
timeout = None
|
||||||
if params:
|
if params:
|
||||||
timeout = params.pop('request_timeout', None)
|
timeout = params.pop("request_timeout", None)
|
||||||
ignore = params.pop('ignore', ())
|
ignore = params.pop("ignore", ())
|
||||||
if isinstance(ignore, int):
|
if isinstance(ignore, int):
|
||||||
ignore = (ignore, )
|
ignore = (ignore,)
|
||||||
|
|
||||||
for attempt in range(self.max_retries + 1):
|
for attempt in range(self.max_retries + 1):
|
||||||
connection = self.get_connection()
|
connection = self.get_connection()
|
||||||
@@ -313,12 +341,20 @@ class Transport(object):
|
|||||||
try:
|
try:
|
||||||
# add a delay before attempting the next retry
|
# add a delay before attempting the next retry
|
||||||
# 0, 1, 3, 7, etc...
|
# 0, 1, 3, 7, etc...
|
||||||
delay = 2**attempt - 1
|
delay = 2 ** attempt - 1
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
status, headers_response, data = connection.perform_request(method, url, params, body, headers=headers, ignore=ignore, timeout=timeout)
|
status, headers_response, data = connection.perform_request(
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
params,
|
||||||
|
body,
|
||||||
|
headers=headers,
|
||||||
|
ignore=ignore,
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
|
||||||
except TransportError as e:
|
except TransportError as e:
|
||||||
if method == 'HEAD' and e.status_code == 404:
|
if method == "HEAD" and e.status_code == 404:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
retry = False
|
retry = False
|
||||||
@@ -342,11 +378,13 @@ class Transport(object):
|
|||||||
# connection didn't fail, confirm it's live status
|
# connection didn't fail, confirm it's live status
|
||||||
self.connection_pool.mark_live(connection)
|
self.connection_pool.mark_live(connection)
|
||||||
|
|
||||||
if method == 'HEAD':
|
if method == "HEAD":
|
||||||
return 200 <= status < 300
|
return 200 <= status < 300
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
data = self.deserializer.loads(data, headers_response.get('content-type'))
|
data = self.deserializer.loads(
|
||||||
|
data, headers_response.get("content-type")
|
||||||
|
)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
|||||||
+89
-86
@@ -14,65 +14,62 @@ from elasticsearch import Elasticsearch
|
|||||||
from elasticsearch.exceptions import TransportError
|
from elasticsearch.exceptions import TransportError
|
||||||
from elasticsearch.helpers import bulk, streaming_bulk
|
from elasticsearch.helpers import bulk, streaming_bulk
|
||||||
|
|
||||||
|
|
||||||
def create_git_index(client, index):
|
def create_git_index(client, index):
|
||||||
# we will use user on several places
|
# we will use user on several places
|
||||||
user_mapping = {
|
user_mapping = {
|
||||||
'properties': {
|
"properties": {
|
||||||
'name': {
|
"name": {"type": "text", "fields": {"keyword": {"type": "keyword"}}}
|
||||||
'type': 'text',
|
|
||||||
'fields': {
|
|
||||||
'keyword': {'type': 'keyword'},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create_index_body = {
|
create_index_body = {
|
||||||
'settings': {
|
"settings": {
|
||||||
# just one shard, no replicas for testing
|
# just one shard, no replicas for testing
|
||||||
'number_of_shards': 1,
|
"number_of_shards": 1,
|
||||||
'number_of_replicas': 0,
|
"number_of_replicas": 0,
|
||||||
|
# custom analyzer for analyzing file paths
|
||||||
# custom analyzer for analyzing file paths
|
"analysis": {
|
||||||
'analysis': {
|
"analyzer": {
|
||||||
'analyzer': {
|
"file_path": {
|
||||||
'file_path': {
|
"type": "custom",
|
||||||
'type': 'custom',
|
"tokenizer": "path_hierarchy",
|
||||||
'tokenizer': 'path_hierarchy',
|
"filter": ["lowercase"],
|
||||||
'filter': ['lowercase']
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"mappings": {
|
||||||
|
"doc": {
|
||||||
|
"properties": {
|
||||||
|
"repository": {"type": "keyword"},
|
||||||
|
"author": user_mapping,
|
||||||
|
"authored_date": {"type": "date"},
|
||||||
|
"committer": user_mapping,
|
||||||
|
"committed_date": {"type": "date"},
|
||||||
|
"parent_shas": {"type": "keyword"},
|
||||||
|
"description": {"type": "text", "analyzer": "snowball"},
|
||||||
|
"files": {
|
||||||
|
"type": "text",
|
||||||
|
"analyzer": "file_path",
|
||||||
|
"fielddata": True,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
},
|
|
||||||
'mappings': {
|
|
||||||
'doc': {
|
|
||||||
'properties': {
|
|
||||||
'repository': {'type': 'keyword'},
|
|
||||||
'author': user_mapping,
|
|
||||||
'authored_date': {'type': 'date'},
|
|
||||||
'committer': user_mapping,
|
|
||||||
'committed_date': {'type': 'date'},
|
|
||||||
'parent_shas': {'type': 'keyword'},
|
|
||||||
'description': {'type': 'text', 'analyzer': 'snowball'},
|
|
||||||
'files': {'type': 'text', 'analyzer': 'file_path', "fielddata": True}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# create empty index
|
# create empty index
|
||||||
try:
|
try:
|
||||||
client.indices.create(
|
client.indices.create(index=index, body=create_index_body)
|
||||||
index=index,
|
|
||||||
body=create_index_body,
|
|
||||||
)
|
|
||||||
except TransportError as e:
|
except TransportError as e:
|
||||||
# ignore already existing index
|
# ignore already existing index
|
||||||
if e.error == 'index_already_exists_exception':
|
if e.error == "index_already_exists_exception":
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def parse_commits(head, name):
|
def parse_commits(head, name):
|
||||||
"""
|
"""
|
||||||
Go through the git repository log and generate a document per commit
|
Go through the git repository log and generate a document per commit
|
||||||
@@ -80,26 +77,24 @@ def parse_commits(head, name):
|
|||||||
"""
|
"""
|
||||||
for commit in head.traverse():
|
for commit in head.traverse():
|
||||||
yield {
|
yield {
|
||||||
'_id': commit.hexsha,
|
"_id": commit.hexsha,
|
||||||
'repository': name,
|
"repository": name,
|
||||||
'committed_date': datetime.fromtimestamp(commit.committed_date),
|
"committed_date": datetime.fromtimestamp(commit.committed_date),
|
||||||
'committer': {
|
"committer": {
|
||||||
'name': commit.committer.name,
|
"name": commit.committer.name,
|
||||||
'email': commit.committer.email,
|
"email": commit.committer.email,
|
||||||
},
|
},
|
||||||
'authored_date': datetime.fromtimestamp(commit.authored_date),
|
"authored_date": datetime.fromtimestamp(commit.authored_date),
|
||||||
'author': {
|
"author": {"name": commit.author.name, "email": commit.author.email},
|
||||||
'name': commit.author.name,
|
"description": commit.message,
|
||||||
'email': commit.author.email,
|
"parent_shas": [p.hexsha for p in commit.parents],
|
||||||
},
|
|
||||||
'description': commit.message,
|
|
||||||
'parent_shas': [p.hexsha for p in commit.parents],
|
|
||||||
# we only care about the filenames, not the per-file stats
|
# we only care about the filenames, not the per-file stats
|
||||||
'files': list(commit.stats.files),
|
"files": list(commit.stats.files),
|
||||||
'stats': commit.stats.total,
|
"stats": commit.stats.total,
|
||||||
}
|
}
|
||||||
|
|
||||||
def load_repo(client, path=None, index='git'):
|
|
||||||
|
def load_repo(client, path=None, index="git"):
|
||||||
"""
|
"""
|
||||||
Parse a git repository with all it's commits and load it into elasticsearch
|
Parse a git repository with all it's commits and load it into elasticsearch
|
||||||
using `client`. If the index doesn't exist it will be created.
|
using `client`. If the index doesn't exist it will be created.
|
||||||
@@ -114,18 +109,18 @@ def load_repo(client, path=None, index='git'):
|
|||||||
# in - since the `parse_commits` function is a generator this will avoid
|
# in - since the `parse_commits` function is a generator this will avoid
|
||||||
# loading all the commits into memory
|
# loading all the commits into memory
|
||||||
for ok, result in streaming_bulk(
|
for ok, result in streaming_bulk(
|
||||||
client,
|
client,
|
||||||
parse_commits(repo.refs.master.commit, repo_name),
|
parse_commits(repo.refs.master.commit, repo_name),
|
||||||
index=index,
|
index=index,
|
||||||
doc_type='doc',
|
doc_type="doc",
|
||||||
chunk_size=50 # keep the batch sizes small for appearances only
|
chunk_size=50, # keep the batch sizes small for appearances only
|
||||||
):
|
):
|
||||||
action, result = result.popitem()
|
action, result = result.popitem()
|
||||||
doc_id = '/%s/doc/%s' % (index, result['_id'])
|
doc_id = "/%s/doc/%s" % (index, result["_id"])
|
||||||
# process the information from ES whether the document has been
|
# process the information from ES whether the document has been
|
||||||
# successfully indexed
|
# successfully indexed
|
||||||
if not ok:
|
if not ok:
|
||||||
print('Failed to %s document %s: %r' % (action, doc_id, result))
|
print("Failed to %s document %s: %r" % (action, doc_id, result))
|
||||||
else:
|
else:
|
||||||
print(doc_id)
|
print(doc_id)
|
||||||
|
|
||||||
@@ -133,36 +128,40 @@ def load_repo(client, path=None, index='git'):
|
|||||||
# we manually update some documents to add additional information
|
# we manually update some documents to add additional information
|
||||||
UPDATES = [
|
UPDATES = [
|
||||||
{
|
{
|
||||||
'_type': 'doc',
|
"_type": "doc",
|
||||||
'_id': '20fbba1230cabbc0f4644f917c6c2be52b8a63e8',
|
"_id": "20fbba1230cabbc0f4644f917c6c2be52b8a63e8",
|
||||||
'_op_type': 'update',
|
"_op_type": "update",
|
||||||
'doc': {'initial_commit': True}
|
"doc": {"initial_commit": True},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'_type': 'doc',
|
"_type": "doc",
|
||||||
'_id': 'ae0073c8ca7e24d237ffd56fba495ed409081bf4',
|
"_id": "ae0073c8ca7e24d237ffd56fba495ed409081bf4",
|
||||||
'_op_type': 'update',
|
"_op_type": "update",
|
||||||
'doc': {'release': '5.0.0'}
|
"doc": {"release": "5.0.0"},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
# get trace logger and set level
|
# get trace logger and set level
|
||||||
tracer = logging.getLogger('elasticsearch.trace')
|
tracer = logging.getLogger("elasticsearch.trace")
|
||||||
tracer.setLevel(logging.INFO)
|
tracer.setLevel(logging.INFO)
|
||||||
tracer.addHandler(logging.FileHandler('/tmp/es_trace.log'))
|
tracer.addHandler(logging.FileHandler("/tmp/es_trace.log"))
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-H", "--host",
|
"-H",
|
||||||
|
"--host",
|
||||||
action="store",
|
action="store",
|
||||||
default="localhost:9200",
|
default="localhost:9200",
|
||||||
help="The elasticsearch host you wish to connect to. (Default: localhost:9200)")
|
help="The elasticsearch host you wish to connect to. (Default: localhost:9200)",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-p", "--path",
|
"-p",
|
||||||
|
"--path",
|
||||||
action="store",
|
action="store",
|
||||||
default=None,
|
default=None,
|
||||||
help="Path to git repo. Commits used as data to load into Elasticsearch. (Default: None")
|
help="Path to git repo. Commits used as data to load into Elasticsearch. (Default: None",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -173,15 +172,19 @@ if __name__ == '__main__':
|
|||||||
load_repo(es, path=args.path)
|
load_repo(es, path=args.path)
|
||||||
|
|
||||||
# run the bulk operations
|
# run the bulk operations
|
||||||
success, _ = bulk(es, UPDATES, index='git')
|
success, _ = bulk(es, UPDATES, index="git")
|
||||||
print('Performed %d actions' % success)
|
print("Performed %d actions" % success)
|
||||||
|
|
||||||
# we can now make docs visible for searching
|
# we can now make docs visible for searching
|
||||||
es.indices.refresh(index='git')
|
es.indices.refresh(index="git")
|
||||||
|
|
||||||
# now we can retrieve the documents
|
# now we can retrieve the documents
|
||||||
initial_commit = es.get(index='git', doc_type='doc', id='20fbba1230cabbc0f4644f917c6c2be52b8a63e8')
|
initial_commit = es.get(
|
||||||
print('%s: %s' % (initial_commit['_id'], initial_commit['_source']['committed_date']))
|
index="git", doc_type="doc", id="20fbba1230cabbc0f4644f917c6c2be52b8a63e8"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"%s: %s" % (initial_commit["_id"], initial_commit["_source"]["committed_date"])
|
||||||
|
)
|
||||||
|
|
||||||
# and now we can count the documents
|
# and now we can count the documents
|
||||||
print(es.count(index='git')['count'], 'documents in index')
|
print(es.count(index="git")["count"], "documents in index")
|
||||||
|
|||||||
+66
-69
@@ -6,95 +6,92 @@ from dateutil.parser import parse as parse_date
|
|||||||
|
|
||||||
from elasticsearch import Elasticsearch
|
from elasticsearch import Elasticsearch
|
||||||
|
|
||||||
|
|
||||||
def print_search_stats(results):
|
def print_search_stats(results):
|
||||||
print('=' * 80)
|
print("=" * 80)
|
||||||
print('Total %d found in %dms' % (results['hits']['total'], results['took']))
|
print("Total %d found in %dms" % (results["hits"]["total"], results["took"]))
|
||||||
print('-' * 80)
|
print("-" * 80)
|
||||||
|
|
||||||
|
|
||||||
def print_hits(results):
|
def print_hits(results):
|
||||||
" Simple utility function to print results of a search query. "
|
" Simple utility function to print results of a search query. "
|
||||||
print_search_stats(results)
|
print_search_stats(results)
|
||||||
for hit in results['hits']['hits']:
|
for hit in results["hits"]["hits"]:
|
||||||
# get created date for a repo and fallback to authored_date for a commit
|
# get created date for a repo and fallback to authored_date for a commit
|
||||||
created_at = parse_date(hit['_source'].get('created_at', hit['_source']['authored_date']))
|
created_at = parse_date(
|
||||||
print('/%s/%s/%s (%s): %s' % (
|
hit["_source"].get("created_at", hit["_source"]["authored_date"])
|
||||||
hit['_index'], hit['_type'], hit['_id'],
|
)
|
||||||
created_at.strftime('%Y-%m-%d'),
|
print(
|
||||||
hit['_source']['description'].split('\n')[0]))
|
"/%s/%s/%s (%s): %s"
|
||||||
|
% (
|
||||||
|
hit["_index"],
|
||||||
|
hit["_type"],
|
||||||
|
hit["_id"],
|
||||||
|
created_at.strftime("%Y-%m-%d"),
|
||||||
|
hit["_source"]["description"].split("\n")[0],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
print('=' * 80)
|
print("=" * 80)
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
# get trace logger and set level
|
# get trace logger and set level
|
||||||
tracer = logging.getLogger('elasticsearch.trace')
|
tracer = logging.getLogger("elasticsearch.trace")
|
||||||
tracer.setLevel(logging.INFO)
|
tracer.setLevel(logging.INFO)
|
||||||
tracer.addHandler(logging.FileHandler('/tmp/es_trace.log'))
|
tracer.addHandler(logging.FileHandler("/tmp/es_trace.log"))
|
||||||
# instantiate es client, connects to localhost:9200 by default
|
# instantiate es client, connects to localhost:9200 by default
|
||||||
es = Elasticsearch()
|
es = Elasticsearch()
|
||||||
|
|
||||||
print('Empty search:')
|
print("Empty search:")
|
||||||
print_hits(es.search(index='git'))
|
print_hits(es.search(index="git"))
|
||||||
|
|
||||||
print('Find commits that says "fix" without touching tests:')
|
print('Find commits that says "fix" without touching tests:')
|
||||||
result = es.search(
|
result = es.search(
|
||||||
index='git',
|
index="git",
|
||||||
doc_type='doc',
|
doc_type="doc",
|
||||||
body={
|
body={
|
||||||
'query': {
|
"query": {
|
||||||
'bool': {
|
"bool": {
|
||||||
'must': {
|
"must": {"match": {"description": "fix"}},
|
||||||
'match': {'description': 'fix'}
|
"must_not": {"term": {"files": "test_elasticsearch"}},
|
||||||
},
|
|
||||||
'must_not': {
|
|
||||||
'term': {'files': 'test_elasticsearch'}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
print_hits(result)
|
|
||||||
|
|
||||||
print('Last 8 Commits for elasticsearch-py:')
|
|
||||||
result = es.search(
|
|
||||||
index='git',
|
|
||||||
doc_type='doc',
|
|
||||||
body={
|
|
||||||
'query': {
|
|
||||||
'term': {
|
|
||||||
'repository': 'elasticsearch-py'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'sort': [
|
|
||||||
{'committed_date': {'order': 'desc'}}
|
|
||||||
],
|
|
||||||
'size': 8
|
|
||||||
}
|
|
||||||
)
|
|
||||||
print_hits(result)
|
|
||||||
|
|
||||||
print('Stats for top 10 committers:')
|
|
||||||
result = es.search(
|
|
||||||
index='git',
|
|
||||||
doc_type='doc',
|
|
||||||
body={
|
|
||||||
'size': 0,
|
|
||||||
'aggs': {
|
|
||||||
'committers': {
|
|
||||||
'terms': {
|
|
||||||
'field': 'committer.name.keyword',
|
|
||||||
},
|
|
||||||
'aggs': {
|
|
||||||
'line_stats': {
|
|
||||||
'stats': {'field': 'stats.lines'}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
)
|
||||||
|
print_hits(result)
|
||||||
|
|
||||||
|
print("Last 8 Commits for elasticsearch-py:")
|
||||||
|
result = es.search(
|
||||||
|
index="git",
|
||||||
|
doc_type="doc",
|
||||||
|
body={
|
||||||
|
"query": {"term": {"repository": "elasticsearch-py"}},
|
||||||
|
"sort": [{"committed_date": {"order": "desc"}}],
|
||||||
|
"size": 8,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
print_hits(result)
|
||||||
|
|
||||||
|
print("Stats for top 10 committers:")
|
||||||
|
result = es.search(
|
||||||
|
index="git",
|
||||||
|
doc_type="doc",
|
||||||
|
body={
|
||||||
|
"size": 0,
|
||||||
|
"aggs": {
|
||||||
|
"committers": {
|
||||||
|
"terms": {"field": "committer.name.keyword"},
|
||||||
|
"aggs": {"line_stats": {"stats": {"field": "stats.lines"}}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
print_search_stats(result)
|
print_search_stats(result)
|
||||||
for committer in result['aggregations']['committers']['buckets']:
|
for committer in result["aggregations"]["committers"]["buckets"]:
|
||||||
print('%15s: %3d commits changing %6d lines' % (
|
print(
|
||||||
committer['key'], committer['doc_count'], committer['line_stats']['sum']))
|
"%15s: %3d commits changing %6d lines"
|
||||||
print('=' * 80)
|
% (committer["key"], committer["doc_count"], committer["line_stats"]["sum"])
|
||||||
|
)
|
||||||
|
print("=" * 80)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# python 2.6
|
# python 2.6
|
||||||
from unittest2 import TestCase, SkipTest
|
from unittest2 import TestCase, SkipTest
|
||||||
@@ -7,6 +8,7 @@ except ImportError:
|
|||||||
|
|
||||||
from elasticsearch import Elasticsearch
|
from elasticsearch import Elasticsearch
|
||||||
|
|
||||||
|
|
||||||
class DummyTransport(object):
|
class DummyTransport(object):
|
||||||
def __init__(self, hosts, responses=None, **kwargs):
|
def __init__(self, hosts, responses=None, **kwargs):
|
||||||
self.hosts = hosts
|
self.hosts = hosts
|
||||||
@@ -46,7 +48,7 @@ class TestElasticsearchTestCase(ElasticsearchTestCase):
|
|||||||
self.assert_call_count_equals(0)
|
self.assert_call_count_equals(0)
|
||||||
|
|
||||||
def test_each_call_is_recorded(self):
|
def test_each_call_is_recorded(self):
|
||||||
self.client.transport.perform_request('GET', '/')
|
self.client.transport.perform_request("GET", "/")
|
||||||
self.client.transport.perform_request('DELETE', '/42', params={}, body='body')
|
self.client.transport.perform_request("DELETE", "/42", params={}, body="body")
|
||||||
self.assert_call_count_equals(2)
|
self.assert_call_count_equals(2)
|
||||||
self.assertEquals([({}, 'body')], self.assert_url_called('DELETE', '/42', 1))
|
self.assertEquals([({}, "body")], self.assert_url_called("DELETE", "/42", 1))
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
from test_elasticsearch.test_cases import ElasticsearchTestCase
|
from test_elasticsearch.test_cases import ElasticsearchTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestIndices(ElasticsearchTestCase):
|
class TestIndices(ElasticsearchTestCase):
|
||||||
def test_create_one_index(self):
|
def test_create_one_index(self):
|
||||||
self.client.indices.create('test-index')
|
self.client.indices.create("test-index")
|
||||||
self.assert_url_called('PUT', '/test-index')
|
self.assert_url_called("PUT", "/test-index")
|
||||||
|
|
||||||
def test_delete_multiple_indices(self):
|
def test_delete_multiple_indices(self):
|
||||||
self.client.indices.delete(['test-index', 'second.index', 'third/index'])
|
self.client.indices.delete(["test-index", "second.index", "third/index"])
|
||||||
self.assert_url_called('DELETE', '/test-index,second.index,third%2Findex')
|
self.assert_url_called("DELETE", "/test-index,second.index,third%2Findex")
|
||||||
|
|
||||||
def test_exists_index(self):
|
def test_exists_index(self):
|
||||||
self.client.indices.exists('second.index,third/index')
|
self.client.indices.exists("second.index,third/index")
|
||||||
self.assert_url_called('HEAD', '/second.index,third%2Findex')
|
self.assert_url_called("HEAD", "/second.index,third%2Findex")
|
||||||
|
|
||||||
def test_passing_empty_value_for_required_param_raises_exception(self):
|
def test_passing_empty_value_for_required_param_raises_exception(self):
|
||||||
self.assertRaises(ValueError, self.client.indices.exists, index=None)
|
self.assertRaises(ValueError, self.client.indices.exists, index=None)
|
||||||
self.assertRaises(ValueError, self.client.indices.exists, index=[])
|
self.assertRaises(ValueError, self.client.indices.exists, index=[])
|
||||||
self.assertRaises(ValueError, self.client.indices.exists, index='')
|
self.assertRaises(ValueError, self.client.indices.exists, index="")
|
||||||
|
|||||||
@@ -6,35 +6,32 @@ from elasticsearch.compat import PY2
|
|||||||
|
|
||||||
from ..test_cases import TestCase, SkipTest
|
from ..test_cases import TestCase, SkipTest
|
||||||
|
|
||||||
|
|
||||||
class TestMakePath(TestCase):
|
class TestMakePath(TestCase):
|
||||||
def test_handles_unicode(self):
|
def test_handles_unicode(self):
|
||||||
id = "中文"
|
id = "中文"
|
||||||
self.assertEquals('/some-index/type/%E4%B8%AD%E6%96%87', _make_path('some-index', 'type', id))
|
self.assertEquals(
|
||||||
|
"/some-index/type/%E4%B8%AD%E6%96%87", _make_path("some-index", "type", id)
|
||||||
|
)
|
||||||
|
|
||||||
def test_handles_utf_encoded_string(self):
|
def test_handles_utf_encoded_string(self):
|
||||||
if not PY2:
|
if not PY2:
|
||||||
raise SkipTest('Only relevant for py2')
|
raise SkipTest("Only relevant for py2")
|
||||||
id = "中文".encode('utf-8')
|
id = "中文".encode("utf-8")
|
||||||
self.assertEquals('/some-index/type/%E4%B8%AD%E6%96%87', _make_path('some-index', 'type', id))
|
self.assertEquals(
|
||||||
|
"/some-index/type/%E4%B8%AD%E6%96%87", _make_path("some-index", "type", id)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestEscape(TestCase):
|
class TestEscape(TestCase):
|
||||||
def test_handles_ascii(self):
|
def test_handles_ascii(self):
|
||||||
string = "abc123"
|
string = "abc123"
|
||||||
self.assertEquals(
|
self.assertEquals(b"abc123", _escape(string))
|
||||||
b'abc123',
|
|
||||||
_escape(string)
|
|
||||||
)
|
|
||||||
def test_handles_unicode(self):
|
def test_handles_unicode(self):
|
||||||
string = "中文"
|
string = "中文"
|
||||||
self.assertEquals(
|
self.assertEquals(b"\xe4\xb8\xad\xe6\x96\x87", _escape(string))
|
||||||
b'\xe4\xb8\xad\xe6\x96\x87',
|
|
||||||
_escape(string)
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_handles_bytestring(self):
|
def test_handles_bytestring(self):
|
||||||
string = b'celery-task-meta-c4f1201f-eb7b-41d5-9318-a75a8cfbdaa0'
|
string = b"celery-task-meta-c4f1201f-eb7b-41d5-9318-a75a8cfbdaa0"
|
||||||
self.assertEquals(
|
self.assertEquals(string, _escape(string))
|
||||||
string,
|
|
||||||
_escape(string)
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -6,9 +6,13 @@ import urllib3
|
|||||||
import warnings
|
import warnings
|
||||||
from requests.auth import AuthBase
|
from requests.auth import AuthBase
|
||||||
|
|
||||||
from elasticsearch.exceptions import TransportError, ConflictError, RequestError, NotFoundError
|
from elasticsearch.exceptions import (
|
||||||
from elasticsearch.connection import RequestsHttpConnection, \
|
TransportError,
|
||||||
Urllib3HttpConnection
|
ConflictError,
|
||||||
|
RequestError,
|
||||||
|
NotFoundError,
|
||||||
|
)
|
||||||
|
from elasticsearch.connection import RequestsHttpConnection, Urllib3HttpConnection
|
||||||
from elasticsearch.exceptions import ImproperlyConfigured
|
from elasticsearch.exceptions import ImproperlyConfigured
|
||||||
from .test_cases import TestCase, SkipTest
|
from .test_cases import TestCase, SkipTest
|
||||||
|
|
||||||
@@ -22,20 +26,18 @@ class TestUrllib3Connection(TestCase):
|
|||||||
# it means SSLContext is not available for that version of python
|
# it means SSLContext is not available for that version of python
|
||||||
# and we should skip this test.
|
# and we should skip this test.
|
||||||
raise SkipTest(
|
raise SkipTest(
|
||||||
"Test test_ssl_context is skipped cause SSLContext is not available for this version of ptyhon")
|
"Test test_ssl_context is skipped cause SSLContext is not available for this version of ptyhon"
|
||||||
|
)
|
||||||
|
|
||||||
con = Urllib3HttpConnection(use_ssl=True, ssl_context=context)
|
con = Urllib3HttpConnection(use_ssl=True, ssl_context=context)
|
||||||
self.assertEqual(len(con.pool.conn_kw.keys()), 1)
|
self.assertEqual(len(con.pool.conn_kw.keys()), 1)
|
||||||
self.assertIsInstance(
|
self.assertIsInstance(con.pool.conn_kw["ssl_context"], ssl.SSLContext)
|
||||||
con.pool.conn_kw['ssl_context'],
|
|
||||||
ssl.SSLContext
|
|
||||||
)
|
|
||||||
self.assertTrue(con.use_ssl)
|
self.assertTrue(con.use_ssl)
|
||||||
|
|
||||||
def test_http_compression(self):
|
def test_http_compression(self):
|
||||||
con = Urllib3HttpConnection(http_compress=True)
|
con = Urllib3HttpConnection(http_compress=True)
|
||||||
self.assertTrue(con.http_compress)
|
self.assertTrue(con.http_compress)
|
||||||
self.assertEquals(con.headers['content-encoding'], 'gzip')
|
self.assertEquals(con.headers["content-encoding"], "gzip")
|
||||||
|
|
||||||
def test_timeout_set(self):
|
def test_timeout_set(self):
|
||||||
con = Urllib3HttpConnection(timeout=42)
|
con = Urllib3HttpConnection(timeout=42)
|
||||||
@@ -43,40 +45,60 @@ class TestUrllib3Connection(TestCase):
|
|||||||
|
|
||||||
def test_keep_alive_is_on_by_default(self):
|
def test_keep_alive_is_on_by_default(self):
|
||||||
con = Urllib3HttpConnection()
|
con = Urllib3HttpConnection()
|
||||||
self.assertEquals({'connection': 'keep-alive',
|
self.assertEquals(
|
||||||
'content-type': 'application/json'}, con.headers)
|
{"connection": "keep-alive", "content-type": "application/json"},
|
||||||
|
con.headers,
|
||||||
|
)
|
||||||
|
|
||||||
def test_http_auth(self):
|
def test_http_auth(self):
|
||||||
con = Urllib3HttpConnection(http_auth='username:secret')
|
con = Urllib3HttpConnection(http_auth="username:secret")
|
||||||
self.assertEquals({
|
self.assertEquals(
|
||||||
'authorization': 'Basic dXNlcm5hbWU6c2VjcmV0',
|
{
|
||||||
'connection': 'keep-alive',
|
"authorization": "Basic dXNlcm5hbWU6c2VjcmV0",
|
||||||
'content-type': 'application/json'
|
"connection": "keep-alive",
|
||||||
}, con.headers)
|
"content-type": "application/json",
|
||||||
|
},
|
||||||
|
con.headers,
|
||||||
|
)
|
||||||
|
|
||||||
def test_http_auth_tuple(self):
|
def test_http_auth_tuple(self):
|
||||||
con = Urllib3HttpConnection(http_auth=('username', 'secret'))
|
con = Urllib3HttpConnection(http_auth=("username", "secret"))
|
||||||
self.assertEquals({'authorization': 'Basic dXNlcm5hbWU6c2VjcmV0',
|
self.assertEquals(
|
||||||
'content-type': 'application/json',
|
{
|
||||||
'connection': 'keep-alive'}, con.headers)
|
"authorization": "Basic dXNlcm5hbWU6c2VjcmV0",
|
||||||
|
"content-type": "application/json",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
},
|
||||||
|
con.headers,
|
||||||
|
)
|
||||||
|
|
||||||
def test_http_auth_list(self):
|
def test_http_auth_list(self):
|
||||||
con = Urllib3HttpConnection(http_auth=['username', 'secret'])
|
con = Urllib3HttpConnection(http_auth=["username", "secret"])
|
||||||
self.assertEquals({'authorization': 'Basic dXNlcm5hbWU6c2VjcmV0',
|
self.assertEquals(
|
||||||
'content-type': 'application/json',
|
{
|
||||||
'connection': 'keep-alive'}, con.headers)
|
"authorization": "Basic dXNlcm5hbWU6c2VjcmV0",
|
||||||
|
"content-type": "application/json",
|
||||||
|
"connection": "keep-alive",
|
||||||
|
},
|
||||||
|
con.headers,
|
||||||
|
)
|
||||||
|
|
||||||
def test_uses_https_if_verify_certs_is_off(self):
|
def test_uses_https_if_verify_certs_is_off(self):
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with warnings.catch_warnings(record=True) as w:
|
||||||
con = Urllib3HttpConnection(use_ssl=True, verify_certs=False)
|
con = Urllib3HttpConnection(use_ssl=True, verify_certs=False)
|
||||||
self.assertEquals(1, len(w))
|
self.assertEquals(1, len(w))
|
||||||
self.assertEquals('Connecting to localhost using SSL with verify_certs=False is insecure.', str(w[0].message))
|
self.assertEquals(
|
||||||
|
"Connecting to localhost using SSL with verify_certs=False is insecure.",
|
||||||
|
str(w[0].message),
|
||||||
|
)
|
||||||
|
|
||||||
self.assertIsInstance(con.pool, urllib3.HTTPSConnectionPool)
|
self.assertIsInstance(con.pool, urllib3.HTTPSConnectionPool)
|
||||||
|
|
||||||
def nowarn_when_test_uses_https_if_verify_certs_is_off(self):
|
def nowarn_when_test_uses_https_if_verify_certs_is_off(self):
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with warnings.catch_warnings(record=True) as w:
|
||||||
con = Urllib3HttpConnection(use_ssl=True, verify_certs=False, ssl_show_warn=False)
|
con = Urllib3HttpConnection(
|
||||||
|
use_ssl=True, verify_certs=False, ssl_show_warn=False
|
||||||
|
)
|
||||||
self.assertEquals(0, len(w))
|
self.assertEquals(0, len(w))
|
||||||
|
|
||||||
self.assertIsInstance(con.pool, urllib3.HTTPSConnectionPool)
|
self.assertIsInstance(con.pool, urllib3.HTTPSConnectionPool)
|
||||||
@@ -85,9 +107,13 @@ class TestUrllib3Connection(TestCase):
|
|||||||
con = Urllib3HttpConnection()
|
con = Urllib3HttpConnection()
|
||||||
self.assertIsInstance(con.pool, urllib3.HTTPConnectionPool)
|
self.assertIsInstance(con.pool, urllib3.HTTPConnectionPool)
|
||||||
|
|
||||||
|
|
||||||
class TestRequestsConnection(TestCase):
|
class TestRequestsConnection(TestCase):
|
||||||
def _get_mock_connection(self, connection_params={}, status_code=200, response_body='{}'):
|
def _get_mock_connection(
|
||||||
|
self, connection_params={}, status_code=200, response_body="{}"
|
||||||
|
):
|
||||||
con = RequestsHttpConnection(**connection_params)
|
con = RequestsHttpConnection(**connection_params)
|
||||||
|
|
||||||
def _dummy_send(*args, **kwargs):
|
def _dummy_send(*args, **kwargs):
|
||||||
dummy_response = Mock()
|
dummy_response = Mock()
|
||||||
dummy_response.headers = {}
|
dummy_response.headers = {}
|
||||||
@@ -97,20 +123,21 @@ class TestRequestsConnection(TestCase):
|
|||||||
dummy_response.cookies = {}
|
dummy_response.cookies = {}
|
||||||
_dummy_send.call_args = (args, kwargs)
|
_dummy_send.call_args = (args, kwargs)
|
||||||
return dummy_response
|
return dummy_response
|
||||||
|
|
||||||
con.session.send = _dummy_send
|
con.session.send = _dummy_send
|
||||||
return con
|
return con
|
||||||
|
|
||||||
def _get_request(self, connection, *args, **kwargs):
|
def _get_request(self, connection, *args, **kwargs):
|
||||||
if 'body' in kwargs:
|
if "body" in kwargs:
|
||||||
kwargs['body'] = kwargs['body'].encode('utf-8')
|
kwargs["body"] = kwargs["body"].encode("utf-8")
|
||||||
|
|
||||||
status, headers, data = connection.perform_request(*args, **kwargs)
|
status, headers, data = connection.perform_request(*args, **kwargs)
|
||||||
self.assertEquals(200, status)
|
self.assertEquals(200, status)
|
||||||
self.assertEquals('{}', data)
|
self.assertEquals("{}", data)
|
||||||
|
|
||||||
timeout = kwargs.pop('timeout', connection.timeout)
|
timeout = kwargs.pop("timeout", connection.timeout)
|
||||||
args, kwargs = connection.session.send.call_args
|
args, kwargs = connection.session.send.call_args
|
||||||
self.assertEquals(timeout, kwargs['timeout'])
|
self.assertEquals(timeout, kwargs["timeout"])
|
||||||
self.assertEquals(1, len(args))
|
self.assertEquals(1, len(args))
|
||||||
return args[0]
|
return args[0]
|
||||||
|
|
||||||
@@ -126,73 +153,96 @@ class TestRequestsConnection(TestCase):
|
|||||||
|
|
||||||
def test_uses_https_if_verify_certs_is_off(self):
|
def test_uses_https_if_verify_certs_is_off(self):
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with warnings.catch_warnings(record=True) as w:
|
||||||
con = self._get_mock_connection({'use_ssl': True, 'url_prefix': 'url', 'verify_certs': False})
|
con = self._get_mock_connection(
|
||||||
|
{"use_ssl": True, "url_prefix": "url", "verify_certs": False}
|
||||||
|
)
|
||||||
self.assertEquals(1, len(w))
|
self.assertEquals(1, len(w))
|
||||||
self.assertEquals('Connecting to https://localhost:9200/url using SSL with verify_certs=False is insecure.', str(w[0].message))
|
self.assertEquals(
|
||||||
|
"Connecting to https://localhost:9200/url using SSL with verify_certs=False is insecure.",
|
||||||
|
str(w[0].message),
|
||||||
|
)
|
||||||
|
|
||||||
request = self._get_request(con, 'GET', '/')
|
request = self._get_request(con, "GET", "/")
|
||||||
|
|
||||||
self.assertEquals('https://localhost:9200/url/', request.url)
|
self.assertEquals("https://localhost:9200/url/", request.url)
|
||||||
self.assertEquals('GET', request.method)
|
self.assertEquals("GET", request.method)
|
||||||
self.assertEquals(None, request.body)
|
self.assertEquals(None, request.body)
|
||||||
|
|
||||||
def nowarn_when_test_uses_https_if_verify_certs_is_off(self):
|
def nowarn_when_test_uses_https_if_verify_certs_is_off(self):
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with warnings.catch_warnings(record=True) as w:
|
||||||
con = self._get_mock_connection({'use_ssl': True, 'url_prefix': 'url', 'verify_certs': False, 'ssl_show_warn': False})
|
con = self._get_mock_connection(
|
||||||
|
{
|
||||||
|
"use_ssl": True,
|
||||||
|
"url_prefix": "url",
|
||||||
|
"verify_certs": False,
|
||||||
|
"ssl_show_warn": False,
|
||||||
|
}
|
||||||
|
)
|
||||||
self.assertEquals(0, len(w))
|
self.assertEquals(0, len(w))
|
||||||
|
|
||||||
request = self._get_request(con, 'GET', '/')
|
request = self._get_request(con, "GET", "/")
|
||||||
|
|
||||||
self.assertEquals('https://localhost:9200/url/', request.url)
|
self.assertEquals("https://localhost:9200/url/", request.url)
|
||||||
self.assertEquals('GET', request.method)
|
self.assertEquals("GET", request.method)
|
||||||
self.assertEquals(None, request.body)
|
self.assertEquals(None, request.body)
|
||||||
|
|
||||||
def test_merge_headers(self):
|
def test_merge_headers(self):
|
||||||
con = self._get_mock_connection(connection_params={'headers': {'h1': 'v1', 'h2': 'v2'}})
|
con = self._get_mock_connection(
|
||||||
req = self._get_request(con, 'GET', '/', headers={'h2': 'v2p', 'h3': 'v3'})
|
connection_params={"headers": {"h1": "v1", "h2": "v2"}}
|
||||||
self.assertEquals(req.headers['h1'], 'v1')
|
)
|
||||||
self.assertEquals(req.headers['h2'], 'v2p')
|
req = self._get_request(con, "GET", "/", headers={"h2": "v2p", "h3": "v3"})
|
||||||
self.assertEquals(req.headers['h3'], 'v3')
|
self.assertEquals(req.headers["h1"], "v1")
|
||||||
|
self.assertEquals(req.headers["h2"], "v2p")
|
||||||
|
self.assertEquals(req.headers["h3"], "v3")
|
||||||
|
|
||||||
def test_http_auth(self):
|
def test_http_auth(self):
|
||||||
con = RequestsHttpConnection(http_auth='username:secret')
|
con = RequestsHttpConnection(http_auth="username:secret")
|
||||||
self.assertEquals(('username', 'secret'), con.session.auth)
|
self.assertEquals(("username", "secret"), con.session.auth)
|
||||||
|
|
||||||
def test_http_auth_tuple(self):
|
def test_http_auth_tuple(self):
|
||||||
con = RequestsHttpConnection(http_auth=('username', 'secret'))
|
con = RequestsHttpConnection(http_auth=("username", "secret"))
|
||||||
self.assertEquals(('username', 'secret'), con.session.auth)
|
self.assertEquals(("username", "secret"), con.session.auth)
|
||||||
|
|
||||||
def test_http_auth_list(self):
|
def test_http_auth_list(self):
|
||||||
con = RequestsHttpConnection(http_auth=['username', 'secret'])
|
con = RequestsHttpConnection(http_auth=["username", "secret"])
|
||||||
self.assertEquals(('username', 'secret'), con.session.auth)
|
self.assertEquals(("username", "secret"), con.session.auth)
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
con = self._get_mock_connection({"host": "elasticsearch.com", "port": 443})
|
con = self._get_mock_connection({"host": "elasticsearch.com", "port": 443})
|
||||||
self.assertEquals('<RequestsHttpConnection: http://elasticsearch.com:443>', repr(con))
|
self.assertEquals(
|
||||||
|
"<RequestsHttpConnection: http://elasticsearch.com:443>", repr(con)
|
||||||
|
)
|
||||||
|
|
||||||
def test_conflict_error_is_returned_on_409(self):
|
def test_conflict_error_is_returned_on_409(self):
|
||||||
con = self._get_mock_connection(status_code=409)
|
con = self._get_mock_connection(status_code=409)
|
||||||
self.assertRaises(ConflictError, con.perform_request, 'GET', '/', {}, '')
|
self.assertRaises(ConflictError, con.perform_request, "GET", "/", {}, "")
|
||||||
|
|
||||||
def test_not_found_error_is_returned_on_404(self):
|
def test_not_found_error_is_returned_on_404(self):
|
||||||
con = self._get_mock_connection(status_code=404)
|
con = self._get_mock_connection(status_code=404)
|
||||||
self.assertRaises(NotFoundError, con.perform_request, 'GET', '/', {}, '')
|
self.assertRaises(NotFoundError, con.perform_request, "GET", "/", {}, "")
|
||||||
|
|
||||||
def test_request_error_is_returned_on_400(self):
|
def test_request_error_is_returned_on_400(self):
|
||||||
con = self._get_mock_connection(status_code=400)
|
con = self._get_mock_connection(status_code=400)
|
||||||
self.assertRaises(RequestError, con.perform_request, 'GET', '/', {}, '')
|
self.assertRaises(RequestError, con.perform_request, "GET", "/", {}, "")
|
||||||
|
|
||||||
@patch('elasticsearch.connection.base.logger')
|
@patch("elasticsearch.connection.base.logger")
|
||||||
def test_head_with_404_doesnt_get_logged(self, logger):
|
def test_head_with_404_doesnt_get_logged(self, logger):
|
||||||
con = self._get_mock_connection(status_code=404)
|
con = self._get_mock_connection(status_code=404)
|
||||||
self.assertRaises(NotFoundError, con.perform_request, 'HEAD', '/', {}, '')
|
self.assertRaises(NotFoundError, con.perform_request, "HEAD", "/", {}, "")
|
||||||
self.assertEquals(0, logger.warning.call_count)
|
self.assertEquals(0, logger.warning.call_count)
|
||||||
|
|
||||||
@patch('elasticsearch.connection.base.tracer')
|
@patch("elasticsearch.connection.base.tracer")
|
||||||
@patch('elasticsearch.connection.base.logger')
|
@patch("elasticsearch.connection.base.logger")
|
||||||
def test_failed_request_logs_and_traces(self, logger, tracer):
|
def test_failed_request_logs_and_traces(self, logger, tracer):
|
||||||
con = self._get_mock_connection(response_body='{"answer": 42}', status_code=500)
|
con = self._get_mock_connection(response_body='{"answer": 42}', status_code=500)
|
||||||
self.assertRaises(TransportError, con.perform_request, 'GET', '/', {'param': 42}, '{}'.encode('utf-8'))
|
self.assertRaises(
|
||||||
|
TransportError,
|
||||||
|
con.perform_request,
|
||||||
|
"GET",
|
||||||
|
"/",
|
||||||
|
{"param": 42},
|
||||||
|
"{}".encode("utf-8"),
|
||||||
|
)
|
||||||
|
|
||||||
# trace request
|
# trace request
|
||||||
self.assertEquals(1, tracer.info.call_count)
|
self.assertEquals(1, tracer.info.call_count)
|
||||||
@@ -200,90 +250,101 @@ class TestRequestsConnection(TestCase):
|
|||||||
self.assertEquals(1, tracer.debug.call_count)
|
self.assertEquals(1, tracer.debug.call_count)
|
||||||
# log url and duration
|
# log url and duration
|
||||||
self.assertEquals(1, logger.warning.call_count)
|
self.assertEquals(1, logger.warning.call_count)
|
||||||
self.assertTrue(re.match(
|
self.assertTrue(
|
||||||
'^GET http://localhost:9200/\?param=42 \[status:500 request:0.[0-9]{3}s\]',
|
re.match(
|
||||||
logger.warning.call_args[0][0] % logger.warning.call_args[0][1:]
|
"^GET http://localhost:9200/\?param=42 \[status:500 request:0.[0-9]{3}s\]",
|
||||||
))
|
logger.warning.call_args[0][0] % logger.warning.call_args[0][1:],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@patch('elasticsearch.connection.base.tracer')
|
@patch("elasticsearch.connection.base.tracer")
|
||||||
@patch('elasticsearch.connection.base.logger')
|
@patch("elasticsearch.connection.base.logger")
|
||||||
def test_success_logs_and_traces(self, logger, tracer):
|
def test_success_logs_and_traces(self, logger, tracer):
|
||||||
con = self._get_mock_connection(response_body='''{"answer": "that's it!"}''')
|
con = self._get_mock_connection(response_body="""{"answer": "that's it!"}""")
|
||||||
status, headers, data = con.perform_request('GET', '/', {'param': 42}, '''{"question": "what's that?"}'''.encode('utf-8'))
|
status, headers, data = con.perform_request(
|
||||||
|
"GET",
|
||||||
|
"/",
|
||||||
|
{"param": 42},
|
||||||
|
"""{"question": "what's that?"}""".encode("utf-8"),
|
||||||
|
)
|
||||||
|
|
||||||
# trace request
|
# trace request
|
||||||
self.assertEquals(1, tracer.info.call_count)
|
self.assertEquals(1, tracer.info.call_count)
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
"""curl -H 'Content-Type: application/json' -XGET 'http://localhost:9200/?pretty¶m=42' -d '{\n "question": "what\\u0027s that?"\n}'""",
|
"""curl -H 'Content-Type: application/json' -XGET 'http://localhost:9200/?pretty¶m=42' -d '{\n "question": "what\\u0027s that?"\n}'""",
|
||||||
tracer.info.call_args[0][0] % tracer.info.call_args[0][1:]
|
tracer.info.call_args[0][0] % tracer.info.call_args[0][1:],
|
||||||
)
|
)
|
||||||
# trace response
|
# trace response
|
||||||
self.assertEquals(1, tracer.debug.call_count)
|
self.assertEquals(1, tracer.debug.call_count)
|
||||||
self.assertTrue(re.match(
|
self.assertTrue(
|
||||||
'#\[200\] \(0.[0-9]{3}s\)\n#\{\n# "answer": "that\\\\u0027s it!"\n#\}',
|
re.match(
|
||||||
tracer.debug.call_args[0][0] % tracer.debug.call_args[0][1:]
|
'#\[200\] \(0.[0-9]{3}s\)\n#\{\n# "answer": "that\\\\u0027s it!"\n#\}',
|
||||||
))
|
tracer.debug.call_args[0][0] % tracer.debug.call_args[0][1:],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# log url and duration
|
# log url and duration
|
||||||
self.assertEquals(1, logger.info.call_count)
|
self.assertEquals(1, logger.info.call_count)
|
||||||
self.assertTrue(re.match(
|
self.assertTrue(
|
||||||
'GET http://localhost:9200/\?param=42 \[status:200 request:0.[0-9]{3}s\]',
|
re.match(
|
||||||
logger.info.call_args[0][0] % logger.info.call_args[0][1:]
|
"GET http://localhost:9200/\?param=42 \[status:200 request:0.[0-9]{3}s\]",
|
||||||
))
|
logger.info.call_args[0][0] % logger.info.call_args[0][1:],
|
||||||
|
)
|
||||||
|
)
|
||||||
# log request body and response
|
# log request body and response
|
||||||
self.assertEquals(2, logger.debug.call_count)
|
self.assertEquals(2, logger.debug.call_count)
|
||||||
req, resp = logger.debug.call_args_list
|
req, resp = logger.debug.call_args_list
|
||||||
self.assertEquals(
|
self.assertEquals('> {"question": "what\'s that?"}', req[0][0] % req[0][1:])
|
||||||
'> {"question": "what\'s that?"}',
|
self.assertEquals('< {"answer": "that\'s it!"}', resp[0][0] % resp[0][1:])
|
||||||
req[0][0] % req[0][1:]
|
|
||||||
)
|
|
||||||
self.assertEquals(
|
|
||||||
'< {"answer": "that\'s it!"}',
|
|
||||||
resp[0][0] % resp[0][1:]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_defaults(self):
|
def test_defaults(self):
|
||||||
con = self._get_mock_connection()
|
con = self._get_mock_connection()
|
||||||
request = self._get_request(con, 'GET', '/')
|
request = self._get_request(con, "GET", "/")
|
||||||
|
|
||||||
self.assertEquals('http://localhost:9200/', request.url)
|
self.assertEquals("http://localhost:9200/", request.url)
|
||||||
self.assertEquals('GET', request.method)
|
self.assertEquals("GET", request.method)
|
||||||
self.assertEquals(None, request.body)
|
self.assertEquals(None, request.body)
|
||||||
|
|
||||||
def test_params_properly_encoded(self):
|
def test_params_properly_encoded(self):
|
||||||
con = self._get_mock_connection()
|
con = self._get_mock_connection()
|
||||||
request = self._get_request(con, 'GET', '/', params={'param': 'value with spaces'})
|
request = self._get_request(
|
||||||
|
con, "GET", "/", params={"param": "value with spaces"}
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEquals('http://localhost:9200/?param=value+with+spaces', request.url)
|
self.assertEquals("http://localhost:9200/?param=value+with+spaces", request.url)
|
||||||
self.assertEquals('GET', request.method)
|
self.assertEquals("GET", request.method)
|
||||||
self.assertEquals(None, request.body)
|
self.assertEquals(None, request.body)
|
||||||
|
|
||||||
def test_body_attached(self):
|
def test_body_attached(self):
|
||||||
con = self._get_mock_connection()
|
con = self._get_mock_connection()
|
||||||
request = self._get_request(con, 'GET', '/', body='{"answer": 42}')
|
request = self._get_request(con, "GET", "/", body='{"answer": 42}')
|
||||||
|
|
||||||
self.assertEquals('http://localhost:9200/', request.url)
|
self.assertEquals("http://localhost:9200/", request.url)
|
||||||
self.assertEquals('GET', request.method)
|
self.assertEquals("GET", request.method)
|
||||||
self.assertEquals('{"answer": 42}'.encode('utf-8'), request.body)
|
self.assertEquals('{"answer": 42}'.encode("utf-8"), request.body)
|
||||||
|
|
||||||
def test_http_auth_attached(self):
|
def test_http_auth_attached(self):
|
||||||
con = self._get_mock_connection({'http_auth': 'username:secret'})
|
con = self._get_mock_connection({"http_auth": "username:secret"})
|
||||||
request = self._get_request(con, 'GET', '/')
|
request = self._get_request(con, "GET", "/")
|
||||||
|
|
||||||
self.assertEquals(request.headers['authorization'], 'Basic dXNlcm5hbWU6c2VjcmV0')
|
self.assertEquals(
|
||||||
|
request.headers["authorization"], "Basic dXNlcm5hbWU6c2VjcmV0"
|
||||||
|
)
|
||||||
|
|
||||||
@patch('elasticsearch.connection.base.tracer')
|
@patch("elasticsearch.connection.base.tracer")
|
||||||
def test_url_prefix(self, tracer):
|
def test_url_prefix(self, tracer):
|
||||||
con = self._get_mock_connection({"url_prefix": "/some-prefix/"})
|
con = self._get_mock_connection({"url_prefix": "/some-prefix/"})
|
||||||
request = self._get_request(con, 'GET', '/_search', body='{"answer": 42}', timeout=0.1)
|
request = self._get_request(
|
||||||
|
con, "GET", "/_search", body='{"answer": 42}', timeout=0.1
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEquals('http://localhost:9200/some-prefix/_search', request.url)
|
self.assertEquals("http://localhost:9200/some-prefix/_search", request.url)
|
||||||
self.assertEquals('GET', request.method)
|
self.assertEquals("GET", request.method)
|
||||||
self.assertEquals('{"answer": 42}'.encode('utf-8'), request.body)
|
self.assertEquals('{"answer": 42}'.encode("utf-8"), request.body)
|
||||||
|
|
||||||
# trace request
|
# trace request
|
||||||
self.assertEquals(1, tracer.info.call_count)
|
self.assertEquals(1, tracer.info.call_count)
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
"curl -H 'Content-Type: application/json' -XGET 'http://localhost:9200/_search?pretty' -d '{\n \"answer\": 42\n}'",
|
"curl -H 'Content-Type: application/json' -XGET 'http://localhost:9200/_search?pretty' -d '{\n \"answer\": 42\n}'",
|
||||||
tracer.info.call_args[0][0] % tracer.info.call_args[0][1:]
|
tracer.info.call_args[0][0] % tracer.info.call_args[0][1:],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from elasticsearch.connection_pool import ConnectionPool, RoundRobinSelector, DummyConnectionPool
|
from elasticsearch.connection_pool import (
|
||||||
|
ConnectionPool,
|
||||||
|
RoundRobinSelector,
|
||||||
|
DummyConnectionPool,
|
||||||
|
)
|
||||||
from elasticsearch.exceptions import ImproperlyConfigured
|
from elasticsearch.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
from .test_cases import TestCase
|
from .test_cases import TestCase
|
||||||
|
|
||||||
|
|
||||||
class TestConnectionPool(TestCase):
|
class TestConnectionPool(TestCase):
|
||||||
def test_dummy_cp_raises_exception_on_more_connections(self):
|
def test_dummy_cp_raises_exception_on_more_connections(self):
|
||||||
self.assertRaises(ImproperlyConfigured, DummyConnectionPool, [])
|
self.assertRaises(ImproperlyConfigured, DummyConnectionPool, [])
|
||||||
self.assertRaises(ImproperlyConfigured, DummyConnectionPool, [object(), object()])
|
self.assertRaises(
|
||||||
|
ImproperlyConfigured, DummyConnectionPool, [object(), object()]
|
||||||
|
)
|
||||||
|
|
||||||
def test_raises_exception_when_no_connections_defined(self):
|
def test_raises_exception_when_no_connections_defined(self):
|
||||||
self.assertRaises(ImproperlyConfigured, ConnectionPool, [])
|
self.assertRaises(ImproperlyConfigured, ConnectionPool, [])
|
||||||
@@ -32,13 +39,20 @@ class TestConnectionPool(TestCase):
|
|||||||
def test_selectors_have_access_to_connection_opts(self):
|
def test_selectors_have_access_to_connection_opts(self):
|
||||||
class MySelector(RoundRobinSelector):
|
class MySelector(RoundRobinSelector):
|
||||||
def select(self, connections):
|
def select(self, connections):
|
||||||
return self.connection_opts[super(MySelector, self).select(connections)]["actual"]
|
return self.connection_opts[
|
||||||
pool = ConnectionPool([(x, {"actual": x*x}) for x in range(100)], selector_class=MySelector, randomize_hosts=False)
|
super(MySelector, self).select(connections)
|
||||||
|
]["actual"]
|
||||||
|
|
||||||
|
pool = ConnectionPool(
|
||||||
|
[(x, {"actual": x * x}) for x in range(100)],
|
||||||
|
selector_class=MySelector,
|
||||||
|
randomize_hosts=False,
|
||||||
|
)
|
||||||
|
|
||||||
connections = []
|
connections = []
|
||||||
for _ in range(100):
|
for _ in range(100):
|
||||||
connections.append(pool.get_connection())
|
connections.append(pool.get_connection())
|
||||||
self.assertEquals(connections, [x*x for x in range(100)])
|
self.assertEquals(connections, [x * x for x in range(100)])
|
||||||
|
|
||||||
def test_dead_nodes_are_removed_from_active_connections(self):
|
def test_dead_nodes_are_removed_from_active_connections(self):
|
||||||
pool = ConnectionPool([(x, {}) for x in range(100)])
|
pool = ConnectionPool([(x, {}) for x in range(100)])
|
||||||
@@ -53,23 +67,26 @@ class TestConnectionPool(TestCase):
|
|||||||
pool = ConnectionPool([(x, {}) for x in range(2)])
|
pool = ConnectionPool([(x, {}) for x in range(2)])
|
||||||
pool.mark_dead(0)
|
pool.mark_dead(0)
|
||||||
|
|
||||||
self.assertEquals([1, 1, 1], [pool.get_connection(), pool.get_connection(), pool.get_connection(), ])
|
self.assertEquals(
|
||||||
|
[1, 1, 1],
|
||||||
|
[pool.get_connection(), pool.get_connection(), pool.get_connection()],
|
||||||
|
)
|
||||||
|
|
||||||
def test_connection_is_forcibly_resurrected_when_no_live_ones_are_availible(self):
|
def test_connection_is_forcibly_resurrected_when_no_live_ones_are_availible(self):
|
||||||
pool = ConnectionPool([(x, {}) for x in range(2)])
|
pool = ConnectionPool([(x, {}) for x in range(2)])
|
||||||
pool.dead_count[0] = 1
|
pool.dead_count[0] = 1
|
||||||
pool.mark_dead(0) # failed twice, longer timeout
|
pool.mark_dead(0) # failed twice, longer timeout
|
||||||
pool.mark_dead(1) # failed the first time, first to be resurrected
|
pool.mark_dead(1) # failed the first time, first to be resurrected
|
||||||
|
|
||||||
self.assertEquals([], pool.connections)
|
self.assertEquals([], pool.connections)
|
||||||
self.assertEquals(1, pool.get_connection())
|
self.assertEquals(1, pool.get_connection())
|
||||||
self.assertEquals([1,], pool.connections)
|
self.assertEquals([1], pool.connections)
|
||||||
|
|
||||||
def test_connection_is_resurrected_after_its_timeout(self):
|
def test_connection_is_resurrected_after_its_timeout(self):
|
||||||
pool = ConnectionPool([(x, {}) for x in range(100)])
|
pool = ConnectionPool([(x, {}) for x in range(100)])
|
||||||
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
pool.mark_dead(42, now=now-61)
|
pool.mark_dead(42, now=now - 61)
|
||||||
pool.get_connection()
|
pool.get_connection()
|
||||||
self.assertEquals(42, pool.connections[-1])
|
self.assertEquals(42, pool.connections[-1])
|
||||||
self.assertEquals(100, len(pool.connections))
|
self.assertEquals(100, len(pool.connections))
|
||||||
@@ -89,7 +106,7 @@ class TestConnectionPool(TestCase):
|
|||||||
pool.mark_dead(42, now=now)
|
pool.mark_dead(42, now=now)
|
||||||
|
|
||||||
self.assertEquals(3, pool.dead_count[42])
|
self.assertEquals(3, pool.dead_count[42])
|
||||||
self.assertEquals((now + 4*60, 42), pool.dead.get())
|
self.assertEquals((now + 4 * 60, 42), pool.dead.get())
|
||||||
|
|
||||||
def test_timeout_for_failed_connections_is_limitted(self):
|
def test_timeout_for_failed_connections_is_limitted(self):
|
||||||
pool = ConnectionPool([(x, {}) for x in range(100)])
|
pool = ConnectionPool([(x, {}) for x in range(100)])
|
||||||
@@ -98,7 +115,7 @@ class TestConnectionPool(TestCase):
|
|||||||
pool.mark_dead(42, now=now)
|
pool.mark_dead(42, now=now)
|
||||||
|
|
||||||
self.assertEquals(246, pool.dead_count[42])
|
self.assertEquals(246, pool.dead_count[42])
|
||||||
self.assertEquals((now + 32*60, 42), pool.dead.get())
|
self.assertEquals((now + 32 * 60, 42), pool.dead.get())
|
||||||
|
|
||||||
def test_dead_count_is_wiped_clean_for_connection_if_marked_live(self):
|
def test_dead_count_is_wiped_clean_for_connection_if_marked_live(self):
|
||||||
pool = ConnectionPool([(x, {}) for x in range(100)])
|
pool = ConnectionPool([(x, {}) for x in range(100)])
|
||||||
@@ -109,4 +126,3 @@ class TestConnectionPool(TestCase):
|
|||||||
self.assertEquals(3, pool.dead_count[42])
|
self.assertEquals(3, pool.dead_count[42])
|
||||||
pool.mark_live(42)
|
pool.mark_live(42)
|
||||||
self.assertNotIn(42, pool.dead_count)
|
self.assertNotIn(42, pool.dead_count)
|
||||||
|
|
||||||
|
|||||||
@@ -5,19 +5,22 @@ from .test_cases import TestCase
|
|||||||
|
|
||||||
class TestTransformError(TestCase):
|
class TestTransformError(TestCase):
|
||||||
def test_transform_error_parse_with_error_reason(self):
|
def test_transform_error_parse_with_error_reason(self):
|
||||||
e = TransportError(500, 'InternalServerError', {
|
e = TransportError(
|
||||||
'error': {
|
500,
|
||||||
'root_cause': [
|
"InternalServerError",
|
||||||
{"type": "error", "reason": "error reason"}
|
{"error": {"root_cause": [{"type": "error", "reason": "error reason"}]}},
|
||||||
]
|
)
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
self.assertEqual(str(e), "TransportError(500, 'InternalServerError', 'error reason')")
|
self.assertEqual(
|
||||||
|
str(e), "TransportError(500, 'InternalServerError', 'error reason')"
|
||||||
|
)
|
||||||
|
|
||||||
def test_transform_error_parse_with_error_string(self):
|
def test_transform_error_parse_with_error_string(self):
|
||||||
e = TransportError(500, 'InternalServerError', {
|
e = TransportError(
|
||||||
'error': 'something error message'
|
500, "InternalServerError", {"error": "something error message"}
|
||||||
})
|
)
|
||||||
|
|
||||||
self.assertEqual(str(e), "TransportError(500, 'InternalServerError', 'something error message')")
|
self.assertEqual(
|
||||||
|
str(e),
|
||||||
|
"TransportError(500, 'InternalServerError', 'something error message')",
|
||||||
|
)
|
||||||
|
|||||||
@@ -5,30 +5,44 @@ import uuid
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from elasticsearch.serializer import JSONSerializer, Deserializer, DEFAULT_SERIALIZERS, TextSerializer
|
from elasticsearch.serializer import (
|
||||||
|
JSONSerializer,
|
||||||
|
Deserializer,
|
||||||
|
DEFAULT_SERIALIZERS,
|
||||||
|
TextSerializer,
|
||||||
|
)
|
||||||
from elasticsearch.exceptions import SerializationError, ImproperlyConfigured
|
from elasticsearch.exceptions import SerializationError, ImproperlyConfigured
|
||||||
|
|
||||||
from .test_cases import TestCase, SkipTest
|
from .test_cases import TestCase, SkipTest
|
||||||
|
|
||||||
|
|
||||||
class TestJSONSerializer(TestCase):
|
class TestJSONSerializer(TestCase):
|
||||||
def test_datetime_serialization(self):
|
def test_datetime_serialization(self):
|
||||||
self.assertEquals('{"d":"2010-10-01T02:30:00"}', JSONSerializer().dumps({'d': datetime(2010, 10, 1, 2, 30)}))
|
self.assertEquals(
|
||||||
|
'{"d":"2010-10-01T02:30:00"}',
|
||||||
|
JSONSerializer().dumps({"d": datetime(2010, 10, 1, 2, 30)}),
|
||||||
|
)
|
||||||
|
|
||||||
def test_decimal_serialization(self):
|
def test_decimal_serialization(self):
|
||||||
if sys.version_info[:2] == (2, 6):
|
if sys.version_info[:2] == (2, 6):
|
||||||
raise SkipTest("Float rounding is broken in 2.6.")
|
raise SkipTest("Float rounding is broken in 2.6.")
|
||||||
self.assertEquals('{"d":3.8}', JSONSerializer().dumps({'d': Decimal('3.8')}))
|
self.assertEquals('{"d":3.8}', JSONSerializer().dumps({"d": Decimal("3.8")}))
|
||||||
|
|
||||||
def test_uuid_serialization(self):
|
def test_uuid_serialization(self):
|
||||||
self.assertEquals('{"d":"00000000-0000-0000-0000-000000000003"}', JSONSerializer().dumps({'d': uuid.UUID('00000000-0000-0000-0000-000000000003')}))
|
self.assertEquals(
|
||||||
|
'{"d":"00000000-0000-0000-0000-000000000003"}',
|
||||||
|
JSONSerializer().dumps(
|
||||||
|
{"d": uuid.UUID("00000000-0000-0000-0000-000000000003")}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def test_raises_serialization_error_on_dump_error(self):
|
def test_raises_serialization_error_on_dump_error(self):
|
||||||
self.assertRaises(SerializationError, JSONSerializer().dumps, object())
|
self.assertRaises(SerializationError, JSONSerializer().dumps, object())
|
||||||
|
|
||||||
def test_raises_serialization_error_on_load_error(self):
|
def test_raises_serialization_error_on_load_error(self):
|
||||||
self.assertRaises(SerializationError, JSONSerializer().loads, object())
|
self.assertRaises(SerializationError, JSONSerializer().loads, object())
|
||||||
self.assertRaises(SerializationError, JSONSerializer().loads, '')
|
self.assertRaises(SerializationError, JSONSerializer().loads, "")
|
||||||
self.assertRaises(SerializationError, JSONSerializer().loads, '{{')
|
self.assertRaises(SerializationError, JSONSerializer().loads, "{{")
|
||||||
|
|
||||||
def test_strings_are_left_untouched(self):
|
def test_strings_are_left_untouched(self):
|
||||||
self.assertEquals("你好", JSONSerializer().dumps("你好"))
|
self.assertEquals("你好", JSONSerializer().dumps("你好"))
|
||||||
@@ -51,11 +65,18 @@ class TestDeserializer(TestCase):
|
|||||||
self.assertEquals({"some": "data"}, self.de.loads('{"some":"data"}'))
|
self.assertEquals({"some": "data"}, self.de.loads('{"some":"data"}'))
|
||||||
|
|
||||||
def test_deserializes_text_with_correct_ct(self):
|
def test_deserializes_text_with_correct_ct(self):
|
||||||
self.assertEquals('{"some":"data"}', self.de.loads('{"some":"data"}', 'text/plain'))
|
self.assertEquals(
|
||||||
self.assertEquals('{"some":"data"}', self.de.loads('{"some":"data"}', 'text/plain; charset=whatever'))
|
'{"some":"data"}', self.de.loads('{"some":"data"}', "text/plain")
|
||||||
|
)
|
||||||
|
self.assertEquals(
|
||||||
|
'{"some":"data"}',
|
||||||
|
self.de.loads('{"some":"data"}', "text/plain; charset=whatever"),
|
||||||
|
)
|
||||||
|
|
||||||
def test_raises_serialization_error_on_unknown_mimetype(self):
|
def test_raises_serialization_error_on_unknown_mimetype(self):
|
||||||
self.assertRaises(SerializationError, self.de.loads, '{}', 'text/html')
|
self.assertRaises(SerializationError, self.de.loads, "{}", "text/html")
|
||||||
|
|
||||||
def test_raises_improperly_configured_when_default_mimetype_cannot_be_deserialized(self):
|
def test_raises_improperly_configured_when_default_mimetype_cannot_be_deserialized(
|
||||||
|
self
|
||||||
|
):
|
||||||
self.assertRaises(ImproperlyConfigured, Deserializer, {})
|
self.assertRaises(ImproperlyConfigured, Deserializer, {})
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
from elasticsearch.helpers.test import get_test_client, ElasticsearchTestCase as BaseTestCase
|
from elasticsearch.helpers.test import (
|
||||||
|
get_test_client,
|
||||||
|
ElasticsearchTestCase as BaseTestCase,
|
||||||
|
)
|
||||||
|
|
||||||
client = None
|
client = None
|
||||||
|
|
||||||
|
|
||||||
def get_client(**kwargs):
|
def get_client(**kwargs):
|
||||||
global client
|
global client
|
||||||
if client is not None and not kwargs:
|
if client is not None and not kwargs:
|
||||||
@@ -10,6 +14,7 @@ def get_client(**kwargs):
|
|||||||
# try and locate manual override in the local environment
|
# try and locate manual override in the local environment
|
||||||
try:
|
try:
|
||||||
from test_elasticsearch.local import get_client as local_get_client
|
from test_elasticsearch.local import get_client as local_get_client
|
||||||
|
|
||||||
new_client = local_get_client(**kwargs)
|
new_client = local_get_client(**kwargs)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# fallback to using vanilla client
|
# fallback to using vanilla client
|
||||||
@@ -24,6 +29,7 @@ def get_client(**kwargs):
|
|||||||
def setup():
|
def setup():
|
||||||
get_client()
|
get_client()
|
||||||
|
|
||||||
|
|
||||||
class ElasticsearchTestCase(BaseTestCase):
|
class ElasticsearchTestCase(BaseTestCase):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_client(**kwargs):
|
def _get_client(**kwargs):
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from . import ElasticsearchTestCase
|
from . import ElasticsearchTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestUnicode(ElasticsearchTestCase):
|
class TestUnicode(ElasticsearchTestCase):
|
||||||
def test_indices_analyze(self):
|
def test_indices_analyze(self):
|
||||||
self.client.indices.analyze(body='{"text": "привет"}')
|
self.client.indices.analyze(body='{"text": "привет"}')
|
||||||
|
|||||||
@@ -310,20 +310,20 @@ class TestBulk(ElasticsearchTestCase):
|
|||||||
class TestScan(ElasticsearchTestCase):
|
class TestScan(ElasticsearchTestCase):
|
||||||
mock_scroll_responses = [
|
mock_scroll_responses = [
|
||||||
{
|
{
|
||||||
'_scroll_id': 'dummy_id',
|
"_scroll_id": "dummy_id",
|
||||||
'_shards': {'successful': 4, 'total': 5},
|
"_shards": {"successful": 4, "total": 5},
|
||||||
'hits': {'hits': [{'scroll_data': 42}]},
|
"hits": {"hits": [{"scroll_data": 42}]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'_scroll_id': 'dummy_id',
|
"_scroll_id": "dummy_id",
|
||||||
'_shards': {'successful': 4, 'total': 5},
|
"_shards": {"successful": 4, "total": 5},
|
||||||
'hits': {'hits': []},
|
"hits": {"hits": []},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
cls.client.transport.perform_request('DELETE', '/_search/scroll/_all')
|
cls.client.transport.perform_request("DELETE", "/_search/scroll/_all")
|
||||||
super(TestScan, cls).tearDownClass()
|
super(TestScan, cls).tearDownClass()
|
||||||
|
|
||||||
def test_order_can_be_preserved(self):
|
def test_order_can_be_preserved(self):
|
||||||
@@ -366,87 +366,101 @@ class TestScan(ElasticsearchTestCase):
|
|||||||
bulk.append({"value": x})
|
bulk.append({"value": x})
|
||||||
self.client.bulk(bulk, refresh=True)
|
self.client.bulk(bulk, refresh=True)
|
||||||
|
|
||||||
with patch.object(self.client, 'scroll') as scroll_mock:
|
with patch.object(self.client, "scroll") as scroll_mock:
|
||||||
scroll_mock.side_effect = self.mock_scroll_responses
|
scroll_mock.side_effect = self.mock_scroll_responses
|
||||||
data = list(helpers.scan(
|
data = list(
|
||||||
self.client,
|
helpers.scan(
|
||||||
index='test_index',
|
self.client,
|
||||||
size=2,
|
index="test_index",
|
||||||
raise_on_error=False,
|
size=2,
|
||||||
clear_scroll=False
|
raise_on_error=False,
|
||||||
))
|
clear_scroll=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
self.assertEqual(len(data), 3)
|
self.assertEqual(len(data), 3)
|
||||||
self.assertEqual(data[-1], {'scroll_data': 42})
|
self.assertEqual(data[-1], {"scroll_data": 42})
|
||||||
|
|
||||||
scroll_mock.side_effect = self.mock_scroll_responses
|
scroll_mock.side_effect = self.mock_scroll_responses
|
||||||
with self.assertRaises(ScanError):
|
with self.assertRaises(ScanError):
|
||||||
data = list(helpers.scan(
|
data = list(
|
||||||
self.client,
|
helpers.scan(
|
||||||
index='test_index',
|
self.client,
|
||||||
size=2,
|
index="test_index",
|
||||||
raise_on_error=True,
|
size=2,
|
||||||
clear_scroll=False
|
raise_on_error=True,
|
||||||
))
|
clear_scroll=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
self.assertEqual(len(data), 3)
|
self.assertEqual(len(data), 3)
|
||||||
self.assertEqual(data[-1], {'scroll_data': 42})
|
self.assertEqual(data[-1], {"scroll_data": 42})
|
||||||
|
|
||||||
def test_initial_search_error(self):
|
def test_initial_search_error(self):
|
||||||
with patch.object(self, 'client') as client_mock:
|
with patch.object(self, "client") as client_mock:
|
||||||
client_mock.search.return_value = {
|
client_mock.search.return_value = {
|
||||||
'_scroll_id': 'dummy_id',
|
"_scroll_id": "dummy_id",
|
||||||
'_shards': {'successful': 4, 'total': 5},
|
"_shards": {"successful": 4, "total": 5},
|
||||||
'hits': {'hits': [{'search_data': 1}]},
|
"hits": {"hits": [{"search_data": 1}]},
|
||||||
}
|
}
|
||||||
client_mock.scroll.side_effect = self.mock_scroll_responses
|
client_mock.scroll.side_effect = self.mock_scroll_responses
|
||||||
|
|
||||||
data = list(helpers.scan(self.client, index='test_index', size=2, raise_on_error=False))
|
data = list(
|
||||||
self.assertEqual(data, [{'search_data': 1}, {'scroll_data': 42}])
|
helpers.scan(
|
||||||
|
self.client, index="test_index", size=2, raise_on_error=False
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertEqual(data, [{"search_data": 1}, {"scroll_data": 42}])
|
||||||
|
|
||||||
client_mock.scroll.side_effect = self.mock_scroll_responses
|
client_mock.scroll.side_effect = self.mock_scroll_responses
|
||||||
with self.assertRaises(ScanError):
|
with self.assertRaises(ScanError):
|
||||||
data = list(
|
data = list(
|
||||||
helpers.scan(self.client, index='test_index', size=2, raise_on_error=True)
|
helpers.scan(
|
||||||
|
self.client, index="test_index", size=2, raise_on_error=True
|
||||||
|
)
|
||||||
)
|
)
|
||||||
self.assertEqual(data, [{'search_data': 1}])
|
self.assertEqual(data, [{"search_data": 1}])
|
||||||
client_mock.scroll.assert_not_called()
|
client_mock.scroll.assert_not_called()
|
||||||
|
|
||||||
def test_no_scroll_id_fast_route(self):
|
def test_no_scroll_id_fast_route(self):
|
||||||
with patch.object(self, 'client') as client_mock:
|
with patch.object(self, "client") as client_mock:
|
||||||
client_mock.search.return_value = {'no': '_scroll_id'}
|
client_mock.search.return_value = {"no": "_scroll_id"}
|
||||||
data = list(helpers.scan(self.client, index='test_index'))
|
data = list(helpers.scan(self.client, index="test_index"))
|
||||||
|
|
||||||
self.assertEqual(data, [])
|
self.assertEqual(data, [])
|
||||||
client_mock.scroll.assert_not_called()
|
client_mock.scroll.assert_not_called()
|
||||||
client_mock.clear_scroll.assert_not_called()
|
client_mock.clear_scroll.assert_not_called()
|
||||||
|
|
||||||
@patch('elasticsearch.helpers.actions.logger')
|
@patch("elasticsearch.helpers.actions.logger")
|
||||||
def test_logger(self, logger_mock):
|
def test_logger(self, logger_mock):
|
||||||
bulk = []
|
bulk = []
|
||||||
for x in range(4):
|
for x in range(4):
|
||||||
bulk.append({'index': {'_index': 'test_index', '_type': '_doc'}})
|
bulk.append({"index": {"_index": "test_index", "_type": "_doc"}})
|
||||||
bulk.append({'value': x})
|
bulk.append({"value": x})
|
||||||
self.client.bulk(bulk, refresh=True)
|
self.client.bulk(bulk, refresh=True)
|
||||||
|
|
||||||
with patch.object(self.client, 'scroll') as scroll_mock:
|
with patch.object(self.client, "scroll") as scroll_mock:
|
||||||
scroll_mock.side_effect = self.mock_scroll_responses
|
scroll_mock.side_effect = self.mock_scroll_responses
|
||||||
list(helpers.scan(
|
list(
|
||||||
self.client,
|
helpers.scan(
|
||||||
index='test_index',
|
self.client,
|
||||||
size=2,
|
index="test_index",
|
||||||
raise_on_error=False,
|
size=2,
|
||||||
clear_scroll=False
|
raise_on_error=False,
|
||||||
))
|
clear_scroll=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
logger_mock.warning.assert_called()
|
logger_mock.warning.assert_called()
|
||||||
|
|
||||||
scroll_mock.side_effect = self.mock_scroll_responses
|
scroll_mock.side_effect = self.mock_scroll_responses
|
||||||
try:
|
try:
|
||||||
list(helpers.scan(
|
list(
|
||||||
self.client,
|
helpers.scan(
|
||||||
index='test_index',
|
self.client,
|
||||||
size=2,
|
index="test_index",
|
||||||
raise_on_error=True,
|
size=2,
|
||||||
clear_scroll=False
|
raise_on_error=True,
|
||||||
))
|
clear_scroll=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
except ScanError:
|
except ScanError:
|
||||||
pass
|
pass
|
||||||
logger_mock.warning.assert_called()
|
logger_mock.warning.assert_called()
|
||||||
@@ -454,20 +468,28 @@ class TestScan(ElasticsearchTestCase):
|
|||||||
def test_clear_scroll(self):
|
def test_clear_scroll(self):
|
||||||
bulk = []
|
bulk = []
|
||||||
for x in range(4):
|
for x in range(4):
|
||||||
bulk.append({'index': {'_index': 'test_index', '_type': '_doc'}})
|
bulk.append({"index": {"_index": "test_index", "_type": "_doc"}})
|
||||||
bulk.append({'value': x})
|
bulk.append({"value": x})
|
||||||
self.client.bulk(bulk, refresh=True)
|
self.client.bulk(bulk, refresh=True)
|
||||||
|
|
||||||
with patch.object(self.client, 'clear_scroll', wraps=self.client.clear_scroll) as spy:
|
with patch.object(
|
||||||
list(helpers.scan(self.client, index='test_index', size=2))
|
self.client, "clear_scroll", wraps=self.client.clear_scroll
|
||||||
|
) as spy:
|
||||||
|
list(helpers.scan(self.client, index="test_index", size=2))
|
||||||
spy.assert_called_once()
|
spy.assert_called_once()
|
||||||
|
|
||||||
spy.reset_mock()
|
spy.reset_mock()
|
||||||
list(helpers.scan(self.client, index='test_index', size=2, clear_scroll=True))
|
list(
|
||||||
|
helpers.scan(self.client, index="test_index", size=2, clear_scroll=True)
|
||||||
|
)
|
||||||
spy.assert_called_once()
|
spy.assert_called_once()
|
||||||
|
|
||||||
spy.reset_mock()
|
spy.reset_mock()
|
||||||
list(helpers.scan(self.client, index='test_index', size=2, clear_scroll=False))
|
list(
|
||||||
|
helpers.scan(
|
||||||
|
self.client, index="test_index", size=2, clear_scroll=False
|
||||||
|
)
|
||||||
|
)
|
||||||
spy.assert_not_called()
|
spy.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ from elasticsearch.exceptions import ConnectionError, ImproperlyConfigured
|
|||||||
|
|
||||||
from .test_cases import TestCase
|
from .test_cases import TestCase
|
||||||
|
|
||||||
|
|
||||||
class DummyConnection(Connection):
|
class DummyConnection(Connection):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.exception = kwargs.pop('exception', None)
|
self.exception = kwargs.pop("exception", None)
|
||||||
self.status, self.data = kwargs.pop('status', 200), kwargs.pop('data', '{}')
|
self.status, self.data = kwargs.pop("status", 200), kwargs.pop("data", "{}")
|
||||||
self.headers = kwargs.pop('headers', {})
|
self.headers = kwargs.pop("headers", {})
|
||||||
self.calls = []
|
self.calls = []
|
||||||
super(DummyConnection, self).__init__(**kwargs)
|
super(DummyConnection, self).__init__(**kwargs)
|
||||||
|
|
||||||
@@ -23,7 +24,8 @@ class DummyConnection(Connection):
|
|||||||
raise self.exception
|
raise self.exception
|
||||||
return self.status, self.headers, self.data
|
return self.status, self.headers, self.data
|
||||||
|
|
||||||
CLUSTER_NODES = '''{
|
|
||||||
|
CLUSTER_NODES = """{
|
||||||
"_nodes" : {
|
"_nodes" : {
|
||||||
"total" : 1,
|
"total" : 1,
|
||||||
"successful" : 1,
|
"successful" : 1,
|
||||||
@@ -46,18 +48,23 @@ CLUSTER_NODES = '''{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}'''
|
}"""
|
||||||
|
|
||||||
|
|
||||||
class TestHostsInfoCallback(TestCase):
|
class TestHostsInfoCallback(TestCase):
|
||||||
def test_master_only_nodes_are_ignored(self):
|
def test_master_only_nodes_are_ignored(self):
|
||||||
nodes = [
|
nodes = [
|
||||||
{'roles': [ "master"]},
|
{"roles": ["master"]},
|
||||||
{'roles': [ "master", "data", "ingest"]},
|
{"roles": ["master", "data", "ingest"]},
|
||||||
{'roles': [ "data", "ingest"]},
|
{"roles": ["data", "ingest"]},
|
||||||
{'roles': [ ]},
|
{"roles": []},
|
||||||
{}
|
{},
|
||||||
|
]
|
||||||
|
chosen = [
|
||||||
|
i
|
||||||
|
for i, node_info in enumerate(nodes)
|
||||||
|
if get_host_info(node_info, i) is not None
|
||||||
]
|
]
|
||||||
chosen = [i for i, node_info in enumerate(nodes) if get_host_info(node_info, i) is not None]
|
|
||||||
self.assertEquals([1, 2, 3, 4], chosen)
|
self.assertEquals([1, 2, 3, 4], chosen)
|
||||||
|
|
||||||
|
|
||||||
@@ -65,57 +72,70 @@ class TestTransport(TestCase):
|
|||||||
def test_single_connection_uses_dummy_connection_pool(self):
|
def test_single_connection_uses_dummy_connection_pool(self):
|
||||||
t = Transport([{}])
|
t = Transport([{}])
|
||||||
self.assertIsInstance(t.connection_pool, DummyConnectionPool)
|
self.assertIsInstance(t.connection_pool, DummyConnectionPool)
|
||||||
t = Transport([{'host': 'localhost'}])
|
t = Transport([{"host": "localhost"}])
|
||||||
self.assertIsInstance(t.connection_pool, DummyConnectionPool)
|
self.assertIsInstance(t.connection_pool, DummyConnectionPool)
|
||||||
|
|
||||||
def test_request_timeout_extracted_from_params_and_passed(self):
|
def test_request_timeout_extracted_from_params_and_passed(self):
|
||||||
t = Transport([{}], connection_class=DummyConnection)
|
t = Transport([{}], connection_class=DummyConnection)
|
||||||
|
|
||||||
t.perform_request('GET', '/', params={'request_timeout': 42})
|
t.perform_request("GET", "/", params={"request_timeout": 42})
|
||||||
self.assertEquals(1, len(t.get_connection().calls))
|
self.assertEquals(1, len(t.get_connection().calls))
|
||||||
self.assertEquals(('GET', '/', {}, None), t.get_connection().calls[0][0])
|
self.assertEquals(("GET", "/", {}, None), t.get_connection().calls[0][0])
|
||||||
self.assertEquals({'timeout': 42, 'ignore': (), 'headers': None}, t.get_connection().calls[0][1])
|
self.assertEquals(
|
||||||
|
{"timeout": 42, "ignore": (), "headers": None},
|
||||||
|
t.get_connection().calls[0][1],
|
||||||
|
)
|
||||||
|
|
||||||
def test_send_get_body_as_source(self):
|
def test_send_get_body_as_source(self):
|
||||||
t = Transport([{}], send_get_body_as='source', connection_class=DummyConnection)
|
t = Transport([{}], send_get_body_as="source", connection_class=DummyConnection)
|
||||||
|
|
||||||
t.perform_request('GET', '/', body={})
|
t.perform_request("GET", "/", body={})
|
||||||
self.assertEquals(1, len(t.get_connection().calls))
|
self.assertEquals(1, len(t.get_connection().calls))
|
||||||
self.assertEquals(('GET', '/', {'source': '{}'}, None), t.get_connection().calls[0][0])
|
self.assertEquals(
|
||||||
|
("GET", "/", {"source": "{}"}, None), t.get_connection().calls[0][0]
|
||||||
|
)
|
||||||
|
|
||||||
def test_send_get_body_as_post(self):
|
def test_send_get_body_as_post(self):
|
||||||
t = Transport([{}], send_get_body_as='POST', connection_class=DummyConnection)
|
t = Transport([{}], send_get_body_as="POST", connection_class=DummyConnection)
|
||||||
|
|
||||||
t.perform_request('GET', '/', body={})
|
t.perform_request("GET", "/", body={})
|
||||||
self.assertEquals(1, len(t.get_connection().calls))
|
self.assertEquals(1, len(t.get_connection().calls))
|
||||||
self.assertEquals(('POST', '/', None, b'{}'), t.get_connection().calls[0][0])
|
self.assertEquals(("POST", "/", None, b"{}"), t.get_connection().calls[0][0])
|
||||||
|
|
||||||
def test_body_gets_encoded_into_bytes(self):
|
def test_body_gets_encoded_into_bytes(self):
|
||||||
t = Transport([{}], connection_class=DummyConnection)
|
t = Transport([{}], connection_class=DummyConnection)
|
||||||
|
|
||||||
t.perform_request('GET', '/', body='你好')
|
t.perform_request("GET", "/", body="你好")
|
||||||
self.assertEquals(1, len(t.get_connection().calls))
|
self.assertEquals(1, len(t.get_connection().calls))
|
||||||
self.assertEquals(('GET', '/', None, b'\xe4\xbd\xa0\xe5\xa5\xbd'), t.get_connection().calls[0][0])
|
self.assertEquals(
|
||||||
|
("GET", "/", None, b"\xe4\xbd\xa0\xe5\xa5\xbd"),
|
||||||
|
t.get_connection().calls[0][0],
|
||||||
|
)
|
||||||
|
|
||||||
def test_body_bytes_get_passed_untouched(self):
|
def test_body_bytes_get_passed_untouched(self):
|
||||||
t = Transport([{}], connection_class=DummyConnection)
|
t = Transport([{}], connection_class=DummyConnection)
|
||||||
|
|
||||||
body = b'\xe4\xbd\xa0\xe5\xa5\xbd'
|
body = b"\xe4\xbd\xa0\xe5\xa5\xbd"
|
||||||
t.perform_request('GET', '/', body=body)
|
t.perform_request("GET", "/", body=body)
|
||||||
self.assertEquals(1, len(t.get_connection().calls))
|
self.assertEquals(1, len(t.get_connection().calls))
|
||||||
self.assertEquals(('GET', '/', None, body), t.get_connection().calls[0][0])
|
self.assertEquals(("GET", "/", None, body), t.get_connection().calls[0][0])
|
||||||
|
|
||||||
def test_body_surrogates_replaced_encoded_into_bytes(self):
|
def test_body_surrogates_replaced_encoded_into_bytes(self):
|
||||||
t = Transport([{}], connection_class=DummyConnection)
|
t = Transport([{}], connection_class=DummyConnection)
|
||||||
|
|
||||||
t.perform_request('GET', '/', body='你好\uda6a')
|
t.perform_request("GET", "/", body="你好\uda6a")
|
||||||
self.assertEquals(1, len(t.get_connection().calls))
|
self.assertEquals(1, len(t.get_connection().calls))
|
||||||
self.assertEquals(('GET', '/', None, b'\xe4\xbd\xa0\xe5\xa5\xbd\xed\xa9\xaa'), t.get_connection().calls[0][0])
|
self.assertEquals(
|
||||||
|
("GET", "/", None, b"\xe4\xbd\xa0\xe5\xa5\xbd\xed\xa9\xaa"),
|
||||||
|
t.get_connection().calls[0][0],
|
||||||
|
)
|
||||||
|
|
||||||
def test_kwargs_passed_on_to_connections(self):
|
def test_kwargs_passed_on_to_connections(self):
|
||||||
t = Transport([{'host': 'google.com'}], port=123)
|
t = Transport([{"host": "google.com"}], port=123)
|
||||||
self.assertEquals(1, len(t.connection_pool.connections))
|
self.assertEquals(1, len(t.connection_pool.connections))
|
||||||
self.assertEquals('http://google.com:123', t.connection_pool.connections[0].host)
|
self.assertEquals(
|
||||||
|
"http://google.com:123", t.connection_pool.connections[0].host
|
||||||
|
)
|
||||||
|
|
||||||
def test_kwargs_passed_on_to_connection_pool(self):
|
def test_kwargs_passed_on_to_connection_pool(self):
|
||||||
dt = object()
|
dt = object()
|
||||||
@@ -126,6 +146,7 @@ class TestTransport(TestCase):
|
|||||||
class MyConnection(object):
|
class MyConnection(object):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
|
||||||
t = Transport([{}], connection_class=MyConnection)
|
t = Transport([{}], connection_class=MyConnection)
|
||||||
self.assertEquals(1, len(t.connection_pool.connections))
|
self.assertEquals(1, len(t.connection_pool.connections))
|
||||||
self.assertIsInstance(t.connection_pool.connections[0], MyConnection)
|
self.assertIsInstance(t.connection_pool.connections[0], MyConnection)
|
||||||
@@ -135,18 +156,26 @@ class TestTransport(TestCase):
|
|||||||
t.add_connection({"host": "google.com", "port": 1234})
|
t.add_connection({"host": "google.com", "port": 1234})
|
||||||
|
|
||||||
self.assertEquals(2, len(t.connection_pool.connections))
|
self.assertEquals(2, len(t.connection_pool.connections))
|
||||||
self.assertEquals('http://google.com:1234', t.connection_pool.connections[1].host)
|
self.assertEquals(
|
||||||
|
"http://google.com:1234", t.connection_pool.connections[1].host
|
||||||
|
)
|
||||||
|
|
||||||
def test_request_will_fail_after_X_retries(self):
|
def test_request_will_fail_after_X_retries(self):
|
||||||
t = Transport([{'exception': ConnectionError('abandon ship')}], connection_class=DummyConnection)
|
t = Transport(
|
||||||
|
[{"exception": ConnectionError("abandon ship")}],
|
||||||
|
connection_class=DummyConnection,
|
||||||
|
)
|
||||||
|
|
||||||
self.assertRaises(ConnectionError, t.perform_request, 'GET', '/')
|
self.assertRaises(ConnectionError, t.perform_request, "GET", "/")
|
||||||
self.assertEquals(4, len(t.get_connection().calls))
|
self.assertEquals(4, len(t.get_connection().calls))
|
||||||
|
|
||||||
def test_failed_connection_will_be_marked_as_dead(self):
|
def test_failed_connection_will_be_marked_as_dead(self):
|
||||||
t = Transport([{'exception': ConnectionError('abandon ship')}] * 2, connection_class=DummyConnection)
|
t = Transport(
|
||||||
|
[{"exception": ConnectionError("abandon ship")}] * 2,
|
||||||
|
connection_class=DummyConnection,
|
||||||
|
)
|
||||||
|
|
||||||
self.assertRaises(ConnectionError, t.perform_request, 'GET', '/')
|
self.assertRaises(ConnectionError, t.perform_request, "GET", "/")
|
||||||
self.assertEquals(0, len(t.connection_pool.connections))
|
self.assertEquals(0, len(t.connection_pool.connections))
|
||||||
|
|
||||||
def test_resurrected_connection_will_be_marked_as_live_on_success(self):
|
def test_resurrected_connection_will_be_marked_as_live_on_success(self):
|
||||||
@@ -156,35 +185,57 @@ class TestTransport(TestCase):
|
|||||||
t.connection_pool.mark_dead(con1)
|
t.connection_pool.mark_dead(con1)
|
||||||
t.connection_pool.mark_dead(con2)
|
t.connection_pool.mark_dead(con2)
|
||||||
|
|
||||||
t.perform_request('GET', '/')
|
t.perform_request("GET", "/")
|
||||||
self.assertEquals(1, len(t.connection_pool.connections))
|
self.assertEquals(1, len(t.connection_pool.connections))
|
||||||
self.assertEquals(1, len(t.connection_pool.dead_count))
|
self.assertEquals(1, len(t.connection_pool.dead_count))
|
||||||
|
|
||||||
def test_sniff_will_use_seed_connections(self):
|
def test_sniff_will_use_seed_connections(self):
|
||||||
t = Transport([{'data': CLUSTER_NODES}], connection_class=DummyConnection)
|
t = Transport([{"data": CLUSTER_NODES}], connection_class=DummyConnection)
|
||||||
t.set_connections([{'data': 'invalid'}])
|
t.set_connections([{"data": "invalid"}])
|
||||||
|
|
||||||
t.sniff_hosts()
|
t.sniff_hosts()
|
||||||
self.assertEquals(1, len(t.connection_pool.connections))
|
self.assertEquals(1, len(t.connection_pool.connections))
|
||||||
self.assertEquals('http://1.1.1.1:123', t.get_connection().host)
|
self.assertEquals("http://1.1.1.1:123", t.get_connection().host)
|
||||||
|
|
||||||
def test_sniff_on_start_fetches_and_uses_nodes_list(self):
|
def test_sniff_on_start_fetches_and_uses_nodes_list(self):
|
||||||
t = Transport([{'data': CLUSTER_NODES}], connection_class=DummyConnection, sniff_on_start=True)
|
t = Transport(
|
||||||
|
[{"data": CLUSTER_NODES}],
|
||||||
|
connection_class=DummyConnection,
|
||||||
|
sniff_on_start=True,
|
||||||
|
)
|
||||||
self.assertEquals(1, len(t.connection_pool.connections))
|
self.assertEquals(1, len(t.connection_pool.connections))
|
||||||
self.assertEquals('http://1.1.1.1:123', t.get_connection().host)
|
self.assertEquals("http://1.1.1.1:123", t.get_connection().host)
|
||||||
|
|
||||||
def test_sniff_on_start_ignores_sniff_timeout(self):
|
def test_sniff_on_start_ignores_sniff_timeout(self):
|
||||||
t = Transport([{'data': CLUSTER_NODES}], connection_class=DummyConnection, sniff_on_start=True, sniff_timeout=12)
|
t = Transport(
|
||||||
self.assertEquals((('GET', '/_nodes/_all/http'), {'timeout': None}), t.seed_connections[0].calls[0])
|
[{"data": CLUSTER_NODES}],
|
||||||
|
connection_class=DummyConnection,
|
||||||
|
sniff_on_start=True,
|
||||||
|
sniff_timeout=12,
|
||||||
|
)
|
||||||
|
self.assertEquals(
|
||||||
|
(("GET", "/_nodes/_all/http"), {"timeout": None}),
|
||||||
|
t.seed_connections[0].calls[0],
|
||||||
|
)
|
||||||
|
|
||||||
def test_sniff_uses_sniff_timeout(self):
|
def test_sniff_uses_sniff_timeout(self):
|
||||||
t = Transport([{'data': CLUSTER_NODES}], connection_class=DummyConnection, sniff_timeout=42)
|
t = Transport(
|
||||||
|
[{"data": CLUSTER_NODES}],
|
||||||
|
connection_class=DummyConnection,
|
||||||
|
sniff_timeout=42,
|
||||||
|
)
|
||||||
t.sniff_hosts()
|
t.sniff_hosts()
|
||||||
self.assertEquals((('GET', '/_nodes/_all/http'), {'timeout': 42}), t.seed_connections[0].calls[0])
|
self.assertEquals(
|
||||||
|
(("GET", "/_nodes/_all/http"), {"timeout": 42}),
|
||||||
|
t.seed_connections[0].calls[0],
|
||||||
|
)
|
||||||
|
|
||||||
def test_sniff_reuses_connection_instances_if_possible(self):
|
def test_sniff_reuses_connection_instances_if_possible(self):
|
||||||
t = Transport([{'data': CLUSTER_NODES}, {"host": "1.1.1.1", "port": 123}], connection_class=DummyConnection, randomize_hosts=False)
|
t = Transport(
|
||||||
|
[{"data": CLUSTER_NODES}, {"host": "1.1.1.1", "port": 123}],
|
||||||
|
connection_class=DummyConnection,
|
||||||
|
randomize_hosts=False,
|
||||||
|
)
|
||||||
connection = t.connection_pool.connections[1]
|
connection = t.connection_pool.connections[1]
|
||||||
|
|
||||||
t.sniff_hosts()
|
t.sniff_hosts()
|
||||||
@@ -192,25 +243,32 @@ class TestTransport(TestCase):
|
|||||||
self.assertIs(connection, t.get_connection())
|
self.assertIs(connection, t.get_connection())
|
||||||
|
|
||||||
def test_sniff_on_fail_triggers_sniffing_on_fail(self):
|
def test_sniff_on_fail_triggers_sniffing_on_fail(self):
|
||||||
t = Transport([{'exception': ConnectionError('abandon ship')}, {"data": CLUSTER_NODES}],
|
t = Transport(
|
||||||
connection_class=DummyConnection, sniff_on_connection_fail=True, max_retries=0, randomize_hosts=False)
|
[{"exception": ConnectionError("abandon ship")}, {"data": CLUSTER_NODES}],
|
||||||
|
connection_class=DummyConnection,
|
||||||
|
sniff_on_connection_fail=True,
|
||||||
|
max_retries=0,
|
||||||
|
randomize_hosts=False,
|
||||||
|
)
|
||||||
|
|
||||||
self.assertRaises(ConnectionError, t.perform_request, 'GET', '/')
|
self.assertRaises(ConnectionError, t.perform_request, "GET", "/")
|
||||||
self.assertEquals(1, len(t.connection_pool.connections))
|
self.assertEquals(1, len(t.connection_pool.connections))
|
||||||
self.assertEquals('http://1.1.1.1:123', t.get_connection().host)
|
self.assertEquals("http://1.1.1.1:123", t.get_connection().host)
|
||||||
|
|
||||||
def test_sniff_after_n_seconds(self):
|
def test_sniff_after_n_seconds(self):
|
||||||
t = Transport([{"data": CLUSTER_NODES}],
|
t = Transport(
|
||||||
connection_class=DummyConnection, sniffer_timeout=5)
|
[{"data": CLUSTER_NODES}],
|
||||||
|
connection_class=DummyConnection,
|
||||||
|
sniffer_timeout=5,
|
||||||
|
)
|
||||||
|
|
||||||
for _ in range(4):
|
for _ in range(4):
|
||||||
t.perform_request('GET', '/')
|
t.perform_request("GET", "/")
|
||||||
self.assertEquals(1, len(t.connection_pool.connections))
|
self.assertEquals(1, len(t.connection_pool.connections))
|
||||||
self.assertIsInstance(t.get_connection(), DummyConnection)
|
self.assertIsInstance(t.get_connection(), DummyConnection)
|
||||||
t.last_sniff = time.time() - 5.1
|
t.last_sniff = time.time() - 5.1
|
||||||
|
|
||||||
t.perform_request('GET', '/')
|
t.perform_request("GET", "/")
|
||||||
self.assertEquals(1, len(t.connection_pool.connections))
|
self.assertEquals(1, len(t.connection_pool.connections))
|
||||||
self.assertEquals('http://1.1.1.1:123', t.get_connection().host)
|
self.assertEquals("http://1.1.1.1:123", t.get_connection().host)
|
||||||
self.assertTrue(time.time() - 1 < t.last_sniff < time.time() + 0.01 )
|
self.assertTrue(time.time() - 1 < t.last_sniff < time.time() + 0.01)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user