hello.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. # Copyright 2021-present MongoDB, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Helpers for the 'hello' and legacy hello commands."""
  15. import itertools
  16. from bson.py3compat import imap
  17. from pymongo import common
  18. from pymongo.hello_compat import HelloCompat
  19. from pymongo.server_type import SERVER_TYPE
  20. def _get_server_type(doc):
  21. """Determine the server type from a hello response."""
  22. if not doc.get('ok'):
  23. return SERVER_TYPE.Unknown
  24. if doc.get('serviceId'):
  25. return SERVER_TYPE.LoadBalancer
  26. elif doc.get('isreplicaset'):
  27. return SERVER_TYPE.RSGhost
  28. elif doc.get('setName'):
  29. if doc.get('hidden'):
  30. return SERVER_TYPE.RSOther
  31. elif doc.get(HelloCompat.PRIMARY):
  32. return SERVER_TYPE.RSPrimary
  33. elif doc.get(HelloCompat.LEGACY_PRIMARY):
  34. return SERVER_TYPE.RSPrimary
  35. elif doc.get('secondary'):
  36. return SERVER_TYPE.RSSecondary
  37. elif doc.get('arbiterOnly'):
  38. return SERVER_TYPE.RSArbiter
  39. else:
  40. return SERVER_TYPE.RSOther
  41. elif doc.get('msg') == 'isdbgrid':
  42. return SERVER_TYPE.Mongos
  43. else:
  44. return SERVER_TYPE.Standalone
  45. class Hello(object):
  46. """Parse a hello response from the server."""
  47. __slots__ = ('_doc', '_server_type', '_is_writable', '_is_readable',
  48. '_awaitable')
  49. def __init__(self, doc, awaitable=False):
  50. self._server_type = _get_server_type(doc)
  51. self._doc = doc
  52. self._is_writable = self._server_type in (
  53. SERVER_TYPE.RSPrimary,
  54. SERVER_TYPE.Standalone,
  55. SERVER_TYPE.Mongos,
  56. SERVER_TYPE.LoadBalancer)
  57. self._is_readable = (
  58. self.server_type == SERVER_TYPE.RSSecondary
  59. or self._is_writable)
  60. self._awaitable = awaitable
  61. @property
  62. def document(self):
  63. """The complete hello command response document.
  64. .. versionadded:: 3.4
  65. """
  66. return self._doc.copy()
  67. @property
  68. def server_type(self):
  69. return self._server_type
  70. @property
  71. def all_hosts(self):
  72. """List of hosts, passives, and arbiters known to this server."""
  73. return set(imap(common.clean_node, itertools.chain(
  74. self._doc.get('hosts', []),
  75. self._doc.get('passives', []),
  76. self._doc.get('arbiters', []))))
  77. @property
  78. def tags(self):
  79. """Replica set member tags or empty dict."""
  80. return self._doc.get('tags', {})
  81. @property
  82. def primary(self):
  83. """This server's opinion about who the primary is, or None."""
  84. if self._doc.get('primary'):
  85. return common.partition_node(self._doc['primary'])
  86. else:
  87. return None
  88. @property
  89. def replica_set_name(self):
  90. """Replica set name or None."""
  91. return self._doc.get('setName')
  92. @property
  93. def max_bson_size(self):
  94. return self._doc.get('maxBsonObjectSize', common.MAX_BSON_SIZE)
  95. @property
  96. def max_message_size(self):
  97. return self._doc.get('maxMessageSizeBytes', 2 * self.max_bson_size)
  98. @property
  99. def max_write_batch_size(self):
  100. return self._doc.get('maxWriteBatchSize', common.MAX_WRITE_BATCH_SIZE)
  101. @property
  102. def min_wire_version(self):
  103. return self._doc.get('minWireVersion', common.MIN_WIRE_VERSION)
  104. @property
  105. def max_wire_version(self):
  106. return self._doc.get('maxWireVersion', common.MAX_WIRE_VERSION)
  107. @property
  108. def set_version(self):
  109. return self._doc.get('setVersion')
  110. @property
  111. def election_id(self):
  112. return self._doc.get('electionId')
  113. @property
  114. def cluster_time(self):
  115. return self._doc.get('$clusterTime')
  116. @property
  117. def logical_session_timeout_minutes(self):
  118. return self._doc.get('logicalSessionTimeoutMinutes')
  119. @property
  120. def is_writable(self):
  121. return self._is_writable
  122. @property
  123. def is_readable(self):
  124. return self._is_readable
  125. @property
  126. def me(self):
  127. me = self._doc.get('me')
  128. if me:
  129. return common.clean_node(me)
  130. @property
  131. def last_write_date(self):
  132. return self._doc.get('lastWrite', {}).get('lastWriteDate')
  133. @property
  134. def compressors(self):
  135. return self._doc.get('compression')
  136. @property
  137. def sasl_supported_mechs(self):
  138. """Supported authentication mechanisms for the current user.
  139. For example::
  140. >>> hello.sasl_supported_mechs
  141. ["SCRAM-SHA-1", "SCRAM-SHA-256"]
  142. """
  143. return self._doc.get('saslSupportedMechs', [])
  144. @property
  145. def speculative_authenticate(self):
  146. """The speculativeAuthenticate field."""
  147. return self._doc.get('speculativeAuthenticate')
  148. @property
  149. def topology_version(self):
  150. return self._doc.get('topologyVersion')
  151. @property
  152. def awaitable(self):
  153. return self._awaitable
  154. @property
  155. def service_id(self):
  156. return self._doc.get('serviceId')
  157. @property
  158. def hello_ok(self):
  159. return self._doc.get('helloOk', False)